bridge-index-last-indexed.test.mjs
179 lines 5.8 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 2 days ago
1 /**
2 * Unit tests for `lib/bridge-index-last-indexed.mjs`.
3 *
4 * The sidecar is what makes the Hub UI's "Last indexed: 2 minutes ago" line
5 * truthful regardless of which path (sync vs background) ran the index. Both
6 * `set` and `get` must round-trip every field the UI displays without dropping
7 * data on a rolling deploy where reader and writer might be different bridge
8 * versions.
9 */
10
11 import test from 'node:test';
12 import assert from 'node:assert/strict';
13 import {
14 setLastIndexedAt,
15 getLastIndexedAt,
16 lastIndexedKey,
17 } from '../lib/bridge-index-last-indexed.mjs';
18
19 function makeFakeBlobStore() {
20 const store = new Map();
21 return {
22 _store: store,
23 async get(key) {
24 const v = store.get(key);
25 if (v == null) return null;
26 return String(v);
27 },
28 async set(key, value) {
29 store.set(key, String(value));
30 },
31 async delete(key) {
32 store.delete(key);
33 },
34 };
35 }
36
37 test('lastIndexedKey: canonical path keyed by (canisterUid, vaultId)', () => {
38 assert.strictEqual(lastIndexedKey('user_1', 'Business'), 'index-meta/user_1/Business.json');
39 });
40
41 test('lastIndexedKey: rejects empty canisterUid or vaultId', () => {
42 assert.throws(() => lastIndexedKey('', 'v'), /canisterUid must be a non-empty string/);
43 assert.throws(() => lastIndexedKey('u', ''), /vaultId must be a non-empty string/);
44 });
45
46 test('setLastIndexedAt + getLastIndexedAt: round-trip every field the UI shows', async () => {
47 const store = makeFakeBlobStore();
48 const fixedNow = Date.parse('2026-05-01T23:54:00.000Z');
49 const written = await setLastIndexedAt(store, {
50 canisterUid: 'user_1',
51 vaultId: 'Business',
52 actorUid: 'actor_a',
53 notesProcessed: 32,
54 chunksIndexed: 251,
55 chunksEmbedded: 0,
56 chunksSkippedCached: 251,
57 vectorsDeleted: 0,
58 embeddingInputTokens: 0,
59 durationMs: 1234,
60 mode: 'sync',
61 provider: 'deepinfra',
62 model: 'BAAI/bge-large-en-v1.5',
63 now: () => fixedNow,
64 });
65 assert.strictEqual(written.written, true);
66 const got = await getLastIndexedAt(store, { canisterUid: 'user_1', vaultId: 'Business' });
67 assert.deepStrictEqual(got, {
68 lastIndexedAt: '2026-05-01T23:54:00.000Z',
69 lastIndexedAtEpochMs: fixedNow,
70 actorUid: 'actor_a',
71 notesProcessed: 32,
72 chunksIndexed: 251,
73 chunksEmbedded: 0,
74 chunksSkippedCached: 251,
75 vectorsDeleted: 0,
76 embeddingInputTokens: 0,
77 durationMs: 1234,
78 mode: 'sync',
79 provider: 'deepinfra',
80 model: 'BAAI/bge-large-en-v1.5',
81 });
82 });
83
84 test('setLastIndexedAt: defaults missing numeric fields to 0 (no NaN/null surprises in UI)', async () => {
85 const store = makeFakeBlobStore();
86 await setLastIndexedAt(store, {
87 canisterUid: 'user_1',
88 vaultId: 'Business',
89 now: () => 1_000_000,
90 });
91 const got = await getLastIndexedAt(store, { canisterUid: 'user_1', vaultId: 'Business' });
92 assert.strictEqual(got.notesProcessed, 0);
93 assert.strictEqual(got.chunksIndexed, 0);
94 assert.strictEqual(got.embeddingInputTokens, 0);
95 assert.strictEqual(got.durationMs, 0);
96 assert.strictEqual(got.mode, 'sync', 'omitting mode defaults to sync');
97 assert.strictEqual(got.provider, null);
98 assert.strictEqual(got.model, null);
99 });
100
101 test('setLastIndexedAt: invalid mode value normalizes to sync', async () => {
102 const store = makeFakeBlobStore();
103 await setLastIndexedAt(store, {
104 canisterUid: 'user_1',
105 vaultId: 'Business',
106 mode: 'asdf',
107 now: () => 1,
108 });
109 const got = await getLastIndexedAt(store, { canisterUid: 'user_1', vaultId: 'Business' });
110 assert.strictEqual(got.mode, 'sync');
111 });
112
113 test('setLastIndexedAt: mode "background" preserved', async () => {
114 const store = makeFakeBlobStore();
115 await setLastIndexedAt(store, {
116 canisterUid: 'user_1',
117 vaultId: 'Business',
118 mode: 'background',
119 now: () => 1,
120 });
121 const got = await getLastIndexedAt(store, { canisterUid: 'user_1', vaultId: 'Business' });
122 assert.strictEqual(got.mode, 'background');
123 });
124
125 test('getLastIndexedAt: returns null when never indexed', async () => {
126 const store = makeFakeBlobStore();
127 const got = await getLastIndexedAt(store, { canisterUid: 'user_1', vaultId: 'Business' });
128 assert.strictEqual(got, null);
129 });
130
131 test('getLastIndexedAt: returns null on malformed JSON (older deploy / partial write)', async () => {
132 const store = makeFakeBlobStore();
133 await store.set(lastIndexedKey('user_1', 'Business'), 'not-valid-json{');
134 const got = await getLastIndexedAt(store, { canisterUid: 'user_1', vaultId: 'Business' });
135 assert.strictEqual(got, null);
136 });
137
138 test('getLastIndexedAt: blob get throwing returns null (does not block the UI render)', async () => {
139 const erroringStore = {
140 async get() {
141 throw new Error('transient blob error');
142 },
143 async set() {},
144 async delete() {},
145 };
146 const got = await getLastIndexedAt(erroringStore, {
147 canisterUid: 'user_1',
148 vaultId: 'Business',
149 });
150 assert.strictEqual(got, null);
151 });
152
153 test('setLastIndexedAt: distinct (canisterUid, vaultId) pairs do not overwrite each other', async () => {
154 const store = makeFakeBlobStore();
155 await setLastIndexedAt(store, {
156 canisterUid: 'user_1',
157 vaultId: 'Business',
158 chunksIndexed: 100,
159 now: () => 1,
160 });
161 await setLastIndexedAt(store, {
162 canisterUid: 'user_1',
163 vaultId: 'Personal',
164 chunksIndexed: 200,
165 now: () => 2,
166 });
167 await setLastIndexedAt(store, {
168 canisterUid: 'user_2',
169 vaultId: 'Business',
170 chunksIndexed: 300,
171 now: () => 3,
172 });
173 const a = await getLastIndexedAt(store, { canisterUid: 'user_1', vaultId: 'Business' });
174 const b = await getLastIndexedAt(store, { canisterUid: 'user_1', vaultId: 'Personal' });
175 const c = await getLastIndexedAt(store, { canisterUid: 'user_2', vaultId: 'Business' });
176 assert.strictEqual(a.chunksIndexed, 100);
177 assert.strictEqual(b.chunksIndexed, 200);
178 assert.strictEqual(c.chunksIndexed, 300);
179 });
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 2 days ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 3 days ago