1
+ import { CACHE_KEY , Cache , CacheKey } from "../../lib/cache/cache.js" ;
2
+ import { UnitOfTime } from "../../lib/util/types.js" ;
3
+
4
+ describe ( 'Cache Decorator' , ( ) => {
5
+ it ( 'should perform NO-OP if CacheKey has not injected a value' , async ( ) => {
6
+ class Test {
7
+ constructor ( private spy : jest . Mock ) { }
8
+
9
+ @Cache ( )
10
+ public missesOutterDecorator ( ) : number {
11
+ this . spy ( ) ;
12
+ return 1
13
+ }
14
+ }
15
+
16
+ const callRecorder = jest . fn ( ) ;
17
+ const target = new Test ( callRecorder ) ;
18
+
19
+ expect ( target . missesOutterDecorator ( ) ) . toEqual ( 1 ) ;
20
+ expect ( target . missesOutterDecorator ( ) ) . toEqual ( 1 ) ;
21
+
22
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 2 ) ;
23
+ } ) ;
24
+
25
+ describe ( 'Sync Method' , ( ) => {
26
+ it ( 'should cache successfull calls for a time' , async ( ) => {
27
+ class Test {
28
+ constructor ( private spy : jest . Mock ) { }
29
+
30
+ @CacheKey ( 1 )
31
+ @Cache ( { ttl : 5 , unit : UnitOfTime . Millisecond } )
32
+ public expensiveCall ( account : string , key : string ) : number {
33
+ this . spy ( key ) ;
34
+ return 1
35
+ }
36
+ }
37
+
38
+ const callRecorder = jest . fn ( ) ;
39
+ const target = new Test ( callRecorder ) ;
40
+
41
+ expect ( target . expensiveCall ( '123456' , 'cache' ) ) . toEqual ( 1 ) ;
42
+ expect ( target . expensiveCall ( '123456' , 'cache' ) ) . toEqual ( 1 ) ;
43
+ expect ( target . expensiveCall ( '123456' , 'cacher' ) ) . toEqual ( 1 ) ;
44
+
45
+ expect ( callRecorder ) . toHaveBeenNthCalledWith ( 1 , 'cache' ) ;
46
+ expect ( callRecorder ) . toHaveBeenNthCalledWith ( 2 , 'cacher' ) ;
47
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 2 ) ;
48
+ } ) ;
49
+
50
+ it ( 'should call method if cached value is expired' , async ( ) => {
51
+ class Test {
52
+ constructor ( private spy : jest . Mock ) { }
53
+
54
+ @CacheKey ( 0 )
55
+ @Cache ( { ttl : 5 , unit : UnitOfTime . Millisecond } )
56
+ public expensiveCall ( account : string ) : number {
57
+ this . spy ( account ) ;
58
+ return 1
59
+ }
60
+ }
61
+
62
+ const callRecorder = jest . fn ( ) ;
63
+ const target = new Test ( callRecorder ) ;
64
+
65
+ expect ( target . expensiveCall ( '123456' ) ) . toEqual ( 1 ) ;
66
+ expect ( target . expensiveCall ( '123456' ) ) . toEqual ( 1 ) ;
67
+
68
+ await new Promise ( resolve => setTimeout ( resolve , 10 ) ) ;
69
+
70
+ expect ( target . expensiveCall ( '123456' ) ) . toEqual ( 1 ) ;
71
+ expect ( target . expensiveCall ( '123456' ) ) . toEqual ( 1 ) ;
72
+
73
+ expect ( callRecorder ) . toHaveBeenCalledWith ( '123456' ) ;
74
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 2 ) ;
75
+ } ) ;
76
+
77
+ it ( 'should not cache errors' , async ( ) => {
78
+ class Test {
79
+ constructor ( private spy : jest . Mock ) { }
80
+
81
+ @CacheKey ( 0 )
82
+ @Cache ( { ttl : 5 , unit : UnitOfTime . Millisecond } )
83
+ public expensiveCall ( account : string ) : number {
84
+ this . spy ( account ) ;
85
+ throw new Error ( 'Oops' ) ;
86
+ }
87
+ }
88
+
89
+ const callRecorder = jest . fn ( ) ;
90
+ const target = new Test ( callRecorder ) ;
91
+
92
+ expect ( ( ) => target . expensiveCall ( '123456' ) ) . toThrow ( 'Oops' ) ;
93
+ expect ( ( ) => target . expensiveCall ( '123456' ) ) . toThrow ( 'Oops' ) ;
94
+
95
+ expect ( callRecorder ) . toHaveBeenCalledWith ( '123456' ) ;
96
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 2 ) ;
97
+ } ) ;
98
+ } ) ;
99
+
100
+ describe ( 'Async Method' , ( ) => {
101
+ it ( 'should cache successfull calls for a time' , async ( ) => {
102
+ class Test {
103
+ constructor ( private spy : jest . Mock ) { }
104
+
105
+ @CacheKey ( 1 )
106
+ @Cache ( { ttl : 5 , unit : UnitOfTime . Millisecond } )
107
+ public async expensiveCall ( account : string , key : string ) : Promise < number > {
108
+ this . spy ( key ) ;
109
+ return 1
110
+ }
111
+ }
112
+
113
+ const callRecorder = jest . fn ( ) ;
114
+ const target = new Test ( callRecorder ) ;
115
+
116
+ await expect ( target . expensiveCall ( '123456' , 'cache' ) ) . resolves . toEqual ( 1 ) ;
117
+ await expect ( target . expensiveCall ( '123456' , 'cache' ) ) . resolves . toEqual ( 1 ) ;
118
+ await expect ( target . expensiveCall ( '123456' , 'cacher' ) ) . resolves . toEqual ( 1 ) ;
119
+
120
+ expect ( callRecorder ) . toHaveBeenNthCalledWith ( 1 , 'cache' ) ;
121
+ expect ( callRecorder ) . toHaveBeenNthCalledWith ( 2 , 'cacher' ) ;
122
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 2 ) ;
123
+ } ) ;
124
+
125
+ it ( 'should call method if cached value is expired' , async ( ) => {
126
+ class Test {
127
+ constructor ( private spy : jest . Mock ) { }
128
+
129
+ @CacheKey ( 0 )
130
+ @Cache ( { ttl : 5 , unit : UnitOfTime . Millisecond } )
131
+ public async expensiveCall ( account : string ) : Promise < number > {
132
+ this . spy ( account ) ;
133
+ return 1
134
+ }
135
+ }
136
+
137
+ const callRecorder = jest . fn ( ) ;
138
+ const target = new Test ( callRecorder ) ;
139
+
140
+ await expect ( target . expensiveCall ( '123456' ) ) . resolves . toEqual ( 1 ) ;
141
+ await expect ( target . expensiveCall ( '123456' ) ) . resolves . toEqual ( 1 ) ;
142
+
143
+ await new Promise ( resolve => setTimeout ( resolve , 10 ) ) ;
144
+
145
+ await expect ( target . expensiveCall ( '123456' ) ) . resolves . toEqual ( 1 ) ;
146
+ await expect ( target . expensiveCall ( '123456' ) ) . resolves . toEqual ( 1 ) ;
147
+
148
+ expect ( callRecorder ) . toHaveBeenCalledWith ( '123456' ) ;
149
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 2 ) ;
150
+ } ) ;
151
+
152
+ it ( 'should not cache errors' , async ( ) => {
153
+ class Test {
154
+ constructor ( private spy : jest . Mock ) { }
155
+
156
+ @CacheKey ( 0 )
157
+ @Cache ( { ttl : 5 , unit : UnitOfTime . Millisecond } )
158
+ public async expensiveCall ( account : string ) : Promise < number > {
159
+ this . spy ( account ) ;
160
+ throw new Error ( 'Oops' ) ;
161
+ }
162
+ }
163
+
164
+ const callRecorder = jest . fn ( ) ;
165
+ const target = new Test ( callRecorder ) ;
166
+
167
+ await expect ( target . expensiveCall ( '123456' ) ) . rejects . toThrow ( 'Oops' ) ;
168
+ await expect ( target . expensiveCall ( '123456' ) ) . rejects . toThrow ( 'Oops' ) ;
169
+
170
+ expect ( callRecorder ) . toHaveBeenCalledWith ( '123456' ) ;
171
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 2 ) ;
172
+ } ) ;
173
+ } ) ;
174
+ } ) ;
175
+
176
+ describe ( 'Cache Key Decorator' , ( ) => {
177
+ test ( 'should inject a valid string parameter into call to @Cache' , async ( ) => {
178
+ class Test {
179
+ constructor ( private spy : jest . Mock ) { }
180
+
181
+ @CacheKey ( 0 )
182
+ public ಠ_ಠ ( ...args : unknown [ ] ) : string {
183
+ this . spy ( ...args ) ;
184
+ return 'Hello'
185
+ }
186
+ }
187
+
188
+ const callRecorder = jest . fn ( ) ;
189
+ const target = new Test ( callRecorder ) ;
190
+ const result = target . ಠ_ಠ ( 'Hello' ) ;
191
+
192
+ expect ( result ) . toEqual ( 'Hello' )
193
+
194
+ expect ( callRecorder ) . toHaveBeenCalledWith ( { [ CACHE_KEY ] : 'Hello' } , 'Hello' ) ;
195
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 1 ) ;
196
+ } ) ;
197
+
198
+ test ( 'should perform NO-OP if decorated target is not @Cache' , async ( ) => {
199
+ class Test {
200
+ constructor ( private spy : jest . Mock ) { }
201
+
202
+ @CacheKey ( 0 )
203
+ public notCache ( args : string ) : string {
204
+ this . spy ( args ) ;
205
+ return 'Hello'
206
+ }
207
+ }
208
+
209
+ const callRecorder = jest . fn ( ) ;
210
+ const target = new Test ( callRecorder ) ;
211
+ const result = target . notCache ( 'Hello' ) ;
212
+
213
+ expect ( result ) . toEqual ( 'Hello' )
214
+ expect ( callRecorder ) . toHaveBeenCalledWith ( 'Hello' ) ;
215
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 1 ) ;
216
+ } ) ;
217
+
218
+ test ( 'should perform NO-OP if configured position is smaller than 0' , async ( ) => {
219
+ class Test {
220
+ constructor ( private spy : jest . Mock ) { }
221
+
222
+ @CacheKey ( - 1 )
223
+ public ಠ_ಠ ( args : string ) : string {
224
+ this . spy ( args ) ;
225
+ return 'Hello'
226
+ }
227
+ }
228
+
229
+ const callRecorder = jest . fn ( ) ;
230
+ const target = new Test ( callRecorder ) ;
231
+ const result = target . ಠ_ಠ ( 'Hello' ) ;
232
+
233
+ expect ( result ) . toEqual ( 'Hello' )
234
+ expect ( callRecorder ) . toHaveBeenCalledWith ( 'Hello' ) ;
235
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 1 ) ;
236
+ } ) ;
237
+
238
+ test ( 'should perform NO-OP if configured position is larger than arguments' , async ( ) => {
239
+ class Test {
240
+ constructor ( private spy : jest . Mock ) { }
241
+
242
+ @CacheKey ( 2 )
243
+ public ಠ_ಠ ( args : string ) : string {
244
+ this . spy ( args ) ;
245
+ return 'Hello'
246
+ }
247
+ }
248
+
249
+ const callRecorder = jest . fn ( ) ;
250
+ const target = new Test ( callRecorder ) ;
251
+ const result = target . ಠ_ಠ ( 'Hello' ) ;
252
+
253
+ expect ( result ) . toEqual ( 'Hello' )
254
+ expect ( callRecorder ) . toHaveBeenCalledWith ( 'Hello' ) ;
255
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 1 ) ;
256
+ } ) ;
257
+
258
+ test ( 'should perform NO-OP if configured postion belongs to none string argument' , async ( ) => {
259
+ class Test {
260
+ constructor ( private spy : jest . Mock ) { }
261
+
262
+ @CacheKey ( 0 )
263
+ public ಠ_ಠ ( args : number ) : string {
264
+ this . spy ( args ) ;
265
+ return 'Hello'
266
+ }
267
+ }
268
+
269
+ const callRecorder = jest . fn ( ) ;
270
+ const target = new Test ( callRecorder ) ;
271
+ const result = target . ಠ_ಠ ( 12 ) ;
272
+
273
+ expect ( result ) . toEqual ( 'Hello' )
274
+ expect ( callRecorder ) . toHaveBeenCalledWith ( 12 ) ;
275
+ expect ( callRecorder ) . toHaveBeenCalledTimes ( 1 ) ;
276
+ } ) ;
277
+ } ) ;
0 commit comments