Skip to content

Commit bfa8db4

Browse files
Copilotdevlux76
andcommitted
fix: address PR review feedback on integration tests
- Wire ingest-query-browser.spec.mjs into test:browser by using playwright test (matches all *.spec.mjs via playwright.config.mjs) - Add hotpath_index object store with by-tier index + index lookup validation to browser spec (matches comment about index patterns) - Fix txPut/txGet/txIndexGetAll to resolve on tx.oncomplete instead of request.onsuccess for transaction-level safety - Clarify persistence test name and add comment explaining that MemoryVectorStore is intentionally shared (metadata-only persistence) Co-authored-by: devlux76 <86517969+devlux76@users.noreply.github.com>
1 parent 04e07f8 commit bfa8db4

3 files changed

Lines changed: 59 additions & 15 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"test:unit": "vitest run tests/*.test.ts tests/**/*.test.ts",
1212
"test:watch": "vitest tests/*.test.ts tests/**/*.test.ts",
1313
"dev:harness": "bun scripts/runtime-harness-server.mjs",
14-
"test:browser": "playwright test tests/runtime/browser-harness.spec.mjs",
14+
"test:browser": "playwright test",
1515
"test:electron": "bun scripts/run-electron-runtime-smoke.mjs",
1616
"test:electron:playwright": "bun scripts/run-electron-runtime-tests.mjs",
1717
"test:electron:desktop": "CORTEX_ELECTRON_HEADLESS=0 CORTEX_ELECTRON_SHOW=1 bun scripts/run-electron-runtime-smoke.mjs",

tests/integration/IngestQuery.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,12 @@ describe("integration: ingest and query", () => {
253253
}
254254
});
255255

