mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
122 lines
3.6 KiB
JavaScript
122 lines
3.6 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const test = require('node:test');
|
|
|
|
const {
|
|
createRunCancellationStatusCommand,
|
|
formatRunCancellationStatusCard,
|
|
projectRunCancellationStatus,
|
|
} = require('../dist/run-management/runCancellationStatus.js');
|
|
|
|
const uuids = [
|
|
'019f9500-0000-4000-8000-000000000001',
|
|
'019f9500-0000-4000-8000-000000000002',
|
|
'019f9500-0000-4000-8000-000000000003',
|
|
];
|
|
|
|
function result(assessment) {
|
|
const blocked = assessment === 'attention_required' ? 1 : 0;
|
|
const pending = assessment === 'converging' ? 1 : 0;
|
|
return {
|
|
schemaVersion: 1,
|
|
requestId: 'request-summary-1',
|
|
result: {
|
|
schemaVersion: 1,
|
|
operation: 'run.cancellation.summary',
|
|
summary: {
|
|
schema: 'qinglong/run-cancellation-dispatch-summary@v1',
|
|
projectId: 'project-1',
|
|
observedAtMs: 1_700_000_000_000,
|
|
assessment,
|
|
operatorAction:
|
|
assessment === 'clear'
|
|
? 'none'
|
|
: assessment === 'converging'
|
|
? 'wait'
|
|
: 'inspect',
|
|
dispatches: {
|
|
total: blocked + pending,
|
|
pending,
|
|
leased: 0,
|
|
retryWait: 0,
|
|
dispatched: 0,
|
|
blocked,
|
|
},
|
|
signals: { due: pending, expiredLease: 0 },
|
|
blockingResults: {
|
|
identityMismatch: blocked,
|
|
pidMismatch: 0,
|
|
unsupported: 0,
|
|
invalid: 0,
|
|
},
|
|
...(blocked === 0 ? {} : { oldestBlockedAtMs: 1_699_999_999_000 }),
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
test('builds one exact Project summary command without a command file', () => {
|
|
let index = 0;
|
|
assert.deepEqual(
|
|
createRunCancellationStatusCommand('project-1', () => uuids[index++]),
|
|
{
|
|
schemaVersion: 1,
|
|
operation: 'run.cancellation.summary',
|
|
request: {
|
|
projectId: 'project-1',
|
|
requestId: uuids[0],
|
|
auditEventId: uuids[1],
|
|
failureAuditEventId: uuids[2],
|
|
body: {
|
|
schema: 'qinglong/run-cancellation-dispatch-summary-request@v1',
|
|
},
|
|
},
|
|
},
|
|
);
|
|
});
|
|
|
|
test('maps clear, converging and attention assessments to stable alert exits', () => {
|
|
const cases = [
|
|
['clear', 'ok', 0],
|
|
['converging', 'warning', 10],
|
|
['attention_required', 'critical', 20],
|
|
];
|
|
for (const [assessment, severity, exitCode] of cases) {
|
|
const status = projectRunCancellationStatus(result(assessment));
|
|
assert.equal(status.schema, 'qinglong/run-cancellation-status@v1');
|
|
assert.equal(status.assessment, assessment);
|
|
assert.equal(status.severity, severity);
|
|
assert.equal(status.exitCode, exitCode);
|
|
assert.equal(status.projectId, 'project-1');
|
|
assert.equal(Object.hasOwn(status, 'oldestBlockedAtMs'), exitCode === 20);
|
|
}
|
|
});
|
|
|
|
test('renders a deterministic low-sensitive operator card', () => {
|
|
const card = formatRunCancellationStatusCard(
|
|
projectRunCancellationStatus(result('attention_required')),
|
|
);
|
|
assert.match(card, /^QingLong 3\.0 \/ Cancellation Availability\n/);
|
|
assert.match(card, /ASSESSMENT ATTENTION REQUIRED/);
|
|
assert.match(card, /ALERT CRITICAL \(exit 20\)/);
|
|
assert.match(card, /DISPATCHES total=1 .* blocked=1/);
|
|
assert.match(card, /BLOCKING identity_mismatch=1/);
|
|
assert.doesNotMatch(
|
|
card,
|
|
/runId|attemptId|leaseOwner|leaseToken|command|environment|secret/i,
|
|
);
|
|
});
|
|
|
|
test('refuses to project a non-summary management result', () => {
|
|
assert.throws(
|
|
() =>
|
|
projectRunCancellationStatus({
|
|
schemaVersion: 1,
|
|
requestId: 'request-1',
|
|
result: { schemaVersion: 1, operation: 'run.stop', stop: {} },
|
|
}),
|
|
/requires a summary result/,
|
|
);
|
|
});
|