From 5451565029b4973a872cac1dee61a324605adc85 Mon Sep 17 00:00:00 2001 From: yashikakhurana Date: Wed, 15 Jan 2025 11:18:08 -0800 Subject: [PATCH] test(cirrus): Uodate test --- cirrus/server/tests/test_main.py | 491 +++++++++++++++----------- cirrus/server/tests/test_telemetry.py | 144 +++++++- 2 files changed, 416 insertions(+), 219 deletions(-) diff --git a/cirrus/server/tests/test_main.py b/cirrus/server/tests/test_main.py index a975afa666..66217ce7c6 100644 --- a/cirrus/server/tests/test_main.py +++ b/cirrus/server/tests/test_main.py @@ -50,26 +50,23 @@ def test_read_root(client): assert response.json() == {"Hello": "World"} -@pytest.mark.parametrize( - "url, expected_output", - [ - ( - "/v1/features/", - {"example-feature": {"enabled": False, "something": "wicked"}}, - ), - ( - "/v2/features/", - { - "Features": { - "example-feature": {"enabled": False, "something": "wicked"}, - # return default features - }, - "Enrollments": [], - }, - ), - ], -) -def test_get_features_with_required_field(client, url, expected_output): +def test_get_features_v1_with_required_field(client): + request_data = { + "client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + }, + } + + response = client.post("/v1/features/", json=request_data) + assert response.status_code == 200 + assert response.json() == { + "example-feature": {"enabled": False, "something": "wicked"} + } + + +def test_get_features_v2_with_required_field(client): request_data = { "client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": { @@ -78,28 +75,21 @@ def test_get_features_with_required_field(client, url, expected_output): }, } - response = client.post(url, json=request_data) + response = client.post("/v2/features/", json=request_data) assert response.status_code == 200 - assert response.json() == expected_output + assert response.json() == { + "Features": { + "example-feature": {"enabled": False, "something": "wicked"}, + # return default features + }, + "Enrollments": [], + } @pytest.mark.parametrize( - "url, request_data, expected_status, expected_message", + "request_data, expected_status, expected_message", [ ( - "/v1/features/", - { - "client_id": "", - "context": { - "key1": "value1", - "key2": {"key2.1": "value2", "key2.2": "value3"}, - }, - }, - status.HTTP_400_BAD_REQUEST, - "Client ID value is missing or empty", - ), - ( - "/v2/features/", { "client_id": "", "context": { @@ -111,30 +101,6 @@ def test_get_features_with_required_field(client, url, expected_output): "Client ID value is missing or empty", ), ( - "/v1/features/", - { - "context": { - "key1": "value1", - "key2": {"key2.1": "value2", "key2.2": "value3"}, - } - }, - status.HTTP_422_UNPROCESSABLE_ENTITY, - [ - { - "type": "missing", - "loc": ["body", "client_id"], - "msg": "Field required", - "input": { - "context": { - "key1": "value1", - "key2": {"key2.1": "value2", "key2.2": "value3"}, - } - }, - } - ], - ), - ( - "/v2/features/", { "context": { "key1": "value1", @@ -157,19 +123,11 @@ def test_get_features_with_required_field(client, url, expected_output): ], ), ( - "/v1/features/", - {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": {}}, - status.HTTP_400_BAD_REQUEST, - "Context value is missing or empty", - ), - ( - "/v2/features/", {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": {}}, status.HTTP_400_BAD_REQUEST, "Context value is missing or empty", ), ( - "/v1/features/", {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449"}, status.HTTP_422_UNPROCESSABLE_ENTITY, [ @@ -182,39 +140,87 @@ def test_get_features_with_required_field(client, url, expected_output): ], ), ( - "/v2/features/", - {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449"}, + {}, status.HTTP_422_UNPROCESSABLE_ENTITY, [ + { + "type": "missing", + "loc": ["body", "client_id"], + "msg": "Field required", + "input": {}, + }, { "type": "missing", "loc": ["body", "context"], "msg": "Field required", - "input": {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449"}, - } + "input": {}, + }, ], ), + ], +) +def test_get_features_v1_missing_required_field( + client, request_data, expected_status, expected_message +): + response = client.post("/v1/features/", json=request_data) + assert response.status_code == expected_status + assert response.json()["detail"] == expected_message + + +@pytest.mark.parametrize( + "request_data, expected_status, expected_message", + [ + ( + { + "client_id": "", + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + }, + }, + status.HTTP_400_BAD_REQUEST, + "Client ID value is missing or empty", + ), ( - "/v1/features/", - {}, + { + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + } + }, status.HTTP_422_UNPROCESSABLE_ENTITY, [ { "type": "missing", "loc": ["body", "client_id"], "msg": "Field required", - "input": {}, - }, + "input": { + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + } + }, + } + ], + ), + ( + {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": {}}, + status.HTTP_400_BAD_REQUEST, + "Context value is missing or empty", + ), + ( + {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449"}, + status.HTTP_422_UNPROCESSABLE_ENTITY, + [ { "type": "missing", "loc": ["body", "context"], "msg": "Field required", - "input": {}, - }, + "input": {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449"}, + } ], ), ( - "/v2/features/", {}, status.HTTP_422_UNPROCESSABLE_ENTITY, [ @@ -234,10 +240,10 @@ def test_get_features_with_required_field(client, url, expected_output): ), ], ) -def test_get_features_missing_required_field( - client, url, request_data, expected_status, expected_message +def test_get_features_v2_missing_required_field( + client, request_data, expected_status, expected_message ): - response = client.post(url, json=request_data) + response = client.post("/v2/features/", json=request_data) assert response.status_code == expected_status assert response.json()["detail"] == expected_message @@ -421,26 +427,7 @@ def test_heartbeat_endpoint(client): assert response.json() == {"status": "ok"} -@pytest.mark.parametrize( - "url, expected_output", - [ - ( - "/v1/features/?nimbus_preview=true", - {"example-feature": {"enabled": False, "something": "wicked"}}, - ), - ( - "/v2/features/?nimbus_preview=true", - { - "Features": { - "example-feature": {"enabled": False, "something": "wicked"}, - # return default features - }, - "Enrollments": [], - }, - ), - ], -) -def test_get_features_with_nimbus_preview(client, url, expected_output): +def test_get_features_v1_with_nimbus_preview_flag(client): request_data = { "client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": { @@ -449,28 +436,37 @@ def test_get_features_with_nimbus_preview(client, url, expected_output): }, } - response = client.post(url, json=request_data) + response = client.post("/v1/features/?nimbus_preview=true", json=request_data) assert response.status_code == 200 - assert response.json() == expected_output + assert response.json() == { + "example-feature": {"enabled": False, "something": "wicked"} + } + + +def test_get_features_v2_with_nimbus_preview(client): + request_data = { + "client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + }, + } + + response = client.post("/v2/features/?nimbus_preview=true", json=request_data) + assert response.status_code == 200 + assert response.json() == { + "Features": { + "example-feature": {"enabled": False, "something": "wicked"}, + # return default features + }, + "Enrollments": [], + } @pytest.mark.parametrize( - "url, request_data, expected_status, expected_message", + "request_data, expected_status, expected_message", [ ( - "/v1/features/?nimbus_preview=true", - { - "client_id": "", - "context": { - "key1": "value1", - "key2": {"key2.1": "value2", "key2.2": "value3"}, - }, - }, - status.HTTP_400_BAD_REQUEST, - "Client ID value is missing or empty", - ), - ( - "/v2/features/?nimbus_preview=true", { "client_id": "", "context": { @@ -482,7 +478,6 @@ def test_get_features_with_nimbus_preview(client, url, expected_output): "Client ID value is missing or empty", ), ( - "/v1/features/?nimbus_preview=true", { "context": { "key1": "value1", @@ -505,42 +500,11 @@ def test_get_features_with_nimbus_preview(client, url, expected_output): ], ), ( - "/v2/features/?nimbus_preview=true", - { - "context": { - "key1": "value1", - "key2": {"key2.1": "value2", "key2.2": "value3"}, - } - }, - status.HTTP_422_UNPROCESSABLE_ENTITY, - [ - { - "type": "missing", - "loc": ["body", "client_id"], - "msg": "Field required", - "input": { - "context": { - "key1": "value1", - "key2": {"key2.1": "value2", "key2.2": "value3"}, - } - }, - } - ], - ), - ( - "/v1/features/?nimbus_preview=true", {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": {}}, status.HTTP_400_BAD_REQUEST, "Context value is missing or empty", ), ( - "/v2/features/?nimbus_preview=true", - {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": {}}, - status.HTTP_400_BAD_REQUEST, - "Context value is missing or empty", - ), - ( - "/v1/features/?nimbus_preview=true", {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449"}, status.HTTP_422_UNPROCESSABLE_ENTITY, [ @@ -553,39 +517,87 @@ def test_get_features_with_nimbus_preview(client, url, expected_output): ], ), ( - "/v2/features/?nimbus_preview=true", - {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449"}, + {}, status.HTTP_422_UNPROCESSABLE_ENTITY, [ + { + "type": "missing", + "loc": ["body", "client_id"], + "msg": "Field required", + "input": {}, + }, { "type": "missing", "loc": ["body", "context"], "msg": "Field required", - "input": {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449"}, - } + "input": {}, + }, ], ), + ], +) +def test_get_features_v1_missing_required_field_nimbus_preview( + client, request_data, expected_status, expected_message +): + response = client.post("/v1/features/?nimbus_preview=true", json=request_data) + assert response.status_code == expected_status + assert response.json()["detail"] == expected_message + + +@pytest.mark.parametrize( + "request_data, expected_status, expected_message", + [ ( - "/v1/features/?nimbus_preview=true", - {}, + { + "client_id": "", + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + }, + }, + status.HTTP_400_BAD_REQUEST, + "Client ID value is missing or empty", + ), + ( + { + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + } + }, status.HTTP_422_UNPROCESSABLE_ENTITY, [ { "type": "missing", "loc": ["body", "client_id"], "msg": "Field required", - "input": {}, - }, + "input": { + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + } + }, + } + ], + ), + ( + {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": {}}, + status.HTTP_400_BAD_REQUEST, + "Context value is missing or empty", + ), + ( + {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449"}, + status.HTTP_422_UNPROCESSABLE_ENTITY, + [ { "type": "missing", "loc": ["body", "context"], "msg": "Field required", - "input": {}, - }, + "input": {"client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449"}, + } ], ), ( - "/v2/features/?nimbus_preview=true", {}, status.HTTP_422_UNPROCESSABLE_ENTITY, [ @@ -605,17 +617,15 @@ def test_get_features_with_nimbus_preview(client, url, expected_output): ), ], ) -def test_get_features_missing_required_field_nimbus_preview( - client, url, request_data, expected_status, expected_message +def test_get_features_v2_missing_required_field_nimbus_preview( + client, request_data, expected_status, expected_message ): - response = client.post(url, json=request_data) + response = client.post("/v2/features/?nimbus_preview=true", json=request_data) assert response.status_code == expected_status assert response.json()["detail"] == expected_message -def test_get_features_with_and_without_nimbus_preview( - client, -): +def test_get_features_v1_without_nimbus_preview(client): request_data = { "client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": { @@ -626,9 +636,7 @@ def test_get_features_with_and_without_nimbus_preview( with patch( "cirrus.main.app.state.sdk_live.compute_enrollments" - ) as mock_sdk_live_compute_enrollments, patch( - "cirrus.main.app.state.sdk_preview.compute_enrollments" - ) as mock_sdk_preview_compute_enrollments: + ) as mock_sdk_live_compute_enrollments: mock_sdk_live_compute_enrollments.return_value = { "enrolledFeatureConfigMap": { @@ -664,6 +672,28 @@ def test_get_features_with_and_without_nimbus_preview( } ], } + + # Without nimbus_preview + response = client.post("/v1/features/", json=request_data) + assert response.status_code == 200 + assert response.json() == { + "example-feature": {"enabled": False, "something": "wicked"} + } + + +def test_get_features_v1_with_nimbus_preview(client): + request_data = { + "client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + }, + } + + with patch( + "cirrus.main.app.state.sdk_preview.compute_enrollments" + ) as mock_sdk_preview_compute_enrollments: + mock_sdk_preview_compute_enrollments.return_value = { "enrolledFeatureConfigMap": { "example-feature": { @@ -699,13 +729,6 @@ def test_get_features_with_and_without_nimbus_preview( ], } - # Without nimbus_preview - response = client.post("/v1/features/", json=request_data) - assert response.status_code == 200 - assert response.json() == { - "example-feature": {"enabled": False, "something": "wicked"} - } - # With nimbus_preview response = client.post("/v1/features/?nimbus_preview=true", json=request_data) assert response.status_code == 200 @@ -714,7 +737,7 @@ def test_get_features_with_and_without_nimbus_preview( } -def test_get_features_enrollments_with_and_without_nimbus_preview(client): +def test_get_features_v2_enrollments_without_nimbus_preview(client): request_data = { "client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": { @@ -726,8 +749,6 @@ def test_get_features_enrollments_with_and_without_nimbus_preview(client): with patch( "cirrus.main.app.state.sdk_live.compute_enrollments" ) as mock_sdk_live_compute_enrollments, patch( - "cirrus.main.app.state.sdk_preview.compute_enrollments" - ) as mock_sdk_preview_compute_enrollments, patch( "cirrus.main.collate_enrollment_metric_data" ) as mock_collate_enrollment_metric_data, patch( "cirrus.main.app.state.fml.compute_feature_configurations" @@ -769,6 +790,66 @@ def test_get_features_enrollments_with_and_without_nimbus_preview(client): ], } + # Mock collate_enrollment_metric_data and compute_feature_configurations + mock_collate_enrollment_metric_data.side_effect = ( + lambda enrolled_partial_configuration, client_id, nimbus_preview_flag: [ + EnrollmentMetricData( + nimbus_user_id=client_id, + app_id="test_app_id", + experiment_slug=event["experiment_slug"], + branch_slug=event["branch_slug"], + experiment_type="rollout", + is_preview=nimbus_preview_flag, + ) + for event in enrolled_partial_configuration["events"] + ] + ) + mock_compute_feature_configurations.side_effect = ( + lambda enrolled_partial_configuration: { + feature_id: feature_data["feature"]["value"] + for feature_id, feature_data in enrolled_partial_configuration[ + "enrolledFeatureConfigMap" + ].items() + } + ) + + # Test for live SDK (no nimbus_preview) + response = client.post("/v2/features/", json=request_data) + assert response.status_code == 200 + assert response.json() == { + "Features": { + "example-feature": {"enabled": False, "something": "wicked"}, + }, + "Enrollments": [ + { + "nimbus_user_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", + "app_id": "test_app_id", + "experiment": "experiment_slug_1", + "branch": "treatment", + "experiment_type": "rollout", + "is_preview": False, + } + ], + } + + +def test_get_features_v2_enrollments_with_nimbus_preview(client): + request_data = { + "client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + }, + } + + with patch( + "cirrus.main.app.state.sdk_preview.compute_enrollments" + ) as mock_sdk_preview_compute_enrollments, patch( + "cirrus.main.collate_enrollment_metric_data" + ) as mock_collate_enrollment_metric_data, patch( + "cirrus.main.app.state.fml.compute_feature_configurations" + ) as mock_compute_feature_configurations: + # Mock preview compute_enrollments response mock_sdk_preview_compute_enrollments.return_value = { "enrolledFeatureConfigMap": { @@ -805,7 +886,7 @@ def test_get_features_enrollments_with_and_without_nimbus_preview(client): ], } - # Mock collate_enrollment_metric_data to process events correctly + # Mock collate_enrollment_metric_data and compute_feature_configurations mock_collate_enrollment_metric_data.side_effect = ( lambda enrolled_partial_configuration, client_id, nimbus_preview_flag: [ EnrollmentMetricData( @@ -813,14 +894,12 @@ def test_get_features_enrollments_with_and_without_nimbus_preview(client): app_id="test_app_id", experiment_slug=event["experiment_slug"], branch_slug=event["branch_slug"], - experiment_type="experiment" if nimbus_preview_flag else "rollout", + experiment_type="experiment", is_preview=nimbus_preview_flag, ) for event in enrolled_partial_configuration["events"] ] ) - - # Mock feature configurations to return a simplified structure mock_compute_feature_configurations.side_effect = ( lambda enrolled_partial_configuration: { feature_id: feature_data["feature"]["value"] @@ -830,26 +909,7 @@ def test_get_features_enrollments_with_and_without_nimbus_preview(client): } ) - # Test for live SDK (no nimbus_preview) - response = client.post("/v2/features/", json=request_data) - assert response.status_code == 200 - assert response.json() == { - "Features": { - "example-feature": {"enabled": False, "something": "wicked"}, - }, - "Enrollments": [ - { - "nimbus_user_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", - "app_id": "test_app_id", - "experiment": "experiment_slug_1", - "branch": "treatment", - "experiment_type": "rollout", - "is_preview": False, - } - ], - } - - # Test for preview (with nimbus_preview=true) + # Test for preview SDK (nimbus_preview=true) response = client.post("/v2/features/?nimbus_preview=true", json=request_data) assert response.status_code == 200 assert response.json() == { @@ -869,10 +929,7 @@ def test_get_features_enrollments_with_and_without_nimbus_preview(client): } -@pytest.mark.parametrize( - "url", ["/v1/features/?nimbus_preview=true", "/v2/features/?nimbus_preview=true"] -) -def test_get_features_preview_url_not_provided(client, url): +def test_get_features_v1_preview_url_not_provided(client): request_data = { "client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", "context": { @@ -883,7 +940,23 @@ def test_get_features_preview_url_not_provided(client, url): # Assuming the remote_setting_preview_url is not set in the settings with patch("cirrus.main.remote_setting_preview_url", ""): - response = client.post(url, json=request_data) + response = client.post("/v1/features/?nimbus_preview=true", json=request_data) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.json() == {"detail": "This Cirrus doesn’t support preview mode"} + + +def test_get_features_v2_preview_url_not_provided(client): + request_data = { + "client_id": "4a1d71ab-29a2-4c5f-9e1d-9d9df2e6e449", + "context": { + "key1": "value1", + "key2": {"key2.1": "value2", "key2.2": "value3"}, + }, + } + + # Assuming the remote_setting_preview_url is not set in the settings + with patch("cirrus.main.remote_setting_preview_url", ""): + response = client.post("/v2/features/?nimbus_preview=true", json=request_data) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == {"detail": "This Cirrus doesn’t support preview mode"} diff --git a/cirrus/server/tests/test_telemetry.py b/cirrus/server/tests/test_telemetry.py index fc752bfda8..ed5e4d817c 100644 --- a/cirrus/server/tests/test_telemetry.py +++ b/cirrus/server/tests/test_telemetry.py @@ -123,9 +123,8 @@ def test_collate_enrollment_metric_data(mocker): @pytest.mark.asyncio -@pytest.mark.parametrize("url", ["/v1/features/", "/v2/features/"]) -async def test_enrollment_metrics_recorded_with_compute_features( - client, mocker, recipes, url +async def test_enrollment_metrics_recorded_with_compute_features_v1( + client, mocker, recipes ): _, app.state.metrics = initialize_glean() context = json.dumps( @@ -161,7 +160,7 @@ def validate_before_submit(data): mocker.patch.object(app.state, "sdk_live", sdk) ping_spy = mocker.spy(app.state.pings.enrollment, "submit") - response = client.post(url, json=request_data) + response = client.post("/v1/features/", json=request_data) assert response.status_code == 200 assert ping_spy.call_count == 1 assert app.state.metrics.cirrus_events.enrollment.test_get_value() is None @@ -180,16 +179,141 @@ def validate_before_submit_preview(data): app.state.pings.enrollment.test_before_next_submit(validate_before_submit_preview) - response = client.post(url + "?nimbus_preview=true", json=request_data) + response = client.post("/v1/features/?nimbus_preview=true", json=request_data) assert response.status_code == 200 assert ping_spy.call_count == 1 assert app.state.metrics.cirrus_events.enrollment.test_get_value() is None @pytest.mark.asyncio -@pytest.mark.parametrize("url", ["/v1/features/", "/v2/features/"]) -async def test_enrollment_status_metrics_recorded_with_metrics_handler( - client, mocker, recipes, url +async def test_enrollment_metrics_recorded_with_compute_features_v2( + client, mocker, recipes +): + _, app.state.metrics = initialize_glean() + context = json.dumps( + { + "app_id": "org.mozilla.test", + "app_name": "test_app", + "channel": "release", + } + ) + sdk = SDK( + context=context, + coenrolling_feature_ids=[], + metrics_handler=CirrusMetricsHandler(app.state.metrics, app.state.pings), + ) + + request_data = { + "client_id": "test_client_id", + "context": {"user_id": "test-client-id"}, + } + + app.state.remote_setting_live.update_recipes(recipes) + sdk.set_experiments(json.dumps(recipes)) + + def validate_before_submit(data): + snapshot = app.state.metrics.cirrus_events.enrollment.test_get_value() + assert snapshot is not None + assert len(snapshot) == 2 + assert snapshot[0].extra["is_preview"] == "false" + assert snapshot[1].extra["is_preview"] == "false" + + app.state.pings.enrollment.test_before_next_submit(validate_before_submit) + + mocker.patch.object(app.state, "sdk_live", sdk) + ping_spy = mocker.spy(app.state.pings.enrollment, "submit") + + response = client.post("/v2/features/", json=request_data) + assert response.status_code == 200 + assert ping_spy.call_count == 1 + assert app.state.metrics.cirrus_events.enrollment.test_get_value() is None + + ping_spy.reset_mock() + + app.state.remote_setting_preview.update_recipes(recipes) + mocker.patch.object(app.state, "sdk_preview", sdk) + + def validate_before_submit_preview(data): + snapshot = app.state.metrics.cirrus_events.enrollment.test_get_value() + assert snapshot is not None + assert len(snapshot) == 2 + assert snapshot[0].extra["is_preview"] == "true" + assert snapshot[1].extra["is_preview"] == "true" + + app.state.pings.enrollment.test_before_next_submit(validate_before_submit_preview) + + response = client.post("/v2/features/?nimbus_preview=true", json=request_data) + assert response.status_code == 200 + assert ping_spy.call_count == 1 + assert app.state.metrics.cirrus_events.enrollment.test_get_value() is None + + +@pytest.mark.asyncio +async def test_enrollment_status_metrics_recorded_with_metrics_handler_v1( + client, mocker, recipes +): + _, app.state.metrics = initialize_glean() + context = json.dumps( + { + "app_id": "org.mozilla.test", + "app_name": "test_app", + "channel": "release", + } + ) + sdk = SDK( + context=context, + coenrolling_feature_ids=[], + metrics_handler=CirrusMetricsHandler(app.state.metrics, app.state.pings), + ) + + request_data = { + "client_id": "test_client_id", + "context": {"user_id": "test-client-id"}, + } + + app.state.remote_setting_live.update_recipes(recipes) + sdk.set_experiments(json.dumps(recipes)) + + def test_ping(data): + assert ( + app.state.metrics.cirrus_events.enrollment_status.test_get_num_recorded_errors( + ErrorType.INVALID_OVERFLOW + ) + == 0 + ) + snapshot = app.state.metrics.cirrus_events.enrollment_status.test_get_value() + assert len(snapshot) == 5 + assert snapshot[0].extra["status"] == "Enrolled" + assert snapshot[1].extra["status"] == "Enrolled" + assert snapshot[2].extra["status"] == "NotEnrolled" + assert snapshot[2].extra["reason"] == "NotSelected" + assert snapshot[3].extra["status"] == "NotEnrolled" + assert snapshot[3].extra["reason"] == "NotTargeted" + assert snapshot[4].extra["status"] == "Error" + + app.state.pings.enrollment_status.test_before_next_submit(test_ping) + + mocker.patch.object(app.state, "sdk_live", sdk) + ping_spy = mocker.spy(app.state.pings.enrollment_status, "submit") + + response = client.post("/v1/features/", json=request_data) + assert response.status_code == 200 + assert ping_spy.call_count == 1 + assert app.state.metrics.cirrus_events.enrollment_status.test_get_value() is None + + ping_spy.reset_mock() + app.state.remote_setting_preview.update_recipes(recipes) + mocker.patch.object(app.state, "sdk_preview", sdk) + + response = client.post("/v1/features/?nimbus_preview=true", json=request_data) + assert response.status_code == 200 + assert ping_spy.call_count == 1 + assert app.state.metrics.cirrus_events.enrollment_status.test_get_value() is None + + +@pytest.mark.asyncio +async def test_enrollment_status_metrics_recorded_with_metrics_handler_v2( + client, mocker, recipes ): _, app.state.metrics = initialize_glean() context = json.dumps( @@ -235,7 +359,7 @@ def test_ping(data): mocker.patch.object(app.state, "sdk_live", sdk) ping_spy = mocker.spy(app.state.pings.enrollment_status, "submit") - response = client.post(url, json=request_data) + response = client.post("/v2/features/", json=request_data) assert response.status_code == 200 assert ping_spy.call_count == 1 assert app.state.metrics.cirrus_events.enrollment_status.test_get_value() is None @@ -244,7 +368,7 @@ def test_ping(data): app.state.remote_setting_preview.update_recipes(recipes) mocker.patch.object(app.state, "sdk_preview", sdk) - response = client.post(url + "?nimbus_preview=true", json=request_data) + response = client.post("/v2/features/?nimbus_preview=true", json=request_data) assert response.status_code == 200 assert ping_spy.call_count == 1 assert app.state.metrics.cirrus_events.enrollment_status.test_get_value() is None