Files
rUv e6f116b5c0 fix(memory): honour an explicit database path, and stop counting one store as if it were all of them (#3196) (#3271)
* fix(memory): honour an explicit database path, and stop counting one store as if it were all of them (#3196)

Two writes, both reporting success, landing in two files neither interface could
see whole. Reproduced on 3.41.0 in a clean directory:

  .swarm/memory.db            CLI-written key 1, MCP-written key 0
  .swarm/agentdb-memory.db    CLI-written key 0, MCP-written key 1

The cause is a cache, not a path calculation. getRegistry() kept one global
instance, so the first caller to touch the bridge decided the file for the whole
process; every later caller's dbPath was accepted and then silently ignored,
because the function returned the existing instance without ever comparing
paths. `--path` and CLAUDE_FLOW_DB_PATH have always advertised that they select
a database. Now they do: the cache is keyed by resolved path, so two paths in one
process are two registries, and a relative path and its absolute form are one.

The second half is worse than the split and is fixed separately. `memory list`
printed "Showing 1 of 1 entries" — not a warning, not a partial result, a
confident total describing one file as though it described the memory. A
downstream integrator measured 51 rows in one store and 16,671 in its sibling,
with 50 keys existing only in the smaller one. Silence would have been
recoverable; a wrong total is not, because nothing prompts anyone to look
further. The CLI now names the store it did not read, and how to read it.

Deliberately NOT done: merging the two files. #2786 gave AgentDB its own
plaintext file so native better-sqlite3 does not open an encrypted memory.db,
where it fails with "file is not a database" and silently disables the learning
system. Unifying the path would re-break encryption at rest. Both corpora stay,
and both stay readable.

The sibling count is read-only, opens nothing it cannot open read-only, and
returns null when the file is missing or unreadable — an unreadable store is not
evidence of an empty one, so the CLI stays silent rather than claim zero.

Verified against the original reproduction: the MCP-written rows are now
disclosed by name and count after the total. Three regression tests cover
sibling identity, path resolution, and silence on an unreadable store.

Reported-by: pacphi (pacphi/agentic-kit#213)
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67

* fix(memory): make the bridge test seam override every path, not two guessed keys

Keying the registry cache by path broke #2968's fixture. That test installs a
fake registry through __setMemoryBridgeRegistryForTests and then calls
bridgeStoreEntry with its own temp dbPath, so an override installed under the
two keys the seam could guess was never consulted: the call resolved a third
path, missed the cache, and tried a real init instead of the fake.

A test seam means 'use this registry', not 'use it if the caller passes the path
I assumed'. It is now a separate override that getRegistry() returns first, kept
deliberately outside the path cache so the two concerns cannot collide again.

Verified: #2968 and the #3196 identity tests pass together, and the bridge dbPath,
durability, purge and row-visibility suites are unchanged.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
2026-09-10 14:14:53 +00:00
..
…

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 inputs
  • getToolMetadata() - Metadata retrieval
  • listMCPTools() - Tool listing and filtering by category
  • hasTool() - Tool existence checks
  • getToolCategories() - Category enumeration
  • validateToolInput() - Input validation against schemas
  • MCPClientError - 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 operations
  • swarm-tools.js - Mocked swarm coordination
  • memory-tools.js - Mocked memory operations
  • config-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