Skip to content

Commit 9e5eb13

Browse files
test(api): add unit tests for flagKeys handler with 100% coverage
- Add comprehensive test suite for flagKeys.js - Test both POST and GET endpoints - Cover error cases and input validation - Achieve 100% code coverage for flagKeys.js
1 parent 59a340d commit 9e5eb13

File tree

1 file changed

+219
-0
lines changed

1 file changed

+219
-0
lines changed

src/_api_/handlers/flagKeys.test.js

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { handleFlagKeys, handleGetFlagKeys } from './flagKeys';
3+
import { AbstractRequest } from '../../_helpers_/abstraction-classes/abstractRequest';
4+
5+
describe('flagKeys handlers', () => {
6+
let mockRequest;
7+
let mockAbstractionHelper;
8+
let mockKvStore;
9+
let mockLogger;
10+
let mockDefaultSettings;
11+
12+
beforeEach(() => {
13+
mockRequest = new Request('https://example.com/v1/api/flag_keys');
14+
mockAbstractionHelper = {
15+
abstractRequest: {
16+
getHttpMethodFromRequest: vi.fn(),
17+
},
18+
createResponse: vi.fn((body, status = 200, headers = {}) => new Response(JSON.stringify(body), { status, headers })),
19+
};
20+
mockKvStore = {
21+
get: vi.fn(),
22+
put: vi.fn(),
23+
};
24+
mockLogger = {
25+
debug: vi.fn(),
26+
debugExt: vi.fn(),
27+
error: vi.fn(),
28+
};
29+
mockDefaultSettings = {
30+
kv_key_optly_flagKeys: 'optly_flagKeys',
31+
};
32+
33+
// Mock the static method
34+
vi.spyOn(AbstractRequest, 'readRequestBody').mockImplementation(() => Promise.resolve({}));
35+
});
36+
37+
describe('handleFlagKeys (POST)', () => {
38+
beforeEach(() => {
39+
mockAbstractionHelper.abstractRequest.getHttpMethodFromRequest.mockReturnValue('POST');
40+
});
41+
42+
it('should return 405 for non-POST requests', async () => {
43+
mockAbstractionHelper.abstractRequest.getHttpMethodFromRequest.mockReturnValue('GET');
44+
45+
const response = await handleFlagKeys(
46+
mockRequest,
47+
mockAbstractionHelper,
48+
mockKvStore,
49+
mockLogger,
50+
mockDefaultSettings,
51+
);
52+
53+
expect(mockAbstractionHelper.createResponse).toHaveBeenCalledWith('Method Not Allowed', 405);
54+
});
55+
56+
it('should return 400 for invalid flag keys array', async () => {
57+
AbstractRequest.readRequestBody.mockResolvedValue({ flagKeys: [] });
58+
59+
const response = await handleFlagKeys(
60+
mockRequest,
61+
mockAbstractionHelper,
62+
mockKvStore,
63+
mockLogger,
64+
mockDefaultSettings,
65+
);
66+
67+
expect(mockAbstractionHelper.createResponse).toHaveBeenCalledWith('Expected an array of Flag Keys', 400);
68+
});
69+
70+
it('should handle valid flag keys successfully', async () => {
71+
const mockFlagKeys = ['flag1', 'flag2', 'flag3'];
72+
AbstractRequest.readRequestBody.mockResolvedValue({ flagKeys: mockFlagKeys });
73+
74+
await handleFlagKeys(
75+
mockRequest,
76+
mockAbstractionHelper,
77+
mockKvStore,
78+
mockLogger,
79+
mockDefaultSettings,
80+
);
81+
82+
expect(mockKvStore.put).toHaveBeenCalledWith('optly_flagKeys', 'flag1,flag2,flag3');
83+
expect(mockAbstractionHelper.createResponse).toHaveBeenCalledWith(
84+
{
85+
message: 'Flag keys were updated successfully in the KV store.',
86+
flagKeys: mockFlagKeys,
87+
},
88+
200,
89+
{ 'Content-Type': 'application/json' },
90+
);
91+
});
92+
93+
it('should trim flag keys before storing', async () => {
94+
const mockFlagKeys = [' flag1 ', ' flag2', 'flag3 '];
95+
AbstractRequest.readRequestBody.mockResolvedValue({ flagKeys: mockFlagKeys });
96+
97+
await handleFlagKeys(
98+
mockRequest,
99+
mockAbstractionHelper,
100+
mockKvStore,
101+
mockLogger,
102+
mockDefaultSettings,
103+
);
104+
105+
expect(mockKvStore.put).toHaveBeenCalledWith('optly_flagKeys', 'flag1,flag2,flag3');
106+
});
107+
108+
it('should handle errors gracefully', async () => {
109+
const errorMessage = 'Test error';
110+
mockKvStore.put.mockRejectedValue(new Error(errorMessage));
111+
AbstractRequest.readRequestBody.mockResolvedValue({ flagKeys: ['flag1'] });
112+
113+
await handleFlagKeys(
114+
mockRequest,
115+
mockAbstractionHelper,
116+
mockKvStore,
117+
mockLogger,
118+
mockDefaultSettings,
119+
);
120+
121+
expect(mockLogger.error).toHaveBeenCalled();
122+
expect(mockAbstractionHelper.createResponse).toHaveBeenCalledWith(
123+
`Error: ${errorMessage}`,
124+
500,
125+
);
126+
});
127+
});
128+
129+
describe('handleGetFlagKeys (GET)', () => {
130+
beforeEach(() => {
131+
mockAbstractionHelper.abstractRequest.getHttpMethodFromRequest.mockReturnValue('GET');
132+
});
133+
134+
it('should return 405 for non-GET requests', async () => {
135+
mockAbstractionHelper.abstractRequest.getHttpMethodFromRequest.mockReturnValue('POST');
136+
137+
await handleGetFlagKeys(
138+
mockRequest,
139+
mockAbstractionHelper,
140+
mockKvStore,
141+
mockLogger,
142+
mockDefaultSettings,
143+
);
144+
145+
expect(mockAbstractionHelper.createResponse).toHaveBeenCalledWith('Method Not Allowed', 405);
146+
});
147+
148+
it('should return 404 when no flag keys are found', async () => {
149+
mockKvStore.get.mockResolvedValue(null);
150+
151+
await handleGetFlagKeys(
152+
mockRequest,
153+
mockAbstractionHelper,
154+
mockKvStore,
155+
mockLogger,
156+
mockDefaultSettings,
157+
);
158+
159+
expect(mockAbstractionHelper.createResponse).toHaveBeenCalledWith('No flag keys found', 404);
160+
});
161+
162+
it('should return flag keys when they exist', async () => {
163+
const storedFlagKeys = 'flag1,flag2,flag3';
164+
mockKvStore.get.mockResolvedValue(storedFlagKeys);
165+
166+
await handleGetFlagKeys(
167+
mockRequest,
168+
mockAbstractionHelper,
169+
mockKvStore,
170+
mockLogger,
171+
mockDefaultSettings,
172+
);
173+
174+
expect(mockAbstractionHelper.createResponse).toHaveBeenCalledWith(
175+
['flag1', 'flag2', 'flag3'],
176+
200,
177+
{ 'Content-Type': 'application/json' },
178+
);
179+
});
180+
181+
it('should trim flag keys and filter empty strings', async () => {
182+
const storedFlagKeys = ' flag1 , flag2, ,flag3 ';
183+
mockKvStore.get.mockResolvedValue(storedFlagKeys);
184+
185+
await handleGetFlagKeys(
186+
mockRequest,
187+
mockAbstractionHelper,
188+
mockKvStore,
189+
mockLogger,
190+
mockDefaultSettings,
191+
);
192+
193+
expect(mockAbstractionHelper.createResponse).toHaveBeenCalledWith(
194+
['flag1', 'flag2', 'flag3'],
195+
200,
196+
{ 'Content-Type': 'application/json' },
197+
);
198+
});
199+
200+
it('should handle errors gracefully', async () => {
201+
const errorMessage = 'Test error';
202+
mockKvStore.get.mockRejectedValue(new Error(errorMessage));
203+
204+
await handleGetFlagKeys(
205+
mockRequest,
206+
mockAbstractionHelper,
207+
mockKvStore,
208+
mockLogger,
209+
mockDefaultSettings,
210+
);
211+
212+
expect(mockLogger.error).toHaveBeenCalled();
213+
expect(mockAbstractionHelper.createResponse).toHaveBeenCalledWith(
214+
`Error: ${errorMessage}`,
215+
500,
216+
);
217+
});
218+
});
219+
});

0 commit comments

Comments
 (0)