23f7624596
ADR-166 MCP Bridge Security Lock / Static-source security lock (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / Compose default binds loopback + Mongo has auth (push) Failing after 2s
CodeQL Advanced / Analyze (rust) (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / plugin-agent-federation bindHost default (push) Failing after 1s
ADR-166 MCP Bridge Security Lock / Runtime behavior — 401 + terminal gate + fail-closed (push) Failing after 4s
business-pods-smoke / smoke (push) Failing after 1s
all-plugins-smoke / smoke-all (push) Failing after 2s
CI/CD Pipeline / Security & Code Quality (push) Failing after 1s
CI/CD Pipeline / Test Suite (ubuntu-latest) (push) Failing after 1s
CI/CD Pipeline / Build & Package (macos-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (ubuntu-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (windows-latest) (push) Has been skipped
CI/CD Pipeline / Documentation & Examples (push) Failing after 1s
Clone Tracker (14-day rolling) / Snapshot clones for ruflo ecosystem (push) Failing after 1s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
federation-peer-rust / stable-noop (push) Failing after 1s
metaharness-ci / score (push) Failing after 1s
metaharness-ci / router-compat (push) Failing after 0s
metaharness-ci / similarity-tests (push) Failing after 0s
no-agentbbs-smoke / smoke-without-agentbbs (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (windows-latest) (push) Has been skipped
codex-integration-audit / Codex integration audit (push) Failing after 1s
helpers-manifest-guard / guard (push) Failing after 1s
🔗 Cross-Agent Integration Tests / 🤝 Agent Coordination Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🧠 Memory Sharing Integration (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🛡️ Fault Tolerance Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / ⚡ Performance Integration Tests (push) Has been skipped
metaharness-ci / mcp-scan (push) Failing after 1s
metaharness-ci / eject-dryrun (push) Failing after 1s
metaharness-ci / metaharness-real-data (push) Failing after 0s
no-cli-optdep-bloat-2561 / guard (push) Failing after 1s
no-metaharness-smoke / smoke-without-metaharness (push) Failing after 1s
no-phantom-agentic-flow-subpath / guard (push) Failing after 1s
🔄 Automated Rollback Manager / 🚨 Failure Detection (push) Failing after 1s
V3 CI/CD Pipeline / Plugin hooks smoke / ubuntu-latest / Node 22 (push) Failing after 1s
V3 CI/CD Pipeline / ruflo-graph-intelligence build + test smoke (#2044, ADR-123) (push) Failing after 1s
CVE Audit Gate / Audit root (critical-blocking) (push) Failing after 2s
cost-tracker-smoke / smoke (push) Failing after 3s
oia-audit-weekly / audit (push) Failing after 2s
ruflo-agent-smoke / ruflo-agent structural smoke (push) Failing after 1s
📊 Status Badges Update / 📊 Update Status Badges (push) Failing after 1s
V3 CI/CD Pipeline / Static regression guards (#2267 YAML + (push) Failing after 1s
V3 CI/CD Pipeline / Test V3 Packages (push) Failing after 0s
V3 CI/CD Pipeline / agent_execute provider routing smoke (#2042) (push) Failing after 0s
CVE Audit Gate / Audit v3 (critical-blocking) (push) Failing after 1s
federation-peer-rust / stable-native (push) Failing after 2s
🔗 Cross-Agent Integration Tests / 🚀 Integration Test Setup (push) Failing after 2s
neural-trader-smoke / runtime-smoke (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (macos-latest) (push) Has been skipped
V3 CI/CD Pipeline / Build V3 (ubuntu-latest) (push) Has been skipped
V3 CI/CD Pipeline / Type Check V3 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 24 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 22 (push) Failing after 2s
V3 CI/CD Pipeline / browser rvf create flag smoke (#2015) (push) Failing after 0s
V3 CI/CD Pipeline / Dependency review (#2046) (push) Has been skipped
V3 CI/CD Pipeline / Supply-chain audit (#2046) (push) Failing after 0s
V3 CI/CD Pipeline / witness marker drift smoke (#2021) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader backtest signing smoke (#2068, ADR-126 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / kg-extract type-import classification smoke (#2049) (push) Failing after 0s
V3 CI/CD Pipeline / witness verify precondition smoke (#1880) (push) Failing after 2s
V3 CI/CD Pipeline / neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5) (push) Failing after 0s
V3 CI/CD Pipeline / neural-trader feature attribution smoke (#2068, ADR-126 Phase 6) (push) Failing after 0s
V3 CI/CD Pipeline / plugin-registry signature verification smoke (#1922, CWE-347) (push) Failing after 4s
V3 CI/CD Pipeline / memory stats legacy-DB smoke (#2120) (push) Failing after 4s
V3 CI/CD Pipeline / github deprecated actions smoke (#2089, ADR-127 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / graph query + pathfinder smoke (ADR-130 P2+P5) (push) Has been skipped
V3 CI/CD Pipeline / graph trajectory hooks smoke (ADR-130 P3) (push) Has been skipped
V3 CI/CD Pipeline / graph plugin adapter smoke (ADR-130 P4) (push) Has been skipped
V3 CI/CD Pipeline / graph benchmark (ADR-130 P6) (push) Has been skipped
V3 CI/CD Pipeline / statusline generator delegation smoke (#2195) (push) Failing after 1s
V3 CI/CD Pipeline / wizard init regression guard (#2206 (push) Failing after 1s
V3 CI/CD Pipeline / memory no-stray-db smoke (ADR-125 P7) (push) Failing after 1s
V3 CI/CD Pipeline / github-safe injection smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github actions pin smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github attribution opt-in smoke (#2089, ADR-127 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / pre-bash hook safety smoke (#2017) (push) Failing after 1s
V3 CI/CD Pipeline / Memory import smoke / ubuntu-latest (push) Failing after 0s
V3 CI/CD Pipeline / MCP protocol smoke / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / ruvllm WASM auto-init smoke (#2086) (push) Failing after 4s
V3 CI/CD Pipeline / MCP paired-tool round-trip smoke (#1889) (push) Failing after 1s
V3 CI/CD Pipeline / Plugin package install-safety (#1902/#1903/#1904) (push) Failing after 1s
V3 CI/CD Pipeline / Tool description discoverability (ADR-112) (push) Failing after 3s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (22) (push) Failing after 1s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (24) (push) Failing after 1s
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Vector-index dimension audit (#1947) (push) Failing after 0s
V3 CI/CD Pipeline / Hook-command install safety (#1921) (push) Failing after 1s
V3 CI/CD Pipeline / ToolOutputGuardrail smoke (ADR-131, (push) Failing after 1s
V3 CI/CD Pipeline / init-bundle invariants smoke (#2095, ADR-128 Phase 5) (push) Failing after 1s
V3 CI/CD Pipeline / wasm provider bridge smoke (ADR-129 P1) (push) Failing after 2s
V3 CI/CD Pipeline / wasm gallery CRUD smoke (ADR-129 P3) (push) Failing after 1s
V3 CI/CD Pipeline / wasm plugin bridge smoke (ADR-129 P4) (push) Failing after 0s
V3 CI/CD Pipeline / wasm compose smoke (ADR-129 P2) (push) Failing after 4s
V3 CI/CD Pipeline / graph schema smoke (ADR-130 P1) (push) Failing after 0s
Validate Marketplace / validate (push) Failing after 1s
🔍 Verification Pipeline / 🚀 Setup Verification (push) Failing after 1s
🔍 Verification Pipeline / 🛡️ Security Verification (push) Has been skipped
🔍 Verification Pipeline / 📝 Code Quality (push) Has been skipped
🔍 Verification Pipeline / 🧪 Test Verification (${{ matrix.os }}, Node ${{ matrix.node }}) (push) Has been skipped
🔍 Verification Pipeline / 🏗️ Build Verification (push) Has been skipped
🔍 Verification Pipeline / 📚 Documentation Verification (push) Has been skipped
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Has been cancelled
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Has been cancelled
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Has been cancelled
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Has been cancelled
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Has been cancelled
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Has been cancelled
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Has been cancelled
CI/CD Pipeline / Deploy & Release (push) Has been cancelled
CI/CD Pipeline / CI Status (push) Has been cancelled
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Has been cancelled
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Has been cancelled
🔍 Verification Pipeline / ⚡ Performance Verification (push) Has been cancelled
🔍 Verification Pipeline / 📊 Verification Report (push) Has been cancelled
803 lines
24 KiB
TypeScript
803 lines
24 KiB
TypeScript
/**
|
|
* V3 CLI Commands Tests
|
|
* Tests for agent, swarm, memory, and config commands
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
import { agentCommand } from '../src/commands/agent.js';
|
|
import { swarmCommand } from '../src/commands/swarm.js';
|
|
import { memoryCommand } from '../src/commands/memory.js';
|
|
import { configCommand } from '../src/commands/config.js';
|
|
import type { CommandContext } from '../src/types.js';
|
|
|
|
// Mock MCP client
|
|
vi.mock('../src/mcp-client.js', () => ({
|
|
callMCPTool: vi.fn(async (toolName: string, input: Record<string, unknown>) => {
|
|
// Mock responses for different tools
|
|
if (toolName === 'agent/spawn') {
|
|
return {
|
|
agentId: input.id || 'mock-agent-123',
|
|
agentType: input.agentType,
|
|
status: 'active',
|
|
createdAt: new Date().toISOString()
|
|
};
|
|
}
|
|
|
|
if (toolName === 'agent/list') {
|
|
return {
|
|
agents: [
|
|
{ id: 'agent-1', agentType: 'coder', status: 'active', createdAt: '2024-01-01T00:00:00Z' },
|
|
{ id: 'agent-2', agentType: 'tester', status: 'idle', createdAt: '2024-01-01T00:01:00Z' }
|
|
],
|
|
total: 2
|
|
};
|
|
}
|
|
|
|
if (toolName === 'agent/status') {
|
|
return {
|
|
id: input.agentId,
|
|
agentType: 'coder',
|
|
status: 'active',
|
|
createdAt: '2024-01-01T00:00:00Z',
|
|
lastActivityAt: new Date().toISOString(),
|
|
metrics: {
|
|
tasksCompleted: 10,
|
|
tasksInProgress: 2,
|
|
tasksFailed: 1,
|
|
averageExecutionTime: 1500,
|
|
uptime: 3600000
|
|
}
|
|
};
|
|
}
|
|
|
|
if (toolName === 'agent/terminate') {
|
|
return {
|
|
agentId: input.agentId,
|
|
terminated: true,
|
|
terminatedAt: new Date().toISOString()
|
|
};
|
|
}
|
|
|
|
if (toolName === 'swarm/init') {
|
|
return {
|
|
swarmId: 'swarm-mock-123',
|
|
topology: input.topology,
|
|
initializedAt: new Date().toISOString(),
|
|
config: {
|
|
topology: input.topology,
|
|
maxAgents: input.maxAgents || 15,
|
|
currentAgents: 0,
|
|
autoScaling: true
|
|
}
|
|
};
|
|
}
|
|
|
|
// Memory tool mocks
|
|
if (toolName === 'memory/store') {
|
|
return {
|
|
success: true,
|
|
key: input.key,
|
|
totalEntries: 42
|
|
};
|
|
}
|
|
|
|
if (toolName === 'memory/retrieve') {
|
|
return {
|
|
key: input.key,
|
|
value: 'mock-value-for-' + input.key,
|
|
found: true,
|
|
storedAt: '2024-01-01T00:00:00Z',
|
|
accessCount: 5,
|
|
metadata: { tags: ['test'], size: 100 }
|
|
};
|
|
}
|
|
|
|
if (toolName === 'memory/search') {
|
|
return {
|
|
query: input.query,
|
|
results: [
|
|
{ key: 'result-1', value: 'auth pattern 1', score: 0.95, storedAt: '2024-01-01T00:00:00Z' },
|
|
{ key: 'result-2', value: 'auth pattern 2', score: 0.85, storedAt: '2024-01-01T00:01:00Z' }
|
|
],
|
|
total: 2,
|
|
searchTime: '0.5ms'
|
|
};
|
|
}
|
|
|
|
if (toolName === 'memory/list') {
|
|
return {
|
|
entries: [
|
|
{ key: 'entry-1', storedAt: '2024-01-01T00:00:00Z', accessCount: 10, preview: 'test value 1' },
|
|
{ key: 'entry-2', storedAt: '2024-01-01T00:01:00Z', accessCount: 5, preview: 'test value 2' }
|
|
],
|
|
total: 2,
|
|
limit: input.limit || 20,
|
|
offset: input.offset || 0
|
|
};
|
|
}
|
|
|
|
if (toolName === 'memory/delete') {
|
|
return {
|
|
success: true,
|
|
key: input.key,
|
|
deleted: true,
|
|
remainingEntries: 41
|
|
};
|
|
}
|
|
|
|
if (toolName === 'memory/stats') {
|
|
// Return raw MCP format that the command expects and transforms
|
|
return {
|
|
totalEntries: 42,
|
|
totalSize: '1.2 MB',
|
|
version: '3.0.0-alpha',
|
|
backend: 'hybrid',
|
|
location: './data/memory',
|
|
oldestEntry: '2024-01-01T00:00:00Z',
|
|
newestEntry: '2024-01-07T00:00:00Z'
|
|
};
|
|
}
|
|
|
|
return {};
|
|
}),
|
|
MCPClientError: class MCPClientError extends Error {
|
|
constructor(message: string, public toolName: string, public cause?: Error) {
|
|
super(message);
|
|
this.name = 'MCPClientError';
|
|
}
|
|
}
|
|
}));
|
|
|
|
// Mock output
|
|
vi.mock('../src/output.js', () => ({
|
|
output: {
|
|
writeln: vi.fn(),
|
|
printInfo: vi.fn(),
|
|
printSuccess: vi.fn(),
|
|
printError: vi.fn(),
|
|
printWarning: vi.fn(),
|
|
printTable: vi.fn(),
|
|
printJson: vi.fn(),
|
|
printList: vi.fn(),
|
|
printBox: vi.fn(),
|
|
createSpinner: vi.fn(() => ({
|
|
start: vi.fn(),
|
|
succeed: vi.fn(),
|
|
fail: vi.fn(),
|
|
stop: vi.fn()
|
|
})),
|
|
highlight: (str: string) => str,
|
|
bold: (str: string) => str,
|
|
dim: (str: string) => str,
|
|
success: (str: string) => str,
|
|
error: (str: string) => str,
|
|
warning: (str: string) => str,
|
|
info: (str: string) => str,
|
|
progressBar: () => '[=====> ]',
|
|
setColorEnabled: vi.fn()
|
|
}
|
|
}));
|
|
|
|
// Mock prompts (always return default values for non-interactive tests)
|
|
vi.mock('../src/prompt.js', () => ({
|
|
select: vi.fn(async (opts) => opts.default || opts.options[0]?.value),
|
|
confirm: vi.fn(async (opts) => opts.default ?? false),
|
|
input: vi.fn(async (opts) => opts.default || 'test-input'),
|
|
multiSelect: vi.fn(async (opts) => opts.default || [])
|
|
}));
|
|
|
|
describe('Agent Commands', () => {
|
|
let ctx: CommandContext;
|
|
|
|
beforeEach(() => {
|
|
ctx = {
|
|
args: [],
|
|
flags: { _: [] },
|
|
cwd: '/test',
|
|
interactive: false
|
|
};
|
|
});
|
|
|
|
describe('agent spawn', () => {
|
|
it.skip('should spawn agent with type flag', async () => { // Skip: requires live MCP context
|
|
const spawnCmd = agentCommand.subcommands?.find(c => c.name === 'spawn');
|
|
expect(spawnCmd).toBeDefined();
|
|
|
|
ctx.flags = { type: 'coder', _: [] };
|
|
const result = await spawnCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('agentId');
|
|
expect(result.data).toHaveProperty('agentType', 'coder');
|
|
});
|
|
|
|
it.skip('should spawn agent with custom name', async () => { // Skip: requires live MCP context
|
|
const spawnCmd = agentCommand.subcommands?.find(c => c.name === 'spawn');
|
|
|
|
ctx.flags = { type: 'tester', name: 'my-tester', _: [] };
|
|
const result = await spawnCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('agentId', 'my-tester');
|
|
});
|
|
|
|
it('should fail without agent type in non-interactive mode', async () => {
|
|
const spawnCmd = agentCommand.subcommands?.find(c => c.name === 'spawn');
|
|
|
|
ctx.flags = { _: [] };
|
|
const result = await spawnCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.exitCode).toBe(1);
|
|
});
|
|
|
|
it('should pass provider and model options', async () => {
|
|
const spawnCmd = agentCommand.subcommands?.find(c => c.name === 'spawn');
|
|
|
|
ctx.flags = {
|
|
type: 'coder',
|
|
provider: 'anthropic',
|
|
model: 'claude-3-5-sonnet',
|
|
_: []
|
|
};
|
|
const result = await spawnCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it('should handle task option', async () => {
|
|
const spawnCmd = agentCommand.subcommands?.find(c => c.name === 'spawn');
|
|
|
|
ctx.flags = {
|
|
type: 'researcher',
|
|
task: 'Research React patterns',
|
|
_: []
|
|
};
|
|
const result = await spawnCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('agent list', () => {
|
|
it.skip('should list all agents', async () => { // Skip: requires live MCP context
|
|
const listCmd = agentCommand.subcommands?.find(c => c.name === 'list');
|
|
expect(listCmd).toBeDefined();
|
|
|
|
const result = await listCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('agents');
|
|
expect(result.data).toHaveProperty('total', 2);
|
|
});
|
|
|
|
it.skip('should filter by agent type', async () => { // Skip: requires live MCP context
|
|
const listCmd = agentCommand.subcommands?.find(c => c.name === 'list');
|
|
|
|
ctx.flags = { type: 'coder', _: [] };
|
|
const result = await listCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it.skip('should filter by status', async () => { // Skip: requires live MCP context
|
|
const listCmd = agentCommand.subcommands?.find(c => c.name === 'list');
|
|
|
|
ctx.flags = { status: 'active', _: [] };
|
|
const result = await listCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it.skip('should include inactive agents with --all flag', async () => { // Skip: requires live MCP context
|
|
const listCmd = agentCommand.subcommands?.find(c => c.name === 'list');
|
|
|
|
ctx.flags = { all: true, _: [] };
|
|
const result = await listCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('agent status', () => {
|
|
it.skip('should show agent status', async () => { // Skip: requires live MCP context
|
|
const statusCmd = agentCommand.subcommands?.find(c => c.name === 'status');
|
|
expect(statusCmd).toBeDefined();
|
|
|
|
ctx.args = ['agent-123'];
|
|
const result = await statusCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('id');
|
|
expect(result.data).toHaveProperty('status');
|
|
expect(result.data).toHaveProperty('metrics');
|
|
});
|
|
|
|
it('should fail without agent ID', async () => {
|
|
const statusCmd = agentCommand.subcommands?.find(c => c.name === 'status');
|
|
|
|
ctx.args = [];
|
|
ctx.interactive = false;
|
|
const result = await statusCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.exitCode).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('agent stop', () => {
|
|
it.skip('should stop agent', async () => { // Skip: requires live MCP context
|
|
const stopCmd = agentCommand.subcommands?.find(c => c.name === 'stop');
|
|
expect(stopCmd).toBeDefined();
|
|
|
|
ctx.args = ['agent-123'];
|
|
ctx.flags = { force: true, _: [] };
|
|
const result = await stopCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('agentId', 'agent-123');
|
|
expect(result.data).toHaveProperty('terminated', true);
|
|
});
|
|
|
|
it('should fail without agent ID', async () => {
|
|
const stopCmd = agentCommand.subcommands?.find(c => c.name === 'stop');
|
|
|
|
ctx.args = [];
|
|
const result = await stopCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('agent metrics', () => {
|
|
it('should show agent metrics', async () => {
|
|
const metricsCmd = agentCommand.subcommands?.find(c => c.name === 'metrics');
|
|
expect(metricsCmd).toBeDefined();
|
|
|
|
const result = await metricsCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('summary');
|
|
expect(result.data).toHaveProperty('performance');
|
|
});
|
|
|
|
it('should accept period option', async () => {
|
|
const metricsCmd = agentCommand.subcommands?.find(c => c.name === 'metrics');
|
|
|
|
ctx.flags = { period: '7d', _: [] };
|
|
const result = await metricsCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Swarm Commands', () => {
|
|
let ctx: CommandContext;
|
|
|
|
beforeEach(() => {
|
|
ctx = {
|
|
args: [],
|
|
flags: { _: [] },
|
|
cwd: '/test',
|
|
interactive: false
|
|
};
|
|
});
|
|
|
|
describe('swarm init', () => {
|
|
it.skip('should initialize swarm with default topology', async () => { // Skip: requires live MCP context
|
|
const initCmd = swarmCommand.subcommands?.find(c => c.name === 'init');
|
|
expect(initCmd).toBeDefined();
|
|
|
|
const result = await initCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('swarmId');
|
|
expect(result.data).toHaveProperty('topology');
|
|
});
|
|
|
|
it.skip('should initialize swarm with custom topology', async () => { // Skip: requires live MCP context
|
|
const initCmd = swarmCommand.subcommands?.find(c => c.name === 'init');
|
|
|
|
ctx.flags = { topology: 'mesh', _: [] };
|
|
const result = await initCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('topology', 'mesh');
|
|
});
|
|
|
|
it.skip('should enable V3 mode', async () => { // Skip: requires live MCP context
|
|
const initCmd = swarmCommand.subcommands?.find(c => c.name === 'init');
|
|
|
|
ctx.flags = { v3Mode: true, _: [] };
|
|
const result = await initCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it.skip('should set max agents', async () => { // Skip: requires live MCP context
|
|
const initCmd = swarmCommand.subcommands?.find(c => c.name === 'init');
|
|
|
|
ctx.flags = { maxAgents: 20, _: [] };
|
|
const result = await initCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data?.config).toHaveProperty('maxAgents', 20);
|
|
});
|
|
});
|
|
|
|
describe('swarm start', () => {
|
|
it('should start swarm with objective', async () => {
|
|
const startCmd = swarmCommand.subcommands?.find(c => c.name === 'start');
|
|
expect(startCmd).toBeDefined();
|
|
|
|
ctx.flags = { objective: 'Build REST API', _: [] };
|
|
const result = await startCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('objective', 'Build REST API');
|
|
});
|
|
|
|
it('should fail without objective', async () => {
|
|
const startCmd = swarmCommand.subcommands?.find(c => c.name === 'start');
|
|
|
|
const result = await startCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.exitCode).toBe(1);
|
|
});
|
|
|
|
it('should accept strategy option', async () => {
|
|
const startCmd = swarmCommand.subcommands?.find(c => c.name === 'start');
|
|
|
|
ctx.flags = { objective: 'Test project', strategy: 'testing', _: [] };
|
|
const result = await startCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('strategy', 'testing');
|
|
});
|
|
});
|
|
|
|
describe('swarm status', () => {
|
|
it('should show swarm status', async () => {
|
|
const statusCmd = swarmCommand.subcommands?.find(c => c.name === 'status');
|
|
expect(statusCmd).toBeDefined();
|
|
|
|
const result = await statusCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('agents');
|
|
expect(result.data).toHaveProperty('tasks');
|
|
expect(result.data).toHaveProperty('metrics');
|
|
});
|
|
});
|
|
|
|
describe('swarm stop', () => {
|
|
it('should stop swarm', async () => {
|
|
const stopCmd = swarmCommand.subcommands?.find(c => c.name === 'stop');
|
|
expect(stopCmd).toBeDefined();
|
|
|
|
ctx.args = ['swarm-123'];
|
|
ctx.flags = { force: true, _: [] };
|
|
const result = await stopCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('swarmId', 'swarm-123');
|
|
expect(result.data).toHaveProperty('stopped', true);
|
|
});
|
|
|
|
it('should fail without swarm ID', async () => {
|
|
const stopCmd = swarmCommand.subcommands?.find(c => c.name === 'stop');
|
|
|
|
const result = await stopCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('swarm scale', () => {
|
|
it('should scale swarm', async () => {
|
|
const scaleCmd = swarmCommand.subcommands?.find(c => c.name === 'scale');
|
|
expect(scaleCmd).toBeDefined();
|
|
|
|
ctx.args = ['swarm-123'];
|
|
ctx.flags = { agents: 20, _: [] };
|
|
const result = await scaleCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('agents', 20);
|
|
});
|
|
|
|
it('should fail without target agent count', async () => {
|
|
const scaleCmd = swarmCommand.subcommands?.find(c => c.name === 'scale');
|
|
|
|
ctx.args = ['swarm-123'];
|
|
const result = await scaleCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('swarm coordinate', () => {
|
|
it('should show V3 coordination structure', async () => {
|
|
const coordinateCmd = swarmCommand.subcommands?.find(c => c.name === 'coordinate');
|
|
expect(coordinateCmd).toBeDefined();
|
|
|
|
const result = await coordinateCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('agents');
|
|
expect(result.data?.agents).toHaveLength(15);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Memory Commands', () => {
|
|
let ctx: CommandContext;
|
|
|
|
beforeEach(() => {
|
|
ctx = {
|
|
args: [],
|
|
flags: { _: [] },
|
|
cwd: '/test',
|
|
interactive: false
|
|
};
|
|
});
|
|
|
|
describe('memory store', () => {
|
|
it.skip('should store data', async () => { // Skip: requires live memory service
|
|
const storeCmd = memoryCommand.subcommands?.find(c => c.name === 'store');
|
|
expect(storeCmd).toBeDefined();
|
|
|
|
ctx.flags = { key: 'test-key', value: 'test-value', _: [] };
|
|
const result = await storeCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('key', 'test-key');
|
|
});
|
|
|
|
it('should fail without key', async () => {
|
|
const storeCmd = memoryCommand.subcommands?.find(c => c.name === 'store');
|
|
|
|
ctx.flags = { value: 'test-value', _: [] };
|
|
const result = await storeCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('memory retrieve', () => {
|
|
it.skip('should retrieve data', async () => { // Skip: requires live memory service
|
|
const retrieveCmd = memoryCommand.subcommands?.find(c => c.name === 'retrieve');
|
|
expect(retrieveCmd).toBeDefined();
|
|
|
|
ctx.args = ['test-key'];
|
|
const result = await retrieveCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('key', 'test-key');
|
|
});
|
|
});
|
|
|
|
describe('memory search', () => {
|
|
// 60s timeout: a cold ONNX cache pulls a 23MB model from huggingface
|
|
// before the first search. Default 5s vitest timeout reliably trips on
|
|
// a fresh checkout. Bumping per-test rather than file-wide so the rest
|
|
// of the suite still fails fast on regressions.
|
|
it('should search memory', { timeout: 60_000 }, async () => {
|
|
const searchCmd = memoryCommand.subcommands?.find(c => c.name === 'search');
|
|
expect(searchCmd).toBeDefined();
|
|
|
|
ctx.flags = { query: 'authentication', _: [] };
|
|
const result = await searchCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(Array.isArray(result.data)).toBe(true);
|
|
});
|
|
|
|
it('should fail without query', async () => {
|
|
const searchCmd = memoryCommand.subcommands?.find(c => c.name === 'search');
|
|
|
|
const result = await searchCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('memory list', () => {
|
|
it('should list memory entries', async () => {
|
|
const listCmd = memoryCommand.subcommands?.find(c => c.name === 'list');
|
|
expect(listCmd).toBeDefined();
|
|
|
|
const result = await listCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(Array.isArray(result.data)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('memory delete', () => {
|
|
it.skip('should delete entry', async () => { // Skip: requires live memory service
|
|
const deleteCmd = memoryCommand.subcommands?.find(c => c.name === 'delete');
|
|
expect(deleteCmd).toBeDefined();
|
|
|
|
ctx.args = ['test-key'];
|
|
ctx.flags = { force: true, _: [] };
|
|
const result = await deleteCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('deleted', true);
|
|
});
|
|
});
|
|
|
|
describe('memory stats', () => {
|
|
it.skip('should show memory statistics', async () => { // Skip: requires live memory service
|
|
const statsCmd = memoryCommand.subcommands?.find(c => c.name === 'stats');
|
|
expect(statsCmd).toBeDefined();
|
|
|
|
const result = await statsCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('entries');
|
|
expect(result.data).toHaveProperty('storage');
|
|
expect(result.data).toHaveProperty('backend');
|
|
expect(result.data).toHaveProperty('version');
|
|
});
|
|
});
|
|
|
|
describe('memory configure', () => {
|
|
it('should configure memory backend', async () => {
|
|
const configureCmd = memoryCommand.subcommands?.find(c => c.name === 'configure');
|
|
expect(configureCmd).toBeDefined();
|
|
|
|
ctx.flags = { backend: 'agentdb', _: [] };
|
|
const result = await configureCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('backend', 'agentdb');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Config Commands', () => {
|
|
let ctx: CommandContext;
|
|
|
|
beforeEach(() => {
|
|
ctx = {
|
|
args: [],
|
|
flags: { _: [] },
|
|
cwd: '/test',
|
|
interactive: false
|
|
};
|
|
});
|
|
|
|
describe('config init', () => {
|
|
it('should initialize configuration', async () => {
|
|
const initCmd = configCommand.subcommands?.find(c => c.name === 'init');
|
|
expect(initCmd).toBeDefined();
|
|
|
|
const result = await initCmd!.action!(ctx);
|
|
|
|
// #1425: config init is not yet implemented
|
|
expect(result.success).toBe(false);
|
|
expect(result.exitCode).toBe(1);
|
|
});
|
|
|
|
it('should initialize with V3 mode', async () => {
|
|
const initCmd = configCommand.subcommands?.find(c => c.name === 'init');
|
|
|
|
ctx.flags = { v3: true, _: [] };
|
|
const result = await initCmd!.action!(ctx);
|
|
|
|
// #1425: config init is not yet implemented
|
|
expect(result.success).toBe(false);
|
|
expect(result.exitCode).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('config get', () => {
|
|
it('should get configuration value', async () => {
|
|
const getCmd = configCommand.subcommands?.find(c => c.name === 'get');
|
|
expect(getCmd).toBeDefined();
|
|
|
|
ctx.args = ['swarm.topology'];
|
|
const result = await getCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data).toHaveProperty('key');
|
|
expect(result.data).toHaveProperty('value');
|
|
});
|
|
|
|
it('should show all config when no key provided', async () => {
|
|
const getCmd = configCommand.subcommands?.find(c => c.name === 'get');
|
|
|
|
const result = await getCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('config set', () => {
|
|
it('should set configuration value', async () => {
|
|
const setCmd = configCommand.subcommands?.find(c => c.name === 'set');
|
|
expect(setCmd).toBeDefined();
|
|
|
|
ctx.flags = { key: 'swarm.maxAgents', value: '20', _: [] };
|
|
const result = await setCmd!.action!(ctx);
|
|
|
|
// #1425: config set is not yet implemented
|
|
expect(result.success).toBe(false);
|
|
expect(result.exitCode).toBe(1);
|
|
});
|
|
|
|
it('should fail without key and value', async () => {
|
|
const setCmd = configCommand.subcommands?.find(c => c.name === 'set');
|
|
|
|
const result = await setCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('config providers', () => {
|
|
it('should list providers', async () => {
|
|
const providersCmd = configCommand.subcommands?.find(c => c.name === 'providers');
|
|
expect(providersCmd).toBeDefined();
|
|
|
|
const result = await providersCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(Array.isArray(result.data)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('config reset', () => {
|
|
it('should reset configuration', async () => {
|
|
const resetCmd = configCommand.subcommands?.find(c => c.name === 'reset');
|
|
expect(resetCmd).toBeDefined();
|
|
|
|
ctx.flags = { force: true, _: [] };
|
|
const result = await resetCmd!.action!(ctx);
|
|
|
|
// #1425: config reset is not yet implemented
|
|
expect(result.success).toBe(false);
|
|
expect(result.exitCode).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('config export', () => {
|
|
it('should export configuration', async () => {
|
|
const exportCmd = configCommand.subcommands?.find(c => c.name === 'export');
|
|
expect(exportCmd).toBeDefined();
|
|
|
|
const result = await exportCmd!.action!(ctx);
|
|
|
|
// #1425: config export is not yet implemented
|
|
expect(result.success).toBe(false);
|
|
expect(result.exitCode).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('config import', () => {
|
|
it('should import configuration', async () => {
|
|
const importCmd = configCommand.subcommands?.find(c => c.name === 'import');
|
|
expect(importCmd).toBeDefined();
|
|
|
|
ctx.flags = { file: './config.json', _: [] };
|
|
const result = await importCmd!.action!(ctx);
|
|
|
|
// #1425: config import is not yet implemented
|
|
expect(result.success).toBe(false);
|
|
expect(result.exitCode).toBe(1);
|
|
});
|
|
|
|
it('should fail without file path', async () => {
|
|
const importCmd = configCommand.subcommands?.find(c => c.name === 'import');
|
|
|
|
const result = await importCmd!.action!(ctx);
|
|
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
});
|