Skip to content

Commit 5f69499

Browse files
committed
lru cache tests
1 parent 260b5e4 commit 5f69499

File tree

5 files changed

+134
-315
lines changed

5 files changed

+134
-315
lines changed

lib/tests/testUtils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ export const exhaustMicrotasks = async (loop = 100) => {
33
await Promise.resolve();
44
}
55
};
6+
7+
export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* Copyright 2024, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { expect, describe, it } from 'vitest';
17+
import { InMemoryLruCache } from './in_memory_lru_cache';
18+
import { wait } from '../../tests/testUtils';
19+
20+
describe('InMemoryLruCache', () => {
21+
it('should save and get values correctly', () => {
22+
const cache = new InMemoryLruCache<number>(2);
23+
cache.set('a', 1);
24+
cache.set('b', 2);
25+
expect(cache.get('a')).toBe(1);
26+
expect(cache.get('b')).toBe(2);
27+
});
28+
29+
it('should return undefined for non-existent keys', () => {
30+
const cache = new InMemoryLruCache<number>(2);
31+
expect(cache.get('a')).toBe(undefined);
32+
});
33+
34+
it('should return all keys in cache when getKeys is called', () => {
35+
const cache = new InMemoryLruCache<number>(20);
36+
cache.set('a', 1);
37+
cache.set('b', 2);
38+
cache.set('c', 3);
39+
cache.set('d', 4);
40+
expect(cache.getKeys()).toEqual(expect.arrayContaining(['d', 'c', 'b', 'a']));
41+
});
42+
43+
it('should evict least recently used keys when full', () => {
44+
const cache = new InMemoryLruCache<number>(3);
45+
cache.set('a', 1);
46+
cache.set('b', 2);
47+
cache.set('c', 3);
48+
49+
expect(cache.get('b')).toBe(2);
50+
expect(cache.get('c')).toBe(3);
51+
expect(cache.get('a')).toBe(1);
52+
expect(cache.getKeys()).toEqual(expect.arrayContaining(['a', 'c', 'b']));
53+
54+
// key use order is now a c b. next insert should evict b
55+
cache.set('d', 4);
56+
expect(cache.get('b')).toBe(undefined);
57+
expect(cache.getKeys()).toEqual(expect.arrayContaining(['d', 'a', 'c']));
58+
59+
// key use order is now d a c. setting c should put it at the front
60+
cache.set('c', 5);
61+
62+
// key use order is now c d a. next insert should evict a
63+
cache.set('e', 6);
64+
expect(cache.get('a')).toBe(undefined);
65+
expect(cache.getKeys()).toEqual(expect.arrayContaining(['e', 'c', 'd']));
66+
67+
// key use order is now e c d. reading d should put it at the front
68+
expect(cache.get('d')).toBe(4);
69+
70+
// key use order is now d e c. next insert should evict c
71+
cache.set('f', 7);
72+
expect(cache.get('c')).toBe(undefined);
73+
expect(cache.getKeys()).toEqual(expect.arrayContaining(['f', 'd', 'e']));
74+
});
75+
76+
it('should not return expired values when get is called', async () => {
77+
const cache = new InMemoryLruCache<number>(2, 100);
78+
cache.set('a', 1);
79+
cache.set('b', 2);
80+
expect(cache.get('a')).toBe(1);
81+
expect(cache.get('b')).toBe(2);
82+
83+
await wait(150);
84+
expect(cache.get('a')).toBe(undefined);
85+
expect(cache.get('b')).toBe(undefined);
86+
});
87+
88+
it('should remove values correctly', () => {
89+
const cache = new InMemoryLruCache<number>(2);
90+
cache.set('a', 1);
91+
cache.set('b', 2);
92+
cache.set('c', 3);
93+
cache.remove('a');
94+
expect(cache.get('a')).toBe(undefined);
95+
expect(cache.get('b')).toBe(2);
96+
expect(cache.get('c')).toBe(3);
97+
});
98+
99+
it('should clear all values correctly', () => {
100+
const cache = new InMemoryLruCache<number>(2);
101+
cache.set('a', 1);
102+
cache.set('b', 2);
103+
cache.clear();
104+
expect(cache.get('a')).toBe(undefined);
105+
expect(cache.get('b')).toBe(undefined);
106+
});
107+
108+
it('should return correct values when getBatched is called', () => {
109+
const cache = new InMemoryLruCache<number>(2);
110+
cache.set('a', 1);
111+
cache.set('b', 2);
112+
expect(cache.getBatched(['a', 'b', 'c'])).toEqual([1, 2, undefined]);
113+
});
114+
115+
it('should return correct values when getBatched is called', () => {
116+
const cache = new InMemoryLruCache<number>(2);
117+
cache.set('a', 1);
118+
cache.set('b', 2);
119+
expect(cache.getBatched(['a', 'b', 'c'])).toEqual([1, 2, undefined]);
120+
});
121+
122+
it('should not return expired values when getBatched is called', async () => {
123+
const cache = new InMemoryLruCache<number>(2, 100);
124+
cache.set('a', 1);
125+
cache.set('b', 2);
126+
expect(cache.getBatched(['a', 'b'])).toEqual([1, 2]);
127+
128+
await wait(150);
129+
expect(cache.getBatched(['a', 'b'])).toEqual([undefined, undefined]);
130+
});
131+
});

lib/utils/cache/in_memory_lru_cache.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ export class InMemoryLruCache<V> implements SyncCache<V> {
3535

3636
get(key: string): Maybe<V> {
3737
const element = this.data.get(key);
38-
3938
if (!element) return undefined;
4039
this.data.delete(key);
4140

@@ -76,8 +75,4 @@ export class InMemoryLruCache<V> implements SyncCache<V> {
7675
getBatched(keys: string[]): Maybe<V>[] {
7776
return keys.map((key) => this.get(key));
7877
}
79-
80-
peek(key: string): Maybe<V> {
81-
return this.data.get(key)?.value;
82-
}
8378
}

0 commit comments

Comments
 (0)