Skip to content

test(agent-service): drive sendMessage with a stand-in language model - #7487

Open
aglinxinyuan wants to merge 2 commits into
apache:mainfrom
aglinxinyuan:cov/texera-agent-sendmessage
Open

test(agent-service): drive sendMessage with a stand-in language model#7487
aglinxinyuan wants to merge 2 commits into
apache:mainfrom
aglinxinyuan:cov/texera-agent-sendmessage

Conversation

@aglinxinyuan

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

sendMessage held 238 of the 292 uncovered lines in texera-agent.ts, and the existing spec stops
at the model boundary. Nothing in there actually needs the network: ai/test ships a
MockLanguageModelV4 satisfying the same LanguageModel type the constructor already takes, so the
loop runs in-process. fetch is spied on as a tripwire, and the no-delegate tests assert that not a
single call escapes.

Adds 25 tests in two blocks:

Block Covers
sendMessage branch bookkeeping and ancestor path, per-step and summed usage, the assembled context replacing the raw message, tool projection and rolling before/after snapshots, the maxSteps cap, turn chaining, an abandoned branch staying invisible to the model, DAG compilation feeding schemas and cached results into the prompt
sendMessage failures a thrown model recorded as an error step, a non-Error throw stringified, a failed turn staying on the branch, cancellation reported as stopped, an AbortError-named provider error read as a user stop, stop() mid-run preventing the next call, GENERATING for the duration
delegate mode the one-time backend refresh and a failed refresh being swallowed, auto-execution after modifyOperator and where its result is keyed, the two guards that suppress it, buildExecutionConfig, and the debounced auto-persist plus its failure path

texera-agent.ts goes from 51.01% to 99.82% lines (80.43% → 98.21% funcs).

On what these tests actually pin

Line coverage overstates this, and it is worth being precise. bun credits a whole function body
once it is entered, so the first test alone takes the uncovered count from 292 to 27. Most of the
remaining 24 buy no additional lines — they exist because each kills a mutation nothing else kills.
They are mutation guards, not coverage.

The matrix ran 44 mutations against a pristine source with a checkout-and-verify between each. Six
were re-run independently after the tests were merged into the existing spec:

Mutation Expected Result
a tool call without operatorId still auto-executes red red
an [ERROR] tool result no longer suppresses the follow-up run red red
the EXECUTE_AFTER_TOOLS filter dropped red red
input tokens not mapped red red
totalUsage preferred over usage survives survives
the content: text || "" fallback survives survives

The last two are listed deliberately. They survive because they are unobservable in ai@7.0.48
totalUsage and usage are the same object, and the SDK already hands "" to a text-less step.
No test claims to pin them, and no test was written to cement them.

Five further fragments are line-covered but not behaviourally pinned, for the same reason:
lastPreparedMessages = undefined (re-assigned before every step), isError: !!(tr.output)?.error
(no tool ever returns an object), the ?? finalUsage?.promptTokens / ?? completionTokens arms
(v4-era key names that no longer exist), and the delegate guard at 408–410 (masked by the catch
below it).

Deliberately not included

  • getStepsById (line 261) — the one line left uncovered. No call site anywhere in the repo;
    server.ts uses getReActSteps / getAllSteps / getVisibleReActSteps. It also hands out the
    live private Map by reference. Deleting it beats testing it, and that belongs in its own change.
  • currentMessageId — five writes, zero reads. An assertion on it would cement dead state.
  • maxSteps: 0 — writing that test hangs the suite rather than failing it. Filed as maxSteps of 0 silently disables the agent's step cap #7484.

Two defects surfaced while writing these and are filed rather than fixed here, since this PR touches no production code: #7484 (a maxSteps of 0 silently disables the step cap) and #7485 (a falsy throw from the model makes sendMessage reject instead of reporting an error step).

No production file is touched.

Any related issues, documentation, discussions?

Closes #7486

How was this PR tested?

bun test
 253 pass
 0 fail

25 new on top of the existing 228. bun run typecheck passes.

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Opus 5)

sendMessage was the largest untested region in the service: 238 of the
292 uncovered lines in texera-agent.ts, and nothing exercised it because
the existing spec stops at the model boundary. Nothing there actually
needs the network - ai/test ships a MockLanguageModelV4 that satisfies
the same LanguageModel type the constructor takes.

Adds 25 tests across two blocks. The first drives the ReAct loop itself:
branch bookkeeping, per-step and summed usage, tool projection, the
maxSteps cap, turn chaining, and every failure path - a thrown model, a
non-Error throw, cancellation, and stop() mid-run. The second covers
delegate mode through fetch: the one-time backend refresh, auto-execution
after a tool call and where its result is keyed, the guards that suppress
it, and the debounced auto-persist.

texera-agent.ts goes from 51.01% to 99.82% lines. The one line left is
getStepsById, which has no call site.

No production file is touched.
Copilot AI lite review requested due to automatic review settings August 10, 2026 00:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • No candidates found from git blame history.

@codecov-commenter

codecov-commenter commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.38%. Comparing base (6790bf1) to head (80a8e5c).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #7487      +/-   ##
============================================
+ Coverage     85.69%   86.38%   +0.69%     
  Complexity     4168     4168              
============================================
  Files          1169     1169              
  Lines         46739    46694      -45     
  Branches       5203     5203              
============================================
+ Hits          40052    40337     +285     
+ Misses         4974     4646     -328     
+ Partials       1713     1711       -2     
Flag Coverage Δ *Carryforward flag
access-control-service 70.00% <ø> (ø) Carriedforward from bff5def
agent-service 97.47% <ø> (+11.97%) ⬆️
amber 81.09% <ø> (+<0.01%) ⬆️ Carriedforward from bff5def
computing-unit-managing-service 50.72% <ø> (ø) Carriedforward from bff5def
config-service 65.97% <ø> (ø) Carriedforward from bff5def
file-service 69.05% <ø> (ø) Carriedforward from bff5def
frontend 88.34% <ø> (-0.17%) ⬇️ Carriedforward from bff5def
notebook-migration-service 78.89% <ø> (ø) Carriedforward from bff5def
pyamber 97.55% <ø> (ø) Carriedforward from bff5def
workflow-compiling-service 26.31% <ø> (ø) Carriedforward from bff5def

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aglinxinyuan
aglinxinyuan requested a review from mengw15 August 10, 2026 00:43

@mengw15 mengw15 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

agent-service/src/agent/texera-agent.spec.ts:996

  • This replaces a process-wide singleton, but restoration occurs only at the end of the test. Any failed assertion or rejected await before line 1039 leaves the test metadata installed for subsequent tests and can create cascading, order-dependent failures. Restore the singleton (and destroy the agent) in a finally block or an afterEach cleanup that runs even when the test fails.
    const saved = (WorkflowSystemMetadata as any).instance;
    (WorkflowSystemMetadata as any).instance = undefined;

Comment on lines +787 to +789
* rather than being seeded on the agent. And every test here ends with `agent.destroy()`,
* because the auto-persist debounce would otherwise fire after the fetch spy is restored and
* issue a real request.
Per-test cleanup ran after the assertions, so a failing test skipped it
and leaked state into the tests that follow:

- agents built by makeAgentWith are now tracked and destroyed from the
  root afterEach, before the fetch spy is restored, so a pending
  auto-persist debounce can no longer fire a real request; the per-test
  destroy() calls are gone
- the WorkflowSystemMetadata singleton swap and the two validator spies
  are restored in finally blocks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Drive TexeraAgent.sendMessage with a stand-in language model

4 participants