Files
qinglong/test/back/ql3PhysicalEdgeDirectServiceStop.test.cjs

370 lines
11 KiB
JavaScript

const assert = require('node:assert/strict');
const crypto = require('node:crypto');
const fs = require('node:fs');
const path = require('node:path');
const { test } = require('node:test');
const {
buildDirectServiceStopReport,
normalizeStopSession,
parseArguments,
validateDirectServiceStopReport,
} = require('../../scripts/ql3-physical-edge-direct-service-stop.cjs');
const {
normalizeDirectServiceStartManifest,
} = require('../../scripts/ql3-physical-edge-direct-service-start.cjs');
const {
buildEvidenceReport,
canonicalDigest,
normalizeManifest,
parseArguments: parseEvidenceArguments,
validateDirectServiceStopEvidenceReport,
} = require('../../scripts/ql3-physical-edge-evidence.cjs');
function manifest() {
return normalizeDirectServiceStartManifest({
schemaVersion: 1,
evidenceClass: 'physical_edge_direct_service_start_candidate',
profile: 'edge',
deviceId: 'router-a1',
serviceManager: 'systemd',
expectedArchitecture: 'arm64',
expectedFilesystem: 'ext4',
expectedArtifactSha256: 'a'.repeat(64),
expectedArtifactFiles: 640,
expectedArtifactBytes: 6_000_000,
expectedNodeSha256: 'b'.repeat(64),
maximumBootToActiveMs: 180_000,
maximumServiceStartBootAgeMs: 60_000,
maximumServiceStartToActiveMs: 30_000,
});
}
function directSession() {
return {
uid: process.getuid?.() ?? 0,
sha256: 'c'.repeat(64),
paths: { deploymentRoot: '/mnt/ql3/direct' },
};
}
function stopSession() {
const source = directSession();
const sessionId = '123e4567-e89b-42d3-a456-426614174031';
const body = {
schemaVersion: 1,
evidenceClass: 'physical_edge_direct_service_stop_session',
sessionId,
uid: source.uid,
preparedAt: '2026-08-11T12:00:00.000Z',
manifestDigest: canonicalDigest(manifest()),
directSessionDigest: source.sha256,
activeReportDigest: 'd'.repeat(64),
activeBootId: '12345678-1234-4abc-8def-123456789abc',
startupReceiptDigest: 'e'.repeat(64),
processId: 101,
processStartTicks: '1200',
bridge: {
actionId: sessionId,
controllerRoot: '/var/lib/qinglong3-service-bridge',
intentDigest: 'f'.repeat(64),
intentPath: `/mnt/ql3/direct/service/service-manager-intents/${sessionId}.json`,
outcomePath: `/mnt/ql3/direct/service/service-manager-outcomes/${sessionId}.json`,
},
};
return normalizeStopSession(
{ ...body, sha256: canonicalDigest(body) },
source,
);
}
function shutdownReceipt() {
const body = {
schemaVersion: 1,
schema: 'qinglong/local-application-shutdown-receipt@v1',
instanceId: 'physical-direct-12345678',
profile: 'edge',
signal: 'SIGTERM',
stopResult: 'stopped',
startupReceiptDigest: 'e'.repeat(64),
bootId: '12345678-1234-4abc-8def-123456789abc',
stoppedBootAgeMs: 20_000,
processId: 101,
processStartTicks: '1200',
nodeExecutable: '/usr/bin/node',
nodeVersion: 'v24.18.0',
};
const sha256 = crypto
.createHash('sha256')
.update('qinglong.local-application-shutdown-receipt.v1\0', 'utf8')
.update(JSON.stringify(body), 'utf8')
.digest('hex');
return { ...body, sha256 };
}
function observed(overrides = {}) {
const session = stopSession();
return {
currentBootId: session.activeBootId,
bridge: {
actionId: session.bridge.actionId,
intentDigest: session.bridge.intentDigest,
outcomeDigest: '1'.repeat(64),
observationDigest: '2'.repeat(64),
state: 'stopped',
},
shutdownReceipt: shutdownReceipt(),
processIdentityGone: true,
service: {
active: false,
enabled: true,
fragmentPath: '/etc/systemd/system/qinglong3.service',
mainPid: 0,
},
...overrides,
};
}
function baseManifest() {
return normalizeManifest({
schemaVersion: 1,
evidenceClass: 'physical_edge_candidate',
profile: 'edge',
deviceId: 'router-a1',
deviceModel: 'router-a1-model',
soc: 'arm-soc',
storageMedium: 'emmc',
expectedArchitecture: 'arm64',
memoryBytes: { minimum: 128 * 1024 * 1024, maximum: 512 * 1024 * 1024 },
expectedFilesystem: 'ext4',
});
}
function physicalObserved() {
return {
platform: 'linux',
architecture: 'arm64',
node: 'v24.18.0',
bootId: stopSession().activeBootId,
kernel: '6.6.0',
distribution: { id: 'openwrt', versionId: '24.10' },
libc: 'musl-ld-musl-aarch64.so.1',
cpuModel: 'test-cpu',
cpuCount: 1,
totalMemoryBytes: 256 * 1024 * 1024,
observedModel: 'router-a1-model',
dataPath: '/mnt/ql3',
dataFilesystem: 'ext4',
dataMountOptions: ['rw', 'noatime'],
dataBytes: 4 * 1024 * 1024 * 1024,
dataAvailableBytes: 2 * 1024 * 1024 * 1024,
virtualizationIndicators: [],
};
}
test('requires exact phase-specific stop evidence paths', () => {
assert.equal(
parseArguments([
'prepare',
'--manifest=/mnt/ql3/manifest.json',
'--session=/mnt/ql3/session.json',
'--active-report=/mnt/ql3/active.json',
'--stop-session=/mnt/ql3/stop-session.json',
'--root-command-output=/mnt/ql3/stop-root-command.json',
]).phase,
'prepare',
);
assert.equal(
parseArguments([
'prepare',
'--manifest=/mnt/ql3/manifest.json',
'--session=/mnt/ql3/session.json',
'--active-report=/mnt/ql3/active.json',
'--stop-session=/mnt/ql3/stop-session.json',
'--root-command-output=/mnt/ql3/stop-root-command.json',
]).rootCommandOutputPath,
'/mnt/ql3/stop-root-command.json',
);
assert.throws(
() =>
parseArguments([
'resume',
'--manifest=/mnt/ql3/manifest.json',
'--session=/mnt/ql3/session.json',
'--active-report=/mnt/ql3/active.json',
'--stop-session=/mnt/ql3/stop-session.json',
'--output=/mnt/ql3/stop.json',
'--root-command-output=/mnt/ql3/stop-root-command.json',
]),
/valid only as a normalized prepare path/,
);
assert.throws(
() =>
parseArguments([
'resume',
'--manifest=manifest.json',
'--session=/mnt/ql3/session.json',
'--active-report=/mnt/ql3/active.json',
'--stop-session=/mnt/ql3/stop-session.json',
'--output=/mnt/ql3/stop.json',
]),
/manifestPath must be absolute/,
);
});
test('binds one stop session to the active report and Owner intent', () => {
const session = stopSession();
assert.equal(session.bridge.actionId, session.sessionId);
assert.equal(session.directSessionDigest, directSession().sha256);
assert.throws(
() =>
normalizeStopSession(
{ ...session, activeReportDigest: '0'.repeat(64) },
directSession(),
),
/invalid or drifted/,
);
});
test('recomputes a graceful direct service stop report', () => {
const report = buildDirectServiceStopReport({
manifest: manifest(),
stopSession: stopSession(),
observed: observed(),
generatedAt: '2026-08-11T12:01:00.000Z',
});
assert.equal(report.qualification.passed, true);
assert.deepEqual(validateDirectServiceStopReport(report, manifest()), []);
});
test('rejects a forged shutdown receipt and a still-live process', () => {
const valid = shutdownReceipt();
const report = buildDirectServiceStopReport({
manifest: manifest(),
stopSession: stopSession(),
observed: observed({
processIdentityGone: false,
shutdownReceipt: {
...valid,
stoppedBootAgeMs: valid.stoppedBootAgeMs + 1,
},
}),
generatedAt: '2026-08-11T12:01:00.000Z',
});
assert.equal(report.qualification.passed, false);
assert.match(
report.qualification.violations.join('; '),
/shutdown receipt binding|stopped state/,
);
});
test('aggregates graceful stop only when bound to direct start evidence', () => {
const stopReport = buildDirectServiceStopReport({
manifest: manifest(),
stopSession: stopSession(),
observed: observed(),
generatedAt: '2026-08-11T12:01:00.000Z',
});
const startEvidence = {
evidenceClass: 'physical_edge_direct_service_start_candidate',
sha256: stopReport.session.activeReportDigest,
};
const aggregate = buildEvidenceReport({
manifest: baseManifest(),
observed: physicalObserved(),
workloads: [],
supplementalEvidence: [startEvidence, stopReport],
generatedAt: '2026-08-11T12:02:00.000Z',
});
assert.ok(
aggregate.qualification.collectedEvidence.includes(
'init_managed_graceful_application_stop',
),
);
assert.equal(
aggregate.qualification.remainingRequiredEvidence.includes(
'init_managed_graceful_application_stop',
),
false,
);
assert.equal(
parseEvidenceArguments([
'--manifest=/mnt/ql3/manifest.json',
'--data-path=/mnt/ql3',
'--direct-service-start-evidence=/mnt/ql3/start.json',
'--direct-service-stop-evidence=/mnt/ql3/stop.json',
]).directServiceStopEvidencePath,
'/mnt/ql3/stop.json',
);
const active = {
...startEvidence,
session: { sessionDigest: stopReport.session.directSessionDigest },
observed: {
after: { bootId: stopReport.session.activeBootId },
receipt: {
sha256: stopReport.session.startupReceiptDigest,
processId: stopReport.session.processId,
processStartTicks: stopReport.session.processStartTicks,
instanceId: stopReport.observed.shutdownReceipt.instanceId,
nodeExecutable: stopReport.observed.shutdownReceipt.nodeExecutable,
},
},
};
assert.deepEqual(
validateDirectServiceStopEvidenceReport(
stopReport,
baseManifest(),
physicalObserved(),
active,
),
[],
);
});
test('documents the exact direct start and graceful stop bridge sequence', () => {
const runbook = fs.readFileSync(
path.join(__dirname, '../../docs/operations/ql3-local-deployment.md'),
'utf8',
);
const startPrepare = runbook.indexOf(
'evidence:physical-edge-direct-service-start -- prepare',
);
const startBridge = runbook.indexOf(
'ql3-service-bridge run --command-file',
startPrepare,
);
const startResume = runbook.indexOf(
'evidence:physical-edge-direct-service-start -- resume',
startBridge,
);
const stopPrepare = runbook.indexOf(
'evidence:physical-edge-direct-service-stop -- prepare',
startResume,
);
const stopBridge = runbook.indexOf(
'ql3-service-bridge run --command-file',
stopPrepare,
);
const stopResume = runbook.indexOf(
'evidence:physical-edge-direct-service-stop -- resume',
stopBridge,
);
assert.ok(
startPrepare >= 0 &&
startPrepare < startBridge &&
startBridge < startResume &&
startResume < stopPrepare &&
stopPrepare < stopBridge &&
stopBridge < stopResume,
);
for (const fragment of [
'--root-command-output=/opt/qinglong/evidence-scratch/direct-service-start-root-command.json',
'--active-report=/opt/qinglong/evidence-scratch/direct-service-start-report.json',
'--stop-session=/opt/qinglong/evidence-scratch/direct-service-stop-session.json',
'--root-command-output=/opt/qinglong/evidence-scratch/direct-service-stop-root-command.json',
'--direct-service-stop-evidence=/opt/qinglong/evidence-scratch/direct-service-stop-report.json',
]) {
assert.ok(runbook.includes(fragment), fragment);
}
});