test(quality): add concurrency and real browser gates

This commit is contained in:
chick
2026-07-18 03:20:42 +08:00
parent 88d41a7a18
commit a884033352
5 changed files with 643 additions and 14 deletions
@@ -1,5 +1,9 @@
import Database from 'better-sqlite3';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterEach, describe, expect, it } from 'vitest';
import { openDatabase } from '../../infrastructure/database/database.js';
import { migrateDatabase } from '../../infrastructure/database/migrations.js';
import type { SecretStore } from '../../infrastructure/secrets/secret-store.js';
import type { Instance } from '@multi-simadmin/contracts';
@@ -38,8 +42,11 @@ class MemorySecrets implements SecretStore {
}
}
const dbs: Database.Database[] = [];
const temporaryDirectories: string[] = [];
afterEach(() => {
for (const db of dbs.splice(0)) db.close();
for (const directory of temporaryDirectories.splice(0))
rmSync(directory, { recursive: true, force: true });
});
function fixture(ids = ['id-1', 'slot-1', 'ref-1', 'id-2', 'slot-2', 'ref-2']) {
const db = new Database(':memory:');
@@ -56,6 +63,30 @@ function fixture(ids = ['id-1', 'slot-1', 'ref-1', 'id-2', 'slot-2', 'ref-2']) {
});
return { db, store, service };
}
function crossConnectionFixture() {
const directory = mkdtempSync(join(tmpdir(), 'instance-cas-'));
temporaryDirectories.push(directory);
const path = join(directory, 'state.sqlite');
const firstDb = openDatabase(path);
migrateDatabase(firstDb);
const secondDb = openDatabase(path);
dbs.push(firstDb, secondDb);
const store = new MemorySecrets();
const options = { store, now: () => new Date('2026-07-16T12:00:00.000Z') };
const firstIds = ['id-1', 'first-slot', 'first-reference'];
const secondIds = ['second-slot', 'second-reference'];
const first = new InstanceService({
...options,
db: firstDb,
idFactory: () => firstIds.shift()!,
});
const second = new InstanceService({
...options,
db: secondDb,
idFactory: () => secondIds.shift()!,
});
return { firstDb, secondDb, store, first, second };
}
const basic = { name: ' Alpha ', origin: 'http://192.168.1.10:8080/', tags: [' z ', 'a', 'a'] };
const code = async (promise: Promise<unknown>) => {
try {
@@ -222,6 +253,23 @@ describe('InstanceService', () => {
});
});
it('rejects stale update and delete CAS across managed connections', async () => {
const { firstDb, first, second } = crossConnectionFixture();
await first.create(basic);
await second.update('id-1', 1, { name: 'Committed elsewhere' });
await expect(first.update('id-1', 1, { name: 'Stale update' })).rejects.toMatchObject({
code: 'REVISION_CONFLICT',
});
await expect(first.delete('id-1', 1)).rejects.toMatchObject({ code: 'REVISION_CONFLICT' });
expect(
firstDb.prepare('SELECT name,config_revision FROM instances WHERE id=?').get('id-1'),
).toEqual({
name: 'Committed elsewhere',
config_revision: 2,
});
});
it('preserves, replaces and clears credentials without exposing them', async () => {
const { db, store, service } = fixture();
await service.create({ ...basic, password: { action: 'set', password: 'first-secret' } });