mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:00:24 +08:00
163 lines
4.7 KiB
JavaScript
163 lines
4.7 KiB
JavaScript
require('ts-node/register/transpile-only');
|
|
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs/promises');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const { afterEach, test } = require('node:test');
|
|
const {
|
|
LegacyShadowCaptureAuthority,
|
|
} = require('../../back/runtime/application/legacyShadowCaptureAuthority');
|
|
const {
|
|
bootstrapLegacyShadowCaptureEvidence,
|
|
} = require('../../back/runtime/adapters/legacy/bootstrapLegacyShadowCaptureEvidence');
|
|
|
|
const directories = [];
|
|
|
|
function startup(state = 'reconciled') {
|
|
if (state !== 'reconciled') return { state };
|
|
return {
|
|
state: 'reconciled',
|
|
profile: 'edge',
|
|
origins: 1,
|
|
summary: {},
|
|
metrics: {},
|
|
report: {
|
|
schema: 'qinglong/legacy-shadow-startup-difference-report@v1',
|
|
schemaVersion: 1,
|
|
profile: 'edge',
|
|
assessment: 'converged',
|
|
configuredOriginCount: 1,
|
|
budget: { pageSize: 8, maxPages: 1, maxCandidates: 8 },
|
|
coverage: {
|
|
pages: 1,
|
|
scanned: 0,
|
|
stopReason: 'complete',
|
|
remaining: false,
|
|
resumeAvailable: false,
|
|
},
|
|
outcomes: {
|
|
completed: 0,
|
|
cancelled: 0,
|
|
abandoned: 0,
|
|
markedLost: 0,
|
|
repaired: 0,
|
|
pending: 0,
|
|
ambiguous: 0,
|
|
skipped: 0,
|
|
failed: 0,
|
|
},
|
|
byOrigin: [
|
|
{
|
|
origin: 'manual',
|
|
scanned: 0,
|
|
completed: 0,
|
|
cancelled: 0,
|
|
abandoned: 0,
|
|
markedLost: 0,
|
|
repaired: 0,
|
|
pending: 0,
|
|
ambiguous: 0,
|
|
skipped: 0,
|
|
failed: 0,
|
|
},
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(
|
|
directories
|
|
.splice(0)
|
|
.map((directory) => fs.rm(directory, { recursive: true, force: true })),
|
|
);
|
|
});
|
|
|
|
test('exports one qualified no-identity capture report on clean shutdown', async () => {
|
|
let now = 1_750_300_000_000;
|
|
const authority = new LegacyShadowCaptureAuthority(
|
|
{ now: () => now },
|
|
'019f75d2-3333-7333-8333-333333333333',
|
|
);
|
|
let evidence;
|
|
const audits = [];
|
|
const handle = await bootstrapLegacyShadowCaptureEvidence({
|
|
startup: startup(),
|
|
origins: ['manual'],
|
|
profile: 'edge',
|
|
outputPath: '/private/evidence.json',
|
|
snapshot: (origins) => authority.snapshot(origins),
|
|
async write(_outputPath, value) {
|
|
evidence = value;
|
|
},
|
|
audit: (record) => audits.push(record),
|
|
});
|
|
for (let index = 0; index < 8; index += 1) {
|
|
authority.admit('manual').captured();
|
|
}
|
|
now += 1_000;
|
|
|
|
assert.equal(handle.active, true);
|
|
assert.equal((await handle.close()).state, 'exported');
|
|
assert.deepEqual(await handle.close(), audits.at(-1));
|
|
assert.equal(evidence.qualification.passed, true);
|
|
assert.equal(evidence.capture.totals.admitted, 8);
|
|
assert.equal(evidence.capture.capturePermille, 1_000);
|
|
assert.doesNotMatch(JSON.stringify(evidence), /taskId|runId|attemptId|pid/);
|
|
assert.deepEqual(
|
|
audits.map((record) => record.state),
|
|
['armed', 'exported'],
|
|
);
|
|
});
|
|
|
|
test('writes owner-private evidence once and refuses overwrite', async () => {
|
|
const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'ql3-capture-'));
|
|
directories.push(directory);
|
|
const outputPath = path.join(directory, 'capture.json');
|
|
let now = 1_750_300_000_000;
|
|
const authority = new LegacyShadowCaptureAuthority(
|
|
{ now: () => now },
|
|
'019f75d2-4444-7444-8444-444444444444',
|
|
);
|
|
const options = {
|
|
startup: startup(),
|
|
origins: ['manual'],
|
|
profile: 'edge',
|
|
outputPath,
|
|
snapshot: (origins) => authority.snapshot(origins),
|
|
audit() {},
|
|
};
|
|
const first = await bootstrapLegacyShadowCaptureEvidence(options);
|
|
authority.admit('manual').captured();
|
|
now += 1_000;
|
|
assert.equal((await first.close()).state, 'exported');
|
|
assert.equal((await fs.stat(outputPath)).mode & 0o777, 0o600);
|
|
|
|
const second = await bootstrapLegacyShadowCaptureEvidence(options);
|
|
authority.admit('manual').captured();
|
|
now += 1_000;
|
|
assert.equal((await second.close()).state, 'failed');
|
|
});
|
|
|
|
test('stays inert without an explicit path and rejects missing startup authority', async () => {
|
|
const disabled = await bootstrapLegacyShadowCaptureEvidence({
|
|
startup: { state: 'disabled' },
|
|
origins: [],
|
|
profile: 'edge',
|
|
audit() {},
|
|
});
|
|
assert.equal(disabled.active, false);
|
|
assert.equal((await disabled.close()).state, 'disabled');
|
|
|
|
const failed = await bootstrapLegacyShadowCaptureEvidence({
|
|
startup: { state: 'incomplete' },
|
|
origins: ['manual'],
|
|
profile: 'edge',
|
|
outputPath: '/private/evidence.json',
|
|
audit() {},
|
|
});
|
|
assert.equal(failed.active, false);
|
|
assert.equal((await failed.close()).state, 'failed');
|
|
});
|