@@ -117,6 +117,67 @@ describe('LMDB fork detection and recovery', () => {
117117 } ) ;
118118 } ) ;
119119
120+ describe ( 'cached reference stability' , ( ) => {
121+ it ( 'should keep cached store references working after recoverFromFork' , async ( ) => {
122+ // Simulate SchemaStore pattern: grab reference once, hold it
123+ const cachedRef = factory . get ( StoreName . public_schemas ) ;
124+ await cachedRef . put ( 'before' , 'fork' ) ;
125+
126+ // Trigger recoverFromFork via handleError with fork-style error
127+ const handleError = ( factory as any ) . handleError . bind ( factory ) ;
128+ Object . defineProperty ( process , 'pid' , { value : originalPid + 1000 , configurable : true } ) ;
129+ handleError ( new Error ( "doesn't match env pid" ) ) ;
130+
131+ // The cached reference must still work — not be a dangling old object
132+ expect ( cachedRef . get ( 'before' ) ) . toBe ( 'fork' ) ;
133+ await cachedRef . put ( 'after' , 'fork' ) ;
134+ expect ( cachedRef . get ( 'after' ) ) . toBe ( 'fork' ) ;
135+ } ) ;
136+
137+ it ( 'should keep cached store references working after recoverFromError' , async ( ) => {
138+ const cachedRef = factory . get ( StoreName . public_schemas ) ;
139+ await cachedRef . put ( 'before' , 'error' ) ;
140+
141+ // Trigger recoverFromError via handleError with a generic error
142+ const reopenSpy = vi . spyOn ( factory as any , 'reopenEnv' ) ;
143+ const handleError = ( factory as any ) . handleError . bind ( factory ) ;
144+ handleError ( new Error ( 'MDB_CORRUPTED: some corruption' ) ) ;
145+
146+ expect ( reopenSpy ) . toHaveBeenCalled ( ) ;
147+
148+ // The cached reference must still be the same object and functional
149+ expect ( factory . get ( StoreName . public_schemas ) ) . toBe ( cachedRef ) ;
150+ await cachedRef . put ( 'after' , 'error' ) ;
151+ expect ( cachedRef . get ( 'after' ) ) . toBe ( 'error' ) ;
152+
153+ reopenSpy . mockRestore ( ) ;
154+ } ) ;
155+
156+ it ( 'should preserve object identity of stores after recreateStores' , ( ) => {
157+ const refBefore = factory . get ( StoreName . public_schemas ) ;
158+ const samRefBefore = factory . get ( StoreName . sam_schemas ) ;
159+
160+ // Directly call recreateStores
161+ ( factory as any ) . recreateStores ( ) ;
162+
163+ // factory.get() must return the exact same object
164+ expect ( factory . get ( StoreName . public_schemas ) ) . toBe ( refBefore ) ;
165+ expect ( factory . get ( StoreName . sam_schemas ) ) . toBe ( samRefBefore ) ;
166+ } ) ;
167+
168+ it ( 'should allow reads and writes on cached reference after recreateStores' , async ( ) => {
169+ const cachedRef = factory . get ( StoreName . public_schemas ) ;
170+ await cachedRef . put ( 'key' , 'value' ) ;
171+
172+ ( factory as any ) . recreateStores ( ) ;
173+
174+ // Cached ref should still read/write correctly
175+ expect ( cachedRef . get ( 'key' ) ) . toBe ( 'value' ) ;
176+ await cachedRef . put ( 'key2' , 'value2' ) ;
177+ expect ( cachedRef . get ( 'key2' ) ) . toBe ( 'value2' ) ;
178+ } ) ;
179+ } ) ;
180+
120181 describe ( 'factory behavior' , ( ) => {
121182 it ( 'should throw for unknown store name' , ( ) => {
122183 expect ( ( ) => factory . get ( 'unknown-store' as StoreName ) ) . toThrow ( 'Store unknown-store not found' ) ;
0 commit comments