Skip to content

test: cover AIChatViewModel.executeToolUses with injectable registry#1072

Merged
datlechin merged 1 commit into
mainfrom
feat/chat-tool-dispatch-tests-and-button-menu-check
May 7, 2026
Merged

test: cover AIChatViewModel.executeToolUses with injectable registry#1072
datlechin merged 1 commit into
mainfrom
feat/chat-tool-dispatch-tests-and-button-menu-check

Conversation

@datlechin

Copy link
Copy Markdown
Member

Summary

Two items from the umbrella followup list, bundled because one obviated the other:

  1. Test coverage for the chat tool-dispatch loop. AIChatViewModel.executeToolUses is the central dispatch helper that maps ToolUseBlockToolResultBlock via the registry. Phase 4 shipped it with zero unit tests. This adds 8 tests covering happy path, parallel ordering, missing tools, throwing tools, isError propagation, mixed cases, input forwarding, and empty input.
  2. Button-menu tables-submenu staleness flagged in feat: typed @ popover in AI chat composer with caret-anchored suggestions #1063 review. Confirmed already fixed by refactor: derive chat view-model schema state from SchemaService #1065's viewModel.tables computed property — same live SchemaService source the popover uses. No code change needed; verified in this PR by reading the current state of AIChatPanelView.mentionMenu.

Refactor for testability

executeToolUses and the inner runToolUse previously read ChatToolRegistry.shared directly, which makes test isolation hard (registering a tool in one test pollutes the singleton for others). Added an injectable registry: ChatToolRegistry? parameter that defaults to nil, falling through to .shared for production. Tests pass a fresh ChatToolRegistry() per test for isolation.

nonisolated static func executeToolUses(
    _ blocks: [ToolUseBlock],
    context: ChatToolContext,
    registry: ChatToolRegistry? = nil
) async -> [ToolResultBlock]

Production call site in runStream doesn't change (default nil falls through). The registry parameter is the same shape we used for the parser test refactor in #1071: an injectable seam, default nil for production.

Test coverage

  • dispatchesToRegisteredTool — happy path: registered tool, result matches.
  • resultsAreInInputOrder — parallel withTaskGroup execution preserves input order in output.
  • unregisteredToolReturnsError — missing tool name yields isError: true with explanation.
  • throwingToolReturnsErrortry await tool.execute(...) failure yields isError: true with Error: prefix.
  • toolIsErrorPropagates — a tool's own ChatToolResult(isError: true) flows through.
  • mixedToolsAllReturnResults — array of registered + unregistered tools each gets its own result block.
  • inputForwarded — the ToolUseBlock.input JSONValue reaches the tool's execute(input:).
  • emptyInput — empty array returns empty results, doesn't crash.

Why these matter

Tool dispatch is the load-bearing path for tool calling. The four most likely regressions from a future refactor:

  • "I cleaned up the parallel withTaskGroup, lost order preservation"
  • "I tweaked the error path, swallowed all errors"
  • "I changed the registry lookup, missing tools crash now"
  • "I refactored tool.execute invocation, isError stops propagating"

All four are pinned by these tests.

CHANGELOG

No entry. Tests + non-breaking parameter addition.

Lint

swiftlint lint --strict clean across all 819 files.

Test plan

  • xcodebuild test -only-testing:TableProTests/ExecuteToolUsesTests passes all 8 tests.
  • Smoke test the chat panel end-to-end (Anthropic with list_tables) — production tool dispatch still works.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin datlechin merged commit b881420 into main May 7, 2026
2 checks passed
@datlechin datlechin deleted the feat/chat-tool-dispatch-tests-and-button-menu-check branch May 7, 2026 08:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant