Skip to content

Commit e4cf7ff

Browse files
committed
test: setup tests for http handlers
1 parent 4dd65ae commit e4cf7ff

6 files changed

Lines changed: 219 additions & 0 deletions

File tree

test/handlers/config.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Server } from "node:http";
2+
import { AddressInfo } from "node:net";
3+
import { createStep } from "../../src/scene/step";
4+
import { createScene } from "../../src/scene/scene";
5+
import { SimpleLogger } from "../../src/logger/logger";
6+
import { createCommand } from "../../src/scene/command";
7+
import { MemoryDatabase } from "../../src/database/memory/memory";
8+
9+
export const testAppServerSetup = {
10+
logger: new SimpleLogger({ levels: [] }),
11+
db: new MemoryDatabase(),
12+
config: { port: 0 }, // random free port
13+
};
14+
15+
const TEST = "test";
16+
17+
export const getTestHost = (s: Server) => `http://localhost:${(s.address() as AddressInfo).port}`;
18+
19+
export const testCommand = createCommand({ value: TEST, description: TEST, label: TEST });
20+
21+
export const testStep = createStep({
22+
key: TEST,
23+
prompt: { type: "text", content: TEST },
24+
reply: { bodyType: "text" },
25+
});
26+
27+
export const testScene = createScene({
28+
steps: [],
29+
handler: async () => ({ type: "text", content: TEST }),
30+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import assert from "node:assert";
2+
import { Server } from "node:http";
3+
import { afterEach, beforeEach, describe, it } from "node:test";
4+
import { getTestHost, testAppServerSetup, testCommand, testScene } from "./config";
5+
import { Command } from "../../src/scene/command";
6+
import { AppServer } from "../../src/server/server";
7+
8+
describe("getCommandsHandler", () => {
9+
let app: AppServer;
10+
let server: Server | null = null;
11+
12+
beforeEach(async () => {
13+
app = new AppServer(testAppServerSetup);
14+
});
15+
16+
afterEach(async () => {
17+
if (server) {
18+
await new Promise((resolve) => server!.close(resolve));
19+
server = null;
20+
}
21+
});
22+
23+
it("should return empty array", async () => {
24+
server = await app.start();
25+
const res = await fetch(`${getTestHost(server)}/api/commands`);
26+
27+
const got = await res.json();
28+
const expected: Command[] = [];
29+
30+
assert.deepStrictEqual(got, expected);
31+
});
32+
33+
it("should return one command", async () => {
34+
app.addScene(testCommand, testScene);
35+
server = await app.start();
36+
const res = await fetch(`${getTestHost(server)}/api/commands`);
37+
38+
const got = await res.json();
39+
const expected = [testCommand];
40+
41+
assert.deepStrictEqual(got, expected);
42+
});
43+
44+
it("should return multiple commands", async () => {
45+
const command1: Command = { value: "test1", description: "test command 1", label: "Test 1" };
46+
const command2: Command = { value: "test2", description: "test command 2", label: "Test 2" };
47+
app.addScene(command1, testScene);
48+
app.addScene(command2, testScene);
49+
server = await app.start();
50+
const res = await fetch(`${getTestHost(server)}/api/commands`);
51+
52+
const got = await res.json();
53+
const expected = [command1, command2];
54+
55+
assert.deepStrictEqual(got, expected);
56+
});
57+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import assert from "node:assert";
2+
import { Server } from "node:http";
3+
import { afterEach, describe, it } from "node:test";
4+
import { getTestHost, testAppServerSetup } from "./config";
5+
import { defaultConfig, ServerConfig } from "../../src/server/config";
6+
import { AppServer } from "../../src/server/server";
7+
import { DeepPartial } from "../../src/utils/types";
8+
9+
describe("getConfigHandler", () => {
10+
let app: AppServer;
11+
let server: Server | null = null;
12+
13+
afterEach(async () => {
14+
if (server) {
15+
await new Promise((resolve) => server!.close(resolve));
16+
server = null;
17+
}
18+
});
19+
20+
it("should return default config", async () => {
21+
app = new AppServer(testAppServerSetup);
22+
server = await app.start();
23+
const res = await fetch(`${getTestHost(server)}/api/config`);
24+
25+
const got = await res.json();
26+
const expected = { app: defaultConfig.app, defaultPageLimit: defaultConfig.defaultPageLimit };
27+
28+
assert.deepStrictEqual(got, expected);
29+
});
30+
31+
it("should return custom config", async () => {
32+
const expected: DeepPartial<ServerConfig> = {
33+
app: { name: "Test", description: "Test Description" },
34+
defaultPageLimit: 5,
35+
};
36+
37+
app = new AppServer({ ...testAppServerSetup, config: expected });
38+
server = await app.start();
39+
const res = await fetch(`${getTestHost(server)}/api/config`);
40+
41+
const got = await res.json();
42+
43+
assert.deepStrictEqual(got, expected);
44+
});
45+
});

test/handlers/public/index.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>test</title>
5+
</head>
6+
<body>
7+
<p>test</p>
8+
</body>
9+
</html>

test/handlers/public/test/test.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import assert from "node:assert";
2+
import { describe, it, beforeEach, afterEach } from "node:test";
3+
import { Server } from "node:http";
4+
import { resolve } from "node:path";
5+
import { readFile } from "node:fs/promises";
6+
import { AppServer } from "../../src/server/server";
7+
import { testAppServerSetup, getTestHost } from "./config";
8+
9+
describe("staticHandler", () => {
10+
let app: AppServer;
11+
let server: Server | null = null;
12+
const publicRoot = resolve(process.cwd(), "test", "handlers", "public");
13+
14+
beforeEach(() => {
15+
app = new AppServer({ ...testAppServerSetup, config: { publicRoot, port: 0 } });
16+
});
17+
18+
afterEach(async () => {
19+
if (server) {
20+
await new Promise((resolve) => server!.close(resolve));
21+
server = null;
22+
}
23+
});
24+
25+
it("should serve index.html for /", async () => {
26+
server = await app.start();
27+
const host = getTestHost(server);
28+
29+
const res = await fetch(`${host}/`);
30+
const text = await res.text();
31+
const expected = await readFile(resolve(publicRoot, "index.html"), "utf8");
32+
33+
assert.strictEqual(res.status, 200);
34+
assert.strictEqual(res.headers.get("content-type"), "text/html");
35+
assert.strictEqual(text.trim(), expected.trim());
36+
});
37+
38+
it("should serve a static text file", async () => {
39+
server = await app.start();
40+
const host = getTestHost(server);
41+
42+
const res = await fetch(`${host}/test/test.txt`);
43+
const text = await res.text();
44+
const expected = await readFile(resolve(publicRoot, "test", "test.txt"), "utf8");
45+
46+
assert.strictEqual(res.status, 200);
47+
assert.strictEqual(res.headers.get("content-type"), "text/plain");
48+
assert.strictEqual(text.trim(), expected.trim());
49+
});
50+
51+
it("should return 404 for missing file", async () => {
52+
server = await app.start();
53+
const host = getTestHost(server);
54+
55+
const res = await fetch(`${host}/no-such-file.xyz`);
56+
57+
assert.strictEqual(res.status, 404);
58+
});
59+
60+
it("should prevent path traversal attempts", async () => {
61+
server = await app.start();
62+
const host = getTestHost(server);
63+
64+
const res = await fetch(`${host}/../../.env`);
65+
66+
assert.strictEqual(res.status, 404);
67+
});
68+
69+
it("should return 404 when accessing not existing directory", async () => {
70+
server = await app.start();
71+
const host = getTestHost(server);
72+
73+
const res = await fetch(`${host}/folder`);
74+
75+
assert.strictEqual(res.status, 404);
76+
});
77+
});

0 commit comments

Comments
 (0)