self-refreshing-cache.spec.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import { beforeAll, describe, expect, it, vi } from 'vitest';
  2. import { createSelfRefreshingCache, SelfRefreshingCache } from './self-refreshing-cache';
  3. describe('SelfRefreshingCache', () => {
  4. let testCache: SelfRefreshingCache<number, [string]>;
  5. const fetchFn = vi.fn().mockImplementation((arg: string) => arg.length);
  6. let currentTime = 0;
  7. beforeAll(async () => {
  8. testCache = await createSelfRefreshingCache<number, [string]>({
  9. name: 'test',
  10. ttl: 1000,
  11. refresh: {
  12. fn: async arg => {
  13. return fetchFn(arg) as number;
  14. },
  15. defaultArgs: ['default'],
  16. },
  17. getTimeFn: () => currentTime,
  18. });
  19. });
  20. it('fetches value on first call', async () => {
  21. const result = await testCache.value();
  22. expect(result).toBe(7);
  23. expect(fetchFn.mock.calls.length).toBe(1);
  24. });
  25. it('passes default args on first call', () => {
  26. expect(fetchFn.mock.calls[0]).toEqual(['default']);
  27. });
  28. it('return from cache on second call', async () => {
  29. const result = await testCache.value();
  30. expect(result).toBe(7);
  31. expect(fetchFn.mock.calls.length).toBe(1);
  32. });
  33. it('automatically refresh after ttl expires', async () => {
  34. currentTime = 1001;
  35. const result = await testCache.value('custom');
  36. expect(result).toBe(6);
  37. expect(fetchFn.mock.calls.length).toBe(2);
  38. });
  39. it('refresh forces fetch with supplied args', async () => {
  40. const result = await testCache.refresh('new arg which is longer');
  41. expect(result).toBe(23);
  42. expect(fetchFn.mock.calls.length).toBe(3);
  43. expect(fetchFn.mock.calls[2]).toEqual(['new arg which is longer']);
  44. });
  45. describe('memoization', () => {
  46. const memoizedFn = vi.fn();
  47. let getMemoized: (arg1: string, arg2: number) => Promise<number>;
  48. beforeAll(() => {
  49. getMemoized = async (arg1, arg2) => {
  50. return testCache.memoize([arg1, arg2], ['quux'], async (value, a1, a2) => {
  51. memoizedFn(a1, a2);
  52. return value * +a2;
  53. });
  54. };
  55. });
  56. it('calls the memoized function only once for the given args', async () => {
  57. const result1 = await getMemoized('foo', 1);
  58. expect(result1).toBe(23 * 1);
  59. expect(memoizedFn.mock.calls.length).toBe(1);
  60. expect(memoizedFn.mock.calls[0]).toEqual(['foo', 1]);
  61. const result2 = await getMemoized('foo', 1);
  62. expect(result2).toBe(23 * 1);
  63. expect(memoizedFn.mock.calls.length).toBe(1);
  64. });
  65. it('calls the memoized function when args change', async () => {
  66. const result1 = await getMemoized('foo', 2);
  67. expect(result1).toBe(23 * 2);
  68. expect(memoizedFn.mock.calls.length).toBe(2);
  69. expect(memoizedFn.mock.calls[1]).toEqual(['foo', 2]);
  70. });
  71. it('retains memoized results from earlier calls', async () => {
  72. const result1 = await getMemoized('foo', 1);
  73. expect(result1).toBe(23 * 1);
  74. expect(memoizedFn.mock.calls.length).toBe(2);
  75. });
  76. it('re-fetches and re-runs memoized function after ttl expires', async () => {
  77. currentTime = 3000;
  78. const result1 = await getMemoized('foo', 1);
  79. expect(result1).toBe(4 * 1);
  80. expect(memoizedFn.mock.calls.length).toBe(3);
  81. await getMemoized('foo', 1);
  82. expect(memoizedFn.mock.calls.length).toBe(3);
  83. });
  84. it('works with alternating calls', async () => {
  85. const result1 = await getMemoized('foo', 1);
  86. expect(result1).toBe(4 * 1);
  87. expect(memoizedFn.mock.calls.length).toBe(3);
  88. const result2 = await getMemoized('foo', 3);
  89. expect(result2).toBe(4 * 3);
  90. expect(memoizedFn.mock.calls.length).toBe(4);
  91. const result3 = await getMemoized('foo', 1);
  92. expect(result3).toBe(4 * 1);
  93. expect(memoizedFn.mock.calls.length).toBe(4);
  94. const result4 = await getMemoized('foo', 3);
  95. expect(result4).toBe(4 * 3);
  96. expect(memoizedFn.mock.calls.length).toBe(4);
  97. const result5 = await getMemoized('foo', 1);
  98. expect(result5).toBe(4 * 1);
  99. expect(memoizedFn.mock.calls.length).toBe(4);
  100. });
  101. });
  102. });