mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:17:47 +08:00
138 lines
4.1 KiB
JavaScript
138 lines
4.1 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
LegacyCrontabAdoptionDecisionReviewFileError,
|
|
withPrivateLegacyCrontabAdoptionDecisionReviewFile,
|
|
} = require('../dist/legacy-adoption/legacyCrontabDecisionIssuer');
|
|
|
|
const DECISION_ID = '019a2b3c-4d5e-7f60-8123-456789abcdef';
|
|
const PLAN_DIGEST = 'a'.repeat(64);
|
|
const INVENTORY_DIGEST = 'b'.repeat(64);
|
|
const DECISION = Object.freeze({
|
|
rowOrdinal: 1,
|
|
sourceDigest: 'c'.repeat(64),
|
|
disposition: 'adopt',
|
|
reason: 'reviewed_lossless',
|
|
});
|
|
|
|
function fixture(t) {
|
|
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-review-file-'));
|
|
fs.chmodSync(directory, 0o700);
|
|
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
|
const filePath = path.join(directory, 'review.ndjson');
|
|
const records = [
|
|
{
|
|
schemaVersion: 1,
|
|
kind: 'qinglong3-legacy-crontab-decision-review-file-header',
|
|
decisionId: DECISION_ID,
|
|
profile: 'edge',
|
|
planDigest: PLAN_DIGEST,
|
|
inventoryDigest: INVENTORY_DIGEST,
|
|
},
|
|
{
|
|
schemaVersion: 1,
|
|
kind: 'qinglong3-legacy-crontab-decision-review-file-row',
|
|
decision: DECISION,
|
|
},
|
|
];
|
|
fs.writeFileSync(
|
|
filePath,
|
|
`${records.map((record) => JSON.stringify(record)).join('\n')}\n`,
|
|
{ mode: 0o600 },
|
|
);
|
|
return { directory, filePath };
|
|
}
|
|
|
|
function options(filePath) {
|
|
return {
|
|
filePath,
|
|
expectedDecisionId: DECISION_ID,
|
|
expectedProfile: 'edge',
|
|
expectedPlanDigest: PLAN_DIGEST,
|
|
expectedInventoryDigest: INVENTORY_DIGEST,
|
|
};
|
|
}
|
|
|
|
test('streams repeatable decisions from one authenticated private descriptor', async (t) => {
|
|
const value = fixture(t);
|
|
const result = await withPrivateLegacyCrontabAdoptionDecisionReviewFile(
|
|
options(value.filePath),
|
|
(scope) => {
|
|
assert.equal(scope.evidence.decisionCount, 1);
|
|
assert.match(scope.evidence.fileDigest, /^[0-9a-f]{64}$/);
|
|
assert.deepEqual([...scope.decisions], [DECISION]);
|
|
assert.deepEqual([...scope.decisions], [DECISION]);
|
|
scope.confirmIdentity();
|
|
return scope.evidence.fileDigest;
|
|
},
|
|
);
|
|
assert.match(result, /^[0-9a-f]{64}$/);
|
|
});
|
|
|
|
test('fails closed on private-path violations and in-flight replacement', async (t) => {
|
|
const value = fixture(t);
|
|
fs.chmodSync(value.filePath, 0o640);
|
|
await assert.rejects(
|
|
withPrivateLegacyCrontabAdoptionDecisionReviewFile(
|
|
options(value.filePath),
|
|
() => undefined,
|
|
),
|
|
LegacyCrontabAdoptionDecisionReviewFileError,
|
|
);
|
|
fs.chmodSync(value.filePath, 0o600);
|
|
|
|
const linkPath = path.join(value.directory, 'review-link.ndjson');
|
|
fs.symlinkSync(value.filePath, linkPath);
|
|
await assert.rejects(
|
|
withPrivateLegacyCrontabAdoptionDecisionReviewFile(
|
|
options(linkPath),
|
|
() => undefined,
|
|
),
|
|
LegacyCrontabAdoptionDecisionReviewFileError,
|
|
);
|
|
|
|
await assert.rejects(
|
|
withPrivateLegacyCrontabAdoptionDecisionReviewFile(
|
|
options(value.filePath),
|
|
() => {
|
|
const replacement = path.join(value.directory, 'replacement.ndjson');
|
|
fs.copyFileSync(value.filePath, replacement);
|
|
fs.chmodSync(replacement, 0o600);
|
|
fs.renameSync(replacement, value.filePath);
|
|
},
|
|
),
|
|
LegacyCrontabAdoptionDecisionReviewFileError,
|
|
);
|
|
});
|
|
|
|
test('rejects widened records and mismatched review identity', async (t) => {
|
|
const value = fixture(t);
|
|
const widened = fs
|
|
.readFileSync(value.filePath, 'utf8')
|
|
.replace(
|
|
'"reason":"reviewed_lossless"',
|
|
'"reason":"reviewed_lossless","override":true',
|
|
);
|
|
fs.writeFileSync(value.filePath, widened, { mode: 0o600 });
|
|
await assert.rejects(
|
|
withPrivateLegacyCrontabAdoptionDecisionReviewFile(
|
|
options(value.filePath),
|
|
() => undefined,
|
|
),
|
|
LegacyCrontabAdoptionDecisionReviewFileError,
|
|
);
|
|
|
|
const fresh = fixture(t);
|
|
await assert.rejects(
|
|
withPrivateLegacyCrontabAdoptionDecisionReviewFile(
|
|
{ ...options(fresh.filePath), expectedInventoryDigest: 'd'.repeat(64) },
|
|
() => undefined,
|
|
),
|
|
LegacyCrontabAdoptionDecisionReviewFileError,
|
|
);
|
|
});
|