diff --git a/e2e/scenario_inputs/pi_web_api_scouter_distinct_tag_timestamps.json b/e2e/scenario_inputs/pi_web_api_scouter_distinct_tag_timestamps_multi_point.json similarity index 91% rename from e2e/scenario_inputs/pi_web_api_scouter_distinct_tag_timestamps.json rename to e2e/scenario_inputs/pi_web_api_scouter_distinct_tag_timestamps_multi_point.json index a5be268..e4c24de 100644 --- a/e2e/scenario_inputs/pi_web_api_scouter_distinct_tag_timestamps.json +++ b/e2e/scenario_inputs/pi_web_api_scouter_distinct_tag_timestamps_multi_point.json @@ -1,8 +1,8 @@ { "workflow_input": { "model_id": "13", - "model_name": "PI Distinct Tag Timestamps", - "schedule_name": "pi-distinct-ts", + "model_name": "PI Distinct Tag Timestamps (max_count>1)", + "schedule_name": "pi-distinct-ts-n", "trigger_laborious": false, "filters": {}, "schema": "sientia_data", diff --git a/e2e/scenario_inputs/pi_web_api_scouter_distinct_tag_timestamps_single_point.json b/e2e/scenario_inputs/pi_web_api_scouter_distinct_tag_timestamps_single_point.json new file mode 100644 index 0000000..f400051 --- /dev/null +++ b/e2e/scenario_inputs/pi_web_api_scouter_distinct_tag_timestamps_single_point.json @@ -0,0 +1,34 @@ +{ + "workflow_input": { + "model_id": "14", + "model_name": "PI Distinct Tag Timestamps (max_count=1)", + "schedule_name": "pi-distinct-ts-1", + "trigger_laborious": false, + "filters": {}, + "schema": "sientia_data", + "table_name": "laborious_data", + "retention_time": 3600, + "fill_missing_tags": false, + "model_tags": { + "tag_early": {"webid": "w1", "aggr_func": "lts", "data_range": [0, 1000], "frequency": 60000}, + "tag_late": {"webid": "w2", "aggr_func": "lts", "data_range": [0, 1000], "frequency": 60000} + }, + "pi_web_api_query": { + "endpoint": "/streamsets/recorded", + "period": "*-1d", + "max_count": 1, + "api_timeout": 30 + } + }, + "pi_web_api_server": { + "mode": "success", + "rows": [ + {"name": "tag_early", "timestamp": "2024-06-01T12:00:00Z", "value": 10}, + {"name": "tag_late", "timestamp": "2024-06-01T15:00:00Z", "value": 100} + ] + }, + "expected_rows": { + "tag_early": {"value": 10.0, "timestamp": "2024-06-01T12:00:00Z"}, + "tag_late": {"value": 100.0, "timestamp": "2024-06-01T15:00:00Z"} + } +} diff --git a/e2e/scenarios.md b/e2e/scenarios.md index deab58e..3c9f87e 100644 --- a/e2e/scenarios.md +++ b/e2e/scenarios.md @@ -63,10 +63,11 @@ Five tags with `avg` / `mdn` / `max` / `min` / `lts`; assert five distinct `vari ### 2.1.4 Distinct tag timestamps preserved -Regression test for SIENTIAPDE-1993. Two tags with `max_count=2`, each with two historical -points, but at very different times (`tag_early` around 12:00, `tag_late` around 15:00); assert -each tag's stored `timestamp` in Postgres equals its own last real point, not the batch-wide -max across tags. +Regression test for SIENTIAPDE-1993, parametrized over `max_count=1` and `max_count>1`. Two +tags (`tag_early` around 12:00, `tag_late` around 15:00) per case; assert each tag's stored +`timestamp` in Postgres equals its own latest point after aggregation, not the other tag's +(the batch-wide max across tags) — the invariant must hold whether a tag has one point or +several historical points. ### 2.2.1 Empty response early exit diff --git a/e2e/test_pi_web_api_scouter_main_workflow.py b/e2e/test_pi_web_api_scouter_main_workflow.py index 6c9bfbb..3f7c447 100644 --- a/e2e/test_pi_web_api_scouter_main_workflow.py +++ b/e2e/test_pi_web_api_scouter_main_workflow.py @@ -106,19 +106,29 @@ async def test_scenario_2_1_3_debug_data_package( @pytest.mark.e2e @pytest.mark.asyncio +@pytest.mark.parametrize( + 'scenario_name, workflow_id_prefix', + [ + ('pi_web_api_scouter_distinct_tag_timestamps_single_point', 'pi-distinct-ts-1'), + ('pi_web_api_scouter_distinct_tag_timestamps_multi_point', 'pi-distinct-ts-n'), + ], + ids=['max_count_1', 'max_count_gt_1'], +) async def test_scenario_2_1_4_distinct_tag_timestamps_preserved( temporal_env: WorkflowEnvironment, temporal_worker: Worker, pi_web_api_server: PIWebAPITestServer, postgres_engine, + scenario_name: str, + workflow_id_prefix: str, ): """ - Regression test for SIENTIAPDE-1993: with max_count > 1 and tags whose real - latest points fall at different times, each tag's stored row must keep its - own real timestamp instead of collapsing to the batch-wide maximum across - all tags. + Regression test for SIENTIAPDE-1993: whether a tag has a single point + (max_count=1) or several historical points (max_count>1), each tag's + stored row must keep the timestamp of its own latest point after + aggregation, not another tag's timestamp from the same batch. """ - scenario = load_scenario_input('pi_web_api_scouter_distinct_tag_timestamps') + scenario = load_scenario_input(scenario_name) apply_pi_web_api_server_config(pi_web_api_server, scenario.get('pi_web_api_server')) workflow_input = scenario['workflow_input'] @@ -126,7 +136,7 @@ async def test_scenario_2_1_4_distinct_tag_timestamps_preserved( temporal_env.client, PIWebAPIScouter.run, workflow_input, - make_workflow_id('pi-distinct-ts'), + make_workflow_id(workflow_id_prefix), ) rows = {