|
1 | 1 | from datetime import UTC, date, datetime, timedelta |
2 | 2 |
|
3 | 3 | import pytest |
4 | | -from django.conf import settings |
5 | 4 | from django.utils import timezone |
6 | 5 | from pytest_django.fixtures import SettingsWrapper |
7 | 6 | from pytest_mock import MockerFixture |
|
14 | 13 | get_usage_data, |
15 | 14 | get_usage_data_from_local_db, |
16 | 15 | ) |
17 | | -from app_analytics.constants import ( |
18 | | - CURRENT_BILLING_PERIOD, |
19 | | - PREVIOUS_BILLING_PERIOD, |
20 | | -) |
| 16 | +from app_analytics.constants import CURRENT_BILLING_PERIOD, PREVIOUS_BILLING_PERIOD |
| 17 | +from app_analytics.dataclasses import FeatureEvaluationData, UsageData |
21 | 18 | from app_analytics.models import ( |
22 | 19 | APIUsageBucket, |
23 | 20 | FeatureEvaluationBucket, |
@@ -47,10 +44,7 @@ def cache(organisation: Organisation) -> OrganisationSubscriptionInformationCach |
47 | 44 | ) |
48 | 45 |
|
49 | 46 |
|
50 | | -@pytest.mark.skipif( |
51 | | - "analytics" not in settings.DATABASES, |
52 | | - reason="Skip test if analytics database is configured", |
53 | | -) |
| 47 | +@pytest.mark.skip_if_no_analytics_db |
54 | 48 | @pytest.mark.django_db(databases=["analytics", "default"]) |
55 | 49 | def test_get_usage_data_from_local_db(organisation, environment, settings): # type: ignore[no-untyped-def] |
56 | 50 | environment_id = environment.id |
@@ -108,10 +102,7 @@ def test_get_usage_data_from_local_db(organisation, environment, settings): # t |
108 | 102 | assert data.day == today - timedelta(days=29 - count) |
109 | 103 |
|
110 | 104 |
|
111 | | -@pytest.mark.skipif( |
112 | | - "analytics" not in settings.DATABASES, |
113 | | - reason="Skip test if analytics database is configured", |
114 | | -) |
| 105 | +@pytest.mark.skip_if_no_analytics_db |
115 | 106 | @pytest.mark.django_db(databases=["analytics", "default"]) |
116 | 107 | def test_get_usage_data_from_local_db_project_id_filter( # type: ignore[no-untyped-def] |
117 | 108 | organisation: Organisation, |
@@ -159,10 +150,127 @@ def test_get_usage_data_from_local_db_project_id_filter( # type: ignore[no-unty |
159 | 150 | assert list(usage_data_for_project_two)[0].flags == total_count # 1 environment |
160 | 151 |
|
161 | 152 |
|
162 | | -@pytest.mark.skipif( |
163 | | - "analytics" not in settings.DATABASES, |
164 | | - reason="Skip test if analytics database is configured", |
165 | | -) |
| 153 | +@pytest.mark.skip_if_no_analytics_db |
| 154 | +@pytest.mark.django_db(databases=["analytics", "default"]) |
| 155 | +def test_get_usage_data_from_local_db__environment_filter__returns_expected( |
| 156 | + organisation: Organisation, |
| 157 | + environment: Environment, |
| 158 | + settings: SettingsWrapper, |
| 159 | +) -> None: |
| 160 | + # Given |
| 161 | + environment_id = environment.id |
| 162 | + now = timezone.now() |
| 163 | + read_bucket_size = 15 |
| 164 | + settings.ANALYTICS_BUCKET_SIZE = read_bucket_size |
| 165 | + |
| 166 | + bucket_created_at = now - timedelta(days=1) |
| 167 | + earlier_bucket_created_at = bucket_created_at - timedelta(minutes=read_bucket_size) |
| 168 | + |
| 169 | + APIUsageBucket.objects.create( |
| 170 | + environment_id=environment_id, |
| 171 | + resource=Resource.FLAGS, |
| 172 | + total_count=10, |
| 173 | + bucket_size=read_bucket_size, |
| 174 | + created_at=bucket_created_at, |
| 175 | + ) |
| 176 | + APIUsageBucket.objects.create( |
| 177 | + environment_id=99999, |
| 178 | + resource=Resource.FLAGS, |
| 179 | + total_count=10, |
| 180 | + bucket_size=read_bucket_size, |
| 181 | + created_at=earlier_bucket_created_at, |
| 182 | + ) |
| 183 | + |
| 184 | + # When |
| 185 | + usage_data_list = get_usage_data_from_local_db(organisation, environment_id) |
| 186 | + |
| 187 | + # Then |
| 188 | + assert usage_data_list == [ |
| 189 | + UsageData( |
| 190 | + day=bucket_created_at.date(), |
| 191 | + flags=10, |
| 192 | + traits=0, |
| 193 | + identities=0, |
| 194 | + environment_document=0, |
| 195 | + labels={}, |
| 196 | + ), |
| 197 | + ] |
| 198 | + |
| 199 | + |
| 200 | +@pytest.mark.skip_if_no_analytics_db |
| 201 | +@pytest.mark.django_db(databases=["analytics", "default"]) |
| 202 | +def test_get_usage_data_from_local_db__labels_filter__returns_expected( |
| 203 | + organisation: Organisation, |
| 204 | + environment: Environment, |
| 205 | + settings: SettingsWrapper, |
| 206 | +) -> None: |
| 207 | + # Given |
| 208 | + environment_id = environment.id |
| 209 | + now = timezone.now() |
| 210 | + read_bucket_size = 15 |
| 211 | + settings.ANALYTICS_BUCKET_SIZE = read_bucket_size |
| 212 | + |
| 213 | + bucket_created_at = now - timedelta(days=1) |
| 214 | + earlier_bucket_created_at = bucket_created_at - timedelta(minutes=read_bucket_size) |
| 215 | + |
| 216 | + APIUsageBucket.objects.create( |
| 217 | + environment_id=environment_id, |
| 218 | + resource=Resource.FLAGS, |
| 219 | + total_count=10, |
| 220 | + bucket_size=read_bucket_size, |
| 221 | + created_at=bucket_created_at, |
| 222 | + labels={ |
| 223 | + "client_application_name": "test-app", |
| 224 | + "client_application_version": "1.0.0", |
| 225 | + }, |
| 226 | + ) |
| 227 | + APIUsageBucket.objects.create( |
| 228 | + environment_id=environment_id, |
| 229 | + resource=Resource.FLAGS, |
| 230 | + total_count=10, |
| 231 | + bucket_size=read_bucket_size, |
| 232 | + created_at=earlier_bucket_created_at, |
| 233 | + labels={"client_application_name": "test-app"}, |
| 234 | + ) |
| 235 | + APIUsageBucket.objects.create( |
| 236 | + environment_id=environment_id, |
| 237 | + resource=Resource.FLAGS, |
| 238 | + total_count=10, |
| 239 | + bucket_size=read_bucket_size, |
| 240 | + created_at=earlier_bucket_created_at, |
| 241 | + labels={"client_application_name": "another-test-app"}, |
| 242 | + ) |
| 243 | + |
| 244 | + # When |
| 245 | + usage_data_list = get_usage_data_from_local_db( |
| 246 | + organisation, labels_filter={"client_application_name": "test-app"} |
| 247 | + ) |
| 248 | + |
| 249 | + # Then |
| 250 | + assert usage_data_list == [ |
| 251 | + UsageData( |
| 252 | + day=bucket_created_at.date(), |
| 253 | + flags=10, |
| 254 | + traits=0, |
| 255 | + identities=0, |
| 256 | + environment_document=0, |
| 257 | + labels={"client_application_name": "test-app"}, |
| 258 | + ), |
| 259 | + UsageData( |
| 260 | + day=earlier_bucket_created_at.date(), |
| 261 | + flags=10, |
| 262 | + traits=0, |
| 263 | + identities=0, |
| 264 | + environment_document=0, |
| 265 | + labels={ |
| 266 | + "client_application_name": "test-app", |
| 267 | + "client_application_version": "1.0.0", |
| 268 | + }, |
| 269 | + ), |
| 270 | + ] |
| 271 | + |
| 272 | + |
| 273 | +@pytest.mark.skip_if_no_analytics_db |
166 | 274 | @pytest.mark.django_db(databases=["analytics", "default"]) |
167 | 275 | def test_get_total_events_count(organisation, environment, settings): # type: ignore[no-untyped-def] |
168 | 276 | settings.USE_POSTGRES_FOR_ANALYTICS = True |
@@ -214,14 +322,13 @@ def test_get_total_events_count(organisation, environment, settings): # type: i |
214 | 322 | assert total_events_count == 20 * len(Resource) * 30 |
215 | 323 |
|
216 | 324 |
|
217 | | -@pytest.mark.skipif( |
218 | | - "analytics" not in settings.DATABASES, |
219 | | - reason="Skip test if analytics database is configured", |
220 | | -) |
| 325 | +@pytest.mark.skip_if_no_analytics_db |
221 | 326 | @pytest.mark.django_db(databases=["analytics", "default"]) |
222 | | -def test_get_feature_evaluation_data_from_local_db( # type: ignore[no-untyped-def] |
223 | | - feature: Feature, environment: Environment, settings: SettingsWrapper |
224 | | -): |
| 327 | +def test_get_feature_evaluation_data_from_local_db( |
| 328 | + feature: Feature, |
| 329 | + environment: Environment, |
| 330 | + settings: SettingsWrapper, |
| 331 | +) -> None: |
225 | 332 | environment_id = environment.id |
226 | 333 | feature_name = feature.name |
227 | 334 | now = timezone.now() |
@@ -284,6 +391,76 @@ def test_get_feature_evaluation_data_from_local_db( # type: ignore[no-untyped-d |
284 | 391 | assert data.day == today - timedelta(days=29 - i) |
285 | 392 |
|
286 | 393 |
|
| 394 | +@pytest.mark.skip_if_no_analytics_db |
| 395 | +@pytest.mark.django_db(databases=["analytics", "default"]) |
| 396 | +def test_get_feature_evaluation_data_from_local_db__labels_filter__returns_expected( |
| 397 | + feature: Feature, |
| 398 | + environment: Environment, |
| 399 | + settings: SettingsWrapper, |
| 400 | +) -> None: |
| 401 | + # Given |
| 402 | + environment_id = environment.id |
| 403 | + feature_name = feature.name |
| 404 | + now = timezone.now() |
| 405 | + read_bucket_size = 15 |
| 406 | + settings.ANALYTICS_BUCKET_SIZE = read_bucket_size |
| 407 | + |
| 408 | + bucket_created_at = now - timedelta(days=1) |
| 409 | + earlier_bucket_created_at = bucket_created_at - timedelta(minutes=read_bucket_size) |
| 410 | + |
| 411 | + FeatureEvaluationBucket.objects.create( |
| 412 | + environment_id=environment_id, |
| 413 | + feature_name=feature_name, |
| 414 | + total_count=10, |
| 415 | + bucket_size=read_bucket_size, |
| 416 | + created_at=bucket_created_at, |
| 417 | + labels={ |
| 418 | + "client_application_name": "test-app", |
| 419 | + "client_application_version": "1.0.0", |
| 420 | + }, |
| 421 | + ) |
| 422 | + FeatureEvaluationBucket.objects.create( |
| 423 | + environment_id=environment_id, |
| 424 | + feature_name=feature_name, |
| 425 | + total_count=10, |
| 426 | + bucket_size=read_bucket_size, |
| 427 | + created_at=earlier_bucket_created_at, |
| 428 | + labels={"client_application_name": "test-app"}, |
| 429 | + ) |
| 430 | + FeatureEvaluationBucket.objects.create( |
| 431 | + environment_id=environment_id, |
| 432 | + feature_name=feature_name, |
| 433 | + total_count=10, |
| 434 | + bucket_size=read_bucket_size, |
| 435 | + created_at=earlier_bucket_created_at, |
| 436 | + labels={"client_application_name": "another-test-app"}, |
| 437 | + ) |
| 438 | + |
| 439 | + # When |
| 440 | + usage_data_list = get_feature_evaluation_data_from_local_db( |
| 441 | + feature, |
| 442 | + environment_id, |
| 443 | + labels_filter={"client_application_name": "test-app"}, |
| 444 | + ) |
| 445 | + |
| 446 | + # Then |
| 447 | + assert usage_data_list == [ |
| 448 | + FeatureEvaluationData( |
| 449 | + day=earlier_bucket_created_at.date(), |
| 450 | + count=10, |
| 451 | + labels={"client_application_name": "test-app"}, |
| 452 | + ), |
| 453 | + FeatureEvaluationData( |
| 454 | + day=bucket_created_at.date(), |
| 455 | + count=10, |
| 456 | + labels={ |
| 457 | + "client_application_name": "test-app", |
| 458 | + "client_application_version": "1.0.0", |
| 459 | + }, |
| 460 | + ), |
| 461 | + ] |
| 462 | + |
| 463 | + |
287 | 464 | def test_get_usage_data_calls_get_usage_data_from_influxdb_if_postgres_not_configured( |
288 | 465 | mocker: MockerFixture, |
289 | 466 | settings: SettingsWrapper, |
@@ -337,6 +514,33 @@ def test_get_usage_data_calls_get_usage_data_from_local_db_if_postgres_is_config |
337 | 514 | ) |
338 | 515 |
|
339 | 516 |
|
| 517 | +def test_get_usage_data__no_analytics_configured__no_calls_expected( |
| 518 | + settings: SettingsWrapper, |
| 519 | + mocker: MockerFixture, |
| 520 | + organisation: Organisation, |
| 521 | +) -> None: |
| 522 | + # Given |
| 523 | + settings.USE_POSTGRES_FOR_ANALYTICS = False |
| 524 | + settings.INFLUXDB_TOKEN = None |
| 525 | + |
| 526 | + mocked_get_usage_data_from_influxdb = mocker.patch( |
| 527 | + "app_analytics.analytics_db_service.get_usage_data_from_influxdb", |
| 528 | + autospec=True, |
| 529 | + ) |
| 530 | + mocked_get_usage_data_from_local_db = mocker.patch( |
| 531 | + "app_analytics.analytics_db_service.get_usage_data_from_local_db", |
| 532 | + autospec=True, |
| 533 | + ) |
| 534 | + |
| 535 | + # When |
| 536 | + result = get_usage_data(organisation) |
| 537 | + |
| 538 | + # Then |
| 539 | + assert result == [] |
| 540 | + mocked_get_usage_data_from_influxdb.assert_not_called() |
| 541 | + mocked_get_usage_data_from_local_db.assert_not_called() |
| 542 | + |
| 543 | + |
340 | 544 | def test_get_total_events_count_calls_influx_method_if_postgres_not_configured( # type: ignore[no-untyped-def] |
341 | 545 | mocker, settings, organisation |
342 | 546 | ): |
|
0 commit comments