mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
195 lines
10 KiB
JavaScript
195 lines
10 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { existsSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import test from 'node:test';
|
|
import { createConsoleServer } from '../../src/console/server.mjs';
|
|
import { openControlPlane } from '../../src/control-plane/service.mjs';
|
|
import { analyzeRepository } from '../../src/kernel/analyze.mjs';
|
|
import { proposeDomains } from '../../src/kernel/domains.mjs';
|
|
import { inspectRepository } from '../../src/kernel/git.mjs';
|
|
import { installProject } from '../../src/kernel/install.mjs';
|
|
import { ScheduleLauncherService } from '../../src/schedules/launcher.mjs';
|
|
|
|
function fixture() {
|
|
const root = mkdtempSync(join(tmpdir(), 'torch-console-schedule-launcher-'));
|
|
const env = {
|
|
...process.env,
|
|
XDG_DATA_HOME: mkdtempSync(join(tmpdir(), 'torch-console-schedule-state-')),
|
|
XDG_CONFIG_HOME: mkdtempSync(join(tmpdir(), 'torch-console-schedule-config-')),
|
|
};
|
|
execFileSync('git', ['init', '-b', 'main', root]);
|
|
execFileSync('git', ['-C', root, 'config', 'user.email', '[email protected]']);
|
|
execFileSync('git', ['-C', root, 'config', 'user.name', 'TORCH Test']);
|
|
writeFileSync(join(root, 'app.js'), 'export const app = true;\n');
|
|
execFileSync('git', ['-C', root, 'add', '.']);
|
|
execFileSync('git', ['-C', root, 'commit', '-m', 'fixture']);
|
|
const repository = inspectRepository(root);
|
|
const proposal = proposeDomains({ repository, analysis: analyzeRepository(repository) });
|
|
proposal.schedules = [
|
|
{
|
|
id: 'manager-check-in', title: 'Session manager check-in', owner: 'session-manager',
|
|
lifetime: 'system', trigger: { type: 'interval', seconds: 900 }, behavior: 'coordination',
|
|
action: { type: 'manager-check-in', manager_id: 'session-manager' },
|
|
required_authority: ['owner'], retry: { max_attempts: 1 },
|
|
failure_recipient: 'session-manager', source_of_truth: 'active organization graph',
|
|
},
|
|
{
|
|
id: 'authorized-integration-drain', title: 'Authorized integration drain', owner: 'owner',
|
|
lifetime: 'system', trigger: { type: 'interval', seconds: 60 }, behavior: 'mutating',
|
|
action: { type: 'integration-drain', landing_authority_id: 'session-manager', limit: 20 },
|
|
required_authority: ['owner'], retry: { max_attempts: 1 },
|
|
failure_recipient: 'session-manager', source_of_truth: 'owner-approved integration policy',
|
|
},
|
|
];
|
|
proposal.review = {
|
|
status: 'approved', reviewedAt: '2026-09-28T00:00:00Z', reviewedBy: 'fixture-owner', notes: [],
|
|
};
|
|
installProject({ repository, proposal, env, projectId: 'console-schedule-fixture' });
|
|
return { root, env };
|
|
}
|
|
|
|
async function request(origin, path, payload) {
|
|
return fetch(`${origin}${path}`, {
|
|
method: 'POST', headers: {
|
|
origin, 'content-type': 'application/json', 'sec-fetch-site': 'same-origin',
|
|
}, body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
test('SCN-console-schedule-launcher: the owner reviews exact timer effects before a single audited install or refresh', async () => {
|
|
const context = fixture();
|
|
const systemdCalls = [];
|
|
const executor = (file, args) => {
|
|
systemdCalls.push({ file, args });
|
|
return { status: 0, stdout: '', stderr: '' };
|
|
};
|
|
const server = createConsoleServer({
|
|
repositoryRoot: context.root, env: context.env, scheduleLauncherExecutor: executor,
|
|
});
|
|
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
const origin = `http://127.0.0.1:${server.address().port}`;
|
|
try {
|
|
const initial = await fetch(`${origin}/api/snapshot`);
|
|
const initialSnapshot = await initial.json();
|
|
assert.equal(initialSnapshot.scheduleLauncher.installed, false);
|
|
const unitDirectory = join(context.env.XDG_CONFIG_HOME, 'systemd', 'user');
|
|
assert.equal(existsSync(unitDirectory), false);
|
|
|
|
const crossOrigin = await fetch(`${origin}/api/schedule-launcher/preview`, {
|
|
method: 'POST', headers: { origin: 'https://untrusted.example', 'content-type': 'application/json' },
|
|
body: JSON.stringify({ action: 'install' }),
|
|
});
|
|
assert.equal(crossOrigin.status, 403);
|
|
assert.deepEqual(systemdCalls, []);
|
|
|
|
const firstPreviewResponse = await request(origin, '/api/schedule-launcher/preview', { action: 'install' });
|
|
assert.equal(firstPreviewResponse.status, 200);
|
|
const firstPreview = await firstPreviewResponse.json();
|
|
assert.equal(firstPreview.plan.canProceed, true);
|
|
assert.equal(firstPreview.plan.mutationPerformed, false);
|
|
assert.equal(firstPreview.plan.systemSchedules.length, 2);
|
|
assert.equal(firstPreview.plan.systemSchedules.find((schedule) => schedule.id === 'authorized-integration-drain').behavior, 'mutating');
|
|
assert.deepEqual(firstPreview.plan.files.map((file) => file.name), [
|
|
firstPreview.plan.serviceName, firstPreview.plan.timerName,
|
|
]);
|
|
assert.match(firstPreview.plan.effect, /enables and starts the timer/);
|
|
assert.equal(existsSync(unitDirectory), false, 'preview must not create unit files');
|
|
assert.deepEqual(systemdCalls, [], 'preview must not call systemd');
|
|
|
|
const configPath = join(context.root, '.torch', 'torch.yaml');
|
|
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
config.schedules[0].title = 'Changed after preview';
|
|
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
const staleConfirmation = await request(origin, '/api/schedule-launcher', {
|
|
action: 'install', token: firstPreview.token, planHash: firstPreview.planHash,
|
|
});
|
|
assert.equal(staleConfirmation.status, 409);
|
|
assert.equal((await staleConfirmation.json()).error, 'SCHEDULE_LAUNCHER_PREVIEW_STALE');
|
|
assert.deepEqual(systemdCalls, []);
|
|
assert.equal(existsSync(unitDirectory), false);
|
|
|
|
const previewResponse = await request(origin, '/api/schedule-launcher/preview', { action: 'install' });
|
|
assert.equal(previewResponse.status, 200);
|
|
const preview = await previewResponse.json();
|
|
assert.equal(preview.plan.canProceed, true);
|
|
const confirmationPayload = {
|
|
action: 'install', token: preview.token, planHash: preview.planHash,
|
|
};
|
|
const confirmed = await request(origin, '/api/schedule-launcher', confirmationPayload);
|
|
assert.equal(confirmed.status, 200);
|
|
const result = await confirmed.json();
|
|
assert.equal(result.mutationPerformed, true);
|
|
assert.equal(result.auditEventId.length > 0, true);
|
|
assert.deepEqual(systemdCalls.map((call) => call.args), [
|
|
['--user', 'daemon-reload'],
|
|
['--user', 'enable', '--now', preview.plan.timerName],
|
|
]);
|
|
assert.equal(existsSync(join(unitDirectory, preview.plan.serviceName)), true);
|
|
assert.equal(existsSync(join(unitDirectory, preview.plan.timerName)), true);
|
|
|
|
const replay = await request(origin, '/api/schedule-launcher', confirmationPayload);
|
|
assert.equal(replay.status, 200);
|
|
assert.equal((await replay.json()).auditEventId, result.auditEventId);
|
|
assert.equal(systemdCalls.length, 2, 'replay cannot repeat systemd mutation');
|
|
const controlPlane = openControlPlane({ repositoryRoot: context.root, env: context.env });
|
|
try {
|
|
const audits = controlPlane.database.prepare(
|
|
"SELECT * FROM audit_events WHERE operation = 'schedule.launcher.install'",
|
|
).all();
|
|
assert.equal(audits.length, 1);
|
|
} finally { controlPlane.close(); }
|
|
|
|
const refreshed = await fetch(`${origin}/api/snapshot`);
|
|
const snapshot = await refreshed.json();
|
|
assert.equal(snapshot.scheduleLauncher.installed, true);
|
|
assert.equal(snapshot.scheduleLauncher.stale, false);
|
|
|
|
const updatedConfig = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
updatedConfig.schedules[0].title = 'Changed after installation';
|
|
writeFileSync(configPath, `${JSON.stringify(updatedConfig, null, 2)}\n`);
|
|
const reconcilePreviewResponse = await request(origin, '/api/schedule-launcher/preview', { action: 'reconcile' });
|
|
assert.equal(reconcilePreviewResponse.status, 200);
|
|
const reconcilePreview = await reconcilePreviewResponse.json();
|
|
assert.equal(reconcilePreview.plan.canProceed, true);
|
|
assert.equal(reconcilePreview.plan.stale, true);
|
|
assert.equal(reconcilePreview.plan.updates.some((entry) => entry.changed), true);
|
|
const reconcilePayload = {
|
|
action: 'reconcile', token: reconcilePreview.token, planHash: reconcilePreview.planHash,
|
|
};
|
|
const reconciled = await request(origin, '/api/schedule-launcher', reconcilePayload);
|
|
assert.equal(reconciled.status, 200);
|
|
assert.equal((await reconciled.json()).mutationPerformed, true);
|
|
assert.equal(systemdCalls.length, 4);
|
|
const reconcileReplay = await request(origin, '/api/schedule-launcher', reconcilePayload);
|
|
assert.equal(reconcileReplay.status, 200);
|
|
assert.equal(systemdCalls.length, 4, 'replayed refresh cannot repeat systemd mutation');
|
|
const finalSnapshot = await fetch(`${origin}/api/snapshot`);
|
|
assert.equal((await finalSnapshot.json()).scheduleLauncher.stale, false);
|
|
const auditControl = openControlPlane({ repositoryRoot: context.root, env: context.env });
|
|
try {
|
|
const audits = auditControl.database.prepare(
|
|
"SELECT operation FROM audit_events WHERE operation LIKE 'schedule.launcher.%' ORDER BY rowid",
|
|
).all();
|
|
assert.deepEqual(audits.map((event) => event.operation), [
|
|
'schedule.launcher.install', 'schedule.launcher.reconcile',
|
|
]);
|
|
} finally { auditControl.close(); }
|
|
assert.match(readFileSync(new URL('../../site/console.html', import.meta.url), 'utf8'), /data-schedule-launcher-confirm/);
|
|
const consoleScript = readFileSync(new URL('../../site/console.js', import.meta.url), 'utf8');
|
|
const consoleServer = readFileSync(new URL('../../src/console/server.mjs', import.meta.url), 'utf8');
|
|
assert.match(consoleScript, /\/api\/schedule-launcher\/preview/);
|
|
assert.match(consoleServer, /SCHEDULE_LAUNCHER_PREVIEW_STALE/);
|
|
} finally {
|
|
await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
|
}
|
|
|
|
const direct = new ScheduleLauncherService({
|
|
repositoryRoot: context.root, env: context.env,
|
|
executor: () => { throw new Error('expected-digest check must precede systemd'); },
|
|
});
|
|
assert.throws(() => direct.reconcile({ expectedDigest: 'out-of-date' }), (error) =>
|
|
error.code === 'SCHEDULE_LAUNCHER_PLAN_STALE');
|
|
});
|