mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 08:05:22 +08:00
144 lines
3.9 KiB
JavaScript
144 lines
3.9 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
const {
|
|
buildFaultEvidenceReport,
|
|
normalizeFaultManifest,
|
|
parseArguments,
|
|
validateFaultEnvelope,
|
|
} = require('../../scripts/ql3-physical-edge-fault-probe.cjs');
|
|
|
|
function manifest(fault, overrides = {}) {
|
|
return normalizeFaultManifest({
|
|
schemaVersion: 1,
|
|
evidenceClass: 'physical_edge_fault_candidate',
|
|
profile: 'edge',
|
|
deviceId: 'router-a1',
|
|
fault,
|
|
probePath: '/mnt/ql3-fault',
|
|
expectedFilesystem: 'ext4',
|
|
maximumFilesystemBytes: 64 * 1024 * 1024,
|
|
...overrides,
|
|
});
|
|
}
|
|
|
|
function observed(overrides = {}) {
|
|
return {
|
|
platform: 'linux',
|
|
architecture: 'arm64',
|
|
node: 'v24.18.0',
|
|
bootId: '019f0000-0000-7000-8000-000000000001',
|
|
processUid: 1000,
|
|
probePath: '/mnt/ql3-fault',
|
|
mountPath: '/mnt/ql3-fault',
|
|
filesystem: 'ext4',
|
|
mountOptions: ['rw', 'nosuid', 'nodev'],
|
|
totalBytes: 32 * 1024 * 1024,
|
|
availableBytes: 0,
|
|
expectedErrorCode: 'ENOSPC',
|
|
probeEntryCreated: false,
|
|
probeEntryRemained: false,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('normalizes only bounded dedicated fault filesystem manifests', () => {
|
|
assert.equal(manifest('enospc_filesystem').fault, 'enospc_filesystem');
|
|
assert.throws(() => manifest('disk_full'), /fault must be one of/);
|
|
assert.throws(
|
|
() => manifest('enospc_filesystem', { probePath: '/' }),
|
|
/absolute non-root path/,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
manifest('enospc_filesystem', {
|
|
maximumFilesystemBytes: 257 * 1024 * 1024,
|
|
}),
|
|
/between 1 and 256 MiB/,
|
|
);
|
|
assert.throws(
|
|
() => manifest('enospc_filesystem', { extra: true }),
|
|
/keys must be exactly/,
|
|
);
|
|
});
|
|
|
|
test('accepts exact non-root ENOSPC and read-only errno evidence', () => {
|
|
assert.deepEqual(
|
|
validateFaultEnvelope(manifest('enospc_filesystem'), observed()),
|
|
[],
|
|
);
|
|
assert.deepEqual(
|
|
validateFaultEnvelope(
|
|
manifest('read_only_filesystem'),
|
|
observed({
|
|
mountOptions: ['ro', 'nosuid', 'nodev'],
|
|
availableBytes: 8 * 1024 * 1024,
|
|
expectedErrorCode: 'EROFS',
|
|
}),
|
|
),
|
|
[],
|
|
);
|
|
});
|
|
|
|
test('fails widened mounts, capacity, root and wrong errno', () => {
|
|
assert.deepEqual(
|
|
validateFaultEnvelope(
|
|
manifest('enospc_filesystem'),
|
|
observed({
|
|
mountPath: '/',
|
|
filesystem: 'tmpfs',
|
|
totalBytes: 128 * 1024 * 1024,
|
|
availableBytes: 4096,
|
|
processUid: 0,
|
|
expectedErrorCode: 'EACCES',
|
|
probeEntryRemained: true,
|
|
}),
|
|
),
|
|
[
|
|
'probe path must be a dedicated mount point',
|
|
'filesystem tmpfs did not equal ext4',
|
|
'fault filesystem exceeded its declared capacity',
|
|
'fault filesystem still had available blocks',
|
|
'ENOSPC probe must run as non-root',
|
|
'write probe did not return ENOSPC',
|
|
'fault probe entry was not cleaned up',
|
|
],
|
|
);
|
|
});
|
|
|
|
test('builds digest-bound candidate evidence with explicit exclusions', () => {
|
|
const report = buildFaultEvidenceReport({
|
|
manifest: manifest('read_only_filesystem'),
|
|
observed: observed({
|
|
mountOptions: ['ro'],
|
|
expectedErrorCode: 'EROFS',
|
|
}),
|
|
generatedAt: '2026-07-22T00:00:00.000Z',
|
|
});
|
|
assert.equal(report.supported, false);
|
|
assert.equal(report.qualification.passed, true);
|
|
assert.ok(
|
|
report.qualification.doesNotProve.includes('application_level_recovery'),
|
|
);
|
|
assert.equal(report.sha256.length, 64);
|
|
});
|
|
|
|
test('requires absolute durable manifest and output paths', () => {
|
|
assert.deepEqual(
|
|
parseArguments([
|
|
'--',
|
|
'--manifest=/etc/qinglong/enospc.json',
|
|
'--output=/var/lib/qinglong/evidence/enospc.json',
|
|
'--json',
|
|
]),
|
|
{
|
|
manifestPath: '/etc/qinglong/enospc.json',
|
|
outputPath: '/var/lib/qinglong/evidence/enospc.json',
|
|
json: true,
|
|
},
|
|
);
|
|
assert.throws(
|
|
() => parseArguments(['--manifest=fault.json', '--output=/tmp/out.json']),
|
|
/manifestPath must be absolute/,
|
|
);
|
|
});
|