-
Notifications
You must be signed in to change notification settings - Fork 0
feat(test): route-unit consumer test harness (#103 stage 1) #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| --- | ||
| "agent-bundle": minor | ||
| --- | ||
|
|
||
| Ship the consumer route test harness as two new public subpaths. | ||
|
|
||
| `agent-bundle/rstest` exposes `agentBundleRstest()`: it runs the same | ||
| route-graph compilation the build runs — one compiler pass, through the shared | ||
| project service, with no artifact build — and returns a plain Rstest | ||
| configuration object that registers the compiled test manifest and the route | ||
| loaders, resolves React under the `react-server` condition, and selects the | ||
| automatic JSX runtime. `agent-bundle/test` exposes `renderRoute`, which executes | ||
| a route by compiled id or by module through the real final-only Flight | ||
| dispatcher and the real request store and resolves to the final Agent Document, | ||
| plus `expectDocument` matchers over the Agent Document contracts | ||
| (`toHaveStatus`, `toContainMarkdown`, `toContainText`, `toHaveValue`, | ||
| `toHaveError`, `toHaveNodeKinds`) and `testManifest()` for iterating the route | ||
| inventory in process. Failures name the route id, target kind, and module | ||
| provenance. | ||
|
|
||
| `@rstest/core` and `react` are optional peer dependencies: a project that does | ||
| not test routes installs neither, and neither becomes a runtime dependency. | ||
| `@agent-bundle/runtime` stays undeclared and is loaded through a dynamic | ||
| import, matching how the generated entry shells already import it from the | ||
| consumer project. | ||
|
|
||
| This is the route-unit proof level, labeled as such. Transport, packed, and | ||
| browser levels are not included and are not scaffolded. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { defineConfig } from '@rstest/core'; | ||
| import { agentBundleRstest } from 'agent-bundle/rstest'; | ||
|
|
||
| /** | ||
| * The framework-generated route-unit configuration. One Agent Bundle compiler | ||
| * pass runs here — no artifact build — and it supplies the route manifest, the | ||
| * TypeScript transform, and the React Server Components conditions this | ||
| * project's route tests need. The example maintains none of that by hand. | ||
| */ | ||
| export default defineConfig(await agentBundleRstest()); |
35 changes: 35 additions & 0 deletions
35
examples/audiobook-curator/tests/route-unit/routes.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { expect, it } from '@rstest/core'; | ||
| import { expectDocument, renderRoute, testManifest } from 'agent-bundle/test'; | ||
|
|
||
| /** | ||
| * The route-unit proof level for this example: it proves the curator's route | ||
| * modules render to the Agent Documents they claim. The manifest below is the | ||
| * framework compiler's own route compilation — the same one the build uses — | ||
| * delivered without building an artifact. It is not transport, packed-artifact, | ||
| * or host proof. | ||
| */ | ||
| const manifest = testManifest(); | ||
|
|
||
| it('compiles the curator routes through the framework test manifest, with no build', () => { | ||
| expect(manifest.proofLevel).toBe('route-unit'); | ||
| expect(manifest.diagnostics.filter((diagnostic) => diagnostic.severity === 'error')).toEqual([]); | ||
| expect(Object.keys(manifest.routes)).toContain('prompt:curator/curate'); | ||
| }); | ||
|
|
||
| it('renders the curation prompt route into a final Agent Document', async () => { | ||
| const rendered = await renderRoute('prompt:curator/curate', { input: { root: '/library' } }); | ||
|
|
||
| expectDocument(rendered) | ||
| .toHaveStatus('success') | ||
| .toContainText('Evidence-first curation prompt ready.') | ||
| .toHaveValue({ | ||
| messages: [{ | ||
| content: { | ||
| text: 'Inspect /library, retain evidence, and require review before mutation.', | ||
| type: 'text', | ||
| }, | ||
| role: 'user', | ||
| }], | ||
| }); | ||
| expect(rendered.provenance).toMatchObject({ proofLevel: 'route-unit', routeId: 'prompt:curator/curate' }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
packages/agent-bundle/fixtures/route-harness/agent-bundle.config.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // A conventional route project for the route-unit test harness. Plain object | ||
| // export, like every other repository fixture: the fixture must compile | ||
| // without the package's own built configuration entry. | ||
| export default { | ||
| plugin: { | ||
| description: 'Conventional route modules for the route-unit test harness.', | ||
| name: 'route-harness', | ||
| version: '1.0.0', | ||
| }, | ||
| targets: ['claude'], | ||
| }; |
10 changes: 10 additions & 0 deletions
10
packages/agent-bundle/fixtures/route-harness/src/events/tool/after.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { Agent, agent } from '@agent-bundle/runtime'; | ||
|
|
||
| export default async function AfterTool({ event, payload }: { readonly event: string; readonly payload: unknown }) { | ||
| const context = await agent(); | ||
| return ( | ||
| <Agent.Result value={{ event, invocationKind: context.invocation.kind, payload: payload as never }}> | ||
| <Agent.Markdown>{`Observed ${event}.`}</Agent.Markdown> | ||
| </Agent.Result> | ||
| ); | ||
| } |
7 changes: 7 additions & 0 deletions
7
packages/agent-bundle/fixtures/route-harness/src/mcp/harness/apps/panel.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| // A browser App surface. The route-unit level refuses to render it by name; | ||
| // nothing in the Node test bundle ever imports this module. | ||
| export const config = { resourceUri: 'ui://harness/panel' }; | ||
|
|
||
| export default function Panel() { | ||
| return null; | ||
| } |
16 changes: 16 additions & 0 deletions
16
packages/agent-bundle/fixtures/route-harness/src/mcp/harness/resources/notes.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { Agent } from '@agent-bundle/runtime'; | ||
| import { z } from 'zod'; | ||
|
|
||
| export const config = { mimeType: 'text/markdown', title: 'Notes', uri: 'harness://notes' }; | ||
|
|
||
| export const inputSchema = z.object({ uri: z.string() }); | ||
|
|
||
| export const resultSchema = z.object({ uri: z.string() }); | ||
|
|
||
| export default async function Notes({ input }: { readonly input: z.infer<typeof inputSchema> }) { | ||
| return ( | ||
| <Agent.Result value={{ uri: input.uri }}> | ||
| <Agent.Markdown>{`# Notes for ${input.uri}`}</Agent.Markdown> | ||
| </Agent.Result> | ||
| ); | ||
| } |
28 changes: 28 additions & 0 deletions
28
packages/agent-bundle/fixtures/route-harness/src/mcp/harness/tools/echo.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { Agent, agent } from '@agent-bundle/runtime'; | ||
| import { z } from 'zod'; | ||
|
|
||
| export const config = { | ||
| description: 'Echoes one message back with the observed workspace root.', | ||
| title: 'Echo', | ||
| }; | ||
|
|
||
| export const inputSchema = z.object({ message: z.string().optional() }); | ||
|
|
||
| export const resultSchema = z.object({ | ||
| message: z.string(), | ||
| operationId: z.string().nullable(), | ||
| workspace: z.string().nullable(), | ||
| }); | ||
|
|
||
| export default async function Echo({ input }: { readonly input: z.infer<typeof inputSchema> }) { | ||
| const context = await agent(); | ||
| await context.progress.report({ completed: 1, message: 'echoing', total: 1 }); | ||
| const workspace = context.workspace.state === 'available' ? context.workspace.value.root : null; | ||
| const message = input.message ?? '(no message)'; | ||
| return ( | ||
| <Agent.Result value={{ message, operationId: context.invocation.operationId ?? null, workspace }}> | ||
| <Agent.Markdown>{`# Echo\n\n${message}`}</Agent.Markdown> | ||
| <Agent.Text>{`workspace: ${workspace ?? 'unavailable'}`}</Agent.Text> | ||
| </Agent.Result> | ||
| ); | ||
| } |
16 changes: 16 additions & 0 deletions
16
packages/agent-bundle/fixtures/route-harness/src/mcp/harness/tools/unavailable.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { Agent } from '@agent-bundle/runtime'; | ||
| import { z } from 'zod'; | ||
|
|
||
| export const config = { title: 'Unavailable' }; | ||
|
|
||
| export const inputSchema = z.object({}); | ||
|
|
||
| export const resultSchema = z.object({ available: z.literal(false) }); | ||
|
|
||
| export default async function Unavailable() { | ||
| return ( | ||
| <Agent.Result value={{ available: false }}> | ||
| <Agent.Error code="AB9001">The harness fixture represents this capability as unavailable.</Agent.Error> | ||
| </Agent.Result> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.