diff --git a/apps/api/src/jobs/awooop_ansible_candidate_backfill_job.py b/apps/api/src/jobs/awooop_ansible_candidate_backfill_job.py index 763134626..a977071bb 100644 --- a/apps/api/src/jobs/awooop_ansible_candidate_backfill_job.py +++ b/apps/api/src/jobs/awooop_ansible_candidate_backfill_job.py @@ -1161,8 +1161,13 @@ async def enqueue_iwooos_wazuh_manager_posture_candidate_once( AND candidate.input ->> 'work_item_id' = :work_item_id AND candidate.input ->> 'proposal_source' = :proposal_source AND candidate.input ->> 'run_namespace' = :run_namespace - AND candidate.input #>> '{source_recurrence,fingerprint}' = :source_fingerprint - AND candidate.input #>> '{source_recurrence,occurrence_id}' = :source_occurrence_id + AND ( + :scheduled_recurrence + OR ( + candidate.input #>> '{source_recurrence,fingerprint}' = :source_fingerprint + AND candidate.input #>> '{source_recurrence,occurrence_id}' = :source_occurrence_id + ) + ) AND candidate.created_at >= NOW() - (:freshness_hours * INTERVAL '1 hour') ORDER BY candidate.created_at DESC LIMIT 1 @@ -1176,6 +1181,10 @@ async def enqueue_iwooos_wazuh_manager_posture_candidate_once( "work_item_id": _work_item_id, "proposal_source": _scheduler_source, "run_namespace": _run_namespace, + "scheduled_recurrence": ( + validated_recurrence["source"] + == _WAZUH_SCHEDULED_RECURRENCE_SOURCE + ), "source_fingerprint": validated_recurrence["fingerprint"], "source_occurrence_id": validated_recurrence["occurrence_id"], }, diff --git a/apps/api/tests/test_iwooos_wazuh_controlled_executor_runtime.py b/apps/api/tests/test_iwooos_wazuh_controlled_executor_runtime.py index d13102bfe..6e53bc7ae 100644 --- a/apps/api/tests/test_iwooos_wazuh_controlled_executor_runtime.py +++ b/apps/api/tests/test_iwooos_wazuh_controlled_executor_runtime.py @@ -1019,8 +1019,11 @@ async def test_wazuh_posture_scheduler_retries_failed_check_once_per_deploy( def mappings(self): return _Mappings() + observed_queries: list[tuple[str, dict[str, object]]] = [] + class _Db: async def execute(self, _statement, _params): + observed_queries.append((str(_statement), dict(_params))) return _Result() @asynccontextmanager @@ -1054,7 +1057,7 @@ async def test_wazuh_posture_scheduler_retries_failed_check_once_per_deploy( recorder=recorder, evidence_collector=AsyncMock(return_value=MagicMock(snapshot_id="wazuh-2")), evidence_verifier=AsyncMock(return_value=True), - source_recurrence=_current_wazuh_source_recurrence( + source_recurrence=_durable_scheduled_wazuh_source_recurrence( "2026-07-11T05:59:58+08:00" ), ) @@ -1071,6 +1074,11 @@ async def test_wazuh_posture_scheduler_retries_failed_check_once_per_deploy( "scheduled-wazuh-manager-posture:2026071100-ABCDEF123456" ) assert recorded["automation_run_id"] == result["automation_run_id"] + existing_sql, existing_params = observed_queries[0] + assert ":scheduled_recurrence" in existing_sql + assert "{source_recurrence,fingerprint}" in existing_sql + assert "{source_recurrence,occurrence_id}" in existing_sql + assert existing_params["scheduled_recurrence"] is True @pytest.mark.asyncio diff --git a/apps/api/tests/test_wazuh_alertmanager_integration.py b/apps/api/tests/test_wazuh_alertmanager_integration.py index 69badca9c..66ba65e67 100644 --- a/apps/api/tests/test_wazuh_alertmanager_integration.py +++ b/apps/api/tests/test_wazuh_alertmanager_integration.py @@ -320,6 +320,7 @@ async def test_wazuh_ingress_scheduler_records_one_high_risk_controlled_candidat assert params["source_occurrence_id"] == ( "wazuh-source-occurrence-5710" ) + assert params["scheduled_recurrence"] is False query = str(statement) assert "candidate.input ->> 'project_id' = :project_id" in query assert "candidate.input ->> 'work_item_id' = :work_item_id" in query