mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
230 lines
6.5 KiB
JavaScript
230 lines
6.5 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
pluginPackagePromptOutputKeyRetirementAbsenceProof,
|
|
} = require('@qinglong/ai/plugin-package-prompt-output-key-retirement');
|
|
const {
|
|
ClusterPromptOutputKeyRetirementProcessConfigError,
|
|
runClusterPromptOutputKeyRetirementProcess,
|
|
} = require('../dist/prompt-output/key-management/promptOutputKeyRetirementProcess');
|
|
|
|
function preparationRow(value) {
|
|
return {
|
|
keyId: value.keyId,
|
|
retirementId: value.retirementId,
|
|
requestId: value.requestId,
|
|
mutationId: value.mutationId,
|
|
catalogDigest: value.catalogDigest,
|
|
materialProof: value.materialProof,
|
|
preparedAtMs: String(value.preparedAtMs),
|
|
preparationDigest: value.preparationDigest,
|
|
preparationJson: value,
|
|
};
|
|
}
|
|
|
|
function completionRow(value) {
|
|
return {
|
|
keyId: value.keyId,
|
|
retirementId: value.retirementId,
|
|
requestId: value.requestId,
|
|
mutationId: value.mutationId,
|
|
preparationDigest: value.preparationDigest,
|
|
retiredCatalogDigest: value.retiredCatalogDigest,
|
|
absenceProof: value.absenceProof,
|
|
completedAtMs: String(value.completedAtMs),
|
|
completionDigest: value.completionDigest,
|
|
completionJson: value,
|
|
};
|
|
}
|
|
|
|
function databaseHarness() {
|
|
let preparation = null;
|
|
let completion = null;
|
|
let closeCount = 0;
|
|
const statements = [];
|
|
const query = async (sql, values = []) => {
|
|
statements.push({ sql, values });
|
|
if (sql.includes('current_user AS "currentUser"')) {
|
|
return {
|
|
rows: [
|
|
{
|
|
currentUser: 'ql3_ai_maintenance',
|
|
maintenanceAuthority: true,
|
|
schemaAuthority: true,
|
|
artifactDeleteOnly: true,
|
|
tombstoneAppendOnly: true,
|
|
keyRetirementAppendOnly: true,
|
|
keyRotationAppendOnly: true,
|
|
terminalEvidenceReadOnly: true,
|
|
},
|
|
],
|
|
rowCount: 1,
|
|
};
|
|
}
|
|
if (sql.includes('key_retirement_preparations') && sql.includes('SELECT')) {
|
|
return {
|
|
rows:
|
|
preparation && preparation.keyId === values[0]
|
|
? [preparationRow(preparation)]
|
|
: [],
|
|
rowCount: preparation ? 1 : 0,
|
|
};
|
|
}
|
|
if (sql.includes('key_retirement_completions') && sql.includes('SELECT')) {
|
|
return {
|
|
rows:
|
|
completion && completion.keyId === values[0]
|
|
? [completionRow(completion)]
|
|
: [],
|
|
rowCount: completion ? 1 : 0,
|
|
};
|
|
}
|
|
if (sql.includes('count(*)::text AS count')) {
|
|
return { rows: [{ count: '0' }], rowCount: 1 };
|
|
}
|
|
if (
|
|
sql.includes('INSERT INTO') &&
|
|
sql.includes('key_retirement_preparations')
|
|
) {
|
|
preparation = JSON.parse(values[8]);
|
|
return { rows: [], rowCount: 1 };
|
|
}
|
|
if (
|
|
sql.includes('INSERT INTO') &&
|
|
sql.includes('key_retirement_completions')
|
|
) {
|
|
completion = JSON.parse(values[9]);
|
|
return { rows: [], rowCount: 1 };
|
|
}
|
|
if (
|
|
sql.startsWith('BEGIN') ||
|
|
sql.startsWith('SET LOCAL') ||
|
|
sql.startsWith('SELECT pg_advisory_xact_lock') ||
|
|
sql === 'COMMIT' ||
|
|
sql === 'ROLLBACK'
|
|
) {
|
|
return { rows: [], rowCount: 0 };
|
|
}
|
|
throw new Error(`unexpected query: ${sql}`);
|
|
};
|
|
const client = { query, release() {} };
|
|
const pool = {
|
|
query,
|
|
async connect() {
|
|
return client;
|
|
},
|
|
};
|
|
return {
|
|
statements,
|
|
get closeCount() {
|
|
return closeCount;
|
|
},
|
|
async openDatabase() {
|
|
return {
|
|
pool,
|
|
async close() {
|
|
closeCount += 1;
|
|
},
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
test('retires one inactive Cluster key through maintenance PostgreSQL and injected material authority', async () => {
|
|
const database = databaseHarness();
|
|
const keyId = 'cluster-key-old';
|
|
const initialCatalogDigest = '1'.repeat(64);
|
|
const materialProof = '2'.repeat(64);
|
|
const retiredCatalogDigest = '3'.repeat(64);
|
|
let retiredPreparation = null;
|
|
const materials = {
|
|
async inspect() {
|
|
if (retiredPreparation) {
|
|
return {
|
|
state: 'absent',
|
|
keyId,
|
|
catalogDigest: retiredCatalogDigest,
|
|
absenceProof: pluginPackagePromptOutputKeyRetirementAbsenceProof(
|
|
retiredPreparation,
|
|
retiredCatalogDigest,
|
|
),
|
|
};
|
|
}
|
|
return {
|
|
state: 'inactive',
|
|
keyId,
|
|
catalogDigest: initialCatalogDigest,
|
|
materialProof,
|
|
};
|
|
},
|
|
async retire({ preparation }) {
|
|
retiredPreparation = preparation;
|
|
return this.inspect(keyId);
|
|
},
|
|
};
|
|
const options = {
|
|
database: { connection: { host: 'postgres.example.test' } },
|
|
request: {
|
|
keyId,
|
|
retirementId: 'retirement-1',
|
|
requestId: 'request-1',
|
|
mutationId: 'mutation-1',
|
|
},
|
|
materials,
|
|
openDatabase: database.openDatabase,
|
|
};
|
|
|
|
const completed = await runClusterPromptOutputKeyRetirementProcess(options);
|
|
assert.equal(completed.status, 'completed');
|
|
assert.equal(completed.keyId, keyId);
|
|
assert.equal(completed.readiness.keyRetirementAppendOnly, true);
|
|
assert.equal(completed.readiness.keyRotationAppendOnly, true);
|
|
assert.match(completed.preparationDigest, /^[0-9a-f]{64}$/);
|
|
assert.match(completed.completionDigest, /^[0-9a-f]{64}$/);
|
|
|
|
const replay = await runClusterPromptOutputKeyRetirementProcess(options);
|
|
assert.equal(replay.status, 'existing');
|
|
assert.equal(replay.preparationDigest, completed.preparationDigest);
|
|
assert.equal(replay.completionDigest, completed.completionDigest);
|
|
assert.equal(database.closeCount, 2);
|
|
assert.equal(
|
|
database.statements.filter(
|
|
({ sql }) =>
|
|
sql.includes('INSERT INTO') &&
|
|
sql.includes('key_retirement_preparations'),
|
|
).length,
|
|
1,
|
|
);
|
|
assert.equal(
|
|
database.statements.filter(
|
|
({ sql }) =>
|
|
sql.includes('INSERT INTO') &&
|
|
sql.includes('key_retirement_completions'),
|
|
).length,
|
|
1,
|
|
);
|
|
});
|
|
|
|
test('rejects an invalid retirement request before opening PostgreSQL', async () => {
|
|
let opened = false;
|
|
await assert.rejects(
|
|
runClusterPromptOutputKeyRetirementProcess({
|
|
database: { connection: { host: 'postgres.example.test' } },
|
|
request: {
|
|
keyId: '../invalid',
|
|
retirementId: 'retirement-1',
|
|
requestId: 'request-1',
|
|
mutationId: 'mutation-1',
|
|
},
|
|
materials: { async inspect() {}, async retire() {} },
|
|
async openDatabase() {
|
|
opened = true;
|
|
throw new Error('must not open');
|
|
},
|
|
}),
|
|
ClusterPromptOutputKeyRetirementProcessConfigError,
|
|
);
|
|
assert.equal(opened, false);
|
|
});
|