Files
qinglong/test/back/ql3ClusterAlphaMilestone.test.cjs

243 lines
7.9 KiB
JavaScript

'use strict';
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 {
ROLES,
createClusterAlphaBundle,
createClusterAlphaVerificationEvidence,
} = require('../../scripts/ql3-cluster-alpha-bundle.cjs');
const {
SUBJECTS,
auditClusterAlphaMilestone,
auditClusterAlphaMilestoneWorkflow,
finalizeClusterAlphaMilestone,
} = require('../../scripts/ql3-cluster-alpha-milestone.cjs');
const {
createClusterImageSbom,
} = require('../../scripts/ql3-cluster-image-sbom.cjs');
const {
readReleaseIdentity,
} = require('../../scripts/lib/ql3-release-identity.cjs');
const root = path.resolve(__dirname, '../..');
const version = readReleaseIdentity(root).version;
const revision = 'b'.repeat(40);
const workflowRef =
'whyour/qinglong/.github/workflows/ql3-ci.yml@refs/heads/next';
function splitSubject(subject) {
const architecture = subject.endsWith('-amd64') ? 'amd64' : 'arm64';
return { role: subject.slice(0, -(architecture.length + 1)), architecture };
}
function inspection(role, architecture, idCharacter) {
const config = ROLES[role];
return {
Id: `sha256:${idCharacter.repeat(64)}`,
Os: 'linux',
Architecture: architecture,
Config: {
User: config.user,
Labels: {
'org.opencontainers.image.title': config.title,
'org.opencontainers.image.source': 'https://github.com/whyour/qinglong',
'org.opencontainers.image.revision': revision,
'org.opencontainers.image.version': version,
},
},
};
}
function fixture(t, runId = '33073349397') {
const fixtureRoot = fs.realpathSync(
fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-cluster-alpha-milestone-')),
);
t.after(() => fs.rmSync(fixtureRoot, { recursive: true, force: true }));
const readme = path.join(fixtureRoot, 'README-source.md');
fs.writeFileSync(readme, '# Cluster Alpha milestone\n');
const sboms = {};
for (const role of Object.keys(ROLES)) {
sboms[role] = path.join(fixtureRoot, `${role}.cdx.json`);
fs.writeFileSync(
sboms[role],
`${JSON.stringify(createClusterImageSbom({ root, image: role }))}\n`,
);
}
const bundles = {};
SUBJECTS.forEach((subject, index) => {
const { role, architecture } = splitSubject(subject);
const idCharacter = String(index + 1);
const evidence = path.join(fixtureRoot, `${subject}-verification.json`);
const image = `${ROLES[role].repository}:ci-${architecture}`;
const inspectImage = () => inspection(role, architecture, idCharacter);
createClusterAlphaVerificationEvidence(
{
root,
output: evidence,
role,
architecture,
image,
sourceRevision: revision,
repository: 'whyour/qinglong',
workflowRef,
workflowSha: revision,
eventName: 'workflow_dispatch',
job: 'cluster-image',
runId,
runAttempt: '2',
},
{ inspectImage },
);
bundles[subject] = path.join(fixtureRoot, subject);
createClusterAlphaBundle(
{
root,
outputRoot: bundles[subject],
role,
architecture,
image,
sourceRevision: revision,
sbom: sboms[role],
verificationEvidence: evidence,
readme,
},
{
inspectImage,
saveImage(_reference, archivePath) {
fs.writeFileSync(archivePath, Buffer.alloc(2048, index + 1), {
flag: 'wx',
});
},
},
);
});
return {
fixtureRoot,
bundles,
readme,
outputRoot: path.join(fixtureRoot, 'milestone'),
};
}
function finalizeOptions(paths, overrides = {}) {
return {
root,
outputRoot: paths.outputRoot,
bundles: paths.bundles,
readme: paths.readme,
sourceRevision: revision,
repository: 'whyour/qinglong',
workflowRef,
workflowSha: revision,
eventName: 'workflow_dispatch',
runId: '33073349397',
runAttempt: '2',
...overrides,
};
}
test('closes eight role/architecture bundles into one offline-verifiable index', (t) => {
const paths = fixture(t);
const manifest = finalizeClusterAlphaMilestone(finalizeOptions(paths));
assert.equal(manifest.schema, 'qinglong/alpha-cluster-milestone@v1');
assert.equal(Object.keys(manifest.artifacts).length, 8);
assert.deepEqual(Object.keys(manifest.artifacts), SUBJECTS);
assert.equal(
new Set(Object.values(manifest.artifacts).map((entry) => entry.imageId))
.size,
8,
);
const report = auditClusterAlphaMilestone({
milestoneRoot: paths.outputRoot,
});
assert.equal(report.compatible, true);
assert.deepEqual(report.subjects, SUBJECTS);
assert.deepEqual(fs.readdirSync(paths.outputRoot).sort(), [
'README.md',
'SHA256SUMS',
'manifest.json',
]);
});
test('rejects one bundle detached from the authorized workflow run', (t) => {
const paths = fixture(t, '33073349398');
assert.throws(
() => finalizeClusterAlphaMilestone(finalizeOptions(paths)),
/detached from the Cluster milestone run/,
);
assert.equal(fs.existsSync(paths.outputRoot), false);
});
test('offline milestone audit rejects index and file-set mutation', (t) => {
for (const mutation of ['manifest', 'extra']) {
const paths = fixture(t);
paths.outputRoot = path.join(paths.fixtureRoot, `milestone-${mutation}`);
finalizeClusterAlphaMilestone(finalizeOptions(paths));
if (mutation === 'manifest') {
fs.appendFileSync(path.join(paths.outputRoot, 'manifest.json'), 'tamper');
} else {
fs.writeFileSync(path.join(paths.outputRoot, 'secret.txt'), 'secret');
}
assert.throws(() =>
auditClusterAlphaMilestone({ milestoneRoot: paths.outputRoot }),
);
}
});
test('workflow audit proves full CI closure before milestone upload', () => {
const report = auditClusterAlphaMilestoneWorkflow(root);
assert.equal(report.compatible, true);
assert.deepEqual(report.findings, []);
assert.equal(report.requiredNeeds.length, 19);
assert.deepEqual(report.subjects, SUBJECTS);
});
test('workflow audit rejects missing final offline audit', (t) => {
const fixtureRoot = fs.mkdtempSync(
path.join(os.tmpdir(), 'ql3-cluster-alpha-workflow-'),
);
t.after(() => fs.rmSync(fixtureRoot, { recursive: true, force: true }));
const workflowDirectory = path.join(fixtureRoot, '.github/workflows');
fs.mkdirSync(workflowDirectory, { recursive: true });
const source = fs
.readFileSync(path.join(root, '.github/workflows/ql3-ci.yml'), 'utf8')
.replace(
'node scripts/ql3-cluster-alpha-milestone.cjs \\\n --mode=audit',
'node scripts/ql3-cluster-alpha-milestone.cjs \\\n --mode=inspect',
);
fs.writeFileSync(path.join(workflowDirectory, 'ql3-ci.yml'), source);
const report = auditClusterAlphaMilestoneWorkflow(fixtureRoot);
assert.equal(report.compatible, false);
assert.ok(
report.findings.includes('CLUSTER_MILESTONE_FINALIZER_CONTRACT_DRIFT'),
);
});
test('workflow audit rejects a finalizer without installed dependencies', (t) => {
const fixtureRoot = fs.mkdtempSync(
path.join(os.tmpdir(), 'ql3-cluster-alpha-dependencies-'),
);
t.after(() => fs.rmSync(fixtureRoot, { recursive: true, force: true }));
const workflowDirectory = path.join(fixtureRoot, '.github/workflows');
fs.mkdirSync(workflowDirectory, { recursive: true });
const source = fs.readFileSync(
path.join(root, '.github/workflows/ql3-ci.yml'),
'utf8',
);
const marker = '\n cluster-alpha-milestone:\n';
const markerIndex = source.indexOf(marker);
const workflow = `${source.slice(0, markerIndex)}${source
.slice(markerIndex)
.replace('pnpm install --frozen-lockfile --ignore-scripts', 'true')}`;
fs.writeFileSync(path.join(workflowDirectory, 'ql3-ci.yml'), workflow);
const report = auditClusterAlphaMilestoneWorkflow(fixtureRoot);
assert.equal(report.compatible, false);
assert.ok(
report.findings.includes('CLUSTER_MILESTONE_FINALIZER_CONTRACT_DRIFT'),
);
});