@skillsmith/core 0.11.4 → 0.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/README.md +10 -7
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.js +20 -5
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/api/client.private-registry.d.ts +80 -0
- package/dist/src/api/client.private-registry.d.ts.map +1 -0
- package/dist/src/api/client.private-registry.js +97 -0
- package/dist/src/api/client.private-registry.js.map +1 -0
- package/dist/src/api/client.private-registry.test.d.ts +9 -0
- package/dist/src/api/client.private-registry.test.d.ts.map +1 -0
- package/dist/src/api/client.private-registry.test.js +127 -0
- package/dist/src/api/client.private-registry.test.js.map +1 -0
- package/dist/src/api/client.test.js +39 -0
- package/dist/src/api/client.test.js.map +1 -1
- package/dist/src/api/client.toSkill.test.d.ts +25 -0
- package/dist/src/api/client.toSkill.test.d.ts.map +1 -0
- package/dist/src/api/client.toSkill.test.js +97 -0
- package/dist/src/api/client.toSkill.test.js.map +1 -0
- package/dist/src/api/client.token-refresh.d.ts +8 -0
- package/dist/src/api/client.token-refresh.d.ts.map +1 -1
- package/dist/src/api/client.token-refresh.js +26 -0
- package/dist/src/api/client.token-refresh.js.map +1 -1
- package/dist/src/api/client.token-refresh.test.js +82 -0
- package/dist/src/api/client.token-refresh.test.js.map +1 -1
- package/dist/src/api/index.d.ts +2 -0
- package/dist/src/api/index.d.ts.map +1 -1
- package/dist/src/api/index.js +9 -0
- package/dist/src/api/index.js.map +1 -1
- package/dist/src/api/security-summary.d.ts +73 -0
- package/dist/src/api/security-summary.d.ts.map +1 -0
- package/dist/src/api/security-summary.js +88 -0
- package/dist/src/api/security-summary.js.map +1 -0
- package/dist/src/api/security-summary.test.d.ts +9 -0
- package/dist/src/api/security-summary.test.d.ts.map +1 -0
- package/dist/src/api/security-summary.test.js +94 -0
- package/dist/src/api/security-summary.test.js.map +1 -0
- package/dist/src/compatibility/slugs.d.ts +7 -3
- package/dist/src/compatibility/slugs.d.ts.map +1 -1
- package/dist/src/compatibility/slugs.js +11 -4
- package/dist/src/compatibility/slugs.js.map +1 -1
- package/dist/src/config/config-atomic-write.d.ts +30 -10
- package/dist/src/config/config-atomic-write.d.ts.map +1 -1
- package/dist/src/config/config-atomic-write.js +33 -80
- package/dist/src/config/config-atomic-write.js.map +1 -1
- package/dist/src/config/config-atomic-write.test.d.ts +8 -0
- package/dist/src/config/config-atomic-write.test.d.ts.map +1 -1
- package/dist/src/config/config-atomic-write.test.js +64 -12
- package/dist/src/config/config-atomic-write.test.js.map +1 -1
- package/dist/src/config/owned-lock.acquire.d.ts +53 -0
- package/dist/src/config/owned-lock.acquire.d.ts.map +1 -0
- package/dist/src/config/owned-lock.acquire.js +140 -0
- package/dist/src/config/owned-lock.acquire.js.map +1 -0
- package/dist/src/config/owned-lock.claim.d.ts +84 -0
- package/dist/src/config/owned-lock.claim.d.ts.map +1 -0
- package/dist/src/config/owned-lock.claim.js +310 -0
- package/dist/src/config/owned-lock.claim.js.map +1 -0
- package/dist/src/config/owned-lock.d.ts +113 -0
- package/dist/src/config/owned-lock.d.ts.map +1 -0
- package/dist/src/config/owned-lock.js +121 -0
- package/dist/src/config/owned-lock.js.map +1 -0
- package/dist/src/config/owned-lock.test.d.ts +12 -0
- package/dist/src/config/owned-lock.test.d.ts.map +1 -0
- package/dist/src/config/owned-lock.test.js +412 -0
- package/dist/src/config/owned-lock.test.js.map +1 -0
- package/dist/src/config/owned-lock.types.d.ts +78 -0
- package/dist/src/config/owned-lock.types.d.ts.map +1 -0
- package/dist/src/config/owned-lock.types.js +30 -0
- package/dist/src/config/owned-lock.types.js.map +1 -0
- package/dist/src/db/createDatabase.d.ts.map +1 -1
- package/dist/src/db/createDatabase.js +19 -7
- package/dist/src/db/createDatabase.js.map +1 -1
- package/dist/src/db/drivers/sqljsDriver.d.ts +8 -0
- package/dist/src/db/drivers/sqljsDriver.d.ts.map +1 -1
- package/dist/src/db/drivers/sqljsDriver.js +24 -2
- package/dist/src/db/drivers/sqljsDriver.js.map +1 -1
- package/dist/src/db/migration-runner.d.ts.map +1 -1
- package/dist/src/db/migration-runner.js +23 -2
- package/dist/src/db/migration-runner.js.map +1 -1
- package/dist/src/db/schema.d.ts.map +1 -1
- package/dist/src/db/schema.js +29 -3
- package/dist/src/db/schema.js.map +1 -1
- package/dist/src/embeddings/hnsw-store.exports.d.ts +11 -0
- package/dist/src/embeddings/hnsw-store.exports.d.ts.map +1 -0
- package/dist/src/embeddings/hnsw-store.exports.js +10 -0
- package/dist/src/embeddings/hnsw-store.exports.js.map +1 -0
- package/dist/src/embeddings/index.d.ts +1 -2
- package/dist/src/embeddings/index.d.ts.map +1 -1
- package/dist/src/embeddings/index.js +17 -3
- package/dist/src/embeddings/index.js.map +1 -1
- package/dist/src/embeddings/probe.d.ts.map +1 -1
- package/dist/src/embeddings/probe.js +4 -8
- package/dist/src/embeddings/probe.js.map +1 -1
- package/dist/src/exports/services.d.ts +5 -1
- package/dist/src/exports/services.d.ts.map +1 -1
- package/dist/src/exports/services.js +27 -0
- package/dist/src/exports/services.js.map +1 -1
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/install/index.d.ts +2 -2
- package/dist/src/install/index.d.ts.map +1 -1
- package/dist/src/install/index.js +1 -1
- package/dist/src/install/index.js.map +1 -1
- package/dist/src/install/paths.d.ts +165 -1
- package/dist/src/install/paths.d.ts.map +1 -1
- package/dist/src/install/paths.js +254 -1
- package/dist/src/install/paths.js.map +1 -1
- package/dist/src/install/paths.test.js +174 -2
- package/dist/src/install/paths.test.js.map +1 -1
- package/dist/src/security/scanner/multiline-category-closure.test.d.ts +80 -0
- package/dist/src/security/scanner/multiline-category-closure.test.d.ts.map +1 -0
- package/dist/src/security/scanner/multiline-category-closure.test.js +287 -0
- package/dist/src/security/scanner/multiline-category-closure.test.js.map +1 -0
- package/dist/src/services/context-words.d.ts +40 -0
- package/dist/src/services/context-words.d.ts.map +1 -0
- package/dist/src/services/context-words.js +178 -0
- package/dist/src/services/context-words.js.map +1 -0
- package/dist/src/services/context-words.test.d.ts +2 -0
- package/dist/src/services/context-words.test.d.ts.map +1 -0
- package/dist/src/services/context-words.test.js +94 -0
- package/dist/src/services/context-words.test.js.map +1 -0
- package/dist/src/services/recommend-guard.d.ts +28 -0
- package/dist/src/services/recommend-guard.d.ts.map +1 -0
- package/dist/src/services/recommend-guard.js +34 -0
- package/dist/src/services/recommend-guard.js.map +1 -0
- package/dist/src/services/recommend-guard.test.d.ts +2 -0
- package/dist/src/services/recommend-guard.test.d.ts.map +1 -0
- package/dist/src/services/recommend-guard.test.js +28 -0
- package/dist/src/services/recommend-guard.test.js.map +1 -0
- package/dist/src/services/skill-installation.content.d.ts +96 -0
- package/dist/src/services/skill-installation.content.d.ts.map +1 -0
- package/dist/src/services/skill-installation.content.js +319 -0
- package/dist/src/services/skill-installation.content.js.map +1 -0
- package/dist/src/services/skill-installation.helpers.d.ts +32 -1
- package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
- package/dist/src/services/skill-installation.helpers.js +62 -7
- package/dist/src/services/skill-installation.helpers.js.map +1 -1
- package/dist/src/services/skill-installation.helpers.test.js +108 -2
- package/dist/src/services/skill-installation.helpers.test.js.map +1 -1
- package/dist/src/services/skill-installation.io.d.ts +22 -1
- package/dist/src/services/skill-installation.io.d.ts.map +1 -1
- package/dist/src/services/skill-installation.io.js +110 -6
- package/dist/src/services/skill-installation.io.js.map +1 -1
- package/dist/src/services/skill-installation.service.d.ts +22 -1
- package/dist/src/services/skill-installation.service.d.ts.map +1 -1
- package/dist/src/services/skill-installation.service.js +30 -5
- package/dist/src/services/skill-installation.service.js.map +1 -1
- package/dist/src/services/skill-installation.types.d.ts +42 -1
- package/dist/src/services/skill-installation.types.d.ts.map +1 -1
- package/dist/src/services/skill-installation.types.js.map +1 -1
- package/dist/src/services/skill-manifest.d.ts +21 -0
- package/dist/src/services/skill-manifest.d.ts.map +1 -1
- package/dist/src/services/skill-manifest.js +51 -6
- package/dist/src/services/skill-manifest.js.map +1 -1
- package/dist/src/services/skill-manifest.test.d.ts +2 -0
- package/dist/src/services/skill-manifest.test.d.ts.map +1 -0
- package/dist/src/services/skill-manifest.test.js +202 -0
- package/dist/src/services/skill-manifest.test.js.map +1 -0
- package/dist/src/services/skill-resolution.d.ts +67 -0
- package/dist/src/services/skill-resolution.d.ts.map +1 -0
- package/dist/src/services/skill-resolution.js +67 -0
- package/dist/src/services/skill-resolution.js.map +1 -0
- package/dist/src/services/skill-resolution.test.d.ts +2 -0
- package/dist/src/services/skill-resolution.test.d.ts.map +1 -0
- package/dist/src/services/skill-resolution.test.js +143 -0
- package/dist/src/services/skill-resolution.test.js.map +1 -0
- package/dist/src/types/skill.d.ts +10 -0
- package/dist/src/types/skill.d.ts.map +1 -1
- package/dist/src/types.d.ts +9 -5
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/index.d.ts +1 -0
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +4 -0
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/quiet-mode.d.ts +21 -0
- package/dist/src/utils/quiet-mode.d.ts.map +1 -0
- package/dist/src/utils/quiet-mode.js +26 -0
- package/dist/src/utils/quiet-mode.js.map +1 -0
- package/dist/tests/EmbeddingService.test.js +58 -1
- package/dist/tests/EmbeddingService.test.js.map +1 -1
- package/dist/tests/SecurityScanner.performance.test.js +5 -2
- package/dist/tests/SecurityScanner.performance.test.js.map +1 -1
- package/dist/tests/db/database-abstraction.test.js +45 -1
- package/dist/tests/db/database-abstraction.test.js.map +1 -1
- package/dist/tests/db/migration-runner-race.test.d.ts +2 -0
- package/dist/tests/db/migration-runner-race.test.d.ts.map +1 -0
- package/dist/tests/db/migration-runner-race.test.js +75 -0
- package/dist/tests/db/migration-runner-race.test.js.map +1 -0
- package/dist/tests/db/schema-async.test.js +37 -1
- package/dist/tests/db/schema-async.test.js.map +1 -1
- package/dist/tests/db/sqljsDriver.test.js +63 -1
- package/dist/tests/db/sqljsDriver.test.js.map +1 -1
- package/dist/tests/helpers/deterministic-dead-pid.d.ts +13 -0
- package/dist/tests/helpers/deterministic-dead-pid.d.ts.map +1 -0
- package/dist/tests/helpers/deterministic-dead-pid.js +31 -0
- package/dist/tests/helpers/deterministic-dead-pid.js.map +1 -0
- package/dist/tests/integration/owned-lock-lost-update.test.d.ts +12 -0
- package/dist/tests/integration/owned-lock-lost-update.test.d.ts.map +1 -0
- package/dist/tests/integration/owned-lock-lost-update.test.js +52 -0
- package/dist/tests/integration/owned-lock-lost-update.test.js.map +1 -0
- package/dist/tests/integration/owned-lock-reclaim-race.test.d.ts +19 -0
- package/dist/tests/integration/owned-lock-reclaim-race.test.d.ts.map +1 -0
- package/dist/tests/integration/owned-lock-reclaim-race.test.js +177 -0
- package/dist/tests/integration/owned-lock-reclaim-race.test.js.map +1 -0
- package/dist/tests/schema.test.js +43 -1
- package/dist/tests/schema.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.content.test.d.ts +10 -0
- package/dist/tests/unit/services/skill-installation.content.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.content.test.js +290 -0
- package/dist/tests/unit/services/skill-installation.content.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.d.ts +34 -0
- package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.js +246 -0
- package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.symlink.test.d.ts +8 -0
- package/dist/tests/unit/services/skill-installation.io.symlink.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.symlink.test.js +72 -0
- package/dist/tests/unit/services/skill-installation.io.symlink.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.companion-basedir.test.d.ts +17 -0
- package/dist/tests/unit/services/skill-installation.service.companion-basedir.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.companion-basedir.test.js +112 -0
- package/dist/tests/unit/services/skill-installation.service.companion-basedir.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.error-codes.test.d.ts +15 -0
- package/dist/tests/unit/services/skill-installation.service.error-codes.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.error-codes.test.js +190 -0
- package/dist/tests/unit/services/skill-installation.service.error-codes.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.multi-client.test.d.ts +17 -0
- package/dist/tests/unit/services/skill-installation.service.multi-client.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.multi-client.test.js +126 -0
- package/dist/tests/unit/services/skill-installation.service.multi-client.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.test.js +7 -112
- package/dist/tests/unit/services/skill-installation.service.test.js.map +1 -1
- package/dist/tests/utils/quiet-mode.test.d.ts +12 -0
- package/dist/tests/utils/quiet-mode.test.d.ts.map +1 -0
- package/dist/tests/utils/quiet-mode.test.js +42 -0
- package/dist/tests/utils/quiet-mode.test.js.map +1 -0
- package/package.json +17 -5
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-6003: regression test for the unguarded concurrent-migration race in
|
|
3
|
+
* runMigrations() (packages/core/src/db/migration-runner.ts).
|
|
4
|
+
*
|
|
5
|
+
* Two processes opening the same fresh DB concurrently can both read
|
|
6
|
+
* currentVersion = getSchemaVersion(db) BEFORE either has stamped a row,
|
|
7
|
+
* both decide the same migration is pending, and the loser's plain
|
|
8
|
+
* `INSERT INTO schema_version (version) VALUES (?)` throws `UNIQUE
|
|
9
|
+
* constraint failed: schema_version.version`. This is the exact real-world
|
|
10
|
+
* failure crash-handler-integration.test.ts's SMI-5999 fix sidesteps by
|
|
11
|
+
* isolating each spawned mcp-server process's HOME — that test proves the
|
|
12
|
+
* SYMPTOM is contained by isolation; this test proves the underlying
|
|
13
|
+
* migration-runner bug itself is fixed, independent of any caller-side
|
|
14
|
+
* isolation.
|
|
15
|
+
*
|
|
16
|
+
* Genuine concurrency is required to reproduce this — see
|
|
17
|
+
* migration-runner-race-child.mjs's header for why a single-process
|
|
18
|
+
* Promise.all() over synchronous better-sqlite3 calls cannot do it. This
|
|
19
|
+
* spawns two REAL child processes (same established pattern as
|
|
20
|
+
* owned-lock-lost-update.test.ts), each opening its own connection to the
|
|
21
|
+
* same DB file, barrier-synced so both call the real `runMigrations()` at
|
|
22
|
+
* effectively the same instant.
|
|
23
|
+
*/
|
|
24
|
+
import { describe, it, expect } from 'vitest';
|
|
25
|
+
import { spawn } from 'node:child_process';
|
|
26
|
+
import { mkdirSync, rmSync } from 'node:fs';
|
|
27
|
+
import * as os from 'node:os';
|
|
28
|
+
import * as path from 'node:path';
|
|
29
|
+
import { fileURLToPath } from 'node:url';
|
|
30
|
+
import { createDatabaseSync } from '../../src/db/createDatabase.js';
|
|
31
|
+
import { SCHEMA_SQL } from '../../src/db/schema-sql.js';
|
|
32
|
+
import { closeDatabase } from '../../src/db/schema.js';
|
|
33
|
+
import { isBetterSqlite3Available } from '../../src/db/drivers/betterSqlite3Driver.js';
|
|
34
|
+
// Evaluated once at module load — used by describe.skipIf() to skip this
|
|
35
|
+
// suite when the native SQLite driver is unavailable (e.g. WASM fallback
|
|
36
|
+
// mode); the race under test is specific to a real, file-backed SQLite
|
|
37
|
+
// connection shared across processes, not the in-memory WASM driver.
|
|
38
|
+
const skipIfNoSqlite = !isBetterSqlite3Available();
|
|
39
|
+
const testDir = path.dirname(fileURLToPath(import.meta.url));
|
|
40
|
+
const childPath = path.join(testDir, '..', 'helpers', 'migration-runner-race-child.mjs');
|
|
41
|
+
function spawnChild(dbPath, barrierDir, id) {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
const child = spawn(process.execPath, ['--import', 'tsx', childPath, dbPath, barrierDir, String(id)], { stdio: ['ignore', 'ignore', 'pipe'] });
|
|
44
|
+
let stderr = '';
|
|
45
|
+
child.stderr.on('data', (d) => (stderr += d.toString()));
|
|
46
|
+
child.on('exit', (code) => resolve({ code, stderr }));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
describe.skipIf(skipIfNoSqlite)('SMI-6003 runMigrations concurrent-migration race', () => {
|
|
50
|
+
it('two concurrent runMigrations() calls against the same fresh DB file do not throw', async () => {
|
|
51
|
+
const dir = path.join(os.tmpdir(), `migration-runner-race-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
52
|
+
mkdirSync(dir, { recursive: true });
|
|
53
|
+
const dbPath = path.join(dir, 'race.db');
|
|
54
|
+
// Set up a "fresh" DB already stamped at v16 — only migration v17 (the
|
|
55
|
+
// last entry in MIGRATIONS) is pending, so BOTH children will race to
|
|
56
|
+
// apply and INSERT the exact same version row. This isolates the test
|
|
57
|
+
// to the check-then-act bug itself (one contested INSERT) rather than
|
|
58
|
+
// incidental concurrent DDL from re-running the full 17-migration chain
|
|
59
|
+
// (which would also happen to trip the same bug, just less
|
|
60
|
+
// deterministically — v17's own apply() short-circuits to a cheap SELECT
|
|
61
|
+
// probe on a fresh SCHEMA_SQL base, since the base schema already
|
|
62
|
+
// includes 'curated' in the trust_tier CHECK — see
|
|
63
|
+
// migrations/v17-curated-trust-tier.ts).
|
|
64
|
+
const setupDb = createDatabaseSync(dbPath);
|
|
65
|
+
setupDb.exec(SCHEMA_SQL);
|
|
66
|
+
setupDb.prepare('INSERT INTO schema_version (version) VALUES (16)').run();
|
|
67
|
+
closeDatabase(setupDb);
|
|
68
|
+
const results = await Promise.all([spawnChild(dbPath, dir, 0), spawnChild(dbPath, dir, 1)]);
|
|
69
|
+
for (const r of results) {
|
|
70
|
+
expect(r.code, `child stderr:\n${r.stderr}`).toBe(0);
|
|
71
|
+
}
|
|
72
|
+
rmSync(dir, { recursive: true, force: true });
|
|
73
|
+
}, 30_000);
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=migration-runner-race.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-runner-race.test.js","sourceRoot":"","sources":["../../../tests/db/migration-runner-race.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* correctly with automatic driver selection.
|
|
7
7
|
*/
|
|
8
8
|
import { describe, it, expect, afterEach } from 'vitest';
|
|
9
|
-
import { existsSync, unlinkSync, mkdirSync, rmSync } from 'node:fs';
|
|
9
|
+
import { existsSync, unlinkSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { tmpdir } from 'node:os';
|
|
12
12
|
import { createDatabaseAsync, openDatabaseAsync, closeDatabase, getSchemaVersion, SCHEMA_VERSION, } from '../../src/db/schema.js';
|
|
@@ -235,6 +235,42 @@ describe('Async Schema Functions (SMI-2206)', () => {
|
|
|
235
235
|
expect(version).toBe(SCHEMA_VERSION);
|
|
236
236
|
});
|
|
237
237
|
});
|
|
238
|
+
describe('openDatabaseAsync — empty/corrupt database detection (SMI-5997)', () => {
|
|
239
|
+
it('throws an actionable error for a 0-byte file (no tables, no schema_version)', async () => {
|
|
240
|
+
if (!isBetterSqlite3Available()) {
|
|
241
|
+
console.log('Skipping test: no SQLite driver available');
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
245
|
+
const testPath = join(TEST_DIR, `test-empty-corrupt-${Date.now()}.db`);
|
|
246
|
+
testPaths.push(testPath);
|
|
247
|
+
// Reproduces the SMI-5997 incident: a 0-byte file left behind by an
|
|
248
|
+
// interrupted persist() (or any other truncation). SQLite treats a
|
|
249
|
+
// 0-byte file as a valid, openable, empty database, so this must be
|
|
250
|
+
// caught by schema.ts's zero-table check rather than SQLITE_CANTOPEN.
|
|
251
|
+
writeFileSync(testPath, Buffer.alloc(0));
|
|
252
|
+
await expect(openDatabaseAsync(testPath)).rejects.toThrow(/empty or corrupt/);
|
|
253
|
+
});
|
|
254
|
+
it('still treats a file with real tables but no schema_version as a legacy import', async () => {
|
|
255
|
+
if (!isBetterSqlite3Available()) {
|
|
256
|
+
console.log('Skipping test: no SQLite driver available');
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
260
|
+
const testPath = join(TEST_DIR, `test-legacy-import-${Date.now()}.db`);
|
|
261
|
+
testPaths.push(testPath);
|
|
262
|
+
// Build a database with a table but no schema_version, without going
|
|
263
|
+
// through createDatabaseAsync (which would stamp schema_version itself).
|
|
264
|
+
const seedDb = await createDatabaseAsync(testPath);
|
|
265
|
+
seedDb.exec('DROP TABLE schema_version');
|
|
266
|
+
closeDatabase(seedDb);
|
|
267
|
+
const db = await openDatabaseAsync(testPath);
|
|
268
|
+
testDatabases.push(db);
|
|
269
|
+
expect(db.open).toBe(true);
|
|
270
|
+
const version = getSchemaVersion(db);
|
|
271
|
+
expect(version).toBe(SCHEMA_VERSION);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
238
274
|
describe('FTS5 functionality with async creation', () => {
|
|
239
275
|
it('should sync FTS on insert', async () => {
|
|
240
276
|
if (!isBetterSqlite3Available()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-async.test.js","sourceRoot":"","sources":["../../../tests/db/schema-async.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"schema-async.test.js","sourceRoot":"","sources":["../../../tests/db/schema-async.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,8 +5,29 @@
|
|
|
5
5
|
* and isSqlJsAvailable. The better-sqlite3 mock is scoped with vi.isolateModules()
|
|
6
6
|
* to prevent contaminating betterSqlite3Driver.test.ts.
|
|
7
7
|
*/
|
|
8
|
-
import { describe, it, expect, afterEach } from 'vitest';
|
|
8
|
+
import { describe, it, expect, afterEach, beforeEach, vi } from 'vitest';
|
|
9
|
+
import { existsSync, readFileSync, mkdtempSync, rmSync } from 'node:fs';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
9
12
|
import { createSqlJsDatabase, SqlJsDatabaseAdapter, isSqlJsAvailable, } from '../../src/db/drivers/sqljsDriver.js';
|
|
13
|
+
// SMI-5997: sqljsDriver.ts imports writeFileSync from 'node:fs' as a live ESM
|
|
14
|
+
// binding, which vi.spyOn cannot redefine ("Module namespace is not
|
|
15
|
+
// configurable in ESM"). vi.mock + importOriginal is the supported way to
|
|
16
|
+
// intercept a single named export while passing everything else through.
|
|
17
|
+
const failNextWrite = vi.hoisted(() => ({ value: false }));
|
|
18
|
+
vi.mock('node:fs', async (importOriginal) => {
|
|
19
|
+
const actual = await importOriginal();
|
|
20
|
+
return {
|
|
21
|
+
...actual,
|
|
22
|
+
writeFileSync: (...args) => {
|
|
23
|
+
if (failNextWrite.value) {
|
|
24
|
+
failNextWrite.value = false;
|
|
25
|
+
throw new Error('simulated crash mid-write');
|
|
26
|
+
}
|
|
27
|
+
return actual.writeFileSync(...args);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
});
|
|
10
31
|
describe('sqljsDriver — edge cases', () => {
|
|
11
32
|
describe('isSqlJsAvailable', () => {
|
|
12
33
|
it('returns true when fts5-sql-bundle is resolvable', () => {
|
|
@@ -71,5 +92,46 @@ describe('sqljsDriver — edge cases', () => {
|
|
|
71
92
|
await expect(createSqlJsDatabase(nonExistentPath, { fileMustExist: true })).rejects.toThrow(/SQLITE_CANTOPEN/);
|
|
72
93
|
});
|
|
73
94
|
});
|
|
95
|
+
describe('persist() — atomic write (SMI-5997)', () => {
|
|
96
|
+
let dir;
|
|
97
|
+
let dbPath;
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
dir = mkdtempSync(join(tmpdir(), 'sqljs-persist-'));
|
|
100
|
+
dbPath = join(dir, 'test.db');
|
|
101
|
+
});
|
|
102
|
+
afterEach(() => {
|
|
103
|
+
failNextWrite.value = false;
|
|
104
|
+
rmSync(dir, { recursive: true, force: true });
|
|
105
|
+
});
|
|
106
|
+
it('writes via temp file + rename, leaving no leftover .tmp file', async () => {
|
|
107
|
+
const db = await createSqlJsDatabase(dbPath);
|
|
108
|
+
db.exec('CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)');
|
|
109
|
+
db.exec("INSERT INTO t (name) VALUES ('alpha')");
|
|
110
|
+
db.persist();
|
|
111
|
+
expect(existsSync(dbPath)).toBe(true);
|
|
112
|
+
expect(existsSync(`${dbPath}.tmp`)).toBe(false);
|
|
113
|
+
expect(readFileSync(dbPath).length).toBeGreaterThan(0);
|
|
114
|
+
db.close();
|
|
115
|
+
});
|
|
116
|
+
it('leaves the original file byte-for-byte untouched if the write is interrupted', async () => {
|
|
117
|
+
const db = await createSqlJsDatabase(dbPath);
|
|
118
|
+
db.exec('CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)');
|
|
119
|
+
db.exec("INSERT INTO t (name) VALUES ('alpha')");
|
|
120
|
+
db.persist();
|
|
121
|
+
const originalContent = readFileSync(dbPath);
|
|
122
|
+
expect(originalContent.length).toBeGreaterThan(0);
|
|
123
|
+
db.exec("INSERT INTO t (name) VALUES ('beta')");
|
|
124
|
+
// Simulate a crash/kill mid-write: writeFileSync (to the temp file)
|
|
125
|
+
// throws before rename() ever runs.
|
|
126
|
+
failNextWrite.value = true;
|
|
127
|
+
expect(() => db.persist()).toThrow('simulated crash mid-write');
|
|
128
|
+
// Reproduces the SMI-5997 incident check in reverse: the real file
|
|
129
|
+
// must never be truncated by a failed write, only ever replaced
|
|
130
|
+
// wholesale by a successful rename().
|
|
131
|
+
expect(readFileSync(dbPath)).toEqual(originalContent);
|
|
132
|
+
expect(existsSync(`${dbPath}.tmp`)).toBe(false);
|
|
133
|
+
db.close();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
74
136
|
});
|
|
75
137
|
//# sourceMappingURL=sqljsDriver.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqljsDriver.test.js","sourceRoot":"","sources":["../../../tests/db/sqljsDriver.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"sqljsDriver.test.js","sourceRoot":"","sources":["../../../tests/db/sqljsDriver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic dead-PID helper for owned-lock tests (SMI-5883 §8).
|
|
3
|
+
* @module @skillsmith/core/tests/helpers/deterministic-dead-pid
|
|
4
|
+
*
|
|
5
|
+
* Replaces the round-3 assumption that a hard-coded PID (e.g. `'99999'`) is
|
|
6
|
+
* dead — flaky on any machine whose PID space reaches that value. Spawns a
|
|
7
|
+
* real, short-lived child process, waits for it to exit, then verifies
|
|
8
|
+
* `process.kill(pid, 0)` throws `ESRCH` before handing the PID back. Retries
|
|
9
|
+
* (PID reuse) up to 5 times, then fails loudly with a clear message rather
|
|
10
|
+
* than silently returning a live PID.
|
|
11
|
+
*/
|
|
12
|
+
export declare function mintDeadPid(): number;
|
|
13
|
+
//# sourceMappingURL=deterministic-dead-pid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deterministic-dead-pid.d.ts","sourceRoot":"","sources":["../../../tests/helpers/deterministic-dead-pid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,wBAAgB,WAAW,IAAI,MAAM,CAgBpC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic dead-PID helper for owned-lock tests (SMI-5883 §8).
|
|
3
|
+
* @module @skillsmith/core/tests/helpers/deterministic-dead-pid
|
|
4
|
+
*
|
|
5
|
+
* Replaces the round-3 assumption that a hard-coded PID (e.g. `'99999'`) is
|
|
6
|
+
* dead — flaky on any machine whose PID space reaches that value. Spawns a
|
|
7
|
+
* real, short-lived child process, waits for it to exit, then verifies
|
|
8
|
+
* `process.kill(pid, 0)` throws `ESRCH` before handing the PID back. Retries
|
|
9
|
+
* (PID reuse) up to 5 times, then fails loudly with a clear message rather
|
|
10
|
+
* than silently returning a live PID.
|
|
11
|
+
*/
|
|
12
|
+
import { spawnSync } from 'node:child_process';
|
|
13
|
+
export function mintDeadPid() {
|
|
14
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
15
|
+
const result = spawnSync(process.execPath, ['-e', '']);
|
|
16
|
+
const pid = result.pid;
|
|
17
|
+
if (typeof pid !== 'number')
|
|
18
|
+
continue;
|
|
19
|
+
try {
|
|
20
|
+
process.kill(pid, 0);
|
|
21
|
+
continue; // still signalable somehow (should not happen for an exited child) -- retry
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
if (err.code === 'ESRCH')
|
|
25
|
+
return pid;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
throw new Error('[owned-lock tests] could not mint a deterministically-dead PID after 5 attempts -- ' +
|
|
29
|
+
'every spawned child still answered a liveness probe.');
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=deterministic-dead-pid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deterministic-dead-pid.js","sourceRoot":"","sources":["../../../tests/helpers/deterministic-dead-pid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE9C,MAAM,UAAU,WAAW;IACzB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;QACtD,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAA;QACtB,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,SAAQ;QACrC,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACpB,SAAQ,CAAC,4EAA4E;QACvF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,OAAO;gBAAE,OAAO,GAAG,CAAA;QACjE,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CACb,qFAAqF;QACnF,sDAAsD,CACzD,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-5883 §8f -- end-to-end lost-update stress test, deliberately kept
|
|
3
|
+
* SEPARATE from the §8b reclaim-race proof: 8 real child processes each
|
|
4
|
+
* acquire -> read -> add a distinct key -> atomicWriteFile -> release, 5
|
|
5
|
+
* rounds each, with NO seam, NO barrier, and NO injection. This exercises
|
|
6
|
+
* the primitive under real contention among genuinely-alive processes (no
|
|
7
|
+
* staleness/reclaim path is ever exercised here), catching whole-mechanism
|
|
8
|
+
* regressions that a seam-driven test could mask. Neither this test nor
|
|
9
|
+
* §8b substitutes for the other.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=owned-lock-lost-update.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owned-lock-lost-update.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/owned-lock-lost-update.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-5883 §8f -- end-to-end lost-update stress test, deliberately kept
|
|
3
|
+
* SEPARATE from the §8b reclaim-race proof: 8 real child processes each
|
|
4
|
+
* acquire -> read -> add a distinct key -> atomicWriteFile -> release, 5
|
|
5
|
+
* rounds each, with NO seam, NO barrier, and NO injection. This exercises
|
|
6
|
+
* the primitive under real contention among genuinely-alive processes (no
|
|
7
|
+
* staleness/reclaim path is ever exercised here), catching whole-mechanism
|
|
8
|
+
* regressions that a seam-driven test could mask. Neither this test nor
|
|
9
|
+
* §8b substitutes for the other.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect } from 'vitest';
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
|
|
14
|
+
import * as os from 'node:os';
|
|
15
|
+
import * as path from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
const testDir = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const childPath = path.join(testDir, '..', 'helpers', 'owned-lock-stress-child.mjs');
|
|
19
|
+
const WORKERS = 8;
|
|
20
|
+
const ROUNDS = 5;
|
|
21
|
+
function spawnWorker(id, target) {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
const child = spawn(process.execPath, ['--import', 'tsx', childPath, String(id), target, String(ROUNDS)], { stdio: ['ignore', 'ignore', 'pipe'] });
|
|
24
|
+
let stderr = '';
|
|
25
|
+
child.stderr.on('data', (d) => (stderr += d.toString()));
|
|
26
|
+
child.on('exit', (code) => resolve({ code, stderr }));
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
describe('owned-lock end-to-end lost update (§8f)', () => {
|
|
30
|
+
it(`${WORKERS} workers x ${ROUNDS} rounds under real contention lose no updates`, async () => {
|
|
31
|
+
const dir = path.join(os.tmpdir(), `owned-lock-lost-update-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
32
|
+
mkdirSync(dir, { recursive: true });
|
|
33
|
+
const target = path.join(dir, 'store.json');
|
|
34
|
+
const results = await Promise.all(Array.from({ length: WORKERS }, (_, i) => spawnWorker(i, target)));
|
|
35
|
+
for (const r of results) {
|
|
36
|
+
expect(r.code, `worker stderr:\n${r.stderr}`).toBe(0);
|
|
37
|
+
}
|
|
38
|
+
expect(existsSync(target)).toBe(true);
|
|
39
|
+
const store = JSON.parse(readFileSync(target, 'utf-8'));
|
|
40
|
+
const keys = Object.keys(store);
|
|
41
|
+
expect(keys).toHaveLength(WORKERS * ROUNDS);
|
|
42
|
+
for (let id = 0; id < WORKERS; id++) {
|
|
43
|
+
for (let round = 0; round < ROUNDS; round++) {
|
|
44
|
+
expect(store[`${id}-${round}`]).toBe(true);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
expect(existsSync(`${target}.lock`)).toBe(false);
|
|
48
|
+
expect(existsSync(`${target}.lock.reclaim`)).toBe(false);
|
|
49
|
+
rmSync(dir, { recursive: true, force: true });
|
|
50
|
+
}, 60_000);
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=owned-lock-lost-update.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owned-lock-lost-update.test.js","sourceRoot":"","sources":["../../../tests/integration/owned-lock-lost-update.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-5883 §8b -- cross-process proof that the two-level lock closes the
|
|
3
|
+
* round-3 lock-theft race (two concurrent reclaimers can both validate the
|
|
4
|
+
* same stale claim; the delayed one steals the winner's freshly-recreated
|
|
5
|
+
* LIVE lock). This is the single most important regression guard in the
|
|
6
|
+
* whole design: it is the test that specifically proves the round-3 race is
|
|
7
|
+
* closed, with a negative control proving the test would actually catch a
|
|
8
|
+
* regression if the fix were removed.
|
|
9
|
+
*
|
|
10
|
+
* Real child processes (`owned-lock-race-child.mjs`, spawned via
|
|
11
|
+
* `node --import tsx`) synchronize on a filesystem barrier at the exact seam
|
|
12
|
+
* (`onReclaimBoundary`) the reviewer required: after BOTH have independently
|
|
13
|
+
* validated the SAME seeded dead claim, before EITHER has touched the
|
|
14
|
+
* reclaim lock or removed anything. No `skipIf` -- every wait is bounded and
|
|
15
|
+
* throws a labelled error on expiry (see `waitForAsync` below), so a
|
|
16
|
+
* regression fails loudly instead of hanging the suite.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=owned-lock-reclaim-race.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owned-lock-reclaim-race.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/owned-lock-reclaim-race.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-5883 §8b -- cross-process proof that the two-level lock closes the
|
|
3
|
+
* round-3 lock-theft race (two concurrent reclaimers can both validate the
|
|
4
|
+
* same stale claim; the delayed one steals the winner's freshly-recreated
|
|
5
|
+
* LIVE lock). This is the single most important regression guard in the
|
|
6
|
+
* whole design: it is the test that specifically proves the round-3 race is
|
|
7
|
+
* closed, with a negative control proving the test would actually catch a
|
|
8
|
+
* regression if the fix were removed.
|
|
9
|
+
*
|
|
10
|
+
* Real child processes (`owned-lock-race-child.mjs`, spawned via
|
|
11
|
+
* `node --import tsx`) synchronize on a filesystem barrier at the exact seam
|
|
12
|
+
* (`onReclaimBoundary`) the reviewer required: after BOTH have independently
|
|
13
|
+
* validated the SAME seeded dead claim, before EITHER has touched the
|
|
14
|
+
* reclaim lock or removed anything. No `skipIf` -- every wait is bounded and
|
|
15
|
+
* throws a labelled error on expiry (see `waitForAsync` below), so a
|
|
16
|
+
* regression fails loudly instead of hanging the suite.
|
|
17
|
+
*/
|
|
18
|
+
import { describe, it, expect } from 'vitest';
|
|
19
|
+
import { spawn } from 'node:child_process';
|
|
20
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
21
|
+
import { hostname } from 'node:os';
|
|
22
|
+
import * as os from 'node:os';
|
|
23
|
+
import * as path from 'node:path';
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
import { mintDeadPid } from '../helpers/deterministic-dead-pid.js';
|
|
26
|
+
const testDir = path.dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const childPath = path.join(testDir, '..', 'helpers', 'owned-lock-race-child.mjs');
|
|
28
|
+
function spawnChild(argsList) {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const child = spawn(process.execPath, ['--import', 'tsx', childPath, ...argsList], {
|
|
31
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
32
|
+
});
|
|
33
|
+
let stdout = '';
|
|
34
|
+
let stderr = '';
|
|
35
|
+
child.stdout.on('data', (d) => (stdout += d.toString()));
|
|
36
|
+
child.stderr.on('data', (d) => (stderr += d.toString()));
|
|
37
|
+
child.on('exit', (code) => resolve({ code, stdout, stderr }));
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async function waitForAsync(predicate, label, capMs = 10_000) {
|
|
41
|
+
const deadline = Date.now() + capMs;
|
|
42
|
+
while (!predicate()) {
|
|
43
|
+
if (Date.now() >= deadline) {
|
|
44
|
+
throw new Error(`[owned-lock-reclaim-race.test] timed out waiting for ${label}`);
|
|
45
|
+
}
|
|
46
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function makeTempDir(prefix) {
|
|
50
|
+
const dir = path.join(os.tmpdir(), `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
51
|
+
const barrierDir = path.join(dir, 'barrier');
|
|
52
|
+
mkdirSync(barrierDir, { recursive: true });
|
|
53
|
+
return { dir, barrierDir };
|
|
54
|
+
}
|
|
55
|
+
function seedStaleLock(lockPath, deadPid) {
|
|
56
|
+
writeFileSync(lockPath, JSON.stringify({
|
|
57
|
+
v: 1,
|
|
58
|
+
pid: deadPid,
|
|
59
|
+
token: 'stale-token-0000',
|
|
60
|
+
host: hostname(),
|
|
61
|
+
acquiredAt: Date.now() - 60_000,
|
|
62
|
+
}) + '\n');
|
|
63
|
+
return readFileSync(lockPath);
|
|
64
|
+
}
|
|
65
|
+
function readIfExists(p) {
|
|
66
|
+
return existsSync(p) ? readFileSync(p) : null;
|
|
67
|
+
}
|
|
68
|
+
describe('owned-lock cross-process reclaim race (§8b) -- the required test', () => {
|
|
69
|
+
it('a delayed contender cannot steal the winner’s freshly-recreated live lock', async () => {
|
|
70
|
+
const { dir, barrierDir } = makeTempDir('owned-lock-race');
|
|
71
|
+
const target = path.join(dir, 'store.json');
|
|
72
|
+
const lockPath = `${target}.lock`;
|
|
73
|
+
const reclaimPath = `${lockPath}.reclaim`;
|
|
74
|
+
const deadPid = mintDeadPid();
|
|
75
|
+
const staleBytes = seedStaleLock(lockPath, deadPid);
|
|
76
|
+
const pendingA = spawnChild([
|
|
77
|
+
'A',
|
|
78
|
+
target,
|
|
79
|
+
barrierDir,
|
|
80
|
+
'--signal-held=held-A',
|
|
81
|
+
'--wait-release=release-A',
|
|
82
|
+
]);
|
|
83
|
+
const pendingB = spawnChild(['B', target, barrierDir, '--gate-on=held-A']);
|
|
84
|
+
// Wait for A to hold and B to have recorded ITS reclaim-attempt outcome
|
|
85
|
+
// (must come from the authoritative re-read finding a live owner, not
|
|
86
|
+
// luck) -- while A STILL holds, i.e. before releasing it.
|
|
87
|
+
await waitForAsync(() => existsSync(path.join(barrierDir, 'held-A')), "child A's held-A marker");
|
|
88
|
+
await waitForAsync(() => readdirSync(barrierDir).some((f) => f.startsWith('attempted-B-')), "child B's reclaim-attempt outcome");
|
|
89
|
+
// ---- Assertions while A still holds ----
|
|
90
|
+
// A-0 (anti-vacuity): the reclaim path actually executed.
|
|
91
|
+
expect(existsSync(path.join(barrierDir, 'attempted-A-reclaimed'))).toBe(true);
|
|
92
|
+
// A-1: the delayed contender did not enter concurrently.
|
|
93
|
+
expect(existsSync(path.join(barrierDir, 'entered-B'))).toBe(false);
|
|
94
|
+
// A-2: the lock is A's LIVE claim -- not removed/replaced by B.
|
|
95
|
+
const duringClaim = JSON.parse(readFileSync(lockPath, 'utf-8'));
|
|
96
|
+
expect(duringClaim.v).toBe(1);
|
|
97
|
+
expect(duringClaim.host).toBe(hostname());
|
|
98
|
+
expect(duringClaim.token).not.toBe('stale-token-0000');
|
|
99
|
+
expect(readFileSync(lockPath).equals(staleBytes)).toBe(false);
|
|
100
|
+
// A-3: B's refusal came from the authoritative re-read, not luck.
|
|
101
|
+
expect(existsSync(path.join(barrierDir, 'attempted-B-not-stale'))).toBe(true);
|
|
102
|
+
// A-4: both reclaim-lock acquisitions released.
|
|
103
|
+
expect(existsSync(reclaimPath)).toBe(false);
|
|
104
|
+
// ---- Liveness: the refusal is a refusal, not a deadlock ----
|
|
105
|
+
writeFileSync(path.join(barrierDir, 'release-A'), '');
|
|
106
|
+
await waitForAsync(() => existsSync(path.join(barrierDir, 'entered-B')), "child B's entered-B marker");
|
|
107
|
+
const [resultA, resultB] = await Promise.all([pendingA, pendingB]);
|
|
108
|
+
expect(resultA.code, `child A stderr:\n${resultA.stderr}`).toBe(0);
|
|
109
|
+
expect(resultB.code, `child B stderr:\n${resultB.stderr}`).toBe(0);
|
|
110
|
+
expect(existsSync(lockPath)).toBe(false);
|
|
111
|
+
expect(existsSync(reclaimPath)).toBe(false);
|
|
112
|
+
expect(readdirSync(dir).some((f) => f.endsWith('.tmp'))).toBe(false);
|
|
113
|
+
rmSync(dir, { recursive: true, force: true });
|
|
114
|
+
}, 20_000);
|
|
115
|
+
it('negative control: removing the re-validation lets the delayed contender steal the live lock (proves the test discriminates)', async () => {
|
|
116
|
+
const { dir, barrierDir } = makeTempDir('owned-lock-race-negctrl');
|
|
117
|
+
const target = path.join(dir, 'store.json');
|
|
118
|
+
const deadPid = mintDeadPid();
|
|
119
|
+
seedStaleLock(`${target}.lock`, deadPid);
|
|
120
|
+
const pendingA = spawnChild([
|
|
121
|
+
'A',
|
|
122
|
+
target,
|
|
123
|
+
barrierDir,
|
|
124
|
+
'--signal-held=held-A',
|
|
125
|
+
'--wait-release=release-A',
|
|
126
|
+
'--unsafe-skip-revalidation',
|
|
127
|
+
]);
|
|
128
|
+
const pendingB = spawnChild([
|
|
129
|
+
'B',
|
|
130
|
+
target,
|
|
131
|
+
barrierDir,
|
|
132
|
+
'--gate-on=held-A',
|
|
133
|
+
'--unsafe-skip-revalidation',
|
|
134
|
+
]);
|
|
135
|
+
await waitForAsync(() => existsSync(path.join(barrierDir, 'held-A')), "child A's held-A marker");
|
|
136
|
+
// Under the unsafe build, B does not wait for a genuine refusal -- it
|
|
137
|
+
// proceeds straight to an unconditional unlink once it holds the
|
|
138
|
+
// reclaim lock, so it reports 'reclaimed' too (never 'not-stale').
|
|
139
|
+
await waitForAsync(() => readdirSync(barrierDir).some((f) => f.startsWith('attempted-B-')), "child B's reclaim-attempt outcome");
|
|
140
|
+
const reclaimedCount = readdirSync(barrierDir).filter((f) => /^attempted-[AB]-reclaimed$/.test(f)).length;
|
|
141
|
+
// THIS is the failure the safe build's A-2/A-3 assertions exist to
|
|
142
|
+
// catch: with the authoritative re-read removed, BOTH children believe
|
|
143
|
+
// they reclaimed the stale lock -- the exact round-3 lock-theft race.
|
|
144
|
+
expect(reclaimedCount).toBe(2);
|
|
145
|
+
expect(existsSync(path.join(barrierDir, 'attempted-B-not-stale'))).toBe(false);
|
|
146
|
+
writeFileSync(path.join(barrierDir, 'release-A'), '');
|
|
147
|
+
const [resultA, resultB] = await Promise.all([pendingA, pendingB]);
|
|
148
|
+
// Best-effort cleanup -- one of the two children's release() may warn
|
|
149
|
+
// (ownership already stolen) but must not hang the suite.
|
|
150
|
+
void resultA;
|
|
151
|
+
void resultB;
|
|
152
|
+
rmSync(dir, { recursive: true, force: true });
|
|
153
|
+
}, 20_000);
|
|
154
|
+
it('symmetric stress (50 iterations): exactly one reclaimed outcome per iteration, never two', async () => {
|
|
155
|
+
const ITERATIONS = 50;
|
|
156
|
+
for (let i = 0; i < ITERATIONS; i++) {
|
|
157
|
+
const { dir, barrierDir } = makeTempDir(`owned-lock-race-stress-${i}`);
|
|
158
|
+
const target = path.join(dir, 'store.json');
|
|
159
|
+
const deadPid = mintDeadPid();
|
|
160
|
+
seedStaleLock(`${target}.lock`, deadPid);
|
|
161
|
+
const pending1 = spawnChild(['1', target, barrierDir]);
|
|
162
|
+
const pending2 = spawnChild(['2', target, barrierDir]);
|
|
163
|
+
const [result1, result2] = await Promise.all([pending1, pending2]);
|
|
164
|
+
expect(result1.code, `child 1 stderr:\n${result1.stderr}`).toBe(0);
|
|
165
|
+
expect(result2.code, `child 2 stderr:\n${result2.stderr}`).toBe(0);
|
|
166
|
+
const reclaimedCount = readdirSync(barrierDir).filter((f) => /^attempted-[12]-reclaimed$/.test(f)).length;
|
|
167
|
+
expect(reclaimedCount, `iteration ${i}: expected exactly one reclaimed outcome`).toBe(1);
|
|
168
|
+
// Never a `gone` for a participant that ALSO shows `reclaimed` (would
|
|
169
|
+
// mean two successful reclaims were both recorded for one lock).
|
|
170
|
+
const goneAndReclaimed = readIfExists(path.join(barrierDir, 'attempted-1-gone')) !== null &&
|
|
171
|
+
readIfExists(path.join(barrierDir, 'attempted-1-reclaimed')) !== null;
|
|
172
|
+
expect(goneAndReclaimed).toBe(false);
|
|
173
|
+
rmSync(dir, { recursive: true, force: true });
|
|
174
|
+
}
|
|
175
|
+
}, 120_000);
|
|
176
|
+
});
|
|
177
|
+
//# sourceMappingURL=owned-lock-reclaim-race.test.js.map
|