'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 { auditReleaseCandidateContract, createReleaseCandidateContract, parseArguments, runCli, } = require('../../scripts/ql3-release-candidate-contract.cjs'); const { readReleaseIdentity, } = require('../../scripts/lib/ql3-release-identity.cjs'); const root = path.resolve(__dirname, '../..'); const version = readReleaseIdentity(root).version; const identity = Object.freeze({ version, sourceRevision: 'a'.repeat(40), sourceRef: `refs/tags/v${version}`, }); test('freezes an independent low-resource local release family', () => { const contract = createReleaseCandidateContract({ root, ...identity, releaseScope: 'local', }); assert.deepEqual( contract.images.map((entry) => entry.image), ['local'], ); assert.equal(contract.releasePlan.clusterEvidenceRequired, false); assert.deepEqual(contract.deploymentFamilies.local.profiles, [ 'edge', 'standalone', ]); assert.equal(contract.workspace.packageCount, 18); assert.equal( contract.compatibility.releaseIdentitySchema, 'qinglong/release-identity@v2', ); assert.deepEqual(contract.compatibility.architectureSupport, { tier1: ['amd64', 'arm64'], candidates: ['ppc64le', 's390x'], experimentalBlocked: ['arm/v7'], legacyOnly: ['arm/v6', '386'], legacyLine: '2.x', }); assert.deepEqual(contract.compatibility.platforms, [ 'linux/amd64', 'linux/arm64', ]); assert.match( contract.compatibility.releaseIdentityDigest, /^sha256:[a-f0-9]{64}$/u, ); assert.match(contract.contractDigest, /^sha256:[a-f0-9]{64}$/u); assert.equal( contract.requiredGates.includes('architecture-support-tier'), true, ); assert.equal( contract.requiredGates.includes('offline-deployment-lock-materialization'), true, ); assert.deepEqual( auditReleaseCandidateContract(contract, { root, ...identity, releaseScope: 'local', }), { compatible: true, contractDigest: contract.contractDigest, releaseScope: 'local', workspacePackageCount: 18, images: ['local'], clusterEvidenceRequired: false, }, ); }); test('closes the cluster release family with the Worker image', () => { const contract = createReleaseCandidateContract({ root, ...identity, releaseScope: 'cluster', }); assert.deepEqual( contract.images.map((entry) => entry.image), ['control', 'control-ai', 'admin', 'worker'], ); assert.equal(contract.releasePlan.clusterEvidenceRequired, true); assert.equal(contract.releasePlan.osMatrix.length, 8); assert.deepEqual( [ ...new Set( contract.releasePlan.osMatrix.map((entry) => entry.image_arch), ), ], ['amd64', 'arm64'], ); assert.deepEqual( contract.releasePlan.osMatrix.slice(0, 2).map((entry) => ({ runner: entry.runner, node_arch: entry.node_arch, image_arch: entry.image_arch, })), [ { runner: 'ubuntu-24.04', node_arch: 'x64', image_arch: 'amd64', }, { runner: 'ubuntu-24.04-arm', node_arch: 'arm64', image_arch: 'arm64', }, ], ); assert.equal( contract.requiredGates.includes('edge-and-standalone-rollout'), false, ); assert.equal( contract.requiredGates.includes('cloudnativepg-disaster-recovery-evidence'), true, ); }); test('combines local and cluster families without weakening either gate', () => { const contract = createReleaseCandidateContract({ root, ...identity, releaseScope: 'all', }); assert.deepEqual( contract.images.map((entry) => entry.image), ['control', 'control-ai', 'admin', 'worker', 'local'], ); assert.equal( contract.requiredGates.includes('edge-and-standalone-rollout'), true, ); assert.equal( contract.requiredGates.includes('cross-image-release-set'), true, ); assert.equal( contract.requiredGates.includes('durable-oci-release-catalog'), true, ); assert.equal( contract.requiredGates.includes('offline-deployment-lock-materialization'), true, ); assert.equal( contract.requiredGates.includes('worker-management-production-evidence'), true, ); }); test('rejects tag, version and source identity drift', () => { assert.throws( () => createReleaseCandidateContract({ root, ...identity, sourceRef: 'refs/heads/next', releaseScope: 'local', }), /exact version tag/, ); assert.throws( () => createReleaseCandidateContract({ root, ...identity, version: '2.21.0', releaseScope: 'local', }), /QingLong 3 SemVer/, ); assert.throws( () => createReleaseCandidateContract({ root, ...identity, sourceRevision: 'movable', releaseScope: 'local', }), /Git SHA-1/, ); const nextVersion = `${version.slice(0, version.lastIndexOf('.') + 1)}1`; assert.throws( () => createReleaseCandidateContract({ root, ...identity, version: nextVersion, sourceRef: `refs/tags/v${nextVersion}`, releaseScope: 'local', }), /repository release identity/, ); }); test('rejects a source-derived report mutated after creation', () => { const contract = createReleaseCandidateContract({ root, ...identity, releaseScope: 'local', }); contract.releasePlan.publishMatrix[0].repository = 'unreviewed'; assert.throws( () => auditReleaseCandidateContract(contract, { root, ...identity, releaseScope: 'local', }), /differs from the source-derived contract/, ); }); test('writes once and independently audits the exact report through the CLI', (t) => { const directory = fs.realpathSync( fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-candidate-')), ); t.after(() => fs.rmSync(directory, { recursive: true, force: true })); const report = path.join(directory, 'contract.json'); const common = [ `--version=${version}`, `--source-revision=${identity.sourceRevision}`, `--source-ref=refs/tags/v${version}`, '--release-scope=local', ]; const output = { write() {} }; runCli(['--mode=create', ...common, `--output=${report}`], root, output); assert.equal(fs.statSync(report).mode & 0o777, 0o600); assert.equal( runCli(['--mode=audit', ...common, `--report=${report}`], root, output) .compatible, true, ); assert.throws( () => runCli(['--mode=create', ...common, `--output=${report}`], root, output), /output must be unused/, ); }); test('parses only exact closed create and audit modes', () => { const common = [ `--version=${version}`, `--source-revision=${identity.sourceRevision}`, `--source-ref=refs/tags/v${version}`, '--release-scope=local', ]; assert.equal( parseArguments(['--mode=create', ...common, '--output=/tmp/report.json']) .mode, 'create', ); assert.throws( () => parseArguments([ '--mode=create', ...common, '--output=/tmp/report.json', '--extra=true', ]), /arguments are invalid/, ); });