Skip to content

Commit f0cc7b9

Browse files
committed
test(unit): improve code coverage on fringe edge cases
Signed-off-by: Jeremy Ho <jujaga@gmail.com>
1 parent 1e6a1f0 commit f0cc7b9

File tree

3 files changed

+86
-35
lines changed

3 files changed

+86
-35
lines changed

tests/unit/app.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,23 @@ describe('App', () => {
5656
expect(response.status).toBe(404);
5757
});
5858

59+
it('should return 429 when rate limit is exceeded', async () => {
60+
vi.resetModules();
61+
vi.stubEnv('APP_RATEENABLE', 'true');
62+
vi.stubEnv('APP_RATELIMIT', '1');
63+
64+
const { app: rateLimitedApp } = await import('../../src/app.ts');
65+
66+
// First request consumes the limit
67+
await request(rateLimitedApp).get('/');
68+
69+
const response = await request(rateLimitedApp).get('/');
70+
expect(response.status).toBe(429);
71+
expect((response.body as { detail: string }).detail).toBe('Too many requests, please try again later.');
72+
vi.unstubAllEnvs();
73+
vi.doUnmock('../../src/db/index.ts');
74+
});
75+
5976
describe('errorHandler', () => {
6077
let req: Request;
6178
let res: Response;

tests/unit/db/utils.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { mockSqlExecuteReturn } from './kysely.helper.ts'; // Must be imported b
33
import { Kysely, sql } from 'kysely';
44

55
import {
6+
SYSTEM_USER,
67
createAuditLogTrigger,
78
createIndex,
89
createUpdatedAtTrigger,
@@ -120,15 +121,33 @@ describe('DB Utils', () => {
120121
});
121122

122123
it('should add timestamps to a table builder', () => {
124+
const colBuilder = {
125+
defaultTo: vi.fn().mockReturnThis(),
126+
notNull: vi.fn().mockReturnThis()
127+
};
123128
const tableBuilder = {
124129
addColumn: vi.fn().mockReturnThis()
125130
} as unknown as CreateTableBuilder<string>;
126131
const result = withTimestamps(tableBuilder);
127132

128133
// eslint-disable-next-line @typescript-eslint/unbound-method
129134
expect(result.addColumn).toHaveBeenCalledWith('created_at', 'timestamptz', expect.any(Function));
135+
const createdAtFn = (tableBuilder.addColumn as Mock).mock.calls.find((call) => call[0] === 'created_at')?.[2] as (
136+
col: typeof colBuilder
137+
) => void;
138+
createdAtFn(colBuilder);
139+
expect(colBuilder.notNull).toHaveBeenCalled();
140+
expect(colBuilder.defaultTo).toHaveBeenCalledWith(expect.objectContaining({ strings: ['CURRENT_TIMESTAMP'] }));
141+
130142
// eslint-disable-next-line @typescript-eslint/unbound-method
131143
expect(result.addColumn).toHaveBeenCalledWith('created_by', 'text', expect.any(Function));
144+
const createdByFn = (tableBuilder.addColumn as Mock).mock.calls.find((call) => call[0] === 'created_by')?.[2] as (
145+
col: typeof colBuilder
146+
) => void;
147+
createdByFn(colBuilder);
148+
expect(colBuilder.notNull).toHaveBeenCalled();
149+
expect(colBuilder.defaultTo).toHaveBeenCalledWith(SYSTEM_USER);
150+
132151
// eslint-disable-next-line @typescript-eslint/unbound-method
133152
expect(result.addColumn).toHaveBeenCalledWith('updated_at', 'timestamptz');
134153
// eslint-disable-next-line @typescript-eslint/unbound-method

tests/unit/services/helpers/event.test.ts

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,6 @@ import {
66

77
import type { Event } from '../../../../src/types/index.d.ts';
88

9-
describe('mergeDateAndTimeToISOString', () => {
10-
it('merges date and time (HH:mm) to ISO string in UTC', () => {
11-
const date = new Date(Date.UTC(2023, 5, 1)); // 2023-06-01T00:00:00.000Z
12-
const time = '14:30';
13-
const result = mergeDateAndTimeToISOString(date, time);
14-
expect(result).toBe('2023-06-01T14:30:00.000Z');
15-
});
16-
17-
it('merges date and time (HH:mm:ss) to ISO string in UTC', () => {
18-
const date = new Date(Date.UTC(2023, 5, 1));
19-
const time = '14:30:15';
20-
const result = mergeDateAndTimeToISOString(date, time);
21-
expect(result).toBe('2023-06-01T14:30:15.000Z');
22-
});
23-
24-
it('merges date and time (HH:mm:ss.sss) to ISO string in UTC', () => {
25-
const date = new Date(Date.UTC(2023, 5, 1));
26-
const time = '14:30:15.123';
27-
const result = mergeDateAndTimeToISOString(date, time);
28-
expect(result).toBe('2023-06-01T14:30:15.123Z');
29-
});
30-
31-
it('ignores timezone offset in time string', () => {
32-
const date = new Date(Date.UTC(2023, 5, 1));
33-
const time = '14:30:15.123+02:00';
34-
const result = mergeDateAndTimeToISOString(date, time);
35-
expect(result).toBe('2023-06-01T14:30:15.123Z');
36-
});
37-
38-
it('throws error for invalid time format', () => {
39-
const date = new Date(Date.UTC(2023, 5, 1));
40-
expect(() => mergeDateAndTimeToISOString(date, 'badtime')).toThrow();
41-
});
42-
});
43-
449
describe('dateTimePartsToEvent', () => {
4510
it('returns start_datetime and end_datetime when times are provided', () => {
4611
const startDate = new Date(Date.UTC(2023, 5, 1));
@@ -149,3 +114,53 @@ describe('eventToDateTimeParts', () => {
149114
});
150115
});
151116
});
117+
118+
describe('mergeDateAndTimeToISOString', () => {
119+
it('merges date and time (HH:mm) to ISO string in UTC', () => {
120+
const date = new Date(Date.UTC(2023, 5, 1)); // 2023-06-01T00:00:00.000Z
121+
const time = '14:30';
122+
const result = mergeDateAndTimeToISOString(date, time);
123+
expect(result).toBe('2023-06-01T14:30:00.000Z');
124+
});
125+
126+
it('merges date and time (HH:mm:ss) to ISO string in UTC', () => {
127+
const date = new Date(Date.UTC(2023, 5, 1));
128+
const time = '14:30:15';
129+
const result = mergeDateAndTimeToISOString(date, time);
130+
expect(result).toBe('2023-06-01T14:30:15.000Z');
131+
});
132+
133+
it('merges date and time (HH:mm:ss.sss) to ISO string in UTC', () => {
134+
const date = new Date(Date.UTC(2023, 5, 1));
135+
const time = '14:30:15.123';
136+
const result = mergeDateAndTimeToISOString(date, time);
137+
expect(result).toBe('2023-06-01T14:30:15.123Z');
138+
});
139+
140+
it('ignores timezone offset in time string', () => {
141+
const date = new Date(Date.UTC(2023, 5, 1));
142+
const time = '14:30:15.123+02:00';
143+
const result = mergeDateAndTimeToISOString(date, time);
144+
expect(result).toBe('2023-06-01T14:30:15.123Z');
145+
});
146+
147+
it('throws error for invalid time format', () => {
148+
const date = new Date(Date.UTC(2023, 5, 1));
149+
expect(() => mergeDateAndTimeToISOString(date, 'badtime')).toThrow();
150+
});
151+
152+
it('throws error for non-numeric hour', () => {
153+
const date = new Date(Date.UTC(2023, 5, 1));
154+
expect(() => mergeDateAndTimeToISOString(date, 'xx:30')).toThrow('Invalid numeric values in time: xx:30');
155+
});
156+
157+
it('throws error for non-numeric minute', () => {
158+
const date = new Date(Date.UTC(2023, 5, 1));
159+
expect(() => mergeDateAndTimeToISOString(date, '14:xx')).toThrow('Invalid numeric values in time: 14:xx');
160+
});
161+
162+
it('throws error for non-numeric second', () => {
163+
const date = new Date(Date.UTC(2023, 5, 1));
164+
expect(() => mergeDateAndTimeToISOString(date, '14:30:xx')).toThrow('Invalid numeric values in time: 14:30:xx');
165+
});
166+
});

0 commit comments

Comments
 (0)