mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 16:01:54 +00:00
434 lines
14 KiB
JavaScript
434 lines
14 KiB
JavaScript
import { describe, it, beforeEach } from 'node:test';
|
|
import assert from 'node:assert';
|
|
import { cmdCheck, cmdLock, cmdList, cmdComplete } 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,
|
|
readdir: async () => [],
|
|
};
|
|
|
|
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());
|
|
});
|
|
|
|
it('suppresses relay query loggers when quiet mode is enabled', async () => {
|
|
let receivedQueryDeps = null;
|
|
const deps = {
|
|
...baseDeps,
|
|
json: true,
|
|
quiet: true,
|
|
queryLocks: async (_relays, _cadence, _dateStr, _namespace, queryDeps) => {
|
|
receivedQueryDeps = queryDeps;
|
|
return [];
|
|
},
|
|
};
|
|
|
|
await cmdCheck('daily', deps);
|
|
|
|
assert.equal(typeof receivedQueryDeps.errorLogger, 'function');
|
|
assert.equal(typeof receivedQueryDeps.healthLogger, 'function');
|
|
});
|
|
});
|
|
|
|
describe('cmdLock', () => {
|
|
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 agent is already completed', async () => {
|
|
const existingLocks = [
|
|
{ agent: 'agent1', eventId: 'done-id', status: 'completed' }
|
|
];
|
|
const deps = {
|
|
...baseDeps,
|
|
queryLocks: async () => existingLocks,
|
|
};
|
|
|
|
await assert.rejects(
|
|
async () => await cmdLock('agent1', 'daily', false, deps),
|
|
(err) => err.code === 3 && err.message === 'Task already completed'
|
|
);
|
|
assert.match(logs.join('\n'), /LOCK_STATUS=denied/);
|
|
assert.match(logs.join('\n'), /LOCK_REASON=already_completed/);
|
|
});
|
|
|
|
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)
|
|
}];
|
|
},
|
|
publishLock: async () => {},
|
|
generateSecretKey: mockGenerateSecretKey,
|
|
getPublicKey: mockGetPublicKey,
|
|
finalizeEvent: mockFinalizeEvent,
|
|
raceCheckDelayMs: 1,
|
|
};
|
|
|
|
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('handles race condition with identical timestamps using eventId tie-breaker (we win)', async () => {
|
|
let callCount = 0;
|
|
const deps = {
|
|
...baseDeps,
|
|
queryLocks: async () => {
|
|
callCount++;
|
|
if (callCount === 1) return [];
|
|
return [{
|
|
agent: 'agent1',
|
|
eventId: 'aaaaa', // competitor (higher ID)
|
|
createdAt: 100,
|
|
createdAtIso: 'time'
|
|
}, {
|
|
agent: 'agent1',
|
|
eventId: 'mock-id', // our ID (we need to ensure mockFinalizeEvent returns this or we control comparison)
|
|
// The test mockFinalizeEvent returns 'mock-id'.
|
|
// 'mock-id' > 'aaaaa'. So we would lose if competitor is 'aaaaa'.
|
|
// To win, competitor must be 'zzzzz'.
|
|
// Let's re-check the logic.
|
|
// sort((a, b) => ... || a.id.localeCompare(b.id))
|
|
// If we are 'mock-id', and competitor is 'zzzzz'.
|
|
// 'mock-id'.localeCompare('zzzzz') is negative. So we come first.
|
|
// racingLocks[0] is us. We win.
|
|
createdAt: 100,
|
|
}];
|
|
},
|
|
publishLock: async () => {},
|
|
generateSecretKey: mockGenerateSecretKey,
|
|
getPublicKey: mockGetPublicKey,
|
|
finalizeEvent: (t) => ({ ...t, id: 'mock-id' }),
|
|
raceCheckDelayMs: 1,
|
|
};
|
|
|
|
// Competitor is 'aaaaa'. We are 'mock-id'. 'aaaaa' comes first. We lose.
|
|
// Wait, I want a test where we WIN.
|
|
// So competitor should be 'zzzzz'.
|
|
deps.queryLocks = async () => {
|
|
callCount++;
|
|
if (callCount === 1) return [];
|
|
return [{
|
|
agent: 'agent1',
|
|
eventId: 'zzzzz', // competitor (higher ID)
|
|
createdAt: 100,
|
|
createdAtIso: 'time'
|
|
}, {
|
|
agent: 'agent1',
|
|
eventId: 'mock-id', // our ID
|
|
createdAt: 100,
|
|
}];
|
|
};
|
|
|
|
const result = await cmdLock('agent1', 'daily', false, deps);
|
|
assert.strictEqual(result.status, 'ok');
|
|
assert.match(logs.join('\n'), /LOCK_STATUS=ok/);
|
|
});
|
|
|
|
it('handles race condition with identical timestamps using eventId tie-breaker (we lose)', async () => {
|
|
let callCount = 0;
|
|
const deps = {
|
|
...baseDeps,
|
|
queryLocks: async () => {
|
|
callCount++;
|
|
if (callCount === 1) return [];
|
|
return [{
|
|
agent: 'agent1',
|
|
eventId: 'aaaaa', // competitor (lower ID)
|
|
createdAt: 100,
|
|
createdAtIso: 'time'
|
|
}, {
|
|
agent: 'agent1',
|
|
eventId: 'mock-id', // our ID ('m' > 'a')
|
|
createdAt: 100,
|
|
}];
|
|
},
|
|
publishLock: async () => {},
|
|
generateSecretKey: mockGenerateSecretKey,
|
|
getPublicKey: mockGetPublicKey,
|
|
finalizeEvent: (t) => ({ ...t, id: 'mock-id' }),
|
|
raceCheckDelayMs: 1,
|
|
};
|
|
|
|
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\)/);
|
|
});
|
|
});
|
|
|
|
describe('cmdComplete', () => {
|
|
const mockGenerateSecretKey = () => new Uint8Array(32);
|
|
const mockGetPublicKey = () => 'mock-pubkey';
|
|
const mockFinalizeEvent = (t) => ({ ...t, id: 'mock-id-complete' });
|
|
|
|
it('successfully completes an active lock', async () => {
|
|
let publishedEvent = null;
|
|
const locks = [
|
|
{ agent: 'agent1', eventId: 'lock-id', createdAt: 100, createdAtIso: 'iso1', status: 'started' }
|
|
];
|
|
const deps = {
|
|
...baseDeps,
|
|
queryLocks: async () => locks,
|
|
publishLock: async (relays, evt) => { publishedEvent = evt; },
|
|
generateSecretKey: mockGenerateSecretKey,
|
|
getPublicKey: mockGetPublicKey,
|
|
finalizeEvent: mockFinalizeEvent,
|
|
};
|
|
|
|
const result = await cmdComplete('agent1', 'daily', false, deps);
|
|
|
|
assert.strictEqual(result.status, 'completed');
|
|
assert.strictEqual(result.eventId, 'mock-id-complete');
|
|
assert.ok(publishedEvent);
|
|
// Verify no expiration tag
|
|
const expTag = publishedEvent.tags.find(t => t[0] === 'expiration');
|
|
assert.strictEqual(expTag, undefined);
|
|
// Verify content
|
|
const content = JSON.parse(publishedEvent.content);
|
|
assert.strictEqual(content.status, 'completed');
|
|
assert.strictEqual(content.startedAt, 'iso1');
|
|
assert.match(logs.join('\n'), /LOCK_STATUS=completed/);
|
|
});
|
|
|
|
it('fails if no active lock found', async () => {
|
|
const deps = {
|
|
...baseDeps,
|
|
queryLocks: async () => [], // No locks
|
|
};
|
|
|
|
await assert.rejects(
|
|
async () => await cmdComplete('agent1', 'daily', false, deps),
|
|
(err) => err.code === 1 && err.message === 'No active lock found'
|
|
);
|
|
});
|
|
|
|
it('detects already completed task', async () => {
|
|
const locks = [
|
|
{ agent: 'agent1', eventId: 'done-id', status: 'completed' }
|
|
];
|
|
const deps = {
|
|
...baseDeps,
|
|
queryLocks: async () => locks,
|
|
};
|
|
|
|
const result = await cmdComplete('agent1', 'daily', false, deps);
|
|
assert.strictEqual(result.status, 'completed');
|
|
assert.strictEqual(result.eventId, 'done-id');
|
|
assert.match(logs.join('\n'), /LOCK_STATUS=completed/);
|
|
});
|
|
});
|
|
});
|