Skip to content

Commit 8aad681

Browse files
committed
test: add lru tests
Signed-off-by: Giovanni De Giorgio <[email protected]>
1 parent bd33c58 commit 8aad681

File tree

4 files changed

+253
-7
lines changed

4 files changed

+253
-7
lines changed
Lines changed: 182 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { SSMClient, SSMClientConfig } from '@aws-sdk/client-ssm';
22
import { AwsSsmProvider } from './aws-ssm-provider';
3+
import { StandardResolutionReasons } from '@openfeature/core';
4+
import { Cache } from './cache';
35

46
const MOCK_SSM_CLIENT_CONFIG: SSMClientConfig = {
57
region: 'us-east-1',
@@ -9,8 +11,186 @@ const MOCK_SSM_CLIENT_CONFIG: SSMClientConfig = {
911
},
1012
};
1113

14+
const provider: AwsSsmProvider = new AwsSsmProvider({
15+
ssmClientConfig: MOCK_SSM_CLIENT_CONFIG,
16+
cacheOpts: {
17+
enabled: true,
18+
ttl: 1000,
19+
size: 100,
20+
},
21+
});
22+
1223
describe(AwsSsmProvider.name, () => {
13-
it('should pass', () => {
14-
expect(true);
24+
describe(AwsSsmProvider.prototype.resolveBooleanEvaluation.name, () => {
25+
beforeEach(() => {
26+
jest.clearAllMocks();
27+
});
28+
describe('when flag is cached', () => {
29+
afterAll(() => {
30+
provider.cache.clear();
31+
});
32+
it('should return cached value', async () => {
33+
provider.cache.set('test', {
34+
value: true,
35+
reason: StandardResolutionReasons.STATIC,
36+
});
37+
await expect(provider.resolveBooleanEvaluation('test', false, {})).resolves.toEqual({
38+
value: true,
39+
reason: StandardResolutionReasons.CACHED,
40+
});
41+
});
42+
});
43+
describe('when flag is not cached', () => {
44+
describe('when getBooleanValue rejects', () => {
45+
it('should return default value', async () => {
46+
jest.spyOn(provider.service, 'getBooleanValue').mockRejectedValue(new Error());
47+
await expect(provider.resolveBooleanEvaluation('test', false, {})).resolves.toEqual({
48+
value: false,
49+
reason: StandardResolutionReasons.DEFAULT,
50+
});
51+
});
52+
});
53+
describe('when getBooleanValue resolves', () => {
54+
it('should resolve with expected value', async () => {
55+
jest.spyOn(provider.service, 'getBooleanValue').mockResolvedValue({
56+
value: true,
57+
reason: StandardResolutionReasons.STATIC,
58+
});
59+
await expect(provider.resolveBooleanEvaluation('test', false, {})).resolves.toEqual({
60+
value: true,
61+
reason: StandardResolutionReasons.STATIC,
62+
});
63+
});
64+
});
65+
});
66+
});
67+
describe(AwsSsmProvider.prototype.resolveStringEvaluation.name, () => {
68+
beforeEach(() => {
69+
jest.clearAllMocks();
70+
});
71+
describe('when flag is cached', () => {
72+
afterAll(() => {
73+
provider.cache.clear();
74+
});
75+
it('should return cached value', async () => {
76+
provider.cache.set('test', {
77+
value: 'somestring',
78+
reason: StandardResolutionReasons.STATIC,
79+
});
80+
await expect(provider.resolveStringEvaluation('test', 'default', {})).resolves.toEqual({
81+
value: 'somestring',
82+
reason: StandardResolutionReasons.CACHED,
83+
});
84+
});
85+
});
86+
describe('when flag is not cached', () => {
87+
describe('when getStringValue rejects', () => {
88+
it('should return default value', async () => {
89+
jest.spyOn(provider.service, 'getStringValue').mockRejectedValue(new Error());
90+
await expect(provider.resolveStringEvaluation('test', 'default', {})).resolves.toEqual({
91+
value: 'default',
92+
reason: StandardResolutionReasons.DEFAULT,
93+
});
94+
});
95+
});
96+
describe('when getStringValue resolves', () => {
97+
it('should resolve with expected value', async () => {
98+
jest.spyOn(provider.service, 'getStringValue').mockResolvedValue({
99+
value: 'somestring',
100+
reason: StandardResolutionReasons.STATIC,
101+
});
102+
await expect(provider.resolveStringEvaluation('test', 'default', {})).resolves.toEqual({
103+
value: 'somestring',
104+
reason: StandardResolutionReasons.STATIC,
105+
});
106+
});
107+
});
108+
});
109+
});
110+
describe(AwsSsmProvider.prototype.resolveNumberEvaluation.name, () => {
111+
beforeEach(() => {
112+
jest.clearAllMocks();
113+
});
114+
describe('when flag is cached', () => {
115+
afterAll(() => {
116+
provider.cache.clear();
117+
});
118+
it('should return cached value', async () => {
119+
provider.cache.set('test', {
120+
value: 489,
121+
reason: StandardResolutionReasons.STATIC,
122+
});
123+
await expect(provider.resolveNumberEvaluation('test', -1, {})).resolves.toEqual({
124+
value: 489,
125+
reason: StandardResolutionReasons.CACHED,
126+
});
127+
});
128+
});
129+
describe('when flag is not cached', () => {
130+
describe('when getNumberValue rejects', () => {
131+
it('should return default value', async () => {
132+
jest.spyOn(provider.service, 'getNumberValue').mockRejectedValue(new Error());
133+
await expect(provider.resolveNumberEvaluation('test', -1, {})).resolves.toEqual({
134+
value: -1,
135+
reason: StandardResolutionReasons.DEFAULT,
136+
});
137+
});
138+
});
139+
describe('when getNumberValue resolves', () => {
140+
it('should resolve with expected value', async () => {
141+
jest.spyOn(provider.service, 'getNumberValue').mockResolvedValue({
142+
value: 489,
143+
reason: StandardResolutionReasons.STATIC,
144+
});
145+
await expect(provider.resolveNumberEvaluation('test', -1, {})).resolves.toEqual({
146+
value: 489,
147+
reason: StandardResolutionReasons.STATIC,
148+
});
149+
});
150+
});
151+
});
152+
});
153+
describe(AwsSsmProvider.prototype.resolveObjectEvaluation.name, () => {
154+
beforeEach(() => {
155+
jest.clearAllMocks();
156+
});
157+
describe('when flag is cached', () => {
158+
afterAll(() => {
159+
provider.cache.clear();
160+
});
161+
it('should return cached value', async () => {
162+
provider.cache.set('test', {
163+
value: { default: false },
164+
reason: StandardResolutionReasons.STATIC,
165+
});
166+
await expect(provider.resolveObjectEvaluation('test', { default: true }, {})).resolves.toEqual({
167+
value: { default: false },
168+
reason: StandardResolutionReasons.CACHED,
169+
});
170+
});
171+
});
172+
describe('when flag is not cached', () => {
173+
describe('when getObjectValue rejects', () => {
174+
it('should return default value', async () => {
175+
jest.spyOn(provider.service, 'getObjectValue').mockRejectedValue(new Error());
176+
await expect(provider.resolveObjectEvaluation('test', { default: true }, {})).resolves.toEqual({
177+
value: { default: true },
178+
reason: StandardResolutionReasons.DEFAULT,
179+
});
180+
});
181+
});
182+
describe('when getObjectValue resolves', () => {
183+
it('should resolve with expected value', async () => {
184+
jest.spyOn(provider.service, 'getObjectValue').mockResolvedValue({
185+
value: { default: true },
186+
reason: StandardResolutionReasons.STATIC,
187+
});
188+
await expect(provider.resolveObjectEvaluation('test', -1, {})).resolves.toEqual({
189+
value: { default: true },
190+
reason: StandardResolutionReasons.STATIC,
191+
});
192+
});
193+
});
194+
});
15195
});
16196
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Cache } from './cache';
2+
3+
describe(Cache.name, () => {
4+
describe(Cache.prototype.get.name, () => {
5+
describe('when cache is disabled', () => {
6+
it('should return undefined', () => {
7+
const cache = new Cache({ enabled: false, size: 1, ttl: 1 });
8+
expect(cache.get('test')).toBeUndefined();
9+
});
10+
});
11+
describe('when cache is enabled', () => {
12+
describe('when key is not in cache', () => {
13+
it('should return undefined', () => {
14+
const cache = new Cache({ enabled: true, size: 1, ttl: 1 });
15+
expect(cache.get('test')).toBeUndefined();
16+
});
17+
});
18+
describe('when key is in cache', () => {
19+
it('should return the value', () => {
20+
const cache = new Cache({ enabled: true, size: 1, ttl: 1 });
21+
cache.set('test', { value: true, reason: 'test' });
22+
expect(cache.get('test')).toEqual({ value: true, reason: 'test' });
23+
});
24+
});
25+
});
26+
});
27+
describe(Cache.prototype.set.name, () => {
28+
describe('when cache is disabled', () => {
29+
it('should not set the value', () => {
30+
const spy = jest.spyOn(Cache.prototype, 'set');
31+
expect(spy).not.toHaveBeenCalled();
32+
});
33+
});
34+
describe('when cache is enabled', () => {
35+
it('should set the value', () => {
36+
const cache = new Cache({ enabled: true, size: 1, ttl: 1 });
37+
cache.set('test', { value: true, reason: 'test' });
38+
expect(cache.get('test')).toEqual({ value: true, reason: 'test' });
39+
});
40+
});
41+
});
42+
43+
describe(Cache.prototype.clear.name, () => {
44+
describe('when cache is disabled', () => {
45+
it('should not clear the cache', () => {
46+
const spy = jest.spyOn(Cache.prototype, 'clear');
47+
expect(spy).not.toHaveBeenCalled();
48+
});
49+
});
50+
describe('when cache is enabled', () => {
51+
it('should clear the cache', () => {
52+
const cache = new Cache({ enabled: true, size: 1, ttl: 1 });
53+
cache.set('test', { value: true, reason: 'test' });
54+
cache.clear();
55+
expect(cache.get('test')).toBeUndefined();
56+
});
57+
});
58+
});
59+
});

libs/providers/aws-ssm/src/lib/cache.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { ResolutionDetails } from '@openfeature/core';
2-
import { LRUCacheOpts } from './types';
2+
import { LRUCacheConfig } from './types';
33
import { LRUCache } from 'lru-cache';
44

55
export class Cache {
66
private cache: LRUCache<string, ResolutionDetails<any>>;
77
private ttl: number;
88
private enabled: boolean;
9-
constructor(opts: LRUCacheOpts) {
9+
constructor(opts: LRUCacheConfig) {
1010
this.cache = new LRUCache({
1111
maxSize: opts.size,
1212
sizeCalculation: () => 1,
@@ -26,6 +26,13 @@ export class Cache {
2626
if (!this.enabled) {
2727
return;
2828
}
29-
this.cache.set(key, value);
29+
this.cache.set(key, value, { ttl: this.ttl });
30+
}
31+
32+
clear() {
33+
if (!this.enabled) {
34+
return;
35+
}
36+
this.cache.clear();
3037
}
3138
}

libs/providers/aws-ssm/src/lib/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { LRUCache } from 'lru-cache';
33

44
export type AwsSsmProviderConfig = {
55
ssmClientConfig: SSMClientConfig;
6-
cacheOpts: LRUCacheOpts;
6+
cacheOpts: LRUCacheConfig;
77
};
88

9-
export type LRUCacheOpts = {
9+
export type LRUCacheConfig = {
1010
enabled: boolean;
1111
ttl: number;
1212
size: number;

0 commit comments

Comments
 (0)