256-
it("persists data across sessions (reopen metadata store)", async () => {
256+
it("persists metadata across sessions (reopen IndexedDB store)", async () => {
257257
const dbName = freshDbName();
258+
// Note: MemoryVectorStore is intentionally shared across "sessions" here
259+
// because it has no persistence layer — this test validates that *metadata*
260+
// (pages, books, activity) survives an IndexedDB reopen. Vector persistence
261+
// is validated separately by the browser harness tests using real OPFS.
258262
const vectorStore = new MemoryVectorStore();
259263
const keyPair = await generateKeyPair();
260264
const profile = makeProfile();

tests/runtime/ingest-query-browser.spec.mjs

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@ import { test, expect } from "@playwright/test";
55
*
66
* These tests validate that real browser storage and compute backends
77
* function correctly for CORTEX's requirements:
8-
* - IndexedDB supports the CRUD and indexing patterns used by
9-
* IndexedDbMetadataStore (write, read, reopen, index query).
8+
* - IndexedDB supports the CRUD, indexing, and persistence patterns used by
9+
* IndexedDbMetadataStore (write, read, index creation, index lookup, reopen).
1010
* - OPFS is accessible (navigator.storage.getDirectory).
1111
* - At least one vector compute backend (WebGPU/WebGL/WASM) is available.
1212
*/
1313

14-
test("IndexedDB supports CORTEX CRUD and persistence patterns", async ({ page }) => {
14+
test("IndexedDB supports CORTEX CRUD, index lookup, and persistence patterns", async ({ page }) => {
1515
await page.goto("/");
1616
await page.waitForFunction(() => globalThis.__cortexHarnessReady === true);
1717

1818
const result = await page.evaluate(async () => {
1919
const DB_NAME = "cortex-e2e-browser-test";
2020
const DB_VERSION = 1;
2121

22-
// Helper to open the database and create object stores
22+
// Helper to open the database and create object stores with indexes
2323
function openDb() {
2424
return new Promise((resolve, reject) => {
2525
const request = globalThis.indexedDB.open(DB_NAME, DB_VERSION);
@@ -32,29 +32,50 @@ test("IndexedDB supports CORTEX CRUD and persistence patterns", async ({ page })
3232
if (!db.objectStoreNames.contains("books")) {
3333
db.createObjectStore("books", { keyPath: "bookId" });
3434
}
35+
if (!db.objectStoreNames.contains("hotpath_index")) {
36+
const hp = db.createObjectStore("hotpath_index", { keyPath: "entityId" });
37+
hp.createIndex("by-tier", "tier");
38+
}
3539
};
3640
request.onsuccess = () => resolve(request.result);
3741
});
3842
}
3943

40-
// Helper to perform a transaction operation
44+
// Transaction-safe put: resolves after the transaction commits
4145
function txPut(db, storeName, record) {
4246
return new Promise((resolve, reject) => {
4347
const tx = db.transaction(storeName, "readwrite");
44-
const store = tx.objectStore(storeName);
45-
const request = store.put(record);
46-
request.onsuccess = () => resolve(request.result);
47-
request.onerror = () => reject(request.error);
48+
tx.objectStore(storeName).put(record);
49+
tx.oncomplete = () => resolve(undefined);
50+
tx.onerror = () => reject(tx.error);
51+
tx.onabort = () => reject(tx.error ?? new Error("Transaction aborted"));
4852
});
4953
}
5054

55+
// Transaction-safe get: captures result then waits for tx commit
5156
function txGet(db, storeName, key) {
5257
return new Promise((resolve, reject) => {
5358
const tx = db.transaction(storeName, "readonly");
54-
const store = tx.objectStore(storeName);
55-
const request = store.get(key);
56-
request.onsuccess = () => resolve(request.result);
57-
request.onerror = () => reject(request.error);
59+
const request = tx.objectStore(storeName).get(key);
60+
let result;
61+
request.onsuccess = () => { result = request.result; };
62+
tx.oncomplete = () => resolve(result);
63+
tx.onerror = () => reject(tx.error);
64+
tx.onabort = () => reject(tx.error ?? new Error("Transaction aborted"));
65+
});
66+
}
67+
68+
// Index lookup via getAll on a named index
69+
function txIndexGetAll(db, storeName, indexName, key) {
70+
return new Promise((resolve, reject) => {
71+
const tx = db.transaction(storeName, "readonly");
72+
const index = tx.objectStore(storeName).index(indexName);
73+
const request = index.getAll(key);
74+
let result;
75+
request.onsuccess = () => { result = request.result; };
76+
tx.oncomplete = () => resolve(result);
77+
tx.onerror = () => reject(tx.error);
78+
tx.onabort = () => reject(tx.error ?? new Error("Transaction aborted"));
5879
});
5980
}
6081

@@ -77,18 +98,31 @@ test("IndexedDB supports CORTEX CRUD and persistence patterns", async ({ page })
7798
meta: {},
7899
};
79100

101+
const hotpathEntry1 = { entityId: "page-001", tier: "page", salience: 0.8 };
102+
const hotpathEntry2 = { entityId: "book-001", tier: "book", salience: 0.5 };
103+
const hotpathEntry3 = { entityId: "page-002", tier: "page", salience: 0.6 };
104+
80105
await txPut(db1, "pages", testPage);
81106
await txPut(db1, "books", testBook);
107+
await txPut(db1, "hotpath_index", hotpathEntry1);
108+
await txPut(db1, "hotpath_index", hotpathEntry2);
109+
await txPut(db1, "hotpath_index", hotpathEntry3);
82110

83111
// Verify read-back in same session
84112
const readPage = await txGet(db1, "pages", "page-browser-test-001");
85113
const readBook = await txGet(db1, "books", "book-browser-test-001");
114+
115+
// Verify index lookup: query by-tier index for "page" entries
116+
const pageEntries = await txIndexGetAll(db1, "hotpath_index", "by-tier", "page");
117+
const bookEntries = await txIndexGetAll(db1, "hotpath_index", "by-tier", "book");
118+
86119
db1.close();
87120

88121
// Session 2: Reopen and verify persistence
89122
const db2 = await openDb();
90123
const persistedPage = await txGet(db2, "pages", "page-browser-test-001");
91124
const persistedBook = await txGet(db2, "books", "book-browser-test-001");
125+
const persistedPageEntries = await txIndexGetAll(db2, "hotpath_index", "by-tier", "page");
92126
db2.close();
93127

94128
// Cleanup
@@ -101,15 +135,21 @@ test("IndexedDB supports CORTEX CRUD and persistence patterns", async ({ page })
101135
return {
102136
sameSessionPageOk: readPage?.pageId === testPage.pageId && readPage?.content === testPage.content,
103137
sameSessionBookOk: readBook?.bookId === testBook.bookId,
138+
indexLookupPageCount: pageEntries?.length,
139+
indexLookupBookCount: bookEntries?.length,
104140
persistedPageOk: persistedPage?.pageId === testPage.pageId && persistedPage?.content === testPage.content,
105141
persistedBookOk: persistedBook?.bookId === testBook.bookId && persistedBook?.pageIds?.length === 1,
142+
persistedIndexOk: persistedPageEntries?.length === 2,
106143
};
107144
});
108145

109146
expect(result.sameSessionPageOk).toBe(true);
110147
expect(result.sameSessionBookOk).toBe(true);
148+
expect(result.indexLookupPageCount).toBe(2);
149+
expect(result.indexLookupBookCount).toBe(1);
111150
expect(result.persistedPageOk).toBe(true);
112151
expect(result.persistedBookOk).toBe(true);
152+
expect(result.persistedIndexOk).toBe(true);
113153
});
114154

115155
test("OPFS is accessible for vector storage", async ({ page }) => {

0 commit comments

Comments
 (0)