Skip to content

Commit a642668

Browse files
feat: add integration test for Redis service (#11)
* feat: add integration test for Redis service * feat: remove c8 package
1 parent 6c7376b commit a642668

File tree

8 files changed

+107
-19
lines changed

8 files changed

+107
-19
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Both RedisService and HttpServer are implemented as singletons:
3838
Example: Testing `RedisService.lpush` with a mocked Redis client.
3939

4040
```ts
41-
test("should call lPush on the client", async () => {
41+
test("Should call lPush on the client", async () => {
4242
const fakeClient = {
4343
lPush: mock(async () => 1),
4444
};

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"start": "bun index.ts",
99
"test": "bun test",
1010
"build": "bun build index.ts --outdir dist --minify --target bun",
11-
"format": "npx prettier --write ."
11+
"format": "npx prettier --write .",
12+
"coverage": "bun test --coverage"
1213
},
1314
"devDependencies": {
1415
"@types/bun": "latest",

test/e2e/messagesApi.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {
33
test,
44
expect,
55
beforeAll,
6-
afterAll,
76
beforeEach,
7+
afterAll,
88
} from "bun:test";
99
import { RedisService } from "../../src/services/redisService.ts";
1010
import { HttpServer } from "../../src/http/server.ts";
@@ -20,16 +20,16 @@ describe("Messages API – End-to-End Tests", () => {
2020
await HttpServer.start();
2121
});
2222

23-
beforeEach(async () => {
24-
// Reset Redis database before each test
25-
await RedisService.op().flushAll();
26-
});
27-
2823
afterAll(async () => {
2924
await RedisService.stop();
3025
await HttpServer.stop();
3126
});
3227

28+
beforeEach(async () => {
29+
// Reset Redis database before each test
30+
await RedisService.op().flushAll();
31+
});
32+
3333
describe("GET /messages endpoint", async () => {
3434
test("Should return an empty list when no messages are stored", async () => {
3535
const response = await axios.get<GetMessagesResponse>(
@@ -124,7 +124,7 @@ describe("Messages API – End-to-End Tests", () => {
124124
);
125125
});
126126

127-
test("should handle multiple POST requests at the same time without losing messages", async () => {
127+
test("Should handle multiple POST requests at the same time without losing messages", async () => {
128128
const messages = ["msg1", "msg2", "msg3", "msg4", "msg5"];
129129

130130
await Promise.all(

test/example.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { describe, test, expect } from "bun:test";
22

33
describe("Math utilities – basic operations", () => {
4-
test("should correctly sum two positive integers", () => {
4+
test("Should correctly sum two positive integers", () => {
55
const a = 2;
66
const b = 2;
77
const result = a + b;

test/integration/messageRoutes.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, test, expect, afterAll, beforeAll } from "bun:test";
1+
import { describe, test, expect, beforeAll, afterAll } from "bun:test";
22
import { messageRoutes } from "../../src/routes/messages";
33
import { RedisService } from "../../src/services/redisService.ts";
44
import type { ErrorResponse, GetMessagesResponse } from "../utils/types.ts";
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { describe, test, expect, beforeAll } from "bun:test";
2+
import { RedisService } from "../../src/services/redisService";
3+
4+
describe("Redis Service – Integration Tests", () => {
5+
beforeAll(async () => {
6+
await RedisService.start();
7+
await RedisService.op().del("messages");
8+
});
9+
10+
test("Should connect and return a working Redis instance", async () => {
11+
const pong = await RedisService.op().ping();
12+
expect(pong).toBe("PONG");
13+
});
14+
15+
test("Should push a message into Redis list", async () => {
16+
const result = await RedisService.lpush("messages", "hello");
17+
expect(typeof result).toBe("number");
18+
expect(result).toBeGreaterThan(0);
19+
20+
const stored = await RedisService.lrange("messages", 0, -1);
21+
expect(stored).toContain("hello");
22+
});
23+
24+
test("Should push multiple messages and maintain order (LIFO)", async () => {
25+
await RedisService.op().del("messages");
26+
27+
await RedisService.lpush("messages", "A");
28+
await RedisService.lpush("messages", "B");
29+
await RedisService.lpush("messages", "C");
30+
31+
const stored = await RedisService.lrange("messages", 0, -1);
32+
expect(stored).toEqual(["C", "B", "A"]);
33+
});
34+
35+
test("Should return empty array when key does not exist", async () => {
36+
await RedisService.op().del("unknown");
37+
38+
const stored = await RedisService.lrange("unknown", 0, -1);
39+
expect(stored).toEqual([]);
40+
});
41+
42+
test("Should delete the list successfully", async () => {
43+
await RedisService.lpush("messages", "to_delete");
44+
const del = await RedisService.op().del("messages");
45+
46+
expect(del).toBe(1);
47+
48+
const after = await RedisService.lrange("messages", 0, -1);
49+
expect(after).toEqual([]);
50+
});
51+
52+
test("Should handle empty string pushes", async () => {
53+
await RedisService.op().del("messages");
54+
55+
await RedisService.lpush("messages", "");
56+
const stored = await RedisService.lrange("messages", 0, -1);
57+
58+
expect(stored).toEqual([""]);
59+
});
60+
61+
test("Should throw an error when pushing without Redis started", async () => {
62+
await RedisService.stop();
63+
64+
try {
65+
await RedisService.lpush("messages", "fail");
66+
} catch (err) {
67+
expect((err as Error).message.toLowerCase()).toContain(
68+
"the client is closed",
69+
);
70+
} finally {
71+
await RedisService.start();
72+
}
73+
});
74+
75+
test("Should allow multiple concurrent pushes without losing data", async () => {
76+
await RedisService.op().del("messages");
77+
78+
const messages = Array.from({ length: 50 }, (_, i) => `msg_${i}`);
79+
80+
await Promise.all(messages.map((m) => RedisService.lpush("messages", m)));
81+
82+
const stored = await RedisService.lrange("messages", 0, -1);
83+
84+
messages.forEach((m) => {
85+
expect(stored).toContain(m);
86+
});
87+
expect(stored.length).toBe(50);
88+
});
89+
});

test/unit/redisService.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ describe("RedisService - Unit tests", () => {
66
(RedisService as any).client = null;
77
});
88

9-
test("should call lPush on the client", async () => {
9+
test("Should call lPush on the client", async () => {
1010
const fakeClient = {
1111
lPush: mock(async () => 1),
1212
};
@@ -19,7 +19,7 @@ describe("RedisService - Unit tests", () => {
1919
expect(fakeClient.lPush).toHaveBeenCalledWith("messages", "hello");
2020
});
2121

22-
test("should call lRange on the client", async () => {
22+
test("Should call lRange on the client", async () => {
2323
const fakeClient = {
2424
lRange: mock(async () => ["hi"]),
2525
};
@@ -33,7 +33,7 @@ describe("RedisService - Unit tests", () => {
3333
expect(result).toEqual(["hi"]);
3434
});
3535

36-
test("should return 15 items from 0 to 14", async () => {
36+
test("Should return 15 items from 0 to 14", async () => {
3737
const fakeClient = {
3838
lRange: mock(async () =>
3939
Array.from({ length: 15 }, (_, i) => `msg-${i}`),

tsconfig.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,27 @@
11
{
22
"compilerOptions": {
3-
// Environment setup & latest features
43
"lib": ["ESNext"],
54
"target": "ESNext",
65
"module": "Preserve",
76
"moduleDetection": "force",
87
"jsx": "react-jsx",
98
"allowJs": true,
109

11-
// Bundler mode
1210
"moduleResolution": "bundler",
1311
"allowImportingTsExtensions": true,
1412
"verbatimModuleSyntax": true,
1513
"noEmit": true,
1614

17-
// Best practices
1815
"strict": true,
1916
"skipLibCheck": true,
2017
"noFallthroughCasesInSwitch": true,
2118
"noUncheckedIndexedAccess": true,
2219
"noImplicitOverride": true,
2320

24-
// Some stricter flags (disabled by default)
2521
"noUnusedLocals": false,
2622
"noUnusedParameters": false,
2723
"noPropertyAccessFromIndexSignature": false
28-
}
24+
},
25+
"include": ["src/**/*.ts"],
26+
"exclude": ["node_modules", "tests"]
2927
}

0 commit comments

Comments
 (0)