Skip to content

Commit 1567a9c

Browse files
committed
test: restore release coverage margin
1 parent c112249 commit 1567a9c

3 files changed

Lines changed: 215 additions & 0 deletions

File tree

‎test/run-tests.cjs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ const UNIT = [
8282
'test/unit/coverage/logger-branches.test.js',
8383
// Coverage gap fill — Phase G
8484
'test/unit/coverage/runtime-core-edge-cases.test.js',
85+
'test/unit/coverage/runtime-connect-cleanup.test.js',
86+
'test/unit/coverage/runtime-safety-helpers.test.js',
8587
// Coverage gap fill — Phase H
8688
'test/unit/coverage/errors-factory.test.js',
8789
'test/unit/coverage/slow-query-records-branches.test.js',
@@ -307,6 +309,8 @@ const SUITE_MAP = {
307309
'model-write-extended': ['test/integration/model/model-write-extended.test.js'],
308310
// Phase G
309311
'runtime-core-edge-cases': ['test/unit/coverage/runtime-core-edge-cases.test.js'],
312+
'runtime-connect-cleanup': ['test/unit/coverage/runtime-connect-cleanup.test.js'],
313+
'runtime-safety-helpers': ['test/unit/coverage/runtime-safety-helpers.test.js'],
310314
'lock-advanced': ['test/integration/mongodb/lock-advanced.test.js'],
311315
'model-advanced': ['test/integration/model/model-advanced.test.js'],
312316
// Phase H
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import assert from 'node:assert/strict';
2+
import { describe, it } from 'node:test';
3+
import { cleanupFailedRuntimeConnection } from '../../../src/entry/runtime-connect-cleanup';
4+
5+
describe('failed runtime connection cleanup', () => {
6+
it('cleans every initialized resource', async () => {
7+
const calls: string[] = [];
8+
const closeable = (name: string) => ({ close: async () => { calls.push(name); } });
9+
10+
await cleanupFailedRuntimeConnection({
11+
client: closeable('client'),
12+
pool: closeable('pool'),
13+
tunnel: closeable('tunnel'),
14+
invalidator: closeable('invalidator'),
15+
sync: { stop: async () => { calls.push('sync'); } },
16+
slowQuery: closeable('slowQuery'),
17+
transaction: { abortAll: async () => { calls.push('transaction'); } },
18+
lock: closeable('lock'),
19+
}, {});
20+
21+
assert.deepEqual(calls, [
22+
'lock',
23+
'sync',
24+
'slowQuery',
25+
'transaction',
26+
'pool',
27+
'invalidator',
28+
'client',
29+
'tunnel',
30+
]);
31+
});
32+
33+
it('accepts a connection failure before any resource was initialized', async () => {
34+
await assert.doesNotReject(() => cleanupFailedRuntimeConnection({
35+
client: null,
36+
pool: null,
37+
tunnel: null,
38+
invalidator: null,
39+
sync: null,
40+
slowQuery: null,
41+
transaction: null,
42+
lock: null,
43+
}, {}));
44+
});
45+
46+
it('collects synchronous and asynchronous cleanup failures without rejecting', async () => {
47+
const warnings: unknown[][] = [];
48+
const rejected = (name: string) => ({ close: async () => { throw new Error(name); } });
49+
50+
await assert.doesNotReject(() => cleanupFailedRuntimeConnection({
51+
client: rejected('client'),
52+
pool: rejected('pool'),
53+
tunnel: rejected('tunnel'),
54+
invalidator: rejected('invalidator'),
55+
sync: { stop: async () => { throw new Error('sync'); } },
56+
slowQuery: rejected('slowQuery'),
57+
transaction: { abortAll: async () => { throw new Error('transaction'); } },
58+
lock: { close: () => { throw new Error('lock'); } },
59+
}, { warn: (...args: unknown[]) => { warnings.push(args); } }));
60+
61+
assert.equal(warnings.length, 8);
62+
assert.match(String(warnings[0]?.[0]), /lock cleanup error/);
63+
assert.ok(warnings.slice(1).every(([message]) => String(message).includes('cleanup error after failed connect')));
64+
});
65+
66+
it('does not require a warning logger while swallowing cleanup failures', async () => {
67+
await assert.doesNotReject(() => cleanupFailedRuntimeConnection({
68+
client: { close: async () => { throw new Error('client'); } },
69+
pool: null,
70+
tunnel: null,
71+
invalidator: null,
72+
sync: null,
73+
slowQuery: null,
74+
transaction: null,
75+
lock: { close: () => { throw new Error('lock'); } },
76+
}, {}));
77+
});
78+
});
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import assert from 'node:assert/strict';
2+
import { describe, it } from 'node:test';
3+
import { isProductionEnvironment } from '../../../src/adapters/mongodb/common/drop-database-safety';
4+
import { Logger } from '../../../src/core/logger';
5+
import {
6+
buildCacheInvalidationBarrierKey,
7+
clearCacheInvalidationBarrier,
8+
extractCacheInvalidationBarrierNamespaces,
9+
isCacheInvalidationBarrierActive,
10+
markCacheInvalidationBarrier,
11+
} from '../../../src/core/cache-invalidation-barrier';
12+
import { createRuntimeAccessors, createRuntimeDbFacade } from '../../../src/entry/runtime-db-facade';
13+
import { disposeRuntimeSchemaDslEngine } from '../../../src/entry/runtime-schema-dsl';
14+
import { prepareSshTunnelConnectConfig } from '../../../src/entry/runtime-ssh';
15+
16+
describe('runtime safety helpers', () => {
17+
it('normalizes production-like environment names', () => {
18+
assert.equal(isProductionEnvironment(42), false);
19+
assert.equal(isProductionEnvironment(' PROD '), true);
20+
assert.equal(isProductionEnvironment('live'), true);
21+
assert.equal(isProductionEnvironment('staging'), false);
22+
});
23+
24+
it('extracts direct namespaces and supported wildcard cache patterns', () => {
25+
assert.deepEqual(extractCacheInvalidationBarrierNamespaces([
26+
'',
27+
'db.items',
28+
'find:db.items:*',
29+
'count:db.items:*',
30+
'unsupported:db.other:*',
31+
'invalid*pattern',
32+
]), ['db.items']);
33+
assert.equal(buildCacheInvalidationBarrierKey('db.items'), 'cacheDirty:db.items');
34+
});
35+
36+
it('marks barriers only when the cache supports set', async () => {
37+
assert.deepEqual(await markCacheInvalidationBarrier(null, ['db.items']), []);
38+
assert.deepEqual(await markCacheInvalidationBarrier({}, ['db.items']), []);
39+
40+
const writes: Array<[string, unknown, number]> = [];
41+
const namespaces = await markCacheInvalidationBarrier({
42+
set: async (key, value, ttl) => { writes.push([key, value, ttl]); },
43+
}, ['find:db.items:*', 'db.other'], 1234);
44+
45+
assert.deepEqual(namespaces, ['db.items', 'db.other']);
46+
assert.equal(writes.length, 2);
47+
assert.deepEqual(writes.map(([key, , ttl]) => [key, ttl]), [
48+
['cacheDirty:db.items', 1234],
49+
['cacheDirty:db.other', 1234],
50+
]);
51+
});
52+
53+
it('clears barriers through del or delete and accepts incapable caches', async () => {
54+
await assert.doesNotReject(() => clearCacheInvalidationBarrier(null, ['db.items']));
55+
await assert.doesNotReject(() => clearCacheInvalidationBarrier({}, ['db.items']));
56+
57+
const deleted: string[] = [];
58+
await clearCacheInvalidationBarrier({ del: async (key) => { deleted.push(`del:${key}`); } }, ['db.items']);
59+
await clearCacheInvalidationBarrier({ delete: async (key) => { deleted.push(`delete:${key}`); } }, ['count:db.other:*']);
60+
assert.deepEqual(deleted, ['del:cacheDirty:db.items', 'delete:cacheDirty:db.other']);
61+
});
62+
63+
it('detects only truthy barrier markers', async () => {
64+
assert.equal(await isCacheInvalidationBarrierActive(null, ['db.items']), false);
65+
assert.equal(await isCacheInvalidationBarrierActive({}, ['db.items']), false);
66+
67+
const values = new Map<string, unknown>([
68+
['cacheDirty:db.undefined', undefined],
69+
['cacheDirty:db.null', null],
70+
['cacheDirty:db.false', false],
71+
['cacheDirty:db.active', { dirty: true }],
72+
]);
73+
const cache = { get: async (key: string) => values.get(key) };
74+
assert.equal(await isCacheInvalidationBarrierActive(cache, ['db.undefined', 'db.null', 'db.false']), false);
75+
assert.equal(await isCacheInvalidationBarrierActive(cache, ['db.undefined', 'db.active', 'db.false']), true);
76+
});
77+
78+
it('contains schema runtime disposal errors with or without a warning sink', () => {
79+
const warnings: unknown[][] = [];
80+
const engine = { dispose: () => { throw new Error('dispose failed'); } };
81+
assert.doesNotThrow(() => disposeRuntimeSchemaDslEngine(engine as never, {
82+
warn: (...args: unknown[]) => { warnings.push(args); },
83+
}, 'during close'));
84+
assert.equal(warnings.length, 1);
85+
assert.doesNotThrow(() => disposeRuntimeSchemaDslEngine(engine as never, {}, 'before reconnect'));
86+
});
87+
88+
it('keeps missing SSH configuration unchanged', async () => {
89+
const logger = Logger.create(null);
90+
assert.deepEqual(await prepareSshTunnelConnectConfig(undefined, 'db', logger), {
91+
connectConfig: undefined,
92+
tunnel: null,
93+
});
94+
const config = { uri: 'mongodb://localhost:27017' };
95+
assert.deepEqual(await prepareSshTunnelConnectConfig(config, 'db', logger), {
96+
connectConfig: config,
97+
tunnel: null,
98+
});
99+
});
100+
101+
it('normalizes cache auto-invalidation options for database facades', () => {
102+
const database = { collection: () => ({}) };
103+
const host = (options: Record<string, unknown>) => ({
104+
options,
105+
_client: { db: () => database },
106+
_logger: Logger.create(null),
107+
_runtimeDefaults: {},
108+
resolveAdapterCache: () => null,
109+
});
110+
111+
assert.ok(createRuntimeDbFacade(host({ cache: { autoInvalidate: true } }) as never, 'db'));
112+
assert.ok(createRuntimeDbFacade(host({ cache: [], cacheAutoInvalidate: true }) as never, 'db'));
113+
assert.ok(createRuntimeDbFacade(host({ cache: 'legacy', cacheAutoInvalidate: false }) as never, 'db'));
114+
});
115+
116+
it('rejects every invalid collection-name shape from runtime accessors', () => {
117+
const accessors = createRuntimeAccessors({
118+
defaultDb: { collection: () => ({}) } as never,
119+
runtime: {},
120+
db: () => ({}) as never,
121+
use: () => ({}) as never,
122+
getIidCache: () => null,
123+
setIidCache: () => undefined,
124+
});
125+
126+
for (const name of ['', 42, ' ']) {
127+
assert.throws(
128+
() => accessors.collection(name as string),
129+
(error: unknown) => error instanceof Error && (error as Error & { code?: string }).code === 'INVALID_COLLECTION_NAME',
130+
);
131+
}
132+
});
133+
});

0 commit comments

Comments
 (0)