test: pin real config values in slot-create inheritance test (#6522) - #6533
test: pin real config values in slot-create inheritance test (#6522)#6533bolichen97 wants to merge 1 commit into
Conversation
test_slot_create_inherits_nearest_folder_project stubbed KiroCrewConfig.load
with a bare MagicMock, which broke api_chat_slot_create in two independent
ways on the agent-less path:
- Leg 1 (harmless, logged): mock_cfg.agents is a truthy auto-MagicMock whose
__iter__ yields nothing, so resolve_agent_bindings' agents guard passes and
next(iter(...)) raises StopIteration - caught by the handler's except and
logged as 'Failed to resolve bindings for slot create'. The loader guard is
correct; the mock's shape defeats it.
- Leg 2 (the actual 500): with no agent in the request body the handler stamps
agent = cfg.default_agent, a truthy MagicMock, into the slot. The coalesced
slots broadcast at suspend_slots_push exit then fails json.dumps ('Object of
type MagicMock is not JSON serializable'); the TypeError escapes __exit__
and aiohttp renders a text/plain 500, so the test's resp.json() raises
ContentTypeError.
Fix is test-only: pin both config attributes this path reads to real values
(default_agent='' keeps agent resolution out of a folder-inheritance test)
and document the read set, following the file's narrow-mock convention.
The issue's suggested product fix (next(iter(...), None) in loader.py) is
deliberately NOT implemented: it would only silence the already-swallowed
Leg-1 warning and leave the test red, since the 500 comes from Leg 2.
Closes #6522
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — 🔴 BLOCK (blocking)Design-level review of Design-Verdict: BLOCK Superseded PR: the identical fix already landed on main in #6517, so this diff adds only a duplicate pin and a second comment block. Blockers
[DESIGN-REVIEWED] c65eb8f |
|
🤖 Kiro Crew Auto-Pipeline [operator: bolichen97#bb3ad1ca] Closing as superseded: the identical fix ( |
Closes #6522
What this fixes
test/test_dashboard_chat.py::TestFolderCRUD::test_slot_create_inherits_nearest_folder_projectfails deterministically on main (since 5c925e4, #6465), redding Backend Tests shards 2+3 on 3.10/3.12/Windows for every open PR. The defect is test-only: the fixture, not the product. This PR touches exactly one file,test/test_dashboard_chat.py(+13 comment/fixture lines).Root cause — two legs, both artifacts of a bare
MagicMockconfigThe test stubbed
KiroCrewConfig.loadwith a bareMagicMock(onlydashboard.default_projectpinned). On the agent-lessPOST /api/chat/slotspath:mock_cfg.agentsis a truthy auto-createdMagicMockwhose__iter__yields nothing.resolve_agent_bindings'elif config.agents:guard passes andnext(iter(config.agents))raisesStopIteration— caught by the handler'sexcept Exceptionatchat_handlers.py:2019-2020and logged (WARNING chat_handlers.py:2020in the CI log). The loader's guard is correct; the mock's shape defeats it.agentin the request body the handler runsagent = cfg.default_agent or "", stamping a truthy MagicMock into the slot. The coalesced slots broadcast atsuspend_slots_pushexit then failsjson.dumps(slots_data)(TypeError: Object of type MagicMock is not JSON serializable,state.py:8393); the TypeError escapes__exit__, aiohttp renders a text/plain 500, and the test'sresp.json()raisesContentTypeError.Why the issue's suggested fix is deliberately NOT implemented
#6522 proposes
next(iter(config.agents), None)insrc/kiro_crew/config/loader.py. That is not the defect: the loader already guards the genuinely-empty case (elif config.agents:/ anelse:branch returning safe defaults), and the StopIteration it would silence is already swallowed by the handler (Leg 1). Implementing it would leave the test red, because the 500 comes from Leg 2 — a mock-shapeddefault_agentreaching slot state. The divergence from the issue text is intentional, not an oversight; the corrected diagnosis was posted on the issue by triage.The fix
Pin the two config attributes this path actually reads to real values, following the file's established narrow-mock convention (e.g. the
_cc_cfgsite documents its read set the same way):mock_cfg.default_agent = ""— the load-bearing pin: nothing mock-shaped reaches slot state, and an empty agent keepsresolve_agent_bindingsout of a test that exercises folder inheritance, not agent resolution.mock_cfg.dashboard.default_project = ""(pre-existing) — belt-and-braces; the handler isinstance-guards that read.Sibling survey (spec item): every other
mock_cfgsite in the file either sets a realagentsdict and exercises agent-explicit paths, patchesresolve_agent_bindingsitself, makesloadraise (socfg is None), or patches a different module'sloadwith a documented read set. None reaches the agent-less slot-create path with an unpinneddefault_agent; all pass today and are untouched per the do-not-mass-rewrite rule.Verification
isort✅flake8✅mypy(1152 files, no issues) ✅ CI-identical black ratchet ✅.git diff --stat: onlytest/test_dashboard_chat.py; nothing undersrc/kiro_crew/.Pre-push review fleet
default_agentvalues so the mock shape is fixture-only (no masked product defect); assertions still discriminate (broken nearest-ancestor walking would surfaceroot_project, the test assertsparent_project).no-test-side-effects) is moved TOWARD, not violated; verified the handler read-set claim and the sibling survey repo-wide. Two comment-wording advisories (name the load-bearing pin; date the read-set snapshot) — both adopted in this head.Inherited main-reds (not this PR's)
Main is currently also red on Frontend Lint & Type Check and Coverage Gate (CI run 33162369859 on 5c925e4), unrelated to this change and out of scope here. Please don't attribute those lanes to this diff.
Out-of-scope follow-up filed
Hardening
api_chat_slot_createso a broadcast failure during unwind cannot 500 an already-successful create is a real improvement but a failure-semantics design decision — filed separately as #6532 per the fix scope, not folded in.