@@ -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
115155test ( "OPFS is accessible for vector storage" , async ( { page } ) => {
0 commit comments