Skip to content

Commit a3cccd7

Browse files
committed
test(ids): add 5 regression tests for syncSelfRegistryEntry
Addresses CodeRabbit feedback on PR #749 — the self-entry sync logic introduced in PR #748 was load-bearing (lastVerified + checksum drift was the original CodeRabbit complaint) but lacked targeted tests. Tests added cover the four invariants: 1. **Self-path match → mutates both fields**: when the entry's path equals `.aiox-core/data/entity-registry.yaml`, `lastVerified` becomes the new timestamp and `checksum` becomes the sentinel. 2. **Non-self path → no mutation**: the companion entry pointing at `.aiox-core/core/doctor/checks/entity-registry.js` (a real JS module, NOT self-referential) must NOT be touched. Catches the most likely regression: if the path guard is removed, the JS module entry would get clobbered with the yaml sentinel. 3. **Missing entities.data** is a no-op (defensive). 4. **Missing self-entry** is a no-op + leaves other data entries alone (defensive). 5. **Malformed registry** (`{}`, `null`) does not throw. Required exporting `syncSelfRegistryEntry` from the script's `module.exports` and consuming it in the test file. Suite: 96/96 pass (was 91/91).
1 parent 05f3e7d commit a3cccd7

3 files changed

Lines changed: 76 additions & 3 deletions

File tree

.aiox-core/development/scripts/populate-entity-registry.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,7 @@ module.exports = {
853853
extractKeywords,
854854
extractPurpose,
855855
looksLikePlaceholder,
856+
syncSelfRegistryEntry,
856857
detectDependencies,
857858
extractYamlDependencies,
858859
extractMarkdownCrossReferences,

.aiox-core/install-manifest.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# - File types for categorization
99
#
1010
version: 5.2.6
11-
generated_at: "2026-05-17T15:12:58.541Z"
11+
generated_at: "2026-05-17T19:14:46.622Z"
1212
generator: scripts/generate-install-manifest.js
1313
file_count: 1128
1414
files:
@@ -1669,9 +1669,9 @@ files:
16691669
type: script
16701670
size: 23410
16711671
- path: development/scripts/populate-entity-registry.js
1672-
hash: sha256:65cc968e9fa82bb18390ee0ed7063082b5341a441b83a9778638f1ce4064ab23
1672+
hash: sha256:67a64f3bf0118eaa58401f4450e04f99643a33903d6b57d433929604b9061315
16731673
type: script
1674-
size: 31446
1674+
size: 31471
16751675
- path: development/scripts/refactoring-suggester.js
16761676
hash: sha256:d50ea6b609c9cf8385979386fee4b4385d11ebcde15460260f66d04c705f6cd9
16771677
type: script

tests/core/ids/populate-entity-registry.test.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const {
77
extractKeywords,
88
extractPurpose,
99
looksLikePlaceholder,
10+
syncSelfRegistryEntry,
1011
detectDependencies,
1112
extractYamlDependencies,
1213
extractMarkdownCrossReferences,
@@ -305,6 +306,77 @@ describe('populate-entity-registry (AC: 3, 4, 12)', () => {
305306
});
306307
});
307308

309+
describe('syncSelfRegistryEntry()', () => {
310+
const SELF_PATH = '.aiox-core/data/entity-registry.yaml';
311+
const SENTINEL = 'sha256:<self-reference>';
312+
313+
it('updates lastVerified and checksum on the self-entry when path matches', () => {
314+
const registry = {
315+
entities: {
316+
data: {
317+
'entity-registry': {
318+
path: SELF_PATH,
319+
checksum: 'sha256:stale-old-hash-1234567890abcdef',
320+
lastVerified: '2026-01-01T00:00:00.000Z',
321+
purpose: 'Entity at .aiox-core/data/entity-registry.yaml',
322+
},
323+
},
324+
},
325+
};
326+
const newTimestamp = '2026-05-17T15:00:00.000Z';
327+
328+
syncSelfRegistryEntry(registry, newTimestamp, SENTINEL);
329+
330+
expect(registry.entities.data['entity-registry'].lastVerified).toBe(newTimestamp);
331+
expect(registry.entities.data['entity-registry'].checksum).toBe(SENTINEL);
332+
});
333+
334+
it('does NOT mutate entries whose path is different (other entity-registry pointing to .js module)', () => {
335+
const registry = {
336+
entities: {
337+
data: {
338+
'entity-registry': {
339+
path: '.aiox-core/core/doctor/checks/entity-registry.js',
340+
checksum: 'sha256:legitimate-js-module-hash-do-not-touch',
341+
lastVerified: '2026-01-01T00:00:00.000Z',
342+
},
343+
},
344+
},
345+
};
346+
const newTimestamp = '2026-05-17T15:00:00.000Z';
347+
348+
syncSelfRegistryEntry(registry, newTimestamp, SENTINEL);
349+
350+
expect(registry.entities.data['entity-registry'].checksum).toBe(
351+
'sha256:legitimate-js-module-hash-do-not-touch',
352+
);
353+
expect(registry.entities.data['entity-registry'].lastVerified).toBe('2026-01-01T00:00:00.000Z');
354+
});
355+
356+
it('is a no-op when entities.data does not exist', () => {
357+
const registry = { entities: { agents: { foo: { path: 'irrelevant' } } } };
358+
expect(() => syncSelfRegistryEntry(registry, '2026-05-17T15:00:00.000Z', SENTINEL)).not.toThrow();
359+
});
360+
361+
it('is a no-op when self-entry is missing', () => {
362+
const registry = {
363+
entities: {
364+
data: {
365+
'some-other-data': { path: '.aiox-core/data/something.yaml', checksum: 'sha256:keep' },
366+
},
367+
},
368+
};
369+
syncSelfRegistryEntry(registry, '2026-05-17T15:00:00.000Z', SENTINEL);
370+
// Other entries unchanged
371+
expect(registry.entities.data['some-other-data'].checksum).toBe('sha256:keep');
372+
});
373+
374+
it('is a no-op when registry is malformed (missing entities)', () => {
375+
expect(() => syncSelfRegistryEntry({}, '2026-05-17T15:00:00.000Z', SENTINEL)).not.toThrow();
376+
expect(() => syncSelfRegistryEntry(null, '2026-05-17T15:00:00.000Z', SENTINEL)).not.toThrow();
377+
});
378+
});
379+
308380
describe('detectDependencies()', () => {
309381
it('detects require() dependencies', () => {
310382
const content = "const foo = require('./foo-module');\nconst bar = require('../bar');";

0 commit comments

Comments
 (0)