@@ -39,7 +39,6 @@ import { BackingDataObject, Cache, StubDataObject } from '../../src/core/Cache';
39
39
import { Code , DataConnectError } from '../../src/core/error' ;
40
40
chai . use ( chaiAsPromised ) ;
41
41
42
- // TODO: convert to actually test cache stuffs...
43
42
// Helper to create a mock QueryResult object for tests
44
43
function createMockQueryResult < Data extends object , Variables > (
45
44
queryName : string ,
@@ -84,28 +83,60 @@ const options: DataConnectOptions = {
84
83
} ;
85
84
86
85
// Sample entity data for testing
86
+ interface Reviewer extends StubDataObject {
87
+ __typename : 'Reviewer' ;
88
+ __id : string ;
89
+ id : string ;
90
+ name : string ;
91
+ }
92
+
93
+ interface Review extends StubDataObject {
94
+ __typename : 'Review' ;
95
+ __id : string ;
96
+ id : string ;
97
+ text : string ;
98
+ reviewer : Reviewer ;
99
+ }
87
100
interface Movie extends StubDataObject {
88
- __typename : string ;
101
+ __typename : 'Movie' ;
89
102
__id : string ;
90
103
id : string ;
91
104
title : string ;
92
105
releaseYear : number ;
106
+ reviews : Review [ ] ;
93
107
}
94
108
109
+ const reviewer1 : Reviewer = {
110
+ __typename : 'Reviewer' ,
111
+ __id : '101' ,
112
+ id : '101' ,
113
+ name : 'John Doe'
114
+ } ;
115
+
116
+ const review1 : Review = {
117
+ __typename : 'Review' ,
118
+ __id : '201' ,
119
+ id : '201' ,
120
+ text : 'Amazing!' ,
121
+ reviewer : reviewer1
122
+ } ;
123
+
95
124
const movie1 : Movie = {
96
125
__typename : 'Movie' ,
97
126
__id : '1' ,
98
127
id : '1' ,
99
128
title : 'Inception' ,
100
- releaseYear : 2010
129
+ releaseYear : 2010 ,
130
+ reviews : [ review1 ]
101
131
} ;
102
132
103
133
const movie2 : Movie = {
104
134
__typename : 'Movie' ,
105
135
__id : '2' ,
106
136
id : '2' ,
107
137
title : 'The Matrix' ,
108
- releaseYear : 1999
138
+ releaseYear : 1999 ,
139
+ reviews : [ ]
109
140
} ;
110
141
111
142
describe ( 'Normalized Cache Tests' , ( ) => {
@@ -172,7 +203,7 @@ describe('Normalized Cache Tests', () => {
172
203
expect ( stubList [ 1 ] . title ) . to . equal ( 'The Matrix' ) ;
173
204
174
205
// 2. Check that two new BDOs were created in the BDO Cache
175
- expect ( cache . bdoCache . size ) . to . equal ( 2 ) ;
206
+ expect ( cache . bdoCache . size ) . to . equal ( 4 ) ; // movie1, review1, reviewer1, movie2
176
207
const bdo1 = cache . bdoCache . get ( Cache . makeBdoCacheKey ( 'Movie' , '1' ) ) ! ;
177
208
const bdo2 = cache . bdoCache . get ( Cache . makeBdoCacheKey ( 'Movie' , '2' ) ) ! ;
178
209
expect ( bdo1 ) . to . exist . and . be . an . instanceof ( BackingDataObject ) ;
@@ -203,7 +234,7 @@ describe('Normalized Cache Tests', () => {
203
234
const resultTreeKey = Cache . makeResultTreeCacheKey ( 'listMovies' , { } ) ;
204
235
const originalStub = cache . resultTreeCache . get ( resultTreeKey ) ! . movies [ 0 ] ;
205
236
expect ( originalStub . title ) . to . equal ( 'Inception' ) ;
206
- expect ( cache . bdoCache . size ) . to . equal ( 1 ) ;
237
+ expect ( cache . bdoCache . size ) . to . equal ( 3 ) ; // movie1, review1, reviewer1
207
238
208
239
// Step 2: A new query result comes in with updated data for the same movie.
209
240
// This should trigger the `updateBdo` logic path.
@@ -222,7 +253,7 @@ describe('Normalized Cache Tests', () => {
222
253
223
254
// Assertions
224
255
// 1. No new BDO was created; the existing one was found and updated.
225
- expect ( cache . bdoCache . size ) . to . equal ( 1 ) ;
256
+ expect ( cache . bdoCache . size ) . to . equal ( 3 ) ;
226
257
227
258
// 2. The new stub from the getMovie query has the new title.
228
259
const newStub = cache . resultTreeCache . get (
@@ -260,5 +291,149 @@ describe('Normalized Cache Tests', () => {
260
291
expect ( stubList ) . to . be . an ( 'array' ) . with . lengthOf ( 0 ) ;
261
292
expect ( cache . bdoCache . size ) . to . equal ( 0 ) ;
262
293
} ) ;
294
+
295
+ it ( 'should correctly normalize nested entities' , ( ) => {
296
+ const queryResult = createMockQueryResult (
297
+ 'getMovieWithReviews' ,
298
+ { id : '1' } ,
299
+ { movie : movie1 } ,
300
+ options ,
301
+ dc
302
+ ) ;
303
+ cache . updateCache ( queryResult ) ;
304
+
305
+ // 1. Check that BDOs were created for Movie, Review, and Reviewer
306
+ expect ( cache . bdoCache . size ) . to . equal ( 3 ) ;
307
+ expect ( cache . bdoCache . has ( Cache . makeBdoCacheKey ( 'Movie' , '1' ) ) ) . to . be
308
+ . true ;
309
+ expect ( cache . bdoCache . has ( Cache . makeBdoCacheKey ( 'Review' , '201' ) ) ) . to . be
310
+ . true ;
311
+ expect ( cache . bdoCache . has ( Cache . makeBdoCacheKey ( 'Reviewer' , '101' ) ) ) . to . be
312
+ . true ;
313
+
314
+ // 2. Check the stub result tree for correct structure
315
+ const resultTree = cache . resultTreeCache . get (
316
+ Cache . makeResultTreeCacheKey ( 'getMovieWithReviews' , { id : '1' } )
317
+ ) ! ;
318
+ const movieStub = resultTree . movie as Movie ;
319
+ expect ( movieStub . title ) . to . equal ( 'Inception' ) ;
320
+ expect ( movieStub . reviews ) . to . be . an ( 'array' ) . with . lengthOf ( 1 ) ;
321
+ const reviewStub = movieStub . reviews [ 0 ] ;
322
+ expect ( reviewStub . text ) . to . equal ( 'Amazing!' ) ;
323
+ expect ( reviewStub . reviewer . name ) . to . equal ( 'John Doe' ) ;
324
+
325
+ // 3. Check that stubs are distinct objects from BDOs
326
+ const movieBdo = cache . bdoCache . get ( Cache . makeBdoCacheKey ( 'Movie' , '1' ) ) ! ;
327
+ expect ( movieStub ) . to . not . equal ( movieBdo ) ;
328
+ expect ( movieStub . backingData ) . to . equal ( movieBdo ) ;
329
+ } ) ;
330
+
331
+ it ( 'should propagate changes from a nested entity to all parent listeners' , ( ) => {
332
+ // 1. Cache a movie with its review
333
+ const movieQueryResult = createMockQueryResult (
334
+ 'getMovie' ,
335
+ { id : '1' } ,
336
+ { movie : movie1 } ,
337
+ options ,
338
+ dc
339
+ ) ;
340
+ cache . updateCache ( movieQueryResult ) ;
341
+
342
+ const movieStub = cache . resultTreeCache . get (
343
+ Cache . makeResultTreeCacheKey ( 'getMovie' , { id : '1' } )
344
+ ) ! . movie as Movie ;
345
+ expect ( movieStub . reviews [ 0 ] . text ) . to . equal ( 'Amazing!' ) ;
346
+
347
+ // 2. A new query updates the review text
348
+ const updatedReview = {
349
+ ...review1 ,
350
+ text : 'Actually, it was just okay.'
351
+ } ;
352
+ const reviewQueryResult = createMockQueryResult (
353
+ 'getReview' ,
354
+ { id : '201' } ,
355
+ { review : updatedReview } ,
356
+ options ,
357
+ dc
358
+ ) ;
359
+ cache . updateCache ( reviewQueryResult ) ;
360
+
361
+ // 3. Assert that the original movie stub now reflects the updated review text
362
+ expect ( cache . bdoCache . size ) . to . equal ( 3 ) ; // BDOs should be updated, not created
363
+ expect ( movieStub . reviews [ 0 ] . text ) . to . equal ( 'Actually, it was just okay.' ) ;
364
+ } ) ;
365
+
366
+ it ( 'should handle non-normalizable data by storing it on the stub' , ( ) => {
367
+ // Movie with an aggregate field and a related object without a primary key
368
+ const queryData = {
369
+ movie : {
370
+ ...movie1 ,
371
+ __typename : 'Movie' ,
372
+ __id : '1' ,
373
+ // Non-normalizable aggregate field
374
+ reviewCount : 1 ,
375
+ // Related object without a primary key (__id)
376
+ primaryGenre : {
377
+ __typename : 'Genre' ,
378
+ name : 'Sci-Fi'
379
+ }
380
+ }
381
+ } ;
382
+
383
+ const queryResult = createMockQueryResult (
384
+ 'getMovieWithExtra' ,
385
+ { id : '1' } ,
386
+ queryData ,
387
+ options ,
388
+ dc
389
+ ) ;
390
+ cache . updateCache ( queryResult ) ;
391
+
392
+ // 1. Check that BDOs were created for normalizable types only
393
+ expect ( cache . bdoCache . size ) . to . equal ( 3 ) ; // Movie, Review, Reviewer
394
+ expect ( cache . bdoCache . has ( Cache . makeBdoCacheKey ( 'Movie' , '1' ) ) ) . to . be
395
+ . true ;
396
+ // CRITICAL: No BDO should be created for Genre
397
+ expect ( cache . bdoCache . has ( Cache . makeBdoCacheKey ( 'Genre' , '' ) ) ) . to . be
398
+ . false ;
399
+
400
+ // 2. Check that non-normalizable fields are present on the stub
401
+ const resultTree = cache . resultTreeCache . get (
402
+ Cache . makeResultTreeCacheKey ( 'getMovieWithExtra' , { id : '1' } )
403
+ ) ! ;
404
+ const movieStub = resultTree . movie as Movie ;
405
+ expect ( movieStub . reviewCount ) . to . equal ( 1 ) ;
406
+ expect ( movieStub . primaryGenre ) . to . deep . equal ( {
407
+ __typename : 'Genre' ,
408
+ name : 'Sci-Fi'
409
+ } ) ;
410
+ } ) ;
411
+
412
+ it ( 'should handle null values in query results gracefully' , ( ) => {
413
+ const queryData = {
414
+ movie : {
415
+ ...movie1 ,
416
+ reviews : null // The list of reviews is null
417
+ }
418
+ } ;
419
+ const queryResult = createMockQueryResult (
420
+ 'getMovie' ,
421
+ { id : '1' } ,
422
+ queryData ,
423
+ options ,
424
+ dc
425
+ ) ;
426
+ cache . updateCache ( queryResult ) ;
427
+
428
+ const resultTree = cache . resultTreeCache . get (
429
+ Cache . makeResultTreeCacheKey ( 'getMovie' , { id : '1' } )
430
+ ) ! ;
431
+ const movieStub = resultTree . movie as Movie ;
432
+ expect ( movieStub . title ) . to . equal ( 'Inception' ) ;
433
+ expect ( movieStub . reviews ) . to . be . null ;
434
+ // BDOs for movie, review, and reviewer from the original `movie1` object
435
+ // should still be created, as the normalization happens recursively before nulling.
436
+ expect ( cache . bdoCache . size ) . to . equal ( 3 ) ;
437
+ } ) ;
263
438
} ) ;
264
439
} ) ;
0 commit comments