Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Both RedisService and HttpServer are implemented as singletons:
Example: Testing `RedisService.lpush` with a mocked Redis client.

```ts
test("should call lPush on the client", async () => {
test("Should call lPush on the client", async () => {
const fakeClient = {
lPush: mock(async () => 1),
};
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"start": "bun index.ts",
"test": "bun test",
"build": "bun build index.ts --outdir dist --minify --target bun",
"format": "npx prettier --write ."
"format": "npx prettier --write .",
"coverage": "bun test --coverage"
},
"devDependencies": {
"@types/bun": "latest",
Expand Down
14 changes: 7 additions & 7 deletions test/e2e/messagesApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {
test,
expect,
beforeAll,
afterAll,
beforeEach,
afterAll,
} from "bun:test";
import { RedisService } from "../../src/services/redisService.ts";
import { HttpServer } from "../../src/http/server.ts";
Expand All @@ -20,16 +20,16 @@ describe("Messages API – End-to-End Tests", () => {
await HttpServer.start();
});

beforeEach(async () => {
// Reset Redis database before each test
await RedisService.op().flushAll();
});

afterAll(async () => {
await RedisService.stop();
await HttpServer.stop();
});

beforeEach(async () => {
// Reset Redis database before each test
await RedisService.op().flushAll();
});

describe("GET /messages endpoint", async () => {
test("Should return an empty list when no messages are stored", async () => {
const response = await axios.get<GetMessagesResponse>(
Expand Down Expand Up @@ -124,7 +124,7 @@ describe("Messages API – End-to-End Tests", () => {
);
});

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

await Promise.all(
Expand Down
2 changes: 1 addition & 1 deletion test/example.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, test, expect } from "bun:test";

describe("Math utilities – basic operations", () => {
test("should correctly sum two positive integers", () => {
test("Should correctly sum two positive integers", () => {
const a = 2;
const b = 2;
const result = a + b;
Expand Down
2 changes: 1 addition & 1 deletion test/integration/messageRoutes.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, test, expect, afterAll, beforeAll } from "bun:test";
import { describe, test, expect, beforeAll, afterAll } from "bun:test";
import { messageRoutes } from "../../src/routes/messages";
import { RedisService } from "../../src/services/redisService.ts";
import type { ErrorResponse, GetMessagesResponse } from "../utils/types.ts";
Expand Down
89 changes: 89 additions & 0 deletions test/integration/redisService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { describe, test, expect, beforeAll } from "bun:test";
import { RedisService } from "../../src/services/redisService";

describe("Redis Service – Integration Tests", () => {
beforeAll(async () => {
await RedisService.start();
await RedisService.op().del("messages");
});

test("Should connect and return a working Redis instance", async () => {
const pong = await RedisService.op().ping();
expect(pong).toBe("PONG");
});

test("Should push a message into Redis list", async () => {
const result = await RedisService.lpush("messages", "hello");
expect(typeof result).toBe("number");
expect(result).toBeGreaterThan(0);

const stored = await RedisService.lrange("messages", 0, -1);
expect(stored).toContain("hello");
});

test("Should push multiple messages and maintain order (LIFO)", async () => {
await RedisService.op().del("messages");

await RedisService.lpush("messages", "A");
await RedisService.lpush("messages", "B");
await RedisService.lpush("messages", "C");

const stored = await RedisService.lrange("messages", 0, -1);
expect(stored).toEqual(["C", "B", "A"]);
});

test("Should return empty array when key does not exist", async () => {
await RedisService.op().del("unknown");

const stored = await RedisService.lrange("unknown", 0, -1);
expect(stored).toEqual([]);
});

test("Should delete the list successfully", async () => {
await RedisService.lpush("messages", "to_delete");
const del = await RedisService.op().del("messages");

expect(del).toBe(1);

const after = await RedisService.lrange("messages", 0, -1);
expect(after).toEqual([]);
});

test("Should handle empty string pushes", async () => {
await RedisService.op().del("messages");

await RedisService.lpush("messages", "");
const stored = await RedisService.lrange("messages", 0, -1);

expect(stored).toEqual([""]);
});

test("Should throw an error when pushing without Redis started", async () => {
await RedisService.stop();

try {
await RedisService.lpush("messages", "fail");
} catch (err) {
expect((err as Error).message.toLowerCase()).toContain(
"the client is closed",
);
} finally {
await RedisService.start();
}
});

test("Should allow multiple concurrent pushes without losing data", async () => {
await RedisService.op().del("messages");

const messages = Array.from({ length: 50 }, (_, i) => `msg_${i}`);

await Promise.all(messages.map((m) => RedisService.lpush("messages", m)));

const stored = await RedisService.lrange("messages", 0, -1);

messages.forEach((m) => {
expect(stored).toContain(m);
});
expect(stored.length).toBe(50);
});
});
6 changes: 3 additions & 3 deletions test/unit/redisService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe("RedisService - Unit tests", () => {
(RedisService as any).client = null;
});

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

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

test("should return 15 items from 0 to 14", async () => {
test("Should return 15 items from 0 to 14", async () => {
const fakeClient = {
lRange: mock(async () =>
Array.from({ length: 15 }, (_, i) => `msg-${i}`),
Expand Down
8 changes: 3 additions & 5 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
{
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "Preserve",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,

// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,

// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,

// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "tests"]
}