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,106 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { test } = require('node:test');
|
||||
const {
|
||||
LocalOwnerPepperCatalogFullError,
|
||||
LocalOwnerPepperGenerationConflictError,
|
||||
LocalOwnerPepperMutationConflictError,
|
||||
} = require('@qinglong/runtime-core/local-owner-pepper');
|
||||
const {
|
||||
openLocalSqliteBootstrapDatabase,
|
||||
} = require('../dist/storage/bootstrap');
|
||||
const { migrateLocalSqlitePath } = require('../dist/migration/migration');
|
||||
|
||||
function fixture(t) {
|
||||
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-pepper-catalog-'));
|
||||
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
||||
return path.join(directory, 'qinglong3.sqlite');
|
||||
}
|
||||
|
||||
function registration(index, overrides = {}) {
|
||||
return {
|
||||
mutationId: `018f4f58-7d5a-4d82-8f7d-5da12f05c0${String(index).padStart(2, '0')}`,
|
||||
pepperKeyId: `owner-key-${index}`,
|
||||
materialDigest: index.toString(16).padStart(64, '0'),
|
||||
backupDigest: (index + 16).toString(16).padStart(64, '0'),
|
||||
registeredAtMs: 100 + index,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test('registers, activates and rotates with one append-only generation winner', async (t) => {
|
||||
const databasePath = fixture(t);
|
||||
const options = { databasePath, profile: 'edge' };
|
||||
await migrateLocalSqlitePath(options);
|
||||
const first = await openLocalSqliteBootstrapDatabase(options);
|
||||
const second = await openLocalSqliteBootstrapDatabase(options);
|
||||
t.after(() => Promise.all([first.close(), second.close()]));
|
||||
|
||||
const key1 = registration(1);
|
||||
assert.equal((await first.ownerPepper.register(key1)).status, 'inserted');
|
||||
assert.equal((await second.ownerPepper.register(key1)).status, 'existing');
|
||||
const activation1 = {
|
||||
mutationId: '018f4f58-7d5a-4d82-8f7d-5da12f05d001',
|
||||
pepperKeyId: key1.pepperKeyId,
|
||||
expectedGeneration: 0,
|
||||
activatedAtMs: 200,
|
||||
};
|
||||
assert.equal(
|
||||
(await first.ownerPepper.activate(activation1)).activation.generation,
|
||||
1,
|
||||
);
|
||||
assert.equal(
|
||||
(await second.ownerPepper.activate(activation1)).status,
|
||||
'existing',
|
||||
);
|
||||
|
||||
const key2 = registration(2);
|
||||
const key3 = registration(3);
|
||||
await first.ownerPepper.register(key2);
|
||||
await first.ownerPepper.register(key3);
|
||||
const contenders = [key2, key3].map((key, index) =>
|
||||
[first, second][index].ownerPepper.activate({
|
||||
mutationId: `018f4f58-7d5a-4d82-8f7d-5da12f05d00${index + 2}`,
|
||||
pepperKeyId: key.pepperKeyId,
|
||||
expectedGeneration: 1,
|
||||
expectedActivePepperKeyId: key1.pepperKeyId,
|
||||
activatedAtMs: 300 + index,
|
||||
}),
|
||||
);
|
||||
const settled = await Promise.allSettled(contenders);
|
||||
assert.equal(settled.filter(({ status }) => status === 'fulfilled').length, 1);
|
||||
const rejection = settled.find(({ status }) => status === 'rejected');
|
||||
assert.ok(rejection.reason instanceof LocalOwnerPepperGenerationConflictError);
|
||||
|
||||
const active = await first.ownerPepper.resolveActive();
|
||||
assert.equal(active.generation, 2);
|
||||
assert.equal((await first.ownerPepper.resolveKey(key1.pepperKeyId)).state, 'retired');
|
||||
assert.equal(
|
||||
(await first.ownerPepper.resolveKey(active.activePepperKeyId)).state,
|
||||
'active',
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects semantic mutation drift and caps the catalog at eight keys', async (t) => {
|
||||
const databasePath = fixture(t);
|
||||
const options = { databasePath, profile: 'standalone' };
|
||||
await migrateLocalSqlitePath(options);
|
||||
const database = await openLocalSqliteBootstrapDatabase(options);
|
||||
t.after(() => database.close());
|
||||
|
||||
const first = registration(1);
|
||||
await database.ownerPepper.register(first);
|
||||
await assert.rejects(
|
||||
database.ownerPepper.register({ ...first, backupDigest: 'f'.repeat(64) }),
|
||||
LocalOwnerPepperMutationConflictError,
|
||||
);
|
||||
for (let index = 2; index <= 8; index += 1) {
|
||||
await database.ownerPepper.register(registration(index));
|
||||
}
|
||||
await assert.rejects(
|
||||
database.ownerPepper.register(registration(9)),
|
||||
LocalOwnerPepperCatalogFullError,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user