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,146 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { createHash } = require('node:crypto');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { DatabaseSync } = require('node:sqlite');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
LocalPluginPackagePromptOutputKeyRetirementRepository,
|
||||
assertLocalPluginPackagePromptOutputKeyNotRetiring,
|
||||
} = require('../dist/prompt-output/storage/localPluginPackagePromptOutputKeyRetirementRepository.js');
|
||||
const {
|
||||
PluginPackagePromptOutputKeyRetirementConflictError,
|
||||
PluginPackagePromptOutputKeyRetirementUnavailableError,
|
||||
createPluginPackagePromptOutputKeyRetirementPreparation,
|
||||
pluginPackagePromptOutputKeyRetirementAbsenceProof,
|
||||
} = require('../dist/prompt-output/key-management/pluginPackagePromptOutputKeyRetirement.js');
|
||||
const {
|
||||
setupScenario,
|
||||
} = require('./fixtures/pluginPackagePromptCrashFixture.cjs');
|
||||
|
||||
const digest = (value) => createHash('sha256').update(value).digest('hex');
|
||||
|
||||
async function harness(t) {
|
||||
const directory = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), 'ql3-prompt-output-key-retirement-'),
|
||||
);
|
||||
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
||||
const databasePath = path.join(directory, 'runtime.sqlite');
|
||||
await setupScenario({
|
||||
databasePath,
|
||||
statePath: path.join(directory, 'state.json'),
|
||||
profile: 'edge',
|
||||
operation: 'admission',
|
||||
});
|
||||
const client = new DatabaseSync(databasePath);
|
||||
client.exec('PRAGMA foreign_keys = ON');
|
||||
t.after(() => client.close());
|
||||
const authority = {
|
||||
client,
|
||||
async enqueue(work) {
|
||||
return work();
|
||||
},
|
||||
};
|
||||
let nowMs = 100;
|
||||
return {
|
||||
client,
|
||||
repository: new LocalPluginPackagePromptOutputKeyRetirementRepository({
|
||||
authority,
|
||||
now: () => nowMs,
|
||||
}),
|
||||
setNow(value) {
|
||||
nowMs = value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function command() {
|
||||
return {
|
||||
keyId: 'prompt-output-key-old',
|
||||
retirementId: 'retire-prompt-output-key-old',
|
||||
requestId: 'request-retire-prompt-output-key-old',
|
||||
mutationId: 'mutation-retire-prompt-output-key-old',
|
||||
catalogDigest: digest('catalog-before'),
|
||||
materialProof: digest('material-old'),
|
||||
};
|
||||
}
|
||||
|
||||
test('SQLite appends one retirement preparation and completion with exact replay', async (t) => {
|
||||
const value = await harness(t);
|
||||
const prepared = await value.repository.prepare(command());
|
||||
assert.equal(prepared.status, 'created');
|
||||
assert.equal(prepared.preparation.preparedAtMs, 100);
|
||||
assert.throws(
|
||||
() =>
|
||||
assertLocalPluginPackagePromptOutputKeyNotRetiring(
|
||||
value.client,
|
||||
command().keyId,
|
||||
),
|
||||
PluginPackagePromptOutputKeyRetirementConflictError,
|
||||
);
|
||||
const retiredCatalogDigest = digest('catalog-after');
|
||||
const absenceProof = pluginPackagePromptOutputKeyRetirementAbsenceProof(
|
||||
prepared.preparation,
|
||||
retiredCatalogDigest,
|
||||
);
|
||||
value.setNow(200);
|
||||
const completed = await value.repository.complete({
|
||||
preparation: prepared.preparation,
|
||||
retiredCatalogDigest,
|
||||
absenceProof,
|
||||
});
|
||||
assert.equal(completed.status, 'created');
|
||||
assert.equal(completed.completion.completedAtMs, 200);
|
||||
value.setNow(999);
|
||||
assert.equal((await value.repository.prepare(command())).status, 'existing');
|
||||
assert.equal(
|
||||
(
|
||||
await value.repository.complete({
|
||||
preparation: prepared.preparation,
|
||||
retiredCatalogDigest,
|
||||
absenceProof,
|
||||
})
|
||||
).status,
|
||||
'existing',
|
||||
);
|
||||
assert.deepEqual(await value.repository.find(command().keyId), {
|
||||
preparation: prepared.preparation,
|
||||
completion: completed.completion,
|
||||
});
|
||||
});
|
||||
|
||||
test('SQLite rejects detached completion, command drift and corrupt durable JSON', async (t) => {
|
||||
const value = await harness(t);
|
||||
const detached = createPluginPackagePromptOutputKeyRetirementPreparation({
|
||||
...command(),
|
||||
preparedAtMs: 100,
|
||||
});
|
||||
await assert.rejects(
|
||||
value.repository.complete({
|
||||
preparation: detached,
|
||||
retiredCatalogDigest: digest('catalog-after'),
|
||||
absenceProof: digest('absence'),
|
||||
}),
|
||||
PluginPackagePromptOutputKeyRetirementConflictError,
|
||||
);
|
||||
const prepared = await value.repository.prepare(command());
|
||||
await assert.rejects(
|
||||
value.repository.prepare({ ...command(), requestId: 'drifted-request' }),
|
||||
PluginPackagePromptOutputKeyRetirementConflictError,
|
||||
);
|
||||
value.client.exec('PRAGMA ignore_check_constraints = ON');
|
||||
value.client
|
||||
.prepare(
|
||||
`UPDATE "ModelInvocationPromptOutputKeyRetirementPreparations"
|
||||
SET preparation_json = '{"corrupt":true}'
|
||||
WHERE key_id = ?`,
|
||||
)
|
||||
.run(prepared.preparation.keyId);
|
||||
value.client.exec('PRAGMA ignore_check_constraints = OFF');
|
||||
await assert.rejects(
|
||||
value.repository.find(prepared.preparation.keyId),
|
||||
PluginPackagePromptOutputKeyRetirementUnavailableError,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user