mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:38:14 +08:00
477 lines
14 KiB
JavaScript
477 lines
14 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { createHash } = require('node:crypto');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
BUILTIN_RUN_READ_TOOL,
|
|
BUILTIN_RUN_READ_TOOL_DEFINITION,
|
|
} = require('@qinglong/runtime-core/builtin-run-read-tool');
|
|
const {
|
|
TRUSTED_TOOL_EXECUTION_RESULT_SCHEMA,
|
|
} = require('@qinglong/runtime-core/trusted-tool-execution');
|
|
const {
|
|
TOOL_EXECUTION_RESULT_KEY_BINDING_SCHEMA,
|
|
createToolExecutionResultArtifact,
|
|
normalizeToolExecutionResultKeyBinding,
|
|
} = require('@qinglong/runtime-core/tool-execution-completion');
|
|
const {
|
|
createToolResultKeyCatalogBootstrapCommand,
|
|
createToolResultKeyRotationCommand,
|
|
normalizeToolResultKeyCatalogRecord,
|
|
requireActiveToolResultKey,
|
|
toolResultKeyCatalogFence,
|
|
toolResultKeyMaterialProof,
|
|
} = require('@qinglong/runtime-core/tool-result-key-catalog');
|
|
const {
|
|
ToolExecutionResultRekeyConflictError,
|
|
createToolExecutionResultRekeyCommand,
|
|
createToolResultKeyRetirementReceiptCommand,
|
|
} = require('@qinglong/runtime-core/tool-result-rekey');
|
|
const {
|
|
ToolDefinitionRegistry,
|
|
} = require('@qinglong/runtime-core/tool-registry');
|
|
const {
|
|
PostgresToolResultRekeyReader,
|
|
} = require('@qinglong/cluster-postgres/runtime');
|
|
const {
|
|
PostgresToolResultRekeyRepository,
|
|
} = require('@qinglong/cluster-postgres/tool-result-rekey');
|
|
|
|
const KEY_A = Buffer.alloc(32, 1);
|
|
const KEY_B = Buffer.alloc(32, 2);
|
|
const OUTPUT_DIGEST_DOMAIN = Buffer.from(
|
|
'qinglong/trusted-tool-execution-output-digest@v1\0',
|
|
);
|
|
const RESULT_DIGEST_DOMAIN = Buffer.from(
|
|
'qinglong/trusted-tool-execution-result-digest@v1\0',
|
|
);
|
|
const BINDING_DIGEST_DOMAIN = Buffer.from(
|
|
'qinglong/tool-execution-result-key-binding-digest@v1\0',
|
|
);
|
|
|
|
function hash(domain, value) {
|
|
return createHash('sha256')
|
|
.update(domain)
|
|
.update(JSON.stringify(value))
|
|
.digest('hex');
|
|
}
|
|
|
|
function jsonb(value) {
|
|
if (Array.isArray(value)) return value.map(jsonb);
|
|
if (!value || typeof value !== 'object') return value;
|
|
return Object.fromEntries(
|
|
Object.keys(value)
|
|
.sort()
|
|
.map((key) => [key, jsonb(value[key])]),
|
|
);
|
|
}
|
|
|
|
function output() {
|
|
return {
|
|
createdAtMs: 1_000,
|
|
eventSequence: 3,
|
|
executionOrigin: 'manual',
|
|
executionOwner: 'runtime',
|
|
found: true,
|
|
id: 'run-rekey-postgres-001',
|
|
priority: 10,
|
|
queuedAtMs: 1_100,
|
|
startedAtMs: 1_200,
|
|
status: 'succeeded',
|
|
taskId: 'task-rekey-postgres-001',
|
|
taskRevision: 'task-rekey-postgres-001@1',
|
|
version: 2,
|
|
};
|
|
}
|
|
|
|
function registry() {
|
|
return new ToolDefinitionRegistry([BUILTIN_RUN_READ_TOOL_DEFINITION]);
|
|
}
|
|
|
|
function sourceArtifact() {
|
|
const value = output();
|
|
const unsigned = {
|
|
schema: TRUSTED_TOOL_EXECUTION_RESULT_SCHEMA,
|
|
startId: 'tool-start-rekey-postgres-001',
|
|
barrierDigest: 'a'.repeat(64),
|
|
adapterDigest: 'b'.repeat(64),
|
|
output: value,
|
|
outputDigest: hash(OUTPUT_DIGEST_DOMAIN, value),
|
|
completedAtMs: 1_500,
|
|
};
|
|
const result = {
|
|
...unsigned,
|
|
resultDigest: hash(RESULT_DIGEST_DOMAIN, unsigned),
|
|
};
|
|
return createToolExecutionResultArtifact(
|
|
{
|
|
artifactId: 'artifact-result-rekey-postgres-001',
|
|
projectId: 'project-rekey-postgres-001',
|
|
runId: 'run-host-rekey-postgres-001',
|
|
stepRunId: 'step-run-rekey-postgres-001',
|
|
tool: BUILTIN_RUN_READ_TOOL,
|
|
executionResult: result,
|
|
keyId: 'result-key-a',
|
|
key: KEY_A,
|
|
},
|
|
registry(),
|
|
() => Buffer.alloc(12, 4),
|
|
);
|
|
}
|
|
|
|
function sourceBinding(artifact, catalog) {
|
|
const unsigned = {
|
|
schema: TOOL_EXECUTION_RESULT_KEY_BINDING_SCHEMA,
|
|
startId: artifact.startId,
|
|
artifactId: artifact.artifactId,
|
|
artifactDigest: artifact.artifactDigest,
|
|
catalogGeneration: catalog.generation,
|
|
catalogDigest: catalog.catalogDigest,
|
|
keyId: artifact.keyId,
|
|
materialProof: toolResultKeyMaterialProof(artifact.keyId, KEY_A),
|
|
};
|
|
return normalizeToolExecutionResultKeyBinding({
|
|
...unsigned,
|
|
bindingDigest: hash(BINDING_DIGEST_DOMAIN, unsigned),
|
|
});
|
|
}
|
|
|
|
function catalogs() {
|
|
const bootstrap = createToolResultKeyCatalogBootstrapCommand({
|
|
keyId: 'result-key-a',
|
|
materialProof: toolResultKeyMaterialProof('result-key-a', KEY_A),
|
|
mutationId: 'result-key-bootstrap-postgres-a',
|
|
});
|
|
const first = normalizeToolResultKeyCatalogRecord({
|
|
...bootstrap.next,
|
|
committedAtMs: 1_000,
|
|
});
|
|
const rotation = createToolResultKeyRotationCommand(first, {
|
|
keyId: 'result-key-b',
|
|
materialProof: toolResultKeyMaterialProof('result-key-b', KEY_B),
|
|
mutationId: 'result-key-rotate-postgres-b',
|
|
});
|
|
return {
|
|
first,
|
|
second: normalizeToolResultKeyCatalogRecord({
|
|
...rotation.next,
|
|
committedAtMs: 1_001,
|
|
}),
|
|
};
|
|
}
|
|
|
|
function overlayRow(overlay, commandDigest) {
|
|
return {
|
|
overlayId: overlay.overlayId,
|
|
artifactId: overlay.sourceArtifact.artifactId,
|
|
sourceBindingDigest: overlay.sourceBindingDigest,
|
|
revision: String(overlay.revision),
|
|
previousOverlayDigest: overlay.previousOverlayDigest,
|
|
fromKeyId: overlay.fromKeyId,
|
|
targetCatalogGeneration: String(overlay.targetCatalogFence.generation),
|
|
targetCatalogDigest: overlay.targetCatalogFence.catalogDigest,
|
|
targetKeyId: overlay.targetCatalogFence.keyId,
|
|
targetMaterialProof: overlay.targetCatalogFence.materialProof,
|
|
mutationId: overlay.mutationId,
|
|
commandDigest,
|
|
overlayDigest: overlay.overlayDigest,
|
|
rekeyedAtMs: String(overlay.rekeyedAtMs),
|
|
overlayJson: jsonb(overlay),
|
|
};
|
|
}
|
|
|
|
function receiptRow(receipt, commandDigest) {
|
|
return {
|
|
receiptDigest: receipt.receiptDigest,
|
|
catalogGeneration: String(receipt.catalogGeneration),
|
|
catalogDigest: receipt.catalogDigest,
|
|
keyId: receipt.keyId,
|
|
materialProof: receipt.materialProof,
|
|
mutationId: receipt.mutationId,
|
|
commandDigest,
|
|
bindingCount: String(receipt.bindingCount),
|
|
overlayHeadCount: String(receipt.overlayHeadCount),
|
|
uncoveredBindingCount: '0',
|
|
uncoveredOverlayHeadCount: '0',
|
|
coverageDigest: receipt.coverageDigest,
|
|
createdAtMs: String(receipt.createdAtMs),
|
|
receiptJson: jsonb(receipt),
|
|
};
|
|
}
|
|
|
|
function harness(artifact, binding, catalog) {
|
|
const calls = [];
|
|
let overlayState = null;
|
|
let overlayCommandDigest = null;
|
|
let head = null;
|
|
let receiptState = null;
|
|
let receiptCommandDigest = null;
|
|
let released = 0;
|
|
const client = {
|
|
async query(text, values = []) {
|
|
calls.push({ text, values });
|
|
if (
|
|
text.startsWith('BEGIN') ||
|
|
text === 'COMMIT' ||
|
|
text === 'ROLLBACK' ||
|
|
text.includes("set_config('") ||
|
|
text.includes('pg_advisory_xact_lock')
|
|
) {
|
|
return { rows: [] };
|
|
}
|
|
if (
|
|
text.startsWith(
|
|
'INSERT INTO "ql3"."tool_execution_result_rekey_overlays"',
|
|
)
|
|
) {
|
|
overlayState = JSON.parse(values[15]);
|
|
overlayCommandDigest = values[12];
|
|
return { rows: [], rowCount: 1 };
|
|
}
|
|
if (text.includes('tool_execution_result_rekey_overlays')) {
|
|
if (!overlayState) return { rows: [] };
|
|
if (text.includes('JOIN "ql3"."tool_execution_result_rekey_heads"')) {
|
|
return {
|
|
rows:
|
|
head && values[0] === overlayState.sourceArtifact.artifactId
|
|
? [overlayRow(overlayState, overlayCommandDigest)]
|
|
: [],
|
|
};
|
|
}
|
|
const matches =
|
|
values.length === 0 ||
|
|
values.includes(overlayState.overlayId) ||
|
|
values.includes(overlayState.overlayDigest) ||
|
|
values.includes(overlayState.mutationId);
|
|
return {
|
|
rows: matches
|
|
? [overlayRow(overlayState, overlayCommandDigest)]
|
|
: [],
|
|
};
|
|
}
|
|
if (
|
|
text.includes('tool_execution_result_key_bindings') &&
|
|
text.includes('tool_execution_completions')
|
|
) {
|
|
return {
|
|
rows: [
|
|
{
|
|
bindingArtifactDigest: binding.artifactDigest,
|
|
bindingKeyId: binding.keyId,
|
|
bindingDigest: binding.bindingDigest,
|
|
artifactDigest: artifact.artifactDigest,
|
|
outputDigest: artifact.outputDigest,
|
|
executionResultDigest: artifact.executionResultDigest,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
if (
|
|
text.includes('tool_execution_result_rekey_heads') &&
|
|
text.includes('FOR UPDATE')
|
|
) {
|
|
return {
|
|
rows: head
|
|
? [
|
|
{
|
|
revision: String(head.revision),
|
|
overlayDigest: head.overlayDigest,
|
|
targetKeyId: head.targetKeyId,
|
|
},
|
|
]
|
|
: [],
|
|
};
|
|
}
|
|
if (text.includes('tool_result_key_catalog_generations')) {
|
|
return { rows: [{ catalogJson: catalog }] };
|
|
}
|
|
if (
|
|
text.startsWith(
|
|
'INSERT INTO "ql3"."tool_execution_result_rekey_heads"',
|
|
)
|
|
) {
|
|
head = {
|
|
revision: values[1],
|
|
overlayDigest: values[3],
|
|
targetKeyId: values[6],
|
|
};
|
|
return { rows: [], rowCount: 1 };
|
|
}
|
|
if (text.includes('tool_result_key_retirement_receipts')) {
|
|
if (text.startsWith('INSERT')) {
|
|
receiptState = JSON.parse(values[12]);
|
|
receiptCommandDigest = values[7];
|
|
return { rows: [], rowCount: 1 };
|
|
}
|
|
const matches =
|
|
receiptState &&
|
|
(values.includes(receiptState.mutationId) ||
|
|
values.includes(receiptState.receiptDigest));
|
|
return {
|
|
rows: matches
|
|
? [receiptRow(receiptState, receiptCommandDigest)]
|
|
: [],
|
|
};
|
|
}
|
|
if (
|
|
text.includes('LEFT JOIN "ql3"."tool_execution_result_rekey_heads"')
|
|
) {
|
|
return {
|
|
rows: [
|
|
{
|
|
artifactId: artifact.artifactId,
|
|
bindingDigest: binding.bindingDigest,
|
|
bindingKeyId: binding.keyId,
|
|
headOverlayDigest: head.overlayDigest,
|
|
headTargetKeyId: head.targetKeyId,
|
|
headTargetCatalogGeneration: String(catalog.generation),
|
|
headTargetCatalogDigest: catalog.catalogDigest,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
if (text.includes('clock_timestamp()')) {
|
|
return { rows: [{ now: '1700000000000' }] };
|
|
}
|
|
throw new Error(`unexpected SQL: ${text}`);
|
|
},
|
|
release() {
|
|
released += 1;
|
|
},
|
|
};
|
|
const pool = {
|
|
query: (text, values) => client.query(text, values),
|
|
connect: async () => client,
|
|
};
|
|
return {
|
|
calls,
|
|
reader: new PostgresToolResultRekeyReader(pool),
|
|
repository: new PostgresToolResultRekeyRepository(pool),
|
|
released: () => released,
|
|
};
|
|
}
|
|
|
|
test('serializes PostgreSQL rekey heads and coverage receipts behind the catalog lock', async () => {
|
|
const { first, second } = catalogs();
|
|
const artifact = sourceArtifact();
|
|
const binding = sourceBinding(artifact, first);
|
|
const current = harness(artifact, binding, second);
|
|
assert.equal(
|
|
await current.reader.findHeadByArtifactId(artifact.artifactId),
|
|
null,
|
|
);
|
|
|
|
const overlayCommand = createToolExecutionResultRekeyCommand({
|
|
artifact,
|
|
binding,
|
|
previousOverlay: null,
|
|
overlayId: 'result-rekey-overlay-postgres-001',
|
|
mutationId: 'result-rekey-mutation-postgres-001',
|
|
targetCatalogFence: toolResultKeyCatalogFence(
|
|
second,
|
|
requireActiveToolResultKey(second),
|
|
),
|
|
targetKey: KEY_B,
|
|
output: output(),
|
|
rekeyedAtMs: 1_700,
|
|
registry: registry(),
|
|
nonceFactory: () => Buffer.alloc(12, 5),
|
|
});
|
|
const appended = await current.repository.append(overlayCommand);
|
|
assert.equal(appended.status, 'created');
|
|
assert.deepEqual(await current.repository.append(overlayCommand), {
|
|
status: 'existing',
|
|
overlay: appended.overlay,
|
|
});
|
|
assert.deepEqual(
|
|
await current.reader.findHeadByArtifactId(artifact.artifactId),
|
|
appended.overlay,
|
|
);
|
|
|
|
const receiptCommand = createToolResultKeyRetirementReceiptCommand({
|
|
expectedCatalogGeneration: second.generation,
|
|
expectedCatalogDigest: second.catalogDigest,
|
|
keyId: 'result-key-a',
|
|
mutationId: 'result-key-retirement-postgres-a',
|
|
});
|
|
const receipt = await current.repository.create(receiptCommand);
|
|
assert.equal(receipt.status, 'created');
|
|
assert.equal(receipt.receipt.bindingCount, 1);
|
|
assert.equal(receipt.receipt.overlayHeadCount, 1);
|
|
assert.deepEqual(await current.repository.create(receiptCommand), {
|
|
status: 'existing',
|
|
receipt: receipt.receipt,
|
|
});
|
|
assert.deepEqual(
|
|
await current.repository.findByDigest(receipt.receipt.receiptDigest),
|
|
receipt.receipt,
|
|
);
|
|
assert.equal(current.released(), 4);
|
|
|
|
const statements = current.calls.map(({ text }) => text);
|
|
const advisory = statements.indexOf(
|
|
'SELECT pg_advisory_xact_lock(190397473, 3)',
|
|
);
|
|
const overlayInsert = statements.findIndex((text) =>
|
|
text.startsWith(
|
|
'INSERT INTO "ql3"."tool_execution_result_rekey_overlays"',
|
|
),
|
|
);
|
|
assert.equal(advisory >= 0 && advisory < overlayInsert, true);
|
|
const coverageCall = current.calls.find(({ text }) =>
|
|
text.includes(
|
|
'LEFT JOIN "ql3"."tool_execution_result_rekey_heads" AS head',
|
|
),
|
|
);
|
|
assert.deepEqual(coverageCall.values, ['result-key-a', '', 64]);
|
|
const currentCatalogCalls = current.calls.filter(({ text }) =>
|
|
text.includes('tool_result_key_catalog_generations'),
|
|
);
|
|
assert.equal(
|
|
currentCatalogCalls.every(({ text }) => text.includes('LIMIT 1')),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
current.calls.some(({ text }) => text.includes('FOR UPDATE OF binding')),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test('rejects a stale PostgreSQL rekey head before writing an overlay', async () => {
|
|
const { first, second } = catalogs();
|
|
const artifact = sourceArtifact();
|
|
const binding = sourceBinding(artifact, first);
|
|
const current = harness(artifact, binding, second);
|
|
const command = createToolExecutionResultRekeyCommand({
|
|
artifact,
|
|
binding,
|
|
previousOverlay: null,
|
|
overlayId: 'result-rekey-overlay-postgres-stale',
|
|
mutationId: 'result-rekey-mutation-postgres-stale',
|
|
targetCatalogFence: toolResultKeyCatalogFence(
|
|
second,
|
|
requireActiveToolResultKey(second),
|
|
),
|
|
targetKey: KEY_B,
|
|
output: output(),
|
|
rekeyedAtMs: 1_700,
|
|
registry: registry(),
|
|
nonceFactory: () => Buffer.alloc(12, 6),
|
|
});
|
|
const drifted = {
|
|
...command,
|
|
overlay: {
|
|
...command.overlay,
|
|
fromKeyId: 'result-key-drift',
|
|
},
|
|
};
|
|
assert.throws(
|
|
() => current.repository.append(drifted),
|
|
/rekey overlay digest does not match/,
|
|
);
|
|
await assert.rejects(
|
|
current.reader.findHeadByArtifactId('../escape'),
|
|
/source Artifact id is invalid/,
|
|
);
|
|
});
|