Files
TORCH/test/lib.test.mjs
T
google-labs-jules[bot]andPR0M3TH3AN 9eda853e7e feat: add unit tests for src/lib.mjs via dependency injection
Refactor src/lib.mjs to support dependency injection for cmdCheck, cmdLock, and cmdList.
This allows unit testing without heavy mocking frameworks or side effects.
Add test/lib.test.mjs covering:
- cmdCheck: happy path, paused/excluded agents, unknown agents.
- cmdLock: success, roster check, existing lock check, race check (lost), dry run.
- cmdList: listing locks, empty state.

Coverage for src/lib.mjs is now significantly improved.
Verification: Ran `npm test` and verified all tests pass.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-14 14:23:03 +00:00

263 lines
8.5 KiB
JavaScript

import { describe, it, beforeEach } from 'node:test';
import assert from 'node:assert';
import { cmdCheck, cmdLock, cmdList } from '../src/lib.mjs';
describe('src/lib.mjs', () => {
// Common mocks
const mockRelays = ['wss://relay.test'];
const mockNamespace = 'test-ns';
const mockTtl = 3600;
const mockRoster = ['agent1', 'agent2', 'agent3'];
const mockDateStr = new Date().toISOString().slice(0, 10);
let logs = [];
let errors = [];
const mockLog = (...args) => logs.push(args.join(' '));
const mockError = (...args) => errors.push(args.join(' '));
const baseDeps = {
getRelays: () => mockRelays,
getNamespace: () => mockNamespace,
getTtl: () => mockTtl,
getRoster: () => mockRoster,
loadTorchConfig: () => ({ scheduler: { paused: { daily: [], weekly: [] } } }),
getDateStr: () => mockDateStr,
log: mockLog,
error: mockError,
};
beforeEach(() => {
logs = [];
errors = [];
});
describe('cmdCheck', () => {
it('returns correct structure with empty locks', async () => {
const deps = {
...baseDeps,
queryLocks: async () => [],
};
const result = await cmdCheck('daily', deps);
assert.strictEqual(result.namespace, mockNamespace);
assert.strictEqual(result.cadence, 'daily');
assert.strictEqual(result.date, mockDateStr);
assert.deepStrictEqual(result.locked, []);
assert.deepStrictEqual(result.available, mockRoster.sort());
assert.strictEqual(result.lockCount, 0);
});
it('identifies locked agents', async () => {
const locks = [
{ agent: 'agent1', eventId: 'ev1', createdAtIso: 'iso1', platform: 'test' }
];
const deps = {
...baseDeps,
queryLocks: async () => locks,
};
const result = await cmdCheck('daily', deps);
assert.deepStrictEqual(result.locked, ['agent1']);
assert.deepStrictEqual(result.available, ['agent2', 'agent3']);
assert.strictEqual(result.lockCount, 1);
});
it('excludes paused agents', async () => {
const deps = {
...baseDeps,
queryLocks: async () => [],
loadTorchConfig: () => ({ scheduler: { paused: { daily: ['agent2'], weekly: [] } } }),
};
const result = await cmdCheck('daily', deps);
assert.deepStrictEqual(result.paused, ['agent2']);
assert.deepStrictEqual(result.excluded, ['agent2']);
assert.deepStrictEqual(result.available, ['agent1', 'agent3']);
});
it('identifies unknown locked agents', async () => {
const locks = [
{ agent: 'unknown-agent', eventId: 'ev1' }
];
const deps = {
...baseDeps,
queryLocks: async () => locks,
};
const result = await cmdCheck('daily', deps);
assert.deepStrictEqual(result.unknownLockedAgents, ['unknown-agent']);
assert.deepStrictEqual(result.locked, ['unknown-agent']);
// unknown agents are not in roster, so available should be full roster (since no roster agent is locked)
assert.deepStrictEqual(result.available, mockRoster.sort());
});
});
describe('cmdLock', () => {
const mockPublishLock = async () => {};
const mockGenerateSecretKey = () => new Uint8Array(32);
const mockGetPublicKey = () => 'mock-pubkey';
const mockFinalizeEvent = (t) => ({ ...t, id: 'mock-id', sig: 'mock-sig' });
it('successfully locks an available agent', async () => {
let published = false;
const deps = {
...baseDeps,
queryLocks: async () => [], // No existing locks, and race check returns empty
publishLock: async () => { published = true; },
generateSecretKey: mockGenerateSecretKey,
getPublicKey: mockGetPublicKey,
finalizeEvent: mockFinalizeEvent,
raceCheckDelayMs: 1, // Fast race check
};
const result = await cmdLock('agent1', 'daily', false, deps);
assert.strictEqual(result.status, 'ok');
assert.strictEqual(result.eventId, 'mock-id');
assert.strictEqual(published, true);
assert.match(logs.join('\n'), /LOCK_STATUS=ok/);
});
it('fails if agent is not in roster', async () => {
const deps = { ...baseDeps };
await assert.rejects(
async () => await cmdLock('unknown', 'daily', false, deps),
(err) => err.code === 1 && err.message === 'Agent not in roster'
);
});
it('fails if agent is already locked', async () => {
const existingLocks = [
{ agent: 'agent1', eventId: 'existing', createdAt: 100, createdAtIso: 'time' }
];
const deps = {
...baseDeps,
queryLocks: async () => existingLocks,
};
await assert.rejects(
async () => await cmdLock('agent1', 'daily', false, deps),
(err) => err.code === 3 && err.message === 'Lock denied'
);
assert.match(logs.join('\n'), /LOCK_STATUS=denied/);
});
it('fails if race check is lost', async () => {
let callCount = 0;
const deps = {
...baseDeps,
queryLocks: async () => {
callCount++;
if (callCount === 1) return []; // First check: no locks
// Second check (race): found an earlier lock
return [{
agent: 'agent1',
eventId: 'winner',
createdAt: 100, // old timestamp
createdAtIso: 'old-time'
}, {
agent: 'agent1',
eventId: 'mock-id',
createdAt: 200, // our timestamp (mocked event)
// We need to ensure cmdLock sees this as later.
// In cmdLock, `now` is mocked via verify? No, `now` is `nowUnix()`.
// But we can't easily mock `nowUnix` inside `cmdLock` without injecting it too.
// However, `cmdLock` compares `createdAt`.
// The mocked event has `created_at: now`.
// If we return a lock with smaller `createdAt`, we lose.
}];
},
publishLock: async () => {},
generateSecretKey: mockGenerateSecretKey,
getPublicKey: mockGetPublicKey,
finalizeEvent: mockFinalizeEvent,
raceCheckDelayMs: 1,
};
// We need to make sure the "winner" has explicit createdAt that is definitely before what cmdLock generates.
// cmdLock uses Math.floor(Date.now() / 1000).
// We can't mock Date.now easily unless we inject it.
// But we can inject `finalizeEvent` to control the created timestamp of our event?
// No, `finalizeEvent` receives the template.
// Let's rely on the fact that `cmdLock` uses `nowUnix()`.
// The "winner" in `queryLocks` return can have `createdAt: 0`.
await assert.rejects(
async () => await cmdLock('agent1', 'daily', false, deps),
(err) => err.code === 3 && err.message === 'Race check lost'
);
assert.match(logs.join('\n'), /LOCK_STATUS=race_lost/);
});
it('dry run does not publish', async () => {
let published = false;
const deps = {
...baseDeps,
queryLocks: async () => [],
publishLock: async () => { published = true; },
generateSecretKey: mockGenerateSecretKey,
getPublicKey: mockGetPublicKey,
finalizeEvent: mockFinalizeEvent,
};
const result = await cmdLock('agent1', 'daily', true, deps);
assert.strictEqual(result.status, 'ok');
assert.strictEqual(published, false);
assert.match(errors.join('\n'), /\[DRY RUN\]/);
});
});
describe('cmdList', () => {
it('lists active locks', async () => {
const locks = [
{ agent: 'agent1', eventId: 'ev1', createdAt: Date.now()/1000 - 60, createdAtIso: 'iso1', platform: 'test' }
];
const deps = {
...baseDeps,
queryLocks: async () => locks,
};
await cmdList('daily', deps);
const output = logs.join('\n');
assert.match(output, /Active test-ns daily locks/);
assert.match(output, /agent1/);
assert.match(output, /Locked: 1\/3/);
});
it('warns about unknown agents', async () => {
const locks = [
{ agent: 'unknown', eventId: 'ev1', createdAt: Date.now()/1000 }
];
const deps = {
...baseDeps,
queryLocks: async () => locks,
};
await cmdList('daily', deps);
const output = logs.join('\n');
assert.match(output, /Warning: lock events found with non-roster agent names: unknown/);
});
it('handles no locks', async () => {
const deps = {
...baseDeps,
queryLocks: async () => [],
};
await cmdList('daily', deps);
const output = logs.join('\n');
assert.match(output, /\(no active locks\)/);
});
});
});