- move NestJS app into server/ subdirectory - add client/ React+TypeScript (Vite) app with Hello World - update docker-compose to build and run both services - add root package.json declaring npm workspaces - update .gitignore to cover node_modules and dist at all depths
152 lines
5.0 KiB
TypeScript
152 lines
5.0 KiB
TypeScript
import { INestApplication } from "@nestjs/common";
|
|
import { getRepositoryToken } from "@nestjs/typeorm";
|
|
import { Repository } from "typeorm";
|
|
import request from "supertest";
|
|
import { buildTestApp } from "./app.harness";
|
|
import { SessionEntity } from "../src/session/session.entity";
|
|
|
|
describe("SessionController", () => {
|
|
let app: INestApplication;
|
|
let repo: Repository<SessionEntity>;
|
|
|
|
beforeAll(async () => {
|
|
app = await buildTestApp();
|
|
repo = app.get<Repository<SessionEntity>>(
|
|
getRepositoryToken(SessionEntity),
|
|
);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await app.close();
|
|
});
|
|
|
|
async function seedSession(name: string) {
|
|
return repo.save(
|
|
repo.create({
|
|
sessionName: name,
|
|
token: "tok",
|
|
cookies: "[]",
|
|
localStorage: "{}",
|
|
}),
|
|
);
|
|
}
|
|
|
|
// ── GET /sessions ──────────────────────────────────────────────────────────
|
|
|
|
describe("GET /sessions", () => {
|
|
it("returns 200 with a paginated result", async () => {
|
|
const res = await request(app.getHttpServer())
|
|
.get("/sessions")
|
|
.expect(200);
|
|
expect(Array.isArray(res.body.data)).toBe(true);
|
|
expect(typeof res.body.total).toBe("number");
|
|
expect(res.body).toHaveProperty("page");
|
|
expect(res.body).toHaveProperty("limit");
|
|
});
|
|
|
|
it("includes seeded sessions", async () => {
|
|
await seedSession("visible-session");
|
|
const res = await request(app.getHttpServer())
|
|
.get("/sessions")
|
|
.expect(200);
|
|
const names = res.body.data.map(
|
|
(s: { sessionName: string }) => s.sessionName,
|
|
);
|
|
expect(names).toContain("visible-session");
|
|
});
|
|
|
|
it("does not expose token, cookies or localStorage fields", async () => {
|
|
await seedSession("private-session");
|
|
const res = await request(app.getHttpServer())
|
|
.get("/sessions")
|
|
.expect(200);
|
|
const item = res.body.data.find(
|
|
(s: { sessionName: string }) => s.sessionName === "private-session",
|
|
) as Record<string, unknown>;
|
|
expect(item).toBeDefined();
|
|
expect(item.token).toBeUndefined();
|
|
expect(item.cookies).toBeUndefined();
|
|
expect(item.localStorage).toBeUndefined();
|
|
});
|
|
|
|
it("respects page and limit params", async () => {
|
|
await seedSession("paged-session-a");
|
|
await seedSession("paged-session-b");
|
|
|
|
const res = await request(app.getHttpServer())
|
|
.get("/sessions?page=1&limit=1")
|
|
.expect(200);
|
|
expect(res.body.data).toHaveLength(1);
|
|
expect(res.body.page).toBe(1);
|
|
expect(res.body.limit).toBe(1);
|
|
});
|
|
|
|
it("returns empty data array for out-of-range page", async () => {
|
|
const res = await request(app.getHttpServer())
|
|
.get("/sessions?page=9999&limit=20")
|
|
.expect(200);
|
|
expect(res.body.data).toHaveLength(0);
|
|
});
|
|
|
|
it("returns 400 for invalid page param", async () => {
|
|
await request(app.getHttpServer()).get("/sessions?page=0").expect(400);
|
|
});
|
|
|
|
it("orders by sessionName ASC", async () => {
|
|
await seedSession("zzz-sort-session");
|
|
await seedSession("aaa-sort-session");
|
|
|
|
const res = await request(app.getHttpServer())
|
|
.get("/sessions?orderBy=sessionName&orderDir=ASC")
|
|
.expect(200);
|
|
const names: string[] = res.body.data.map(
|
|
(s: { sessionName: string }) => s.sessionName,
|
|
);
|
|
expect(names).toEqual([...names].sort());
|
|
});
|
|
|
|
it("orders by sessionName DESC", async () => {
|
|
const res = await request(app.getHttpServer())
|
|
.get("/sessions?orderBy=sessionName&orderDir=DESC")
|
|
.expect(200);
|
|
const names: string[] = res.body.data.map(
|
|
(s: { sessionName: string }) => s.sessionName,
|
|
);
|
|
expect(names).toEqual([...names].sort().reverse());
|
|
});
|
|
|
|
it("returns 400 for invalid orderDir", async () => {
|
|
await request(app.getHttpServer())
|
|
.get("/sessions?orderDir=SIDEWAYS")
|
|
.expect(400);
|
|
});
|
|
});
|
|
|
|
// ── DELETE /sessions/:id ───────────────────────────────────────────────────
|
|
|
|
describe("DELETE /sessions/:id", () => {
|
|
it("deletes an existing session and returns 200", async () => {
|
|
const s = await seedSession("delete-me-session");
|
|
await request(app.getHttpServer())
|
|
.delete(`/sessions/${s.id}`)
|
|
.expect(200);
|
|
|
|
const res = await request(app.getHttpServer())
|
|
.get("/sessions")
|
|
.expect(200);
|
|
const names = res.body.data.map(
|
|
(sess: { sessionName: string }) => sess.sessionName,
|
|
);
|
|
expect(names).not.toContain("delete-me-session");
|
|
});
|
|
|
|
it("returns 404 for unknown id", async () => {
|
|
await request(app.getHttpServer()).delete("/sessions/99999").expect(404);
|
|
});
|
|
|
|
it("returns 400 for non-numeric id", async () => {
|
|
await request(app.getHttpServer()).delete("/sessions/abc").expect(400);
|
|
});
|
|
});
|
|
});
|