mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:00:24 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
ClusterPromptOutputKeyRotationProcessConfigError,
|
||||
runClusterPromptOutputKeyRotationProcess,
|
||||
} = require('../dist/prompt-output/key-management/promptOutputKeyRotationProcess.js');
|
||||
const {
|
||||
pluginPackagePromptOutputKeyRotationMaterialProof,
|
||||
} = require('@qinglong/ai/plugin-package-prompt-output-key-rotation');
|
||||
|
||||
function request() {
|
||||
return {
|
||||
rotationId: 'rotation-process-1',
|
||||
requestId: 'rotation-process-request-1',
|
||||
mutationId: 'rotation-process-mutation-1',
|
||||
expectedSecretUid: 'rotation-secret-uid-1',
|
||||
expectedActiveKeyId: 'key-before',
|
||||
expectedCatalogDigest: '1'.repeat(64),
|
||||
newKeyId: 'key-after',
|
||||
};
|
||||
}
|
||||
|
||||
function fakeDatabase(material) {
|
||||
let preparation;
|
||||
let completion;
|
||||
let closed = 0;
|
||||
const client = {
|
||||
async query(sql, parameters = []) {
|
||||
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,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
if (sql.includes('key_rotation_preparations') && sql.includes('SELECT')) {
|
||||
return {
|
||||
rows: preparation
|
||||
? [
|
||||
{
|
||||
rotationId: preparation.rotationId,
|
||||
requestId: preparation.requestId,
|
||||
mutationId: preparation.mutationId,
|
||||
expectedSecretUid: preparation.expectedSecretUid,
|
||||
expectedActiveKeyId: preparation.expectedActiveKeyId,
|
||||
expectedCatalogDigest: preparation.expectedCatalogDigest,
|
||||
newKeyId: preparation.newKeyId,
|
||||
materialProof: preparation.materialProof,
|
||||
preparedAtMs: String(preparation.preparedAtMs),
|
||||
preparationDigest: preparation.preparationDigest,
|
||||
preparationJson: preparation,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
};
|
||||
}
|
||||
if (sql.includes('key_rotation_completions') && sql.includes('SELECT')) {
|
||||
return {
|
||||
rows: completion
|
||||
? [
|
||||
{
|
||||
rotationId: completion.rotationId,
|
||||
requestId: completion.requestId,
|
||||
mutationId: completion.mutationId,
|
||||
preparationDigest: completion.preparationDigest,
|
||||
generation: String(completion.generation),
|
||||
previousActiveKeyId: completion.previousActiveKeyId,
|
||||
activeKeyId: completion.activeKeyId,
|
||||
catalogDigest: completion.catalogDigest,
|
||||
materialProof: completion.materialProof,
|
||||
completedAtMs: String(completion.completedAtMs),
|
||||
completionDigest: completion.completionDigest,
|
||||
completionJson: completion,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
};
|
||||
}
|
||||
if (
|
||||
sql.includes('INSERT INTO') &&
|
||||
sql.includes('key_rotation_preparations')
|
||||
) {
|
||||
preparation = JSON.parse(parameters[10]);
|
||||
}
|
||||
if (
|
||||
sql.includes('INSERT INTO') &&
|
||||
sql.includes('key_rotation_completions')
|
||||
) {
|
||||
completion = JSON.parse(parameters[11]);
|
||||
}
|
||||
return { rows: [], rowCount: 1 };
|
||||
},
|
||||
release() {},
|
||||
};
|
||||
const pool = {
|
||||
async connect() {
|
||||
return client;
|
||||
},
|
||||
async query(sql, parameters) {
|
||||
return client.query(sql, parameters);
|
||||
},
|
||||
};
|
||||
return {
|
||||
database: {
|
||||
pool,
|
||||
async close() {
|
||||
closed += 1;
|
||||
},
|
||||
},
|
||||
get closed() {
|
||||
return closed;
|
||||
},
|
||||
get completion() {
|
||||
return completion;
|
||||
},
|
||||
materials: {
|
||||
async rotate(command) {
|
||||
return {
|
||||
generation: 2,
|
||||
previousActiveKeyId: command.expectedActiveKeyId,
|
||||
activeKeyId: command.newKeyId,
|
||||
catalogDigest: '2'.repeat(64),
|
||||
materialProof: pluginPackagePromptOutputKeyRotationMaterialProof(
|
||||
command.newKeyId,
|
||||
material,
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('Cluster rotation process commits content-free prepare and completion', async () => {
|
||||
const material = Buffer.alloc(32, 0x71);
|
||||
const fixture = fakeDatabase(material);
|
||||
const result = await runClusterPromptOutputKeyRotationProcess({
|
||||
database: { connection: { connectionString: 'postgres://unused' } },
|
||||
request: request(),
|
||||
material,
|
||||
materials: fixture.materials,
|
||||
openDatabase: async () => fixture.database,
|
||||
});
|
||||
assert.equal(result.status, 'completed');
|
||||
assert.equal(result.generation, 2);
|
||||
assert.equal(result.activeKeyId, 'key-after');
|
||||
assert.equal(result.readiness.keyRotationAppendOnly, true);
|
||||
assert.equal(fixture.closed, 1);
|
||||
const durable = JSON.stringify(fixture.completion);
|
||||
assert.equal(durable.includes(material.toString('base64url')), false);
|
||||
assert.equal(durable.includes(material.toString('hex')), false);
|
||||
});
|
||||
|
||||
test('Cluster rotation process rejects invalid material before opening PostgreSQL', async () => {
|
||||
let opened = false;
|
||||
await assert.rejects(
|
||||
runClusterPromptOutputKeyRotationProcess({
|
||||
database: { connection: { connectionString: 'postgres://unused' } },
|
||||
request: request(),
|
||||
material: Buffer.alloc(31),
|
||||
materials: {
|
||||
async rotate() {
|
||||
throw new Error('unreachable');
|
||||
},
|
||||
},
|
||||
openDatabase: async () => {
|
||||
opened = true;
|
||||
throw new Error('must not open');
|
||||
},
|
||||
}),
|
||||
ClusterPromptOutputKeyRotationProcessConfigError,
|
||||
);
|
||||
assert.equal(opened, false);
|
||||
});
|
||||
Reference in New Issue
Block a user