Skip to content

Commit 337fe46

Browse files
committed
initial unit tests
1 parent b61056f commit 337fe46

File tree

1 file changed

+182
-7
lines changed

1 file changed

+182
-7
lines changed

packages/data-connect/test/unit/Cache.test.ts

Lines changed: 182 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import { BackingDataObject, Cache, StubDataObject } from '../../src/core/Cache';
3939
import { Code, DataConnectError } from '../../src/core/error';
4040
chai.use(chaiAsPromised);
4141

42-
// TODO: convert to actually test cache stuffs...
4342
// Helper to create a mock QueryResult object for tests
4443
function createMockQueryResult<Data extends object, Variables>(
4544
queryName: string,
@@ -84,28 +83,60 @@ const options: DataConnectOptions = {
8483
};
8584

8685
// 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+
}
87100
interface Movie extends StubDataObject {
88-
__typename: string;
101+
__typename: 'Movie';
89102
__id: string;
90103
id: string;
91104
title: string;
92105
releaseYear: number;
106+
reviews: Review[];
93107
}
94108

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+
95124
const movie1: Movie = {
96125
__typename: 'Movie',
97126
__id: '1',
98127
id: '1',
99128
title: 'Inception',
100-
releaseYear: 2010
129+
releaseYear: 2010,
130+
reviews: [review1]
101131
};
102132

103133
const movie2: Movie = {
104134
__typename: 'Movie',
105135
__id: '2',
106136
id: '2',
107137
title: 'The Matrix',
108-
releaseYear: 1999
138+
releaseYear: 1999,
139+
reviews: []
109140
};
110141

111142
describe('Normalized Cache Tests', () => {
@@ -172,7 +203,7 @@ describe('Normalized Cache Tests', () => {
172203
expect(stubList[1].title).to.equal('The Matrix');
173204

174205
// 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
176207
const bdo1 = cache.bdoCache.get(Cache.makeBdoCacheKey('Movie', '1'))!;
177208
const bdo2 = cache.bdoCache.get(Cache.makeBdoCacheKey('Movie', '2'))!;
178209
expect(bdo1).to.exist.and.be.an.instanceof(BackingDataObject);
@@ -203,7 +234,7 @@ describe('Normalized Cache Tests', () => {
203234
const resultTreeKey = Cache.makeResultTreeCacheKey('listMovies', {});
204235
const originalStub = cache.resultTreeCache.get(resultTreeKey)!.movies[0];
205236
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
207238

208239
// Step 2: A new query result comes in with updated data for the same movie.
209240
// This should trigger the `updateBdo` logic path.
@@ -222,7 +253,7 @@ describe('Normalized Cache Tests', () => {
222253

223254
// Assertions
224255
// 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);
226257

227258
// 2. The new stub from the getMovie query has the new title.
228259
const newStub = cache.resultTreeCache.get(
@@ -260,5 +291,149 @@ describe('Normalized Cache Tests', () => {
260291
expect(stubList).to.be.an('array').with.lengthOf(0);
261292
expect(cache.bdoCache.size).to.equal(0);
262293
});
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+
});
263438
});
264439
});

0 commit comments

Comments
 (0)