Setting the file. One moment. Test Build Diagram · Agent Advisor · aws/agent-toolkit-for-aws · Skills DocsAdd Capabilities
81
Creating Amazon Aurora Db Cluster With Instances
104
Routing Traffic With Route53 And CloudFront
Resilience Program Design
Creating API Gateway Stage
def test_mermaid_has_runtime_model_and_services
— line 59
This file
- Number
- 23.69
- Position
- 69 of 81
- Type
- Python
- Size
- 22 KB
- Lines
- 503
scripts/test_build_diagram.py
Python·503 lines·22 KB
,
"eks"
]}
11 assert build_diagram.resolve_runtime(result, {"chosen_runtime": "eks"}) == "eks"
12
13
14def test_resolve_runtime_co_recommend_falls_back_to_first():
15 result = {"verdict": "co_recommend", "co_recommend": ["ecs", "eks"]}
16 assert build_diagram.resolve_runtime(result, {}) == "ecs"
17
18
19def test_resolve_runtime_no_viable():
20 assert build_diagram.resolve_runtime(
21 {"verdict": "no_viable_runtime"}, {}) == "none"
22
23
24def test_labels_cover_all_runtimes():
25 for rid in ("agentcore", "lambda_microvms", "ecs", "eks", "lambda"):
26 assert rid in build_diagram.RUNTIME_LABELS
27
28
29def test_service_labels_include_payments_and_registry():
30 # Conditional AgentCore services (SA feedback: relevant but not always).
31 assert build_diagram.SERVICE_LABELS["payments"] == "Payments"
32 assert build_diagram.SERVICE_LABELS["registry"] == "Registry"
33
34
35def test_payments_registry_render_in_diagram():
36 out = build_diagram.render_mermaid(
37 "agentcore", ["identity", "payments", "registry"], "claude_sonnet_5", "harness")
38 assert "Payments" in out and "Registry" in out
39
40
41def test_resolve_services_prefers_confirm():
42 result = {"agentcore_services": ["identity", "observability"]}
43 confirm = {"agentcore_services": ["identity", "memory", "gateway"]}
44 assert build_diagram.resolve_services(result, confirm) == [
45 "identity", "memory", "gateway"]
46
47
48def test_resolve_services_falls_back_to_result():
49 result = {"agentcore_services": ["identity", "observability"]}
50 assert build_diagram.resolve_services(result, {}) == [
51 "identity", "observability"]
52
53
54def test_resolve_services_filters_unknown_and_dedupes():
55 confirm = {"agentcore_services": ["identity", "identity", "bogus", "memory"]}
56 assert build_diagram.resolve_services({}, confirm) == ["identity", "memory"]
57
58
59def test_mermaid_has_runtime_model_and_services():
60 out = build_diagram.render_mermaid(
61 "agentcore", ["identity", "memory"], "claude_sonnet_5", "harness")
62 assert out.startswith("flowchart TD")
63 assert "AgentCore Runtime" in out
64 assert "harness" in out.lower()
65 assert "claude_sonnet_5" in out
66 assert "Identity" in out and "Memory" in out
67 assert "migration skill" not in out # no handoff for agentcore
68
69
70def test_mermaid_model_is_solid_edge_services_are_dotted():
71 # Topology: the model is a downstream call (solid invoke edge); services are
72 # cross-cutting capabilities attached with a dotted edge into a subgraph.
73 out = build_diagram.render_mermaid(
74 "agentcore", ["identity", "memory"], "claude_sonnet_5", "harness")
75 assert "rt -->|invoke| model" in out # model = solid data-flow edge
76 assert 'subgraph svcs["AgentCore services"]' in out
77 assert "rt -.-> svcs" in out # services = dotted attachment
78 # Services must NOT be flat solid children of the runtime anymore.
79 assert "rt --> svc_identity" not in out
80
81
82def test_mermaid_no_services_no_subgraph():
83 out = build_diagram.render_mermaid(
84 "agentcore", [], "claude_sonnet_5", "harness")
85 assert "subgraph" not in out
86 assert "rt -->|invoke| model" in out
87
88
89def test_mermaid_adds_handoff_note_for_ecs():
90 out = build_diagram.render_mermaid("ecs", [], "claude_sonnet_5", None)
91 assert "Amazon ECS (Fargate)" in out
92 assert "migration skill" in out
93
94
95def test_mermaid_deterministic():
96 a = build_diagram.render_mermaid("agentcore", ["identity", "memory"],
97 "claude_sonnet_5", "harness")
98 b = build_diagram.render_mermaid("agentcore", ["identity", "memory"],
99 "claude_sonnet_5", "harness")
100 assert a == b
101
102
103def test_ascii_contains_runtime_model_services():
104 out = build_diagram.render_ascii(
105 "agentcore", ["identity", "memory"], "claude_sonnet_5", "harness")
106 assert "AgentCore Runtime" in out
107 assert "harness" in out.lower()
108 assert "claude_sonnet_5" in out
109 assert "- Identity" in out and "- Memory" in out
110 assert "migration skill" not in out
111 # Model is on the primary flow; services are attached, not call targets.
112 assert "invoke" in out
113 assert "attached AgentCore services" in out
114
115
116def test_ascii_handoff_for_ecs():
117 out = build_diagram.render_ascii("ecs", [], "claude_sonnet_5", None)
118 assert "Amazon ECS" in out
119 assert "migration skill" in out
120
121
122def test_ascii_no_handoff_for_standard_lambda():
123 # Standard Lambda is a self-contained Build target (function skeleton), not a
124 # heavy-infrastructure handoff — no migration skill note.
125 out = build_diagram.render_ascii("lambda", [], "claude_sonnet_5", None)
126 assert "AWS Lambda" in out
127 assert "migration skill" not in out
128
129
130def test_ascii_deterministic():
131 a = build_diagram.render_ascii("eks", ["identity"], "claude_sonnet_5", None)
132 b = build_diagram.render_ascii("eks", ["identity"], "claude_sonnet_5", None)
133 assert a == b
134
135
136def test_build_diagram_end_to_end():
137 result = {"verdict": "agentcore", "deployment_model": "harness",
138 "agentcore_services": ["identity"],
139 "model_recommendation": {"model": "claude_sonnet_5"}}
140 out = build_diagram.build_diagram(result, {})
141 assert "flowchart TD" in out["mermaid"]
142 assert "AgentCore Runtime" in out["ascii"]
143
144
145def test_build_diagram_no_viable():
146 out = build_diagram.build_diagram({"verdict": "no_viable_runtime"}, {})
147 assert "No viable runtime" in out["mermaid"]
148 assert "No viable runtime" in out["ascii"]
149
150
151def test_golden_agentcore_full():
152 result = {"verdict": "agentcore", "deployment_model": "framework_on_runtime",
153 "agentcore_services": ["identity", "observability", "memory", "gateway"],
154 "model_recommendation": {"model": "claude_sonnet_5"}}
155 out = build_diagram.build_diagram(result, {})
156 # runtime + deployment model + all four services + model, no handoff
157 assert "framework_on_runtime" in out["mermaid"]
158 for svc in ("Identity", "Observability", "Memory", "Gateway"):
159 assert svc in out["mermaid"] and svc in out["ascii"]
160 assert "migration skill" not in out["mermaid"]
161
162
163def test_golden_lambda_microvms_no_services_no_handoff():
164 result = {"verdict": "lambda_microvms", "deployment_model": None,
165 "agentcore_services": [],
166 "model_recommendation": {"model": "claude_sonnet_5"}}
167 out = build_diagram.build_diagram(result, {})
168 assert "Lambda MicroVMs" in out["mermaid"]
169 assert "migration skill" not in out["mermaid"] # MicroVMs is not a handoff runtime
170
171
172def test_golden_ecs_has_handoff():
173 result = {"verdict": "ecs", "deployment_model": None,
174 "agentcore_services": ["identity"],
175 "model_recommendation": {"model": "claude_sonnet_5"}}
176 out = build_diagram.build_diagram(result, {})
177 assert "migration skill" in out["mermaid"]
178 assert "migration skill" in out["ascii"]
179
180
181def test_golden_co_recommend_renders_chosen():
182 result = {"verdict": "co_recommend", "co_recommend": ["ecs", "eks"],
183 "deployment_model": None, "agentcore_services": [],
184 "model_recommendation": {"model": "claude_sonnet_5"}}
185 out = build_diagram.build_diagram(result, {"chosen_runtime": "eks"})
186 assert "Amazon EKS" in out["mermaid"]
187 assert "migration skill" in out["mermaid"] # eks is a handoff runtime
188
189
190def _two_unit_design():
191 return {
192 "units": [
193 {"id": "chat-agent", "workload_class": "agent_session",
194 "verdict": "agentcore",
195 "model_recommendation": {"model": "claude_sonnet_5"}},
196 {"id": "summarizer", "workload_class": "batch", "verdict": "batch",
197 "model_recommendation": None},
198 ],
199 "platform": {"mode": "split", "runtime": None, "interconnect": "queue",
200 "shared_services": []},
201 }
202
203
204def test_multi_unit_renders_subgraph_per_unit():
205 out = build_diagram.build_diagram({}, {}, design=_two_unit_design())
206 assert "chat-agent" in out["mermaid"] and "summarizer" in out["mermaid"]
207 assert out["mermaid"].count("subgraph") >= 2
208
209
210def test_multi_unit_interconnect_edge():
211 out = build_diagram.build_diagram({}, {}, design=_two_unit_design())
212 assert "queue" in out["mermaid"].lower()
213
214
215def _consolidated_design():
216 # Codex #2: consolidated onto ECS — the diagram must render each unit on its
217 # effective_runtime (ecs), NOT its raw verdict (agentcore/batch).
218 return {
219 "units": [
220 {"id": "chat-agent", "workload_class": "agent_session",
221 "verdict": "agentcore", "effective_runtime": "ecs",
222 "model_recommendation": {"model": "claude_sonnet_5"}},
223 {"id": "summarizer", "workload_class": "batch",
224 "verdict": "batch", "effective_runtime": "ecs",
225 "model_recommendation": None},
226 ],
227 "platform": {"mode": "consolidated", "runtime": "ecs", "interconnect": "none",
228 "shared_services": []},
229 }
230
231
232def test_consolidated_diagram_renders_effective_runtime_not_verdict():
233 out = build_diagram.build_diagram({}, {}, design=_consolidated_design())
234 m = out["mermaid"]
235 # Both units render as ECS (the consolidated target)...
236 assert m.count("Amazon ECS (Fargate)") >= 2
237 # ...not their raw best-fit verdicts.
238 assert "AgentCore Runtime" not in m
239 assert "AWS Batch" not in m
240
241
242def _mixed_coupling_design():
243 # Codex #5: interconnect == queue means "at least one queue coupling exists", not
244 # "every unit is on the queue". The independent (coupling none) unit must NOT be wired.
245 return {
246 "units": [
247 {"id": "producer", "workload_class": "service", "verdict": "fargate",
248 "coupling": {"mode": "queue", "interacts_with": ["consumer"]},
249 "model_recommendation": None},
250 {"id": "consumer", "workload_class": "light_io", "verdict": "lambda",
251 "coupling": {"mode": "none", "interacts_with": []},
252 "model_recommendation": None},
253 {"id": "loner", "workload_class": "batch", "verdict": "batch",
254 "coupling": {"mode": "none", "interacts_with": []},
255 "model_recommendation": None},
256 ],
257 "platform": {"mode": "split", "runtime": None, "interconnect": "queue",
258 "shared_services": []},
259 }
260
261
262def test_queue_interconnect_follows_one_way_coupling():
263 # Codex round-3 #4: a one-way coupling is producer mode:queue interacts_with [consumer],
264 # consumer mode:none. BOTH ends must be on the queue; the truly independent unit must not.
265 out = build_diagram.build_diagram({}, {}, design=_mixed_coupling_design())
266 m = out["mermaid"]
267 assert "producer -.-> queue" in m
268 # The consumer is mode:none but is the target of the producer's queue coupling — wired.
269 assert "consumer -.-> queue" in m
270 # The independent unit (neither queue nor a coupling target) is drawn but NOT wired.
271 assert "loner" in m
272 assert "loner -.-> queue" not in m
273
274
275def _mixed_gateway_design():
276 # Codex round-4 #2: gateway must filter like queue — connect only api/a2a-coupled units
277 # (and their interacts_with targets), never independent mode:none units.
278 return {
279 "units": [
280 {"id": "orchestrator", "workload_class": "agent_session", "verdict": "agentcore",
281 "coupling": {"mode": "a2a", "interacts_with": ["tool-agent"]},
282 "model_recommendation": {"model": "claude_sonnet_5"}},
283 {"id": "tool-agent", "workload_class": "agent_session", "verdict": "agentcore",
284 "coupling": {"mode": "none", "interacts_with": []},
285 "model_recommendation": {"model": "claude_sonnet_5"}},
286 {"id": "loner", "workload_class": "batch", "verdict": "batch",
287 "coupling": {"mode": "none", "interacts_with": []},
288 "model_recommendation": None},
289 ],
290 "platform": {"mode": "split", "runtime": None, "interconnect": "gateway",
291 "shared_services": []},
292 }
293
294
295def test_gateway_interconnect_only_wires_gateway_coupled_units():
296 out = build_diagram.build_diagram({}, {}, design=_mixed_gateway_design())
297 m = out["mermaid"]
298 # Node ids are sanitized (hyphens → underscores).
299 assert "orchestrator -.-> gateway" in m
300 # tool-agent is mode:none but is the a2a target — wired.
301 assert "tool_agent -.-> gateway" in m
302 # The independent unit must NOT be wired to the gateway.
303 assert "loner" in m
304 assert "loner -.-> gateway" not in m
305
306
307def _mixed_queue_and_gateway_design():
308 # Codex round-5 #1: a system can mix couplings — an api pair AND a queue pair. Design records
309 # only the dominant platform.interconnect (gateway wins), but BOTH must be drawn; the queue
310 # relationship must not vanish just because a gateway coupling also exists.
311 return {
312 "units": [
313 {"id": "api-a", "workload_class": "agent_session", "verdict": "agentcore",
314 "coupling": {"mode": "api", "interacts_with": ["api-b"]},
315 "model_recommendation": {"model": "claude_sonnet_5"}},
316 {"id": "api-b", "workload_class": "service", "verdict": "fargate",
317 "coupling": {"mode": "none", "interacts_with": []},
318 "model_recommendation": None},
319 {"id": "q-producer", "workload_class": "service", "verdict": "fargate",
320 "coupling": {"mode": "queue", "interacts_with": ["q-consumer"]},
321 "model_recommendation": None},
322 {"id": "q-consumer", "workload_class": "batch", "verdict": "batch",
323 "coupling": {"mode": "none", "interacts_with": []},
324 "model_recommendation": None},
325 ],
326 # gateway wins as the single dominant interconnect value...
327 "platform": {"mode": "split", "runtime": None, "interconnect": "gateway",
328 "shared_services": []},
329 }
330
331
332def test_mixed_queue_and_gateway_both_rendered():
333 out = build_diagram.build_diagram({}, {}, design=_mixed_queue_and_gateway_design())
334 m = out["mermaid"]
335 # Gateway pair wired.
336 assert "api_a -.-> gateway" in m and "api_b -.-> gateway" in m
337 # Queue pair must ALSO be wired — not lost to the exclusive branch.
338 assert 'queue["Queue"]' in m
339 assert "q_producer -.-> queue" in m and "q_consumer -.-> queue" in m
340 # Cross-wiring must not happen (queue units not on gateway, api units not on queue).
341 assert "q_producer -.-> gateway" not in m
342 assert "api_a -.-> queue" not in m
343
344
345def test_single_unit_design_falls_back_to_legacy_render():
346 d = {"units": [_two_unit_design()["units"][0]],
347 "platform": {"mode": "split", "runtime": None,
348 "interconnect": "in_process", "shared_services": []}}
349 legacy = build_diagram.build_diagram(
350 {"verdict": "agentcore",
351 "agentcore_services": [],
352 "model_recommendation": {"model": "claude_sonnet_5"}}, {})
353 unitized = build_diagram.build_diagram(
354 {"verdict": "agentcore",
355 "agentcore_services": [],
356 "model_recommendation": {"model": "claude_sonnet_5"}}, {}, design=d)
357 assert unitized["mermaid"] == legacy["mermaid"], \
358 "collapse invariant: one unit renders exactly the legacy diagram"
359
360
361def _temporal_design():
362 return {
363 "units": [
364 {"id": "worker-fleet", "workload_class": "temporal_worker_poll",
365 "verdict": "ecs", "queues": ["agent-tasks", "batch-tasks"],
366 "model_recommendation": None},
367 {"id": "doc-chat", "workload_class": "agent_session",
368 "verdict": "agentcore",
369 "model_recommendation": {"model": "claude_sonnet_5"},
370 "task_queue": "agent-tasks"},
371 {"id": "ocr", "workload_class": "batch", "verdict": "batch",
372 "model_recommendation": None,
373 "task_queue": "batch-tasks"},
374 ],
375 "platform": {"mode": "split", "runtime": None, "interconnect": "none",
376 "shared_services": []},
377 "temporal": {"way": "cloud", "server": "Temporal Cloud"},
378 }
379
380
381def _temporal_design_multi_fleet():
382 # Two fleets, each polling its OWN queue; each Activity runs on exactly one queue.
383 # A correct diagram connects fleet-a -> chat (agent-tasks) and fleet-b -> ocr
384 # (batch-tasks), NEVER the cartesian product (fleet-a -> ocr, fleet-b -> chat).
385 return {
386 "units": [
387 {"id": "fleet-a", "workload_class": "temporal_worker_poll",
388 "verdict": "ecs", "queues": ["agent-tasks"], "model_recommendation": None},
389 {"id": "fleet-b", "workload_class": "temporal_worker_poll",
390 "verdict": "ecs", "queues": ["batch-tasks"], "model_recommendation": None},
391 {"id": "chat", "workload_class": "agent_session", "verdict": "agentcore",
392 "model_recommendation": {"model": "claude_sonnet_5"},
393 "task_queue": "agent-tasks"},
394 {"id": "ocr", "workload_class": "batch", "verdict": "batch",
395 "model_recommendation": None, "task_queue": "batch-tasks"},
396 ],
397 "platform": {"mode": "split", "runtime": None, "interconnect": "none",
398 "shared_services": []},
399 "temporal": {"way": "self_hosted", "server": "self-hosted"},
400 }
401
402
403def test_temporal_self_hosted_not_labeled_cloud():
404 # #4: a self_hosted Way must not be rendered as "Temporal Cloud".
405 out = build_diagram.build_diagram({}, {}, design=_temporal_design_multi_fleet())
406 assert "Temporal Cloud" not in out["mermaid"]
407 assert "Temporal Cloud" not in out["ascii"]
408 assert "self-hosted" in out["mermaid"]
409 assert "self-hosted" in out["ascii"]
410
411
412def test_temporal_multi_fleet_no_cartesian_product():
413 # #3: each fleet connects ONLY to the Activity on its own queue.
414 out = build_diagram.build_diagram({}, {}, design=_temporal_design_multi_fleet())
415 m = out["mermaid"]
416 # Correct edges present.
417 assert "fleet_a -->|agent-tasks| chat" in m
418 assert "fleet_b -->|batch-tasks| ocr" in m
419 # Cross edges (cartesian product) must NOT appear.
420 assert "fleet_a -->|batch-tasks| ocr" not in m
421 assert "fleet_a --> ocr" not in m
422 assert "fleet_b --> chat" not in m
423 assert "fleet_b -->|agent-tasks| chat" not in m
424
425
426def test_temporal_topology_renders_temporal_cloud_node():
427 out = build_diagram.build_diagram({}, {}, design=_temporal_design())
428 assert "Temporal Cloud" in out["mermaid"]
429 assert "orchestrator" in out["mermaid"]
430 assert "Temporal Cloud" in out["ascii"]
431
432
433def test_temporal_topology_connects_worker_poll_unit():
434 out = build_diagram.build_diagram({}, {}, design=_temporal_design())
435 # Temporal Cloud should connect to worker-fleet
436 assert "temporal_cloud --> worker_fleet" in out["mermaid"]
437 # Worker should connect to activity units
438 assert "worker_fleet -->" in out["mermaid"]
439 assert "doc_chat" in out["mermaid"] or "doc-chat" in out["mermaid"]
440
441
442def test_temporal_topology_no_user_edge():
443 out = build_diagram.build_diagram({}, {}, design=_temporal_design())
444 # Should NOT have user -> agent edge in Temporal systems
445 assert 'user["User' not in out["mermaid"]
446 assert "User / Client" not in out["ascii"] or "Temporal Cloud" in out["ascii"]
447
448
449def test_temporal_topology_no_inter_unit_queue_chain():
450 out = build_diagram.build_diagram({}, {}, design=_temporal_design())
451 # Should NOT have a separate queue node chaining units
452 assert 'queue["Queue"]' not in out["mermaid"]
453 assert "Interconnect: Queue" not in out["ascii"]
454
455
456def test_temporal_topology_with_task_queue_labels():
457 out = build_diagram.build_diagram({}, {}, design=_temporal_design())
458 # Should label edges with task queue names when available
459 assert "agent-tasks" in out["mermaid"] or "batch-tasks" in out["mermaid"]
460
461
462def test_multi_unit_handoff_node_for_ecs_units():
463 # Audit finding #2: the multi-unit diagram must show the "configured by migration skill"
464 # handoff indicator for units on a HANDOFF_RUNTIME — same as the single-unit path — instead
465 # of silently dropping it. Consolidated-onto-ECS is the canonical case.
466 out = build_diagram.build_diagram({}, {}, design=_consolidated_design())
467 assert "migration skill" in out["mermaid"], \
468 "multi-unit Mermaid must render the handoff node for ECS/EKS/Fargate/Batch units"
469 assert "migration skill" in out["ascii"], \
470 "multi-unit ASCII must render the handoff note (twin of the Mermaid path)"
471
472
473def test_multi_unit_no_handoff_for_self_contained_units():
474 # The inverse: an all-Lambda/AgentCore multi-unit system needs NO handoff indicator.
475 design = {
476 "units": [
477 {"id": "a", "workload_class": "agent_session", "verdict": "agentcore",
478 "effective_runtime": "agentcore", "model_recommendation": None},
479 {"id": "b", "workload_class": "light_io", "verdict": "lambda",
480 "effective_runtime": "lambda", "model_recommendation": None},
481 ],
482 "platform": {"mode": "split", "runtime": None, "interconnect": "none",
483 "shared_services": []},
484 }
485 out = build_diagram.build_diagram({}, {}, design=design)
486 assert "migration skill" not in out["mermaid"]
487 assert "migration skill" not in out["ascii"]
488
489
490def test_units_needing_handoff_covers_all_four_runtimes():
491 # units_needing_handoff must key on the same set as HANDOFF_RUNTIMES (ecs/eks/fargate/batch).
492 units = [
493 {"id": "e", "effective_runtime": "ecs"},
494 {"id": "k", "effective_runtime": "eks"},
495 {"id": "f", "effective_runtime": "fargate"},
496 {"id": "b", "effective_runtime": "batch"},
497 {"id": "l", "effective_runtime": "lambda"},
498 {"id": "a", "effective_runtime": "agentcore"},
499 {"id": "m", "effective_runtime": "lambda_microvms"},
500 ]
501 got = {u["id"] for u in build_diagram.units_needing_handoff(units)}
502 assert got == {"e", "k", "f", "b"}, \
503 "handoff set must be exactly ecs/eks/fargate/batch; lambda/agentcore/microvms self-contained"