|
8 | 8 | _deep_subset, |
9 | 9 | _normalize_expected_output, |
10 | 10 | _to_number, |
| 11 | + render_alert_proposal, |
11 | 12 | run_agentic_alert_skill, |
12 | 13 | ) |
13 | 14 | from gooddata_eval.core.models import ChatResult |
14 | 15 |
|
| 16 | +_PROPOSAL = { |
| 17 | + "title": "# of Orders Alert - Greater Than 500", |
| 18 | + "cta": "Should I create this alert?", |
| 19 | + "recipients": [{"email": "admin@gooddata.com"}], |
| 20 | + "dashboard": {"id": "dash-1", "title": "Orders overview"}, |
| 21 | + "alert": { |
| 22 | + "trigger": "ALWAYS", |
| 23 | + "condition": {"comparison": {"operator": "GREATER_THAN", "right": {"value": 500}}}, |
| 24 | + "execution": {"measures": [{"opaque": "afm"}]}, |
| 25 | + }, |
| 26 | +} |
| 27 | + |
15 | 28 |
|
16 | 29 | def test_to_number_int(): |
17 | 30 | assert _to_number("42") == 42 |
@@ -156,3 +169,82 @@ def test_run_agentic_alert_skill_creates_fresh_conversations_for_remaining_runs( |
156 | 169 | ) |
157 | 170 | assert mock_client.create_conversation.call_count == 2 |
158 | 171 | assert mock_client.delete_conversation.call_count == 2 |
| 172 | + |
| 173 | + |
| 174 | +def test_render_alert_proposal_keeps_verifiable_fields_and_drops_afm(): |
| 175 | + rendered = render_alert_proposal(_PROPOSAL) |
| 176 | + # The CTA leads so the simulated user reads it as a question. |
| 177 | + assert rendered.startswith("Should I create this alert?") |
| 178 | + # Rule 3 of the sim-user prompt requires verifying recipients against its goal. |
| 179 | + assert "admin@gooddata.com" in rendered |
| 180 | + assert "GREATER_THAN" in rendered |
| 181 | + assert "Orders overview" in rendered |
| 182 | + # Opaque AFM wire dicts must not crowd out the fields above. |
| 183 | + assert "execution" not in rendered |
| 184 | + |
| 185 | + |
| 186 | +def test_render_alert_proposal_drops_afm_when_execution_is_the_only_alert_field(): |
| 187 | + # Truthiness-gated replacement used to leave the original execution-bearing dict in place. |
| 188 | + rendered = render_alert_proposal({"alert": {"execution": {"measures": [{"opaque": "afm"}]}}}) |
| 189 | + assert "execution" not in rendered |
| 190 | + assert "opaque" not in rendered |
| 191 | + |
| 192 | + |
| 193 | +def test_render_alert_proposal_falls_back_to_default_cta(): |
| 194 | + assert render_alert_proposal({}).startswith("Should I create this alert?") |
| 195 | + |
| 196 | + |
| 197 | +def test_run_agentic_alert_skill_answers_proposal_only_confirmation_turn(): |
| 198 | + """GDAI-2032 regression: confirmation turn has no text part, only an alertProposal. |
| 199 | +
|
| 200 | + Without the fallback the simulated user is handed an empty agent message, so the agent |
| 201 | + never receives an explicit "yes" and create_metric_alert is never called. |
| 202 | + """ |
| 203 | + proposal_turn = ChatResult.model_validate( |
| 204 | + { |
| 205 | + "text_response": None, |
| 206 | + "alertProposals": [_PROPOSAL], |
| 207 | + "tool_call_events": [ |
| 208 | + {"functionName": "prepare_metric_alert_proposal", "functionArguments": "{}", "result": None} |
| 209 | + ], |
| 210 | + } |
| 211 | + ) |
| 212 | + created_turn = ChatResult.model_validate( |
| 213 | + { |
| 214 | + "text_response": "Alert created.", |
| 215 | + "tool_call_events": [ |
| 216 | + { |
| 217 | + "functionName": "create_metric_alert", |
| 218 | + "functionArguments": '{"operator": "GREATER_THAN", "threshold": 500}', |
| 219 | + "result": '{"id": "alert-1"}', |
| 220 | + } |
| 221 | + ], |
| 222 | + } |
| 223 | + ) |
| 224 | + mock_client = MagicMock() |
| 225 | + mock_client.send_message.side_effect = [proposal_turn, created_turn] |
| 226 | + |
| 227 | + with ( |
| 228 | + patch("gooddata_eval.core.agentic.alert_skill.ChatClient", return_value=mock_client), |
| 229 | + patch( |
| 230 | + "gooddata_eval.core.agentic.alert_skill.generate_simulated_alert_response", |
| 231 | + return_value="Yes, please proceed to create the alert.", |
| 232 | + ) as mock_sim, |
| 233 | + patch("gooddata_eval.core.agentic.alert_skill._delete_alert"), |
| 234 | + ): |
| 235 | + summary = run_agentic_alert_skill( |
| 236 | + host="http://host", |
| 237 | + token="tok", |
| 238 | + workspace_id="ws1", |
| 239 | + question="Notify me whenever the number of orders goes above 500", |
| 240 | + expected_output={"operator": "GREATER_THAN", "threshold": 500}, |
| 241 | + k=1, |
| 242 | + max_iterations=6, |
| 243 | + initial_conversation_id="conv-1", |
| 244 | + ) |
| 245 | + |
| 246 | + agent_message = mock_sim.call_args.args[0] |
| 247 | + assert "Should I create this alert?" in agent_message |
| 248 | + assert "admin@gooddata.com" in agent_message |
| 249 | + assert summary.best.eval.alert_created is True |
| 250 | + assert summary.best.alert_id == "alert-1" |
0 commit comments