Skip to content

refactor: Add comprehensive test suite and fix API field naming - #1

Merged
piotrlaczkowski merged 3 commits into
mainfrom
claude/golangraph-production-ready-lqoxjc
Aug 27, 2026
Merged

piotrlaczkowski merged 3 commits into
mainfrom
claude/golangraph-production-ready-lqoxjc

Conversation

@piotrlaczkowski

Copy link
Copy Markdown
Contributor

Summary

This PR adds a comprehensive test suite covering the studio store, API client, and UI components, while fixing critical bugs in API field naming and state management that were causing data loss and incorrect behavior.

Key Changes

Test Infrastructure

  • New test utilities (src/test-utils/studioTestUtils.ts): Shared test harness with fixtures and mock server that stubs only fetch, allowing tests to drive the real store through the real API client
  • Store tests (src/store/__tests__/useStudioStore.test.ts): 490 lines of behavioral tests covering connection lifecycle, graph data handling, agent execution, and state cleanup
  • API client tests (src/api/__tests__/client.test.ts): Contract tests validating the client correctly reads real Go server payloads
  • Component tests: ChatView, DebugView, GraphView, Header, and StudioLayout integration tests

Critical Bug Fixes

  • Fixed snake_case field naming: API responses use snake_case (e.g., tool_calls, execution_path, success) but types were incorrectly declared as PascalCase, causing all fields to read as undefined
  • Fixed state cleanup on disconnect: disconnect() now properly clears lastExecution, currentGraphNode, isExecuting, and other fields that were left behind, preventing stale data from appearing in the UI
  • Fixed catalogue error handling: Catalogue endpoints (agents, tools, providers) now report failures instead of silently falling back to empty lists, making broken endpoints visible to users
  • Fixed race condition in graph fetches: Added monotonic tokens to discard results from superseded async work (e.g., selecting agent A then B no longer leaves A's slow topology response overwriting B's graph)
  • Fixed execution cancellation: Stop button now properly prevents abandoned in-flight requests from appending answers to the thread

Type Corrections

  • Updated AgentExecution interface to use correct snake_case field names matching Go struct JSON tags
  • Replaced AgentStep with StateChange to match actual server payload structure
  • Removed non-existent fields (interrupt_on) and added optional fields (streaming_mode)
  • Added comprehensive JSDoc comments explaining field naming and structure

Store Improvements

  • Added describeError() utility to normalize error handling
  • Improved error messages with context (e.g., "Gave up after 3 connection attempts")
  • Added execution and graph request tokens to prevent race conditions
  • Enhanced attemptReconnection() to properly report when retry budget is exhausted

CI/CD Updates

  • Added typecheck step to CI pipeline
  • Made linter and test failures block CI (removed || true)
  • Added CI=true environment variable for test runs

Notable Implementation Details

  • Tests use real store and API client with only fetch stubbed, ensuring code under test matches production exactly
  • Fixtures are taken from GoLangGraph's end-to-end test suite, guaranteeing compatibility with actual server payloads
  • Monotonic token pattern prevents race conditions in concurrent async operations without requiring cancellation tokens
  • Error reporting now distinguishes between connection failures and catalogue endpoint failures

https://claude.ai/code/session_017JM31riXDsAT7MEtDs9crc

Piotr Laczkowski and others added 3 commits August 26, 2026 21:39
The AgentExecution and AgentStep types claimed the server used Go's
default PascalCase field names. It does not: agent.AgentExecution tags
every field, so the wire format is snake_case like the rest of the API.
Every read of an execution — status, duration, output, tool calls and the
execution path in both the debug view and the store — was therefore
reading undefined, and the debug panel showed a blank result for runs that
had actually succeeded.

Verified against a live server: the GoLangGraph repository now carries an
end-to-end suite that drives each of these endpoints and asserts the field
names this file declares.

Changes:

- AgentExecution now mirrors the real payload: id, timestamp, input,
  output, structured_output, tool_calls, duration, success, error,
  metadata, execution_path, state_changes. There is one timestamp marking
  the start plus a duration, rather than the separate StartTime and
  EndTime that were declared but never sent.

- AgentStep is replaced by StateChange, which is what the server actually
  records per node visit. No AgentStep type exists on the server.

- DebugView and useStudioStore read the corrected field names, and the
  execution status shown is derived from the success flag, since the
  server sends no separate status field.

Typecheck, lint and production build are clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017JM31riXDsAT7MEtDs9crc
The repository had no tests, and CI ran lint and tests with `|| true`, so
neither could ever fail the build. Nothing would have caught the type
mismatch this branch fixes: the declared execution shape compiled fine
while disagreeing with what the server actually sends, and a compiler
cannot know the wire format.

Adds 17 tests for the API client that assert the real payload shapes.
The fixtures are the payloads the Go server produces, taken from the
end-to-end suite that drives a live server, so the two repositories now
pin the same contract from both sides. They cover the API key header,
error-field surfacing, unreachable servers and non-JSON error bodies;
agent configurations and the empty-list fallbacks; successful and failed
executions in snake_case; tools and provider descriptions; the graph
topology the graph view renders, including conditional edges; and the
output and tool-call display helpers.

CI now typechecks with the project's TypeScript, and lint and tests fail
the build rather than being swallowed.

Also removes interrupt_on from AgentConfig, which the server has never
sent, and adds streaming_mode, which it does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017JM31riXDsAT7MEtDs9crc
The console hid failures. A failed run reported only its duration, and
because a failed execution usually has empty output the assistant bubble
said "No output returned." — the server sends the reason in `error` and
nothing read it. The debug panel showed the word "Failed", an empty output
block and no explanation. Both now show the reason, and the store carries
it into the execution log and the message metadata.

A failed run also painted the whole graph green: every node was marked
completed unconditionally before the execution path was applied. The
NodeStatus 'error' existed in the types and nothing in the codebase ever
produced it. Nodes on the server's execution_path are now completed and
the rest idle; a failed run is marked error.

Other defects fixed in the store:

- connect() caught and discarded failures from listAgents, listTools and
  listProviders, so a server whose /agents endpoint returned 500 looked
  identical to one with no agents: "Connected", an empty list, no message.
- Selecting one agent and then another let the first request's slower
  response overwrite the second's graph; topology fetches are now guarded
  by a request token.
- Selecting an unknown agent left the previous agent's graph on screen,
  silently attributed to the new selection.
- Stop did not stop anything: the request kept running and its answer was
  appended to the thread seconds later. A superseded result is now dropped.
- The value computed for currentGraphNode was discarded immediately by the
  finally block, so the graph never highlighted where a run ended.
- Reconnection failed silently once the retry budget was spent, leaving a
  Retry button that did nothing with no explanation.
- disconnect() cleared some state and kept the rest, so the debug view
  described a run whose logs had just been wiped.

Header indexed a three-key style map with a type string the server
controls, emitting `class="… undefined"` for anything else.

Adds 62 tests across the store and every view, driving the real store
through the real API client with only fetch stubbed, asserting on rendered
DOM and resulting state rather than on mock calls. Reverting the store
fixes makes 11 of them fail, so they hold. Test suite: 20 -> 79.

GraphView's Math.min over an empty array was investigated and is already
guarded by an early return; a test pins that guard rather than changing it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017JM31riXDsAT7MEtDs9crc
@piotrlaczkowski piotrlaczkowski changed the title Add comprehensive test suite and fix API field naming refactor: Add comprehensive test suite and fix API field naming Aug 27, 2026
@piotrlaczkowski
piotrlaczkowski merged commit 580a027 into main Aug 27, 2026
6 checks passed
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