@@ -202,6 +202,237 @@ describe('FastCache', () => {
202202 } ) ;
203203 } ) ;
204204
205+ describe ( 'sortedSet' , ( ) => {
206+ describe ( 'add' , ( ) => {
207+ test ( '값을 추가하면 정상적으로 저장된다' , async ( ) => {
208+ const sortedSet = cache . sortedSet ( 'hello' ) ;
209+ sortedSet . add ( 100 , 'foo' ) ;
210+ sortedSet . add ( 200 , 'bar' ) ;
211+ sortedSet . add ( 300 , 'baz' ) ;
212+
213+ const result = await sortedSet . range ( { start : 0 , stop : 2 , withScores : true } ) ;
214+ expect ( result ) . toEqual ( [
215+ { value : 'foo' , score : 100 } ,
216+ { value : 'bar' , score : 200 } ,
217+ { value : 'baz' , score : 300 } ,
218+ ] ) ;
219+ } ) ;
220+ } ) ;
221+
222+ describe ( 'addAll' , ( ) => {
223+ test ( '여러 값을 순서와 상관없이 넣어도 정렬된 값으로 저장된다' , async ( ) => {
224+ const sortedSet = cache . sortedSet ( 'hello' ) ;
225+ await sortedSet . addAll ( [
226+ { score : 300 , value : 'baz' } ,
227+ { score : 200 , value : 'bar' } ,
228+ { score : 100 , value : 'foo' } ,
229+ { score : 400 , value : 'qux' } ,
230+ { score : 500 , value : 'quux' } ,
231+ ] ) ;
232+
233+ const result1 = await sortedSet . range ( { start : 0 , stop : 2 , withScores : true } ) ;
234+ const result2 = await sortedSet . range ( { start : 3 , stop : 4 , withScores : true } ) ;
235+
236+ expect ( result1 ) . toEqual ( [
237+ { value : 'foo' , score : 100 } ,
238+ { value : 'bar' , score : 200 } ,
239+ { value : 'baz' , score : 300 } ,
240+ ] ) ;
241+ expect ( result2 ) . toEqual ( [
242+ { value : 'qux' , score : 400 } ,
243+ { value : 'quux' , score : 500 } ,
244+ ] ) ;
245+ } ) ;
246+ } ) ;
247+
248+ describe ( 'remove' , ( ) => {
249+ test ( '값을 삭제하면 해당 값이 사라진다' , async ( ) => {
250+ const sortedSet = cache . sortedSet ( 'hello' ) ;
251+
252+ await sortedSet . addAll ( [
253+ { score : 100 , value : 'foo' } ,
254+ { score : 200 , value : 'bar' } ,
255+ { score : 300 , value : 'baz' } ,
256+ ] ) ;
257+ await sortedSet . remove ( 'foo' ) ;
258+
259+ const result = await sortedSet . range ( { start : 0 , stop : 2 , withScores : true } ) ;
260+
261+ expect ( result ) . toEqual ( [
262+ { value : 'bar' , score : 200 } ,
263+ { value : 'baz' , score : 300 } ,
264+ ] ) ;
265+ } ) ;
266+ } ) ;
267+
268+ describe ( 'range' , ( ) => {
269+ test ( 'score 없이 조회하면 값만 반환된다' , async ( ) => {
270+ const sortedSet = cache . sortedSet ( 'hello' ) ;
271+
272+ await sortedSet . addAll ( [
273+ { score : 100 , value : 'foo' } ,
274+ { score : 200 , value : 'bar' } ,
275+ { score : 300 , value : 'baz' } ,
276+ ] ) ;
277+
278+ const result = await sortedSet . range ( { start : 0 , stop : 2 , withScores : false } ) ;
279+
280+ expect ( result ) . toEqual ( [ 'foo' , 'bar' , 'baz' ] ) ;
281+ } ) ;
282+
283+ test ( 'score와 함께 조회하면 값과 score가 반환된다' , async ( ) => {
284+ const sortedSet = cache . sortedSet ( 'hello' ) ;
285+ await sortedSet . addAll ( [
286+ { score : 100 , value : 'foo' } ,
287+ { score : 200 , value : 'bar' } ,
288+ { score : 300 , value : 'baz' } ,
289+ ] ) ;
290+
291+ const result = await sortedSet . range ( { start : 0 , stop : 2 , withScores : true } ) ;
292+
293+ expect ( result ) . toEqual ( [
294+ { value : 'foo' , score : 100 } ,
295+ { value : 'bar' , score : 200 } ,
296+ { value : 'baz' , score : 300 } ,
297+ ] ) ;
298+ } ) ;
299+
300+ test ( 'reverse 옵션을 주면 역순으로 조회된다' , async ( ) => {
301+ const sortedSet = cache . sortedSet ( 'hello' ) ;
302+ await sortedSet . addAll ( [
303+ { score : 100 , value : 'foo' } ,
304+ { score : 200 , value : 'bar' } ,
305+ { score : 300 , value : 'baz' } ,
306+ ] ) ;
307+
308+ const result = await sortedSet . range ( { start : 0 , stop : 2 , withScores : true , reverse : true } ) ;
309+
310+ expect ( result ) . toEqual ( [
311+ { value : 'baz' , score : 300 } ,
312+ { value : 'bar' , score : 200 } ,
313+ { value : 'foo' , score : 100 } ,
314+ ] ) ;
315+ } ) ;
316+ } ) ;
317+
318+ describe ( 'rangeByScore' , ( ) => {
319+ test ( 'score 없이 score 범위로 조회하면 값만 반환된다' , async ( ) => {
320+ const sortedSet = cache . sortedSet ( 'hello' ) ;
321+ await sortedSet . addAll ( [
322+ { score : 100 , value : 'foo' } ,
323+ { score : 200 , value : 'bar' } ,
324+ { score : 300 , value : 'baz' } ,
325+ ] ) ;
326+ const result = await sortedSet . rangeByScore ( { min : 150 , max : 250 } ) ;
327+ expect ( result ) . toEqual ( [ 'bar' ] ) ;
328+ } ) ;
329+
330+ test ( 'score와 함께 score 범위로 조회하면 값과 score가 반환된다' , async ( ) => {
331+ const sortedSet = cache . sortedSet ( 'hello' ) ;
332+ await sortedSet . addAll ( [
333+ { score : 100 , value : 'foo' } ,
334+ { score : 200 , value : 'bar' } ,
335+ { score : 300 , value : 'baz' } ,
336+ ] ) ;
337+ const result = await sortedSet . rangeByScore ( { min : 150 , max : 250 , withScores : true } ) ;
338+ expect ( result ) . toEqual ( [ { value : 'bar' , score : 200 } ] ) ;
339+ } ) ;
340+ } ) ;
341+
342+ describe ( 'score' , ( ) => {
343+ test ( '특정 값의 score를 조회할 수 있다' , async ( ) => {
344+ const sortedSet = cache . sortedSet ( 'hello' ) ;
345+ await sortedSet . addAll ( [
346+ { score : 100 , value : 'foo' } ,
347+ { score : 200 , value : 'bar' } ,
348+ ] ) ;
349+
350+ const result1 = await sortedSet . score ( 'foo' ) ;
351+ const result2 = await sortedSet . score ( 'bar' ) ;
352+ const result3 = await sortedSet . score ( '__not_found__' ) ;
353+
354+ expect ( result1 ) . toBe ( 100 ) ;
355+ expect ( result2 ) . toBe ( 200 ) ;
356+ expect ( result3 ) . toBeNull ( ) ;
357+ } ) ;
358+ } ) ;
359+
360+ describe ( 'length' , ( ) => {
361+ test ( '전체 값의 개수를 조회할 수 있다' , async ( ) => {
362+ const sortedSet = cache . sortedSet ( 'hello' ) ;
363+ await sortedSet . addAll ( [
364+ { score : 100 , value : 'foo' } ,
365+ { score : 200 , value : 'bar' } ,
366+ { score : 300 , value : 'baz' } ,
367+ ] ) ;
368+
369+ const result = await sortedSet . length ( ) ;
370+
371+ expect ( result ) . toBe ( 3 ) ;
372+ } ) ;
373+ } ) ;
374+
375+ describe ( 'clear' , ( ) => {
376+ test ( '전체 값을 삭제하면 길이가 0이 된다' , async ( ) => {
377+ const sortedSet = cache . sortedSet ( 'hello' ) ;
378+ await sortedSet . addAll ( [
379+ { score : 100 , value : 'foo' } ,
380+ { score : 200 , value : 'bar' } ,
381+ { score : 300 , value : 'baz' } ,
382+ ] ) ;
383+ await sortedSet . clear ( ) ;
384+
385+ const result = await sortedSet . length ( ) ;
386+ expect ( result ) . toBe ( 0 ) ;
387+ } ) ;
388+ } ) ;
389+
390+ describe ( 'replaceAll' , ( ) => {
391+ test ( '전체 값을 새로운 값으로 교체할 수 있다' , async ( ) => {
392+ const sortedSet = cache . sortedSet ( 'hello' ) ;
393+ await sortedSet . addAll ( [
394+ { score : 100 , value : 'foo' } ,
395+ { score : 200 , value : 'bar' } ,
396+ { score : 300 , value : 'baz' } ,
397+ ] ) ;
398+ const newEntries = [
399+ { score : 400 , value : 'qux' } ,
400+ { score : 500 , value : 'quux' } ,
401+ ] ;
402+
403+ await sortedSet . replaceAll ( newEntries ) ;
404+
405+ const [ values , tempKeys ] = await Promise . all ( [
406+ sortedSet . range ( { start : 0 , stop : - 1 , withScores : true } ) ,
407+ client . keys ( 'hello:temp:*' ) ,
408+ ] ) ;
409+
410+ expect ( values ) . toEqual ( [
411+ { value : 'qux' , score : 400 } ,
412+ { value : 'quux' , score : 500 } ,
413+ ] ) ;
414+ // tempKeys는 존재하지 않아야 한다.
415+ expect ( tempKeys ) . toHaveLength ( 0 ) ;
416+ } ) ;
417+
418+ test ( 'score가 number가 아니면 예외가 발생하고 임시키가 정리된다' , async ( ) => {
419+ const sortedSet = cache . sortedSet ( 'hello' ) ;
420+ const invalidEntries = [ { score : 'invalid' as any , value : 'qux' } ] ;
421+
422+ await expect ( sortedSet . replaceAll ( invalidEntries ) ) . rejects . toThrow ( 'score is not a number' ) ;
423+
424+ const [ tempKeys , values ] = await Promise . all ( [
425+ client . keys ( 'hello:temp:*' ) ,
426+ sortedSet . range ( { start : 0 , stop : - 1 } ) ,
427+ ] ) ;
428+
429+ // tempKeys는 존재하지 않아야 한다.
430+ expect ( tempKeys ) . toHaveLength ( 0 ) ;
431+ expect ( values ) . toHaveLength ( 0 ) ;
432+ } ) ;
433+ } ) ;
434+ } ) ;
435+
205436 describe ( 'withCache' , ( ) => {
206437 test ( 'should be set after next tick' , ( done ) => {
207438 const a = { foo : 100 } ;
0 commit comments