Independent adversarial review of 2768c29 found three blockers and two
should-fixes. All five are addressed. Three of the blockers were mine, and two
of them were the same mistake I had already been given once in this work.
B1 — THE LABELLING FIX HAD NO TEST ON THE WIRING.
Reverting channel_read's handler to the pre-PR flat return — the entire defect
this branch exists to fix — left the suite green. All four tests exercised
`labelRelayRead` as a pure function; none reached the handler, which is the half
that regresses by accident. The module reaches the relay through
`await import('ws')`, so that is now mocked and the handler is driven end to end:
a fixture event goes in, and the test asserts the envelope fields and that
`out.channel` is undefined because the payload is nested. That mutant now
reddens two tests.
B2 — AND THE ONE TEST THAT NAMED THE WIRING WAS VACUOUS.
It asserted the tool description matched /Use when/, which matches all six
descriptions in the file: a house-style check wearing a security test's name.
Replaced.
B3 — "ONLY channel_read NEEDED IT" WAS WRONG.
channel_accept pushed `JSON.parse(ev.content).channel` verbatim into
`unopenable[]` on both failure paths — unvalidated third-party text reaching the
caller beside this machine's key-store path, which is the class #3300 exists to
prevent. A grant whose id is not a channel id cannot be a real grant, so it is
refused and reported as `malformedGrants`, a count. No publisher string reaches
the caller.
My first fix for B3 repeated B1's mistake: the test asserted CHANNEL_ID_RE's
behaviour rather than that the handler uses it, and deleting the guard left the
suite green. There is now a handler-level test driving a hostile grant through
the fake relay.
S3 — THE RETURNED LEDGER KEPT ITS NULL PROTOTYPE.
The previous commit spread it back to an ordinary object, which restored the
exact hazard being fixed on the read side: `board.toString` is a truthy
function, so `!board[r]` — the same idiom the reducer uses — reports an unowned
resource as owned, and `Object.assign({}, board)` silently drops a `__proto__`
claim. The justification given for the spread was also wrong: loose
`assert.deepEqual(Object.create(null), {})` passes, and the two tests that broke
did so because this file imports `node:assert/strict`. Both were asserting the
shape of an EMPTY board, not a caller contract, so they now assert
`Object.keys(...)` and the prototype stays off.
S5 — A NON-STRING resourceId IS REFUSED RATHER THAN COERCED.
`fetchRecent` spreads arbitrary JSON, so resourceId is not necessarily a string:
`["repo/website"]` keys as 'repo/website' and squats that resource, and any
object collapses to '[object Object]'. The comment claiming it is a string is
now true.
Mutants, all reddening their own tests: handler reverted to a flat return,
CHANNEL_ID_RE guard deleted, label dropped, label fields made overwritable,
plain-object ledger, ledger returned through a spread, string guard removed.
CLI suite 90 failed files / 112 failed tests before and after (pre-existing),
passing 2426 -> 2433. tsc 463 before and after. Gateway 42 -> 49 pass, 0 fail.
graph-analyzer 0/6 runs failed.
Co-Authored-By: Ruflo & AQE
Claude-Session: https://claude.ai/code/session_01NpXyuvm9QACWhD8egqw8zU
CLI Module Tests
This directory contains comprehensive tests for the V3 CLI module using Vitest.
Test Files
1. cli.test.ts (~15+ tests)
Tests for the main CLI class covering:
- Version command (
--version,-V) - Help output (
--help,-h) - Command parsing (long flags, short flags, equals syntax)
- Positional arguments
- Boolean flags and negation
- Global flags (
--quiet,--format,--config,--no-color) - Error handling (unknown commands, missing options)
- Subcommand execution and aliases
- Exit codes
2. mcp-client.test.ts (~10+ tests)
Tests for MCP tool invocation:
callMCPTool()- Tool execution with various inputsgetToolMetadata()- Metadata retrievallistMCPTools()- Tool listing and filtering by categoryhasTool()- Tool existence checksgetToolCategories()- Category enumerationvalidateToolInput()- Input validation against schemasMCPClientError- Custom error handling
3. commands.test.ts (~48+ tests)
Tests for all CLI commands:
Agent Commands (spawn, list, status, stop, metrics):
- Spawning agents with various configurations
- Listing agents with filters
- Getting agent status and metrics
- Stopping agents (graceful/force)
- Agent performance metrics
Swarm Commands (init, start, status, stop, scale, coordinate):
- Initializing swarms with different topologies
- Starting swarm execution with objectives
- Checking swarm status
- Stopping and scaling swarms
- V3 15-agent coordination structure
Memory Commands (store, retrieve, search, list, delete, stats, configure):
- Storing data in memory (with/without vectors)
- Retrieving data by key
- Semantic/vector search
- Listing memory entries
- Deleting entries
- Viewing statistics
- Backend configuration
Config Commands (init, get, set, providers, reset, export, import):
- Initializing configuration
- Getting/setting config values
- Managing AI providers
- Resetting to defaults
- Exporting/importing configuration
Mocking Strategy
MCP Tools
All MCP tools are mocked at the module level to prevent actual tool execution:
agent-tools.js- Mocked agent operationsswarm-tools.js- Mocked swarm coordinationmemory-tools.js- Mocked memory operationsconfig-tools.js- Mocked configuration
Output
The output module is fully mocked to capture formatted output without console pollution.
Prompts
Interactive prompts (select, confirm, input, multiSelect) are mocked to return default values for non-interactive testing.
Process
process.exit() is mocked to throw errors instead of terminating the test process.
Running Tests
# Run all CLI tests
npm test -- v3/@claude-flow/cli/__tests__/
# Run specific test file
npm test -- v3/@claude-flow/cli/__tests__/cli.test.ts
# Run with coverage
npm test -- v3/@claude-flow/cli/__tests__/ --coverage
# Run in watch mode
npm test -- v3/@claude-flow/cli/__tests__/ --watch
Test Coverage Goals
- Statements: >80%
- Branches: >75%
- Functions: >80%
- Lines: >80%
Key Testing Patterns
1. Command Execution
const result = await command.action!(ctx);
expect(result.success).toBe(true);
expect(result.data).toHaveProperty('expectedField');
2. Flag Parsing
ctx.flags = { myFlag: 'value', _: [] };
await command.action!(ctx);
// Assertions in action callback
3. Error Handling
try {
await cli.run(['invalid-command']);
} catch (e) {
expect((e as Error).message).toContain('process.exit');
}
4. Output Validation
const output = consoleOutput.join('');
expect(output).toContain('Expected text');
Notes
- All tests are non-interactive (
interactive: false) - Console output is captured for verification
- Process exits are converted to exceptions
- MCP client is fully isolated from actual MCP server
- Tests use unique command names to avoid conflicts