mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 18:08:20 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
ToolResultKeyCatalogConflictError,
|
||||
createToolResultKeyCatalogBootstrapCommand,
|
||||
createToolResultKeyRetirementCommand,
|
||||
createToolResultKeyRotationCommand,
|
||||
toolResultKeyMaterialProof,
|
||||
} = require('@qinglong/runtime-core/tool-result-key-catalog');
|
||||
const {
|
||||
createToolResultKeyRetirementReceipt,
|
||||
} = require('@qinglong/runtime-core/tool-result-rekey');
|
||||
const {
|
||||
PostgresToolResultKeyCatalogReader,
|
||||
} = require('@qinglong/cluster-postgres/runtime');
|
||||
const {
|
||||
PostgresToolResultKeyCatalogRepository,
|
||||
} = require('@qinglong/cluster-postgres/tool-result-key-catalog');
|
||||
|
||||
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 row(catalog, commandDigest) {
|
||||
return {
|
||||
generation: String(catalog.generation),
|
||||
previousCatalogDigest: catalog.previousCatalogDigest,
|
||||
activeKeyId: catalog.activeKeyId,
|
||||
mutationKind: catalog.mutationKind,
|
||||
mutationId: catalog.mutationId,
|
||||
catalogDigest: catalog.catalogDigest,
|
||||
commandDigest,
|
||||
committedAtMs: String(catalog.committedAtMs),
|
||||
catalogJson: jsonb(catalog),
|
||||
};
|
||||
}
|
||||
|
||||
function harness() {
|
||||
const calls = [];
|
||||
const history = [];
|
||||
const receipts = [];
|
||||
let released = 0;
|
||||
const client = {
|
||||
async query(text, values = []) {
|
||||
calls.push({ text, values });
|
||||
if (
|
||||
text.startsWith('BEGIN') ||
|
||||
text === 'COMMIT' ||
|
||||
text === 'ROLLBACK' ||
|
||||
text.includes('pg_advisory_xact_lock')
|
||||
) {
|
||||
return { rows: [] };
|
||||
}
|
||||
if (text.includes('AND (mutation_id =')) {
|
||||
const found = history.filter(
|
||||
({ catalog }) =>
|
||||
catalog.mutationId === values[1] ||
|
||||
catalog.generation === values[2] ||
|
||||
catalog.catalogDigest === values[3],
|
||||
);
|
||||
return {
|
||||
rows: found.map(({ catalog, commandDigest }) =>
|
||||
row(catalog, commandDigest),
|
||||
),
|
||||
};
|
||||
}
|
||||
if (text.includes('tool_result_key_retirement_receipts')) {
|
||||
const receipt = receipts.find(
|
||||
(candidate) => candidate.receiptDigest === values[0],
|
||||
);
|
||||
return { rows: receipt ? [{ receiptJson: receipt }] : [] };
|
||||
}
|
||||
if (text.includes('FROM "ql3"."tool_result_key_catalog_generations"')) {
|
||||
return {
|
||||
rows: history
|
||||
.slice()
|
||||
.reverse()
|
||||
.slice(0, 2)
|
||||
.map(({ catalog, commandDigest }) => row(catalog, commandDigest)),
|
||||
};
|
||||
}
|
||||
if (text.includes('clock_timestamp()')) {
|
||||
return { rows: [{ now: String(1_000 + history.length) }] };
|
||||
}
|
||||
if (
|
||||
text.startsWith(
|
||||
'INSERT INTO "ql3"."tool_result_key_catalog_generations"',
|
||||
)
|
||||
) {
|
||||
history.push({
|
||||
catalog: JSON.parse(values[10]),
|
||||
commandDigest: values[8],
|
||||
});
|
||||
return { rows: [], rowCount: 1 };
|
||||
}
|
||||
throw new Error(`unexpected SQL: ${text}`);
|
||||
},
|
||||
release() {
|
||||
released += 1;
|
||||
},
|
||||
};
|
||||
const pool = {
|
||||
async query(text, values) {
|
||||
return client.query(text, values);
|
||||
},
|
||||
async connect() {
|
||||
return client;
|
||||
},
|
||||
};
|
||||
return {
|
||||
calls,
|
||||
history,
|
||||
receipts,
|
||||
pool,
|
||||
repository: new PostgresToolResultKeyCatalogRepository(pool),
|
||||
reader: new PostgresToolResultKeyCatalogReader(pool),
|
||||
released: () => released,
|
||||
};
|
||||
}
|
||||
|
||||
function bootstrapCommand() {
|
||||
return createToolResultKeyCatalogBootstrapCommand({
|
||||
keyId: 'tool-result-key-001',
|
||||
materialProof: toolResultKeyMaterialProof(
|
||||
'tool-result-key-001',
|
||||
Buffer.alloc(32, 1),
|
||||
),
|
||||
mutationId: 'tool-result-key-bootstrap-001',
|
||||
});
|
||||
}
|
||||
|
||||
test('serializes PostgreSQL key generations behind the catalog lock', async () => {
|
||||
const current = harness();
|
||||
assert.equal(await current.reader.findCurrent(), null);
|
||||
const bootstrap = bootstrapCommand();
|
||||
const first = await current.repository.append(bootstrap);
|
||||
assert.equal(first.status, 'created');
|
||||
assert.deepEqual(await current.repository.append(bootstrap), {
|
||||
status: 'existing',
|
||||
catalog: first.catalog,
|
||||
});
|
||||
|
||||
const second = await current.repository.append(
|
||||
createToolResultKeyRotationCommand(first.catalog, {
|
||||
keyId: 'tool-result-key-002',
|
||||
materialProof: toolResultKeyMaterialProof(
|
||||
'tool-result-key-002',
|
||||
Buffer.alloc(32, 2),
|
||||
),
|
||||
mutationId: 'tool-result-key-rotate-002',
|
||||
}),
|
||||
);
|
||||
assert.equal(second.catalog.generation, 2);
|
||||
assert.deepEqual(await current.reader.findCurrent(), second.catalog);
|
||||
assert.equal(current.history.length, 2);
|
||||
assert.equal(current.released(), 3);
|
||||
|
||||
await assert.rejects(
|
||||
current.repository.append(
|
||||
createToolResultKeyRetirementCommand(second.catalog, {
|
||||
keyId: 'tool-result-key-001',
|
||||
retirementReceiptDigest: 'f'.repeat(64),
|
||||
mutationId: 'tool-result-key-retire-forged-001',
|
||||
}),
|
||||
),
|
||||
ToolResultKeyCatalogConflictError,
|
||||
);
|
||||
const retiring = second.catalog.keys.find(
|
||||
(entry) => entry.keyId === 'tool-result-key-001',
|
||||
);
|
||||
const receipt = createToolResultKeyRetirementReceipt({
|
||||
catalogGeneration: second.catalog.generation,
|
||||
catalogDigest: second.catalog.catalogDigest,
|
||||
keyId: retiring.keyId,
|
||||
materialProof: retiring.materialProof,
|
||||
mutationId: 'tool-result-key-retirement-receipt-001',
|
||||
bindingCount: 0,
|
||||
overlayHeadCount: 0,
|
||||
coverageDigest: 'c'.repeat(64),
|
||||
createdAtMs: 1_500,
|
||||
});
|
||||
current.receipts.push(receipt);
|
||||
const retired = await current.repository.append(
|
||||
createToolResultKeyRetirementCommand(second.catalog, {
|
||||
keyId: 'tool-result-key-001',
|
||||
retirementReceiptDigest: receipt.receiptDigest,
|
||||
mutationId: 'tool-result-key-retire-001',
|
||||
}),
|
||||
);
|
||||
assert.equal(
|
||||
retired.catalog.keys.find(
|
||||
(entry) => entry.keyId === 'tool-result-key-001',
|
||||
).state,
|
||||
'retired',
|
||||
);
|
||||
|
||||
const transactionCalls = current.calls
|
||||
.map(({ text }) => text)
|
||||
.filter(
|
||||
(text) =>
|
||||
text.startsWith('BEGIN') ||
|
||||
text.includes('pg_advisory_xact_lock') ||
|
||||
text.startsWith(
|
||||
'INSERT INTO "ql3"."tool_result_key_catalog_generations"',
|
||||
) ||
|
||||
text === 'COMMIT',
|
||||
);
|
||||
assert.equal(
|
||||
transactionCalls.indexOf('SELECT pg_advisory_xact_lock(190397473, 3)') <
|
||||
transactionCalls.findIndex((text) =>
|
||||
text.startsWith(
|
||||
'INSERT INTO "ql3"."tool_result_key_catalog_generations"',
|
||||
),
|
||||
),
|
||||
true,
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
current.repository.append(
|
||||
createToolResultKeyRotationCommand(first.catalog, {
|
||||
keyId: 'tool-result-key-stale',
|
||||
materialProof: toolResultKeyMaterialProof(
|
||||
'tool-result-key-stale',
|
||||
Buffer.alloc(32, 3),
|
||||
),
|
||||
mutationId: 'tool-result-key-stale-003',
|
||||
}),
|
||||
),
|
||||
ToolResultKeyCatalogConflictError,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user