mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:38:14 +08:00
223 lines
6.9 KiB
JavaScript
223 lines
6.9 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
CLUSTER_LEGACY_ENV_MIGRATION_APPLICATION_RECEIPT_SCHEMA,
|
|
InvalidClusterLegacyEnvMigrationApplicationError,
|
|
clusterLegacyEnvMigrationApplicationReceiptMatchesIntent,
|
|
createClusterLegacyEnvMigrationApplicationReceipt,
|
|
createClusterLegacyEnvMigrationTaskMutationSetDigester,
|
|
createClusterLegacyEnvMigrationTriggerMutationSetDigester,
|
|
normalizeClusterLegacyEnvMigrationApplicationReceipt,
|
|
} = require('@qinglong/runtime-core/cluster-legacy-env-migration-application');
|
|
const { createSecretRef } = require('@qinglong/runtime-core/secret-reference');
|
|
|
|
const IDS = Object.freeze({
|
|
application: '11111111-1111-4111-8111-111111111111',
|
|
applicationMutation: '22222222-2222-4222-8222-222222222222',
|
|
taskOneMutation: '33333333-3333-4333-8333-333333333333',
|
|
taskTwoMutation: '44444444-4444-4444-8444-444444444444',
|
|
triggerMutation: '55555555-5555-4555-8555-555555555555',
|
|
});
|
|
|
|
function taskMutations() {
|
|
return [
|
|
{
|
|
ordinal: 0,
|
|
taskId: 'task-a',
|
|
previousRevision: 3,
|
|
previousContentDigest: '1'.repeat(64),
|
|
mutationId: IDS.taskOneMutation,
|
|
},
|
|
{
|
|
ordinal: 1,
|
|
taskId: 'task-b',
|
|
previousRevision: 8,
|
|
previousContentDigest: '2'.repeat(64),
|
|
mutationId: IDS.taskTwoMutation,
|
|
},
|
|
];
|
|
}
|
|
|
|
function triggerMutations() {
|
|
return [
|
|
{
|
|
ordinal: 0,
|
|
triggerId: 'trigger-a',
|
|
taskId: 'task-a',
|
|
previousRevision: 4,
|
|
previousContentDigest: '3'.repeat(64),
|
|
previousTaskRevision: 3,
|
|
previousTaskContentDigest: '1'.repeat(64),
|
|
mutationId: IDS.triggerMutation,
|
|
},
|
|
];
|
|
}
|
|
|
|
function digestTaskMutations(values = taskMutations()) {
|
|
const digester = createClusterLegacyEnvMigrationTaskMutationSetDigester();
|
|
for (const value of values) digester.update(value);
|
|
return digester.finish();
|
|
}
|
|
|
|
function digestTriggerMutations(values = triggerMutations()) {
|
|
const digester = createClusterLegacyEnvMigrationTriggerMutationSetDigester();
|
|
for (const value of values) digester.update(value);
|
|
return digester.finish();
|
|
}
|
|
|
|
function receiptInput(overrides = {}) {
|
|
const task = digestTaskMutations();
|
|
const trigger = digestTriggerMutations();
|
|
return {
|
|
applicationId: 'legacy-env-application-a',
|
|
mutationId: IDS.applicationMutation,
|
|
projectId: 'project-a',
|
|
planId: 'legacy-env-plan-a',
|
|
planDigest: '6'.repeat(64),
|
|
environmentBundleRef: createSecretRef({
|
|
projectId: 'project-a',
|
|
name: 'legacy-env-bundle',
|
|
version: 7,
|
|
}),
|
|
taskRevisionSetDigest: task.revisionSetDigest,
|
|
triggerRevisionSetDigest: trigger.revisionSetDigest,
|
|
taskMutationSetDigest: task.mutationSetDigest,
|
|
triggerMutationSetDigest: trigger.mutationSetDigest,
|
|
taskCount: task.count,
|
|
triggerCount: trigger.count,
|
|
committedAtMs: 12_345,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('streams canonical Task and Trigger revision and mutation sets', () => {
|
|
const firstTask = digestTaskMutations();
|
|
const secondTask = digestTaskMutations();
|
|
const firstTrigger = digestTriggerMutations();
|
|
const secondTrigger = digestTriggerMutations();
|
|
|
|
assert.deepEqual(firstTask, secondTask);
|
|
assert.deepEqual(firstTrigger, secondTrigger);
|
|
assert.equal(firstTask.count, 2);
|
|
assert.equal(firstTrigger.count, 1);
|
|
assert.notEqual(firstTask.revisionSetDigest, firstTask.mutationSetDigest);
|
|
assert.notEqual(
|
|
firstTrigger.revisionSetDigest,
|
|
firstTrigger.mutationSetDigest,
|
|
);
|
|
});
|
|
|
|
test('rejects gaps, duplicate ordering, malformed UUIDs and unknown fields', () => {
|
|
const gap = taskMutations();
|
|
gap[1] = { ...gap[1], ordinal: 2 };
|
|
assert.throws(() => digestTaskMutations(gap), {
|
|
name: 'InvalidClusterLegacyEnvMigrationApplicationError',
|
|
});
|
|
|
|
const unordered = taskMutations();
|
|
unordered[1] = { ...unordered[1], taskId: 'task-a' };
|
|
assert.throws(
|
|
() => digestTaskMutations(unordered),
|
|
InvalidClusterLegacyEnvMigrationApplicationError,
|
|
);
|
|
|
|
const malformedMutation = taskMutations();
|
|
malformedMutation[0] = { ...malformedMutation[0], mutationId: 'not-a-uuid' };
|
|
assert.throws(
|
|
() => digestTaskMutations(malformedMutation),
|
|
InvalidClusterLegacyEnvMigrationApplicationError,
|
|
);
|
|
|
|
const extra = taskMutations();
|
|
extra[0] = { ...extra[0], command: 'must-not-enter-ledger' };
|
|
assert.throws(
|
|
() => digestTaskMutations(extra),
|
|
InvalidClusterLegacyEnvMigrationApplicationError,
|
|
);
|
|
});
|
|
|
|
test('creates a frozen content-free receipt and detects tampering', () => {
|
|
const receipt = createClusterLegacyEnvMigrationApplicationReceipt(
|
|
receiptInput(),
|
|
);
|
|
assert.equal(
|
|
receipt.schema,
|
|
CLUSTER_LEGACY_ENV_MIGRATION_APPLICATION_RECEIPT_SCHEMA,
|
|
);
|
|
assert.equal(Object.isFrozen(receipt), true);
|
|
assert.match(receipt.receiptDigest, /^[0-9a-f]{64}$/);
|
|
assert.deepEqual(
|
|
normalizeClusterLegacyEnvMigrationApplicationReceipt(receipt),
|
|
receipt,
|
|
);
|
|
assert.equal(
|
|
clusterLegacyEnvMigrationApplicationReceiptMatchesIntent(receipt, {
|
|
applicationId: receipt.applicationId,
|
|
mutationId: receipt.mutationId,
|
|
projectId: receipt.projectId,
|
|
planId: receipt.planId,
|
|
planDigest: receipt.planDigest,
|
|
taskMutationSetDigest: receipt.taskMutationSetDigest,
|
|
triggerMutationSetDigest: receipt.triggerMutationSetDigest,
|
|
}),
|
|
true,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
normalizeClusterLegacyEnvMigrationApplicationReceipt({
|
|
...receipt,
|
|
taskCount: receipt.taskCount + 1,
|
|
}),
|
|
InvalidClusterLegacyEnvMigrationApplicationError,
|
|
);
|
|
});
|
|
|
|
test('requires one Task and a canonical version-pinned bundle in the same Project', () => {
|
|
assert.throws(
|
|
() =>
|
|
createClusterLegacyEnvMigrationApplicationReceipt(
|
|
receiptInput({ taskCount: 0 }),
|
|
),
|
|
InvalidClusterLegacyEnvMigrationApplicationError,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
createClusterLegacyEnvMigrationApplicationReceipt(
|
|
receiptInput({
|
|
environmentBundleRef: createSecretRef({
|
|
projectId: 'project-a',
|
|
name: 'legacy-env-bundle',
|
|
}),
|
|
}),
|
|
),
|
|
InvalidClusterLegacyEnvMigrationApplicationError,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
createClusterLegacyEnvMigrationApplicationReceipt(
|
|
receiptInput({
|
|
environmentBundleRef: createSecretRef({
|
|
projectId: 'project-b',
|
|
name: 'legacy-env-bundle',
|
|
version: 7,
|
|
}),
|
|
}),
|
|
),
|
|
InvalidClusterLegacyEnvMigrationApplicationError,
|
|
);
|
|
});
|
|
|
|
test('supports an empty Trigger mutation set without weakening Task authority', () => {
|
|
const trigger = digestTriggerMutations([]);
|
|
const receipt = createClusterLegacyEnvMigrationApplicationReceipt(
|
|
receiptInput({
|
|
triggerRevisionSetDigest: trigger.revisionSetDigest,
|
|
triggerMutationSetDigest: trigger.mutationSetDigest,
|
|
triggerCount: 0,
|
|
}),
|
|
);
|
|
assert.equal(receipt.taskCount, 2);
|
|
assert.equal(receipt.triggerCount, 0);
|
|
});
|