diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ef81fa..ce7fcd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,3 +29,8 @@ jobs: run: npm run test:unit env: NODE_OPTIONS: --max-old-space-size=12288 + + - name: Tool inventory smoke test + run: npm run test:smoke + env: + NODE_OPTIONS: --max-old-space-size=12288 diff --git a/package.json b/package.json index 5d47798..cfa4212 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "start": "node build/index.js", "test": "npm run test:unit", "test:unit": "node --import tsx --test test/unit/*.test.ts", - "test:integration": "npm run build && node --import tsx --test test/integration/*.test.ts" + "test:integration": "npm run build && node --import tsx --test test/integration/*.test.ts", + "test:smoke": "npm run build && node --import tsx --test test/smoke/*.test.ts" }, "files": [ "build" diff --git a/test/setup.ts b/test/setup.ts index 0651be1..37a7cac 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -31,7 +31,7 @@ export async function startServer(): Promise { }); await new Promise((resolve, reject) => { - const timeout = setTimeout(() => reject(new Error('Server start timeout (15s)')), 15_000); + const timeout = setTimeout(() => reject(new Error('Server start timeout (60s)')), 60_000); child.stderr!.on('data', (chunk: Buffer) => { if (chunk.toString().includes('listening on port')) { clearTimeout(timeout); diff --git a/test/smoke/tool-inventory.test.ts b/test/smoke/tool-inventory.test.ts new file mode 100644 index 0000000..fd855f8 --- /dev/null +++ b/test/smoke/tool-inventory.test.ts @@ -0,0 +1,75 @@ +import { describe, test, before, after } from 'node:test'; +import assert from 'node:assert/strict'; +import { startServer, stopServer, ServerContext } from '../setup.ts'; + +/** + * Snapshot of the pre-migration tool surface (23 tools), captured from a real + * `listTools()` call against a built server on the `ci/harness-and-gitignore` + * branch. This is the baseline for the non-custodial split series + * (see docs/architecture/non-custodial-split.md) — PRs 2, 6, 7 and 8 + * deliberately restructure and rename tool families, and each of those PRs is + * expected to update this list deliberately, not accidentally. No wallet, + * no network beyond localhost — this never needs a mnemonic. + */ +const EXPECTED_TOOLS = [ + 'vector_build_transaction', + 'vector_deploy_contract', + 'vector_deregister_agent', + 'vector_discover_agents', + 'vector_dry_run', + 'vector_get_address', + 'vector_get_agent_profile', + 'vector_get_balance', + 'vector_get_spend_limits', + 'vector_get_transaction_history', + 'vector_get_utxos', + 'vector_interact_contract', + 'vector_message_agent', + 'vector_register_agent', + 'vector_self_improvement_analyze_metrics', + 'vector_self_improvement_browse', + 'vector_self_improvement_critique', + 'vector_self_improvement_endorse', + 'vector_self_improvement_submit_proposal', + 'vector_send_apex', + 'vector_send_tokens', + 'vector_transfer_agent', + 'vector_update_agent', +]; + +let ctx: ServerContext; +let actualNames: string[]; + +before(async () => { + ctx = await startServer(); + const result = await ctx.client.listTools(); + actualNames = result.tools.map((t) => t.name).sort(); +}); + +after(async () => { + await stopServer(ctx); +}); + +describe('tool inventory smoke test', () => { + test('exposes exactly 23 tools', () => { + assert.equal( + actualNames.length, + 23, + `expected 23 tools, got ${actualNames.length}: ${JSON.stringify(actualNames)}` + ); + }); + + test('tool names match the checked-in inventory exactly', () => { + const added = actualNames.filter((name) => !EXPECTED_TOOLS.includes(name)); + const removed = EXPECTED_TOOLS.filter((name) => !actualNames.includes(name)); + + assert.deepEqual( + actualNames, + EXPECTED_TOOLS, + 'Tool inventory drifted from the checked-in snapshot in test/smoke/tool-inventory.test.ts.\n' + + ` added: ${added.length ? JSON.stringify(added) : '(none)'}\n` + + ` removed: ${removed.length ? JSON.stringify(removed) : '(none)'}\n` + + 'If this drift is intentional, update EXPECTED_TOOLS in this file to match.' + ); + }); +});