From 75e3e83bd94ea9512513ffa77432d9f9ac87ac13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rge=20N=C3=A6ss?= Date: Mon, 30 Sep 2024 17:44:17 +0200 Subject: [PATCH] test(core): add tests for rxSwr --- .../src/core/util/__tests__/rxSwr.test.ts | 55 +++++++++++++++++++ packages/sanity/src/core/util/rxSwr.ts | 7 +++ 2 files changed, 62 insertions(+) create mode 100644 packages/sanity/src/core/util/__tests__/rxSwr.test.ts diff --git a/packages/sanity/src/core/util/__tests__/rxSwr.test.ts b/packages/sanity/src/core/util/__tests__/rxSwr.test.ts new file mode 100644 index 00000000000..18d273e7fcd --- /dev/null +++ b/packages/sanity/src/core/util/__tests__/rxSwr.test.ts @@ -0,0 +1,55 @@ +import {describe, expect, it} from '@jest/globals' +import {lastValueFrom, timer} from 'rxjs' +import {map, toArray} from 'rxjs/operators' + +import {createSWR} from '../rxSwr' + +describe('rxSwr', () => { + it('should cache the last known value and emit sync', async () => { + const swr = createSWR({maxSize: 1}) + + const observable = timer(100).pipe( + map(() => 'value!'), + swr('someKey'), + toArray(), + ) + + expect(await lastValueFrom(observable)).toEqual([{fromCache: false, value: 'value!'}]) + + // Second subscription, now with warm cache + expect(await lastValueFrom(observable)).toEqual([ + {fromCache: true, value: 'value!'}, + {fromCache: false, value: 'value!'}, + ]) + }) + + it('should discard old cache keys when exceeding maxSize', async () => { + const swr = createSWR({maxSize: 1}) + + const observable1 = timer(100).pipe( + map(() => 'observable1!'), + swr('key1'), + toArray(), + ) + + expect(await lastValueFrom(observable1)).toEqual([{fromCache: false, value: 'observable1!'}]) + + // Second subscription, now with warm cache + expect(await lastValueFrom(observable1)).toEqual([ + {fromCache: true, value: 'observable1!'}, + {fromCache: false, value: 'observable1!'}, + ]) + + const observable2 = timer(100).pipe( + map(() => 'observable2!'), + swr('key2'), + toArray(), + ) + + // Subscribing to observable2 should purge the key of observable1 + expect(await lastValueFrom(observable2)).toEqual([{fromCache: false, value: 'observable2!'}]) + + // re-subscribing to the first should now not have a cache + expect(await lastValueFrom(observable1)).toEqual([{fromCache: false, value: 'value!'}]) + }) +}) diff --git a/packages/sanity/src/core/util/rxSwr.ts b/packages/sanity/src/core/util/rxSwr.ts index e22f352c032..9dd2b04ec5f 100644 --- a/packages/sanity/src/core/util/rxSwr.ts +++ b/packages/sanity/src/core/util/rxSwr.ts @@ -2,6 +2,9 @@ import QuickLRU from 'quick-lru' import {concat, defer, EMPTY, map, type Observable, of, type OperatorFunction} from 'rxjs' import {tap} from 'rxjs/operators' +/** + * The interface that any caching layer must implement + */ interface SWRCache { /** * Note: This will throw if key does not exist. Always check for existence with `has` before calling @@ -32,6 +35,10 @@ export function createSWR(options: {maxSize: number}) { } } +/** + * For now, the only cache layer implemented is an in-memory LRU. + * @param options - LRU options + */ function createLRUCache(options: {maxSize: number}): SWRCache { const lru = new QuickLRU(options) return {