You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
9.9 KiB
311 lines
9.9 KiB
import moize from '../src'; |
|
|
|
type Type = { |
|
number: number; |
|
}; |
|
|
|
const method = (one: number, two: Type) => one + two.number; |
|
const promiseMethodResolves = (one: number, two: Type) => |
|
new Promise((resolve) => setTimeout(() => resolve(one + two.number), 1000)); |
|
const promiseMethodRejects = |
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars |
|
(one: number, two: Type) => |
|
new Promise((resolve, reject) => |
|
setTimeout(() => reject(new Error('boom')), 1000) |
|
); |
|
|
|
describe('moize.updateCacheForKey', () => { |
|
describe('success', () => { |
|
it('will refresh the cache', () => { |
|
const moized = moize.maxSize(2)(method, { |
|
updateCacheForKey(args) { |
|
return args[1].number % 2 === 0; |
|
}, |
|
}); |
|
|
|
const mutated = { number: 5 }; |
|
|
|
const result = moized(6, mutated); |
|
|
|
expect(result).toBe(11); |
|
|
|
mutated.number = 11; |
|
|
|
const mutatedResult = moized(6, mutated); |
|
|
|
// Result was not recalculated because `updateCacheForKey` returned `false` and the values are |
|
// seen as unchanged. |
|
expect(mutatedResult).toBe(result); |
|
|
|
mutated.number = 10; |
|
|
|
const refreshedResult = moized(6, mutated); |
|
|
|
// Result was recalculated because `updateCacheForKey` returned `true`. |
|
expect(refreshedResult).not.toBe(result); |
|
expect(refreshedResult).toBe(16); |
|
|
|
const { keys, values } = moized.cacheSnapshot; |
|
|
|
expect(keys).toEqual([[6, mutated]]); |
|
expect(values).toEqual([16]); |
|
}); |
|
|
|
it('will refresh the cache based on external values', async () => { |
|
const mockMethod = jest.fn(method); |
|
|
|
let lastUpdate = Date.now(); |
|
|
|
const moized = moize.maxSize(2)(mockMethod, { |
|
updateCacheForKey() { |
|
const now = Date.now(); |
|
const last = lastUpdate; |
|
|
|
lastUpdate = now; |
|
|
|
return last + 1000 < now; |
|
}, |
|
}); |
|
|
|
const mutated = { number: 5 }; |
|
|
|
moized(6, mutated); |
|
moized(6, mutated); |
|
moized(6, mutated); |
|
|
|
expect(mockMethod).toHaveBeenCalledTimes(1); |
|
|
|
await new Promise((resolve) => setTimeout(resolve, 2000)); |
|
|
|
moized(6, mutated); |
|
|
|
expect(mockMethod).toHaveBeenCalledTimes(2); |
|
}); |
|
|
|
it('will refresh the cache when used with promises', async () => { |
|
const moized = moize.maxSize(2)(promiseMethodResolves, { |
|
isPromise: true, |
|
updateCacheForKey(args) { |
|
return args[1].number % 2 === 0; |
|
}, |
|
}); |
|
|
|
const mutated = { number: 5 }; |
|
|
|
const result = await moized(6, mutated); |
|
|
|
expect(result).toBe(11); |
|
|
|
mutated.number = 11; |
|
|
|
const mutatedResult = await moized(6, mutated); |
|
|
|
// Result was not recalculated because `updateCacheForKey` returned `false` and the values are |
|
// seen as unchanged. |
|
expect(mutatedResult).toBe(result); |
|
|
|
mutated.number = 10; |
|
|
|
const refreshedResult = await moized(6, mutated); |
|
|
|
// Result was recalculated because `updateCacheForKey` returned `true`. |
|
expect(refreshedResult).not.toBe(result); |
|
expect(refreshedResult).toBe(16); |
|
|
|
const { keys, values } = moized.cacheSnapshot; |
|
|
|
expect(keys).toEqual([[6, mutated]]); |
|
expect(values).toEqual([Promise.resolve(16)]); |
|
}); |
|
|
|
it('will refresh the cache when used with custom key transformers', () => { |
|
type ConditionalIncrement = { |
|
force?: boolean; |
|
}; |
|
|
|
let count = 0; |
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars |
|
const increment = (_?: ConditionalIncrement) => ++count; |
|
|
|
const moized = moize.maxSize(2)(increment, { |
|
isSerialized: true, |
|
updateCacheForKey: (args: [ConditionalIncrement]) => |
|
args[0] && args[0].force === true, |
|
serializer: () => ['always same'], |
|
}); |
|
|
|
expect(moized()).toBe(1); |
|
expect(moized()).toBe(1); |
|
expect(moized({ force: true })).toBe(2); |
|
expect(moized()).toBe(2); |
|
}); |
|
|
|
it('will refresh the cache with shorthand', () => { |
|
const moized = moize.updateCacheForKey( |
|
(args) => args[1].number % 2 === 0 |
|
)(method); |
|
|
|
const mutated = { number: 5 }; |
|
|
|
const result = moized(6, mutated); |
|
|
|
expect(result).toBe(11); |
|
|
|
mutated.number = 11; |
|
|
|
const mutatedResult = moized(6, mutated); |
|
|
|
// Result was not recalculated because `updateCacheForKey` returned `false` and the values are |
|
// seen as unchanged. |
|
expect(mutatedResult).toBe(result); |
|
|
|
mutated.number = 10; |
|
|
|
const refreshedResult = moized(6, mutated); |
|
|
|
// Result was recalculated because `updateCacheForKey` returned `true`. |
|
expect(refreshedResult).not.toBe(result); |
|
expect(refreshedResult).toBe(16); |
|
|
|
const { keys, values } = moized.cacheSnapshot; |
|
|
|
expect(keys).toEqual([[6, mutated]]); |
|
expect(values).toEqual([16]); |
|
}); |
|
|
|
it('will refresh the cache with composed shorthand', () => { |
|
const moizer = moize.compose( |
|
moize.maxSize(2), |
|
moize.updateCacheForKey((args) => args[1].number % 2 === 0) |
|
); |
|
const moized = moizer(method); |
|
|
|
const mutated = { number: 5 }; |
|
|
|
const result = moized(6, mutated); |
|
|
|
expect(result).toBe(11); |
|
|
|
mutated.number = 11; |
|
|
|
const mutatedResult = moized(6, mutated); |
|
|
|
// Result was not recalculated because `updateCacheForKey` returned `false` and the values are |
|
// seen as unchanged. |
|
expect(mutatedResult).toBe(result); |
|
|
|
mutated.number = 10; |
|
|
|
const refreshedResult = moized(6, mutated); |
|
|
|
// Result was recalculated because `updateCacheForKey` returned `true`. |
|
expect(refreshedResult).not.toBe(result); |
|
expect(refreshedResult).toBe(16); |
|
|
|
const { keys, values } = moized.cacheSnapshot; |
|
|
|
expect(keys).toEqual([[6, mutated]]); |
|
expect(values).toEqual([16]); |
|
}); |
|
}); |
|
|
|
describe('fail', () => { |
|
it('surfaces the error if the function fails', () => { |
|
const moized = moize.maxSize(2)( |
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars |
|
(_1: number, _2: Type) => { |
|
throw new Error('boom'); |
|
}, |
|
{ |
|
updateCacheForKey(args) { |
|
return args[1].number % 2 === 0; |
|
}, |
|
} |
|
); |
|
|
|
const mutated = { number: 5 }; |
|
|
|
expect(() => moized(6, mutated)).toThrow(new Error('boom')); |
|
}); |
|
|
|
it('surfaces the error if the promise rejects', async () => { |
|
const moized = moize.maxSize(2)(promiseMethodRejects, { |
|
isPromise: true, |
|
updateCacheForKey(args) { |
|
return args[1].number % 2 === 0; |
|
}, |
|
}); |
|
|
|
const mutated = { number: 5 }; |
|
|
|
await expect(moized(6, mutated)).rejects.toEqual(new Error('boom')); |
|
}); |
|
|
|
it('should have nothing in cache if promise is rejected and key was never present', async () => { |
|
const moized = moize.maxSize(2)(promiseMethodRejects, { |
|
isPromise: true, |
|
updateCacheForKey(args) { |
|
return args[1].number % 2 === 0; |
|
}, |
|
}); |
|
|
|
const mutated = { number: 5 }; |
|
|
|
await expect(moized(6, mutated)).rejects.toEqual(new Error('boom')); |
|
|
|
expect(moized.keys()).toEqual([]); |
|
expect(moized.values()).toEqual([]); |
|
}); |
|
|
|
// For some reason, this is causing `jest` to crash instead of handle the rejection |
|
it.skip('should have nothing in cache if promise is rejected and key was present', async () => { |
|
const moized = moize.maxSize(2)(promiseMethodRejects, { |
|
isPromise: true, |
|
updateCacheForKey(args) { |
|
return args[1].number % 2 === 0; |
|
}, |
|
}); |
|
|
|
const mutated = { number: 5 }; |
|
|
|
moized.set([6, mutated], Promise.resolve(11)); |
|
|
|
expect(moized.get([6, mutated])).toEqual(Promise.resolve(11)); |
|
|
|
mutated.number = 10; |
|
|
|
await expect(moized(6, mutated)).rejects.toEqual(new Error('boom')); |
|
|
|
expect(moized.keys()).toEqual([]); |
|
expect(moized.values()).toEqual([]); |
|
}); |
|
}); |
|
|
|
describe('infrastructure', () => { |
|
it('should have all the static properties of a standard moized method', () => { |
|
const moized = moize.maxSize(2)(promiseMethodResolves, { |
|
updateCacheForKey(args) { |
|
return args[1].number % 2 === 0; |
|
}, |
|
}); |
|
const standardMoized = moize.maxSize(2)(promiseMethodResolves); |
|
|
|
expect(Object.getOwnPropertyNames(moized)).toEqual( |
|
Object.getOwnPropertyNames(standardMoized) |
|
); |
|
}); |
|
}); |
|
|
|
describe('edge cases', () => { |
|
it('should retain the original function name', () => { |
|
function myNamedFunction() {} |
|
|
|
const memoized = moize(myNamedFunction, { |
|
updateCacheForKey: () => false, |
|
}); |
|
|
|
expect(memoized.name).toBe('moized(myNamedFunction)'); |
|
}); |
|
}); |
|
});
|
|
|