-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmemoize.decorator.spec.ts
More file actions
148 lines (131 loc) · 5.52 KB
/
memoize.decorator.spec.ts
File metadata and controls
148 lines (131 loc) · 5.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import { memoize } from './memoize.decorator';
import { MemoizationContext } from '../common/models/executionMemoization.model';
describe('memoize decorator', () => {
it('should memoize Fibonacci results and prevent redundant function calls', async () => {
let memoizationCheckCount = 0;
let memoizedCalls = 0;
let totalFunctionCalls = 0;
class Calculator {
@memoize((memoContext: MemoizationContext<number>) => {
memoizationCheckCount++;
if (memoContext.isMemoized) {
memoizedCalls++;
}
})
fibonacci(n: number): number {
totalFunctionCalls++;
if (n <= 1) {
return n;
}
return this.fibonacci(n - 1) + this.fibonacci(n - 2);
}
}
const calculator = new Calculator();
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
const fib3 = calculator.fibonacci(3);
expect(memoizedCalls).toBe(0);
expect(totalFunctionCalls).toBe(5); // fib(3) = (fib(2) = fib(1) + fib(0)) + fib(1)
expect(memoizationCheckCount).toEqual(totalFunctionCalls + memoizedCalls);
expect(fib3).toBe(2);
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
// first call:
const fib50_1 = calculator.fibonacci(50);
expect(memoizedCalls).toBeGreaterThan(0);
expect(totalFunctionCalls).toBeLessThan(1274); // 1274 calls for fibonacci(50) if all exist
expect(memoizationCheckCount).toEqual(totalFunctionCalls + memoizedCalls);
expect(fib50_1).toBe(12586269025);
const memoizedCallsAfterFirstCall = memoizedCalls;
const totalFunctionCallsAfterFirstCall = totalFunctionCalls;
// second call:
const fib50_2 = calculator.fibonacci(50);
expect(memoizedCalls).toBe(memoizedCallsAfterFirstCall + 1); // a new get of memoized fib50
expect(totalFunctionCalls).toBe(totalFunctionCallsAfterFirstCall); // no new call, fib50 is memoized
expect(memoizationCheckCount).toEqual(totalFunctionCalls + memoizedCalls);
expect(fib50_2).toBe(12586269025);
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
const fib51 = calculator.fibonacci(51);
expect(totalFunctionCalls).toBe(1); // we need 1 extra call to get fibonacci of 51 as we did fibonacci(50)
expect(memoizedCalls).toBe(2); // yes fib(51-1) and fib(51-2) are memoized
expect(memoizationCheckCount).toBe(3); // 2memoized and 1 call
expect(fib51).toBe(20365011074);
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
const fib5 = calculator.fibonacci(6);
expect(totalFunctionCalls).toBe(0); // no need for extra call to get fibonacci of 5 as we did fibonacci(50)
expect(memoizedCalls).toBe(1); // yes fib(5) is memoized implicitly
expect(memoizationCheckCount).toBe(1); // 1memoized
expect(fib5).toBe(8);
});
it('should memoize async function results and prevent redundant calls', async () => {
let memoizationCheckCount = 0;
let memoizedCalls = 0;
let totalFunctionCalls = 0;
class DataService {
@memoize(async (memoContext: MemoizationContext<string>) => {
memoizationCheckCount++;
if (memoContext.isMemoized) {
memoizedCalls++;
}
})
async fetchData(id: number): Promise<string> {
totalFunctionCalls++;
return new Promise((resolve) =>
setTimeout(() => resolve(`Data for ID: ${id}`), 100)
);
}
@memoize(async (memoContext: MemoizationContext<string>) => {
memoizationCheckCount++;
if (memoContext.isMemoized) {
memoizedCalls++;
}
})
async throwData(name: string): Promise<string> {
totalFunctionCalls++;
throw new Error(`hello ${name} but I throw!`);
}
}
const service = new DataService();
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
const result1 = await service.fetchData(1);
expect(result1).toBe('Data for ID: 1');
expect(memoizedCalls).toBe(0);
expect(totalFunctionCalls).toBe(1);
expect(memoizationCheckCount).toBe(1); // Called once
const result2 = await service.fetchData(1);
expect(result2).toBe('Data for ID: 1');
expect(memoizedCalls).toBe(1); // Now it should be memoized
expect(totalFunctionCalls).toBe(1); // No new calls
expect(memoizationCheckCount).toBe(2); // Checked twice
const result3 = await service.fetchData(2);
expect(result3).toBe('Data for ID: 2');
expect(memoizedCalls).toBe(1); // No extra memoized calls yet
expect(totalFunctionCalls).toBe(2); // New call for different ID
expect(memoizationCheckCount).toBe(3); // Three checks (1st, 2nd for ID 1, and 3rd for ID 2)
const result4 = await service.fetchData(2);
expect(result4).toBe('Data for ID: 2');
expect(memoizedCalls).toBe(2); // ID 2 result is now memoized
expect(totalFunctionCalls).toBe(2); // No extra new calls
expect(memoizationCheckCount).toBe(4); // 4 checks in total
// test memoize a throwing async method
memoizationCheckCount = 0;
memoizedCalls = 0;
totalFunctionCalls = 0;
await Promise.all([
expect(service.throwData('akram')).rejects.toThrow('hello akram but I throw!'),
expect(service.throwData('akram')).rejects.toThrow('hello akram but I throw!'),
expect(service.throwData('akram')).rejects.toThrow('hello akram but I throw!')
]);
expect(memoizationCheckCount).toEqual(totalFunctionCalls + memoizedCalls);
expect(memoizedCalls).toEqual(2);
expect(totalFunctionCalls).toBe(1); // No extra new calls
});
});