mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
PostgresPluginPackagePromptOutputKeyRotationRepository,
|
||||
} = require('../dist/prompt-output/storage/postgresPluginPackagePromptOutputKeyRotationRepository.js');
|
||||
|
||||
function preparationRow(value) {
|
||||
return {
|
||||
rotationId: value.rotationId,
|
||||
requestId: value.requestId,
|
||||
mutationId: value.mutationId,
|
||||
expectedSecretUid: value.expectedSecretUid,
|
||||
expectedActiveKeyId: value.expectedActiveKeyId,
|
||||
expectedCatalogDigest: value.expectedCatalogDigest,
|
||||
newKeyId: value.newKeyId,
|
||||
materialProof: value.materialProof,
|
||||
preparedAtMs: String(value.preparedAtMs),
|
||||
preparationDigest: value.preparationDigest,
|
||||
preparationJson: value,
|
||||
};
|
||||
}
|
||||
|
||||
function completionRow(value) {
|
||||
return {
|
||||
rotationId: value.rotationId,
|
||||
requestId: value.requestId,
|
||||
mutationId: value.mutationId,
|
||||
preparationDigest: value.preparationDigest,
|
||||
generation: String(value.generation),
|
||||
previousActiveKeyId: value.previousActiveKeyId,
|
||||
activeKeyId: value.activeKeyId,
|
||||
catalogDigest: value.catalogDigest,
|
||||
materialProof: value.materialProof,
|
||||
completedAtMs: String(value.completedAtMs),
|
||||
completionDigest: value.completionDigest,
|
||||
completionJson: value,
|
||||
};
|
||||
}
|
||||
|
||||
function storagePool(options = {}) {
|
||||
const queries = [];
|
||||
const preparations = new Map();
|
||||
const completions = new Map();
|
||||
let inserts = 0;
|
||||
let commitFailures = options.commitFailures ?? 0;
|
||||
const client = {
|
||||
async query(sql, parameters = []) {
|
||||
queries.push({ sql, parameters });
|
||||
if (sql.includes('key_rotation_preparations') && sql.includes('SELECT')) {
|
||||
const value = preparations.get(parameters[0]);
|
||||
return { rows: value ? [preparationRow(value)] : [] };
|
||||
}
|
||||
if (sql.includes('key_rotation_completions') && sql.includes('SELECT')) {
|
||||
const value = completions.get(parameters[0]);
|
||||
return { rows: value ? [completionRow(value)] : [] };
|
||||
}
|
||||
if (
|
||||
sql.includes('INSERT INTO') &&
|
||||
sql.includes('key_rotation_preparations')
|
||||
) {
|
||||
if (options.conflictOnSecondSource && preparations.size > 0) {
|
||||
const error = new Error('unique source');
|
||||
error.code = '23505';
|
||||
throw error;
|
||||
}
|
||||
const value = JSON.parse(parameters[10]);
|
||||
preparations.set(value.rotationId, value);
|
||||
inserts += 1;
|
||||
return { rows: [], rowCount: 1 };
|
||||
}
|
||||
if (
|
||||
sql.includes('INSERT INTO') &&
|
||||
sql.includes('key_rotation_completions')
|
||||
) {
|
||||
const value = JSON.parse(parameters[11]);
|
||||
completions.set(value.rotationId, value);
|
||||
inserts += 1;
|
||||
return { rows: [], rowCount: 1 };
|
||||
}
|
||||
if (sql === 'COMMIT' && commitFailures > 0 && inserts > 0) {
|
||||
commitFailures -= 1;
|
||||
throw new Error('connection reset after durable COMMIT');
|
||||
}
|
||||
return { rows: [], rowCount: 0 };
|
||||
},
|
||||
release() {},
|
||||
};
|
||||
return {
|
||||
queries,
|
||||
preparations,
|
||||
completions,
|
||||
client,
|
||||
async connect() {
|
||||
return client;
|
||||
},
|
||||
async query(sql, parameters) {
|
||||
return client.query(sql, parameters);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function request(overrides = {}) {
|
||||
return {
|
||||
rotationId: 'rotation-a',
|
||||
requestId: 'request-a',
|
||||
mutationId: 'mutation-a',
|
||||
expectedSecretUid: 'secret-uid-a',
|
||||
expectedActiveKeyId: 'key-a',
|
||||
expectedCatalogDigest: '1'.repeat(64),
|
||||
newKeyId: 'key-b',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function state() {
|
||||
return {
|
||||
generation: 2,
|
||||
previousActiveKeyId: 'key-a',
|
||||
activeKeyId: 'key-b',
|
||||
catalogDigest: '2'.repeat(64),
|
||||
materialProof: '3'.repeat(64),
|
||||
};
|
||||
}
|
||||
|
||||
test('PostgreSQL appends rotation prepare and completion behind one source fence', async () => {
|
||||
const pool = storagePool();
|
||||
const times = [10_000, 20_000, 30_000, 40_000];
|
||||
const repository = new PostgresPluginPackagePromptOutputKeyRotationRepository(
|
||||
{
|
||||
pool,
|
||||
now: () => times.shift(),
|
||||
},
|
||||
);
|
||||
const prepared = await repository.prepare({
|
||||
request: request(),
|
||||
materialProof: '3'.repeat(64),
|
||||
});
|
||||
assert.equal(prepared.status, 'created');
|
||||
assert.equal(prepared.preparation.preparedAtMs, 10_000);
|
||||
const prepareReplay = await repository.prepare({
|
||||
request: request(),
|
||||
materialProof: '3'.repeat(64),
|
||||
});
|
||||
assert.equal(prepareReplay.status, 'existing');
|
||||
|
||||
const completed = await repository.complete({
|
||||
preparation: prepared.preparation,
|
||||
state: state(),
|
||||
});
|
||||
assert.equal(completed.status, 'created');
|
||||
assert.equal(completed.completion.completedAtMs, 30_000);
|
||||
const completionReplay = await repository.complete({
|
||||
preparation: prepared.preparation,
|
||||
state: state(),
|
||||
});
|
||||
assert.equal(completionReplay.status, 'existing');
|
||||
assert.deepEqual(await repository.find('rotation-a'), {
|
||||
preparation: prepared.preparation,
|
||||
completion: completed.completion,
|
||||
});
|
||||
|
||||
const lockIndex = pool.queries.findIndex(({ sql }) =>
|
||||
sql.includes('pg_advisory_xact_lock'),
|
||||
);
|
||||
const insertIndex = pool.queries.findIndex(
|
||||
({ sql }) =>
|
||||
sql.includes('INSERT INTO') && sql.includes('key_rotation_preparations'),
|
||||
);
|
||||
assert.equal(lockIndex >= 0 && lockIndex < insertIndex, true);
|
||||
assert.equal(
|
||||
JSON.stringify([
|
||||
...pool.preparations.values(),
|
||||
...pool.completions.values(),
|
||||
]).includes(Buffer.alloc(32, 0x42).toString('base64url')),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test('PostgreSQL durable facts resolve COMMIT response loss and source conflicts', async () => {
|
||||
const lost = storagePool({ commitFailures: 1 });
|
||||
const repository = new PostgresPluginPackagePromptOutputKeyRotationRepository(
|
||||
{
|
||||
pool: lost,
|
||||
now: () => 10_000,
|
||||
},
|
||||
);
|
||||
await assert.rejects(
|
||||
repository.prepare({ request: request(), materialProof: '3'.repeat(64) }),
|
||||
{ code: 'PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_UNAVAILABLE' },
|
||||
);
|
||||
const durable = await repository.find('rotation-a');
|
||||
assert.equal(durable.preparation.rotationId, 'rotation-a');
|
||||
|
||||
const competing = storagePool({ conflictOnSecondSource: true });
|
||||
const competingRepository =
|
||||
new PostgresPluginPackagePromptOutputKeyRotationRepository({
|
||||
pool: competing,
|
||||
now: () => 10_000,
|
||||
});
|
||||
await competingRepository.prepare({
|
||||
request: request(),
|
||||
materialProof: '3'.repeat(64),
|
||||
});
|
||||
await assert.rejects(
|
||||
competingRepository.prepare({
|
||||
request: request({
|
||||
rotationId: 'rotation-b',
|
||||
requestId: 'request-b',
|
||||
mutationId: 'mutation-b',
|
||||
newKeyId: 'key-c',
|
||||
}),
|
||||
materialProof: '4'.repeat(64),
|
||||
}),
|
||||
{ code: 'PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_CONFLICT' },
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user