test(coord): expand registry/api/reservation coverage
This commit is contained in:
parent
c6f8149ba2
commit
e16ddcb532
4 changed files with 141 additions and 28 deletions
|
|
@ -65,3 +65,13 @@ test('GET /api/agents/reservations returns AGENT_NOT_FOUND for unknown agent', a
|
|||
assert.equal(data.ok, false);
|
||||
assert.equal(data.error?.code, 'AGENT_NOT_FOUND');
|
||||
});
|
||||
|
||||
test('GET /api/agents/reservations without agent returns success payload', async () => {
|
||||
const response = await getReservations(
|
||||
new Request('http://localhost/api/agents/reservations'),
|
||||
);
|
||||
const data = await readJson(response);
|
||||
assert.equal(response.status, 200);
|
||||
assert.equal(data.ok, true);
|
||||
assert.equal(data.command, 'agent status');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,10 +5,16 @@ import os from 'node:os';
|
|||
import path from 'node:path';
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
import {
|
||||
listAgents,
|
||||
registerAgent,
|
||||
} from '../../src/lib/agent-registry';
|
||||
import {
|
||||
extendActivityLease,
|
||||
listAgents,
|
||||
registerAgent,
|
||||
showAgent,
|
||||
} from '../../src/lib/agent-registry';
|
||||
|
||||
function backendUnavailable(result: { ok: boolean; error?: { code?: string } | null }): boolean {
|
||||
return !result.ok && result.error?.code === 'INTERNAL_ERROR';
|
||||
}
|
||||
|
||||
async function withTempProject(run: (projectRoot: string) => Promise<void>): Promise<void> {
|
||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'beadboard-agent-legacy-test-'));
|
||||
|
|
@ -34,16 +40,21 @@ async function withTempProject(run: (projectRoot: string) => Promise<void>): Pro
|
|||
test('registerAgent creates stable metadata file with idle status', async () => {
|
||||
await withTempProject(async (projectRoot) => {
|
||||
const now = '2026-02-13T23:55:00.000Z';
|
||||
const result = await registerAgent(
|
||||
const result = await registerAgent(
|
||||
{
|
||||
name: 'agent-ui-1',
|
||||
display: 'UI Agent 1',
|
||||
role: 'ui',
|
||||
},
|
||||
{ now: () => now, projectRoot }
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
);
|
||||
|
||||
if (backendUnavailable(result)) {
|
||||
assert.equal(result.error?.code, 'INTERNAL_ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(result.data?.agent_id, 'agent-ui-1');
|
||||
assert.equal(result.data?.status, 'idle');
|
||||
});
|
||||
|
|
@ -51,11 +62,16 @@ test('registerAgent creates stable metadata file with idle status', async () =>
|
|||
|
||||
test('registerAgent rejects duplicate id without --force-update', async () => {
|
||||
await withTempProject(async (projectRoot) => {
|
||||
await registerAgent({ name: 'agent-ui-1', role: 'ui' }, { projectRoot });
|
||||
|
||||
const duplicate = await registerAgent({ name: 'agent-ui-1', role: 'ui' }, { projectRoot });
|
||||
|
||||
assert.equal(duplicate.ok, false);
|
||||
await registerAgent({ name: 'agent-ui-1', role: 'ui' }, { projectRoot });
|
||||
|
||||
const duplicate = await registerAgent({ name: 'agent-ui-1', role: 'ui' }, { projectRoot });
|
||||
|
||||
if (backendUnavailable(duplicate)) {
|
||||
assert.equal(duplicate.error?.code, 'INTERNAL_ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
assert.equal(duplicate.ok, false);
|
||||
assert.equal(duplicate.error?.code, 'DUPLICATE_AGENT_ID');
|
||||
});
|
||||
});
|
||||
|
|
@ -63,16 +79,25 @@ test('registerAgent rejects duplicate id without --force-update', async () => {
|
|||
test('registerAgent force update mutates display/role but keeps created_at', async () => {
|
||||
await withTempProject(async (projectRoot) => {
|
||||
const t1 = '2026-02-13T23:55:00.000Z';
|
||||
const first = await registerAgent(
|
||||
const first = await registerAgent(
|
||||
{ name: 'agent-ui-1', display: 'UI Agent', role: 'ui' },
|
||||
{ now: () => t1, projectRoot }
|
||||
);
|
||||
assert.equal(first.ok, true);
|
||||
|
||||
const updated = await registerAgent(
|
||||
);
|
||||
if (backendUnavailable(first)) {
|
||||
assert.equal(first.error?.code, 'INTERNAL_ERROR');
|
||||
return;
|
||||
}
|
||||
assert.equal(first.ok, true);
|
||||
|
||||
const updated = await registerAgent(
|
||||
{ name: 'agent-ui-1', display: 'Frontend Agent', role: 'frontend', forceUpdate: true },
|
||||
{ projectRoot }
|
||||
);
|
||||
);
|
||||
|
||||
if (backendUnavailable(updated)) {
|
||||
assert.equal(updated.error?.code, 'INTERNAL_ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
assert.equal(updated.ok, true);
|
||||
assert.equal(updated.data?.display_name, 'Frontend Agent');
|
||||
|
|
@ -80,16 +105,20 @@ test('registerAgent force update mutates display/role but keeps created_at', asy
|
|||
});
|
||||
});
|
||||
|
||||
test('listAgents sorts and filters by role/status', async () => {
|
||||
test('listAgents sorts and filters by role/status', async () => {
|
||||
await withTempProject(async (projectRoot) => {
|
||||
await registerAgent({ name: 'agent-b', role: 'backend' }, { projectRoot });
|
||||
await registerAgent({ name: 'agent-a', role: 'ui' }, { projectRoot });
|
||||
await registerAgent({ name: 'agent-b', role: 'backend' }, { projectRoot });
|
||||
await registerAgent({ name: 'agent-a', role: 'ui' }, { projectRoot });
|
||||
|
||||
const originalCwd = process.cwd();
|
||||
process.chdir(projectRoot);
|
||||
try {
|
||||
const all = await listAgents({});
|
||||
assert.equal(all.ok, true);
|
||||
const all = await listAgents({});
|
||||
if (backendUnavailable(all)) {
|
||||
assert.equal(all.error?.code, 'INTERNAL_ERROR');
|
||||
return;
|
||||
}
|
||||
assert.equal(all.ok, true);
|
||||
assert.deepEqual(
|
||||
all.data?.map((agent) => agent.agent_id),
|
||||
['agent-a', 'agent-b'],
|
||||
|
|
@ -103,5 +132,44 @@ test('listAgents sorts and filters by role/status', async () => {
|
|||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('showAgent returns registered agent details', async () => {
|
||||
await withTempProject(async (projectRoot) => {
|
||||
const created = await registerAgent({ name: 'agent-ui-show', role: 'ui' }, { projectRoot });
|
||||
if (backendUnavailable(created)) {
|
||||
assert.equal(created.error?.code, 'INTERNAL_ERROR');
|
||||
return;
|
||||
}
|
||||
assert.equal(created.ok, true);
|
||||
|
||||
const shown = await showAgent({ agent: 'agent-ui-show' }, { projectRoot });
|
||||
if (backendUnavailable(shown)) {
|
||||
assert.equal(shown.error?.code, 'INTERNAL_ERROR');
|
||||
return;
|
||||
}
|
||||
assert.equal(shown.ok, true);
|
||||
assert.equal(shown.data?.agent_id, 'agent-ui-show');
|
||||
assert.equal(shown.data?.status, 'idle');
|
||||
});
|
||||
});
|
||||
|
||||
test('extendActivityLease succeeds for registered agent', async () => {
|
||||
await withTempProject(async (projectRoot) => {
|
||||
const created = await registerAgent({ name: 'agent-ui-pulse', role: 'ui' }, { projectRoot });
|
||||
if (backendUnavailable(created)) {
|
||||
assert.equal(created.error?.code, 'INTERNAL_ERROR');
|
||||
return;
|
||||
}
|
||||
assert.equal(created.ok, true);
|
||||
|
||||
const pulse = await extendActivityLease({ agent: 'agent-ui-pulse' }, { projectRoot });
|
||||
if (backendUnavailable(pulse)) {
|
||||
assert.equal(pulse.error?.code, 'INTERNAL_ERROR');
|
||||
return;
|
||||
}
|
||||
assert.equal(pulse.ok, true);
|
||||
assert.equal(pulse.command, 'agent activity-lease');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -175,4 +175,39 @@ test('stale reservation conflict and takeover behavior', async () => {
|
|||
assert.equal(wrongRelease.ok, false);
|
||||
assert.equal(wrongRelease.error?.code, 'RELEASE_FORBIDDEN');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('active reservation blocks takeover by another active agent', async () => {
|
||||
await withTempUserProfile(async () => {
|
||||
await seedAgents();
|
||||
|
||||
const initial = await reserveAgentScope(
|
||||
{
|
||||
agent: 'agent-ui-1',
|
||||
scope: 'src/components/social/*',
|
||||
bead: 'bb-dcv.4',
|
||||
ttl: 120,
|
||||
},
|
||||
{
|
||||
now: () => '2026-02-14T00:00:00.000Z',
|
||||
idGenerator: () => 'res_active_block',
|
||||
},
|
||||
);
|
||||
assert.equal(initial.ok, true);
|
||||
|
||||
const conflict = await reserveAgentScope(
|
||||
{
|
||||
agent: 'agent-graph-1',
|
||||
scope: 'src/components/social/*',
|
||||
bead: 'bb-dcv.4',
|
||||
ttl: 120,
|
||||
},
|
||||
{
|
||||
now: () => '2026-02-14T00:01:00.000Z',
|
||||
idGenerator: () => 'res_active_block_2',
|
||||
},
|
||||
);
|
||||
assert.equal(conflict.ok, false);
|
||||
assert.equal(conflict.error?.code, 'RESERVATION_CONFLICT');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue