2026-06-22 20:38:34 +07:00
|
|
|
/**
|
|
|
|
|
* E2E API tests — runs against a running backend instance.
|
|
|
|
|
* Usage: vitest run (or: API_BASE=http://localhost:3001 vitest run)
|
|
|
|
|
*/
|
2026-07-04 03:03:36 +07:00
|
|
|
import { describe, expect, it } from "vitest";
|
2026-06-22 20:38:34 +07:00
|
|
|
|
2026-07-02 03:54:44 +07:00
|
|
|
const BASE = process.env.API_BASE ?? "https://imphnen.asepharyana.my.id/api";
|
2026-06-22 20:38:34 +07:00
|
|
|
|
|
|
|
|
async function api(path: string, init?: RequestInit) {
|
|
|
|
|
const res = await fetch(`${BASE}${path}`, {
|
|
|
|
|
...init,
|
|
|
|
|
headers: { "Content-Type": "application/json", ...init?.headers },
|
|
|
|
|
});
|
|
|
|
|
const body = res.status !== 204 ? await res.json().catch(() => null) : null;
|
|
|
|
|
return { status: res.status, body };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe("API Health", () => {
|
|
|
|
|
it("GET /health returns 200 with status=healthy", async () => {
|
|
|
|
|
const { status, body } = await api("/health");
|
|
|
|
|
expect(status).toBe(200);
|
|
|
|
|
expect(body?.status).toBe("healthy");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("GET /metrics returns prometheus text", async () => {
|
|
|
|
|
const res = await fetch(`${BASE.replace("/api", "")}/api/metrics`);
|
|
|
|
|
expect(res.status).toBe(200);
|
|
|
|
|
const text = await res.text();
|
|
|
|
|
expect(text).toContain("nodejs");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("API Dashboard", () => {
|
|
|
|
|
it("GET /dashboard/stats returns stats fields", async () => {
|
|
|
|
|
const { status, body } = await api("/dashboard/stats");
|
|
|
|
|
expect(status).toBe(200);
|
|
|
|
|
expect(body).toHaveProperty("total_messages");
|
|
|
|
|
expect(body).toHaveProperty("total_flagged");
|
|
|
|
|
expect(body).toHaveProperty("active_users_24h");
|
|
|
|
|
expect(typeof body.total_messages).toBe("number");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("API Recordings", () => {
|
|
|
|
|
it("GET /recordings returns items with pagination", async () => {
|
|
|
|
|
const { status, body } = await api("/recordings?limit=5");
|
|
|
|
|
expect(status).toBe(200);
|
|
|
|
|
expect(body).toHaveProperty("items");
|
|
|
|
|
expect(Array.isArray(body.items)).toBe(true);
|
|
|
|
|
if (body.items.length > 0) {
|
|
|
|
|
expect(body.items[0]).toHaveProperty("id");
|
|
|
|
|
expect(body.items[0]).toHaveProperty("username");
|
|
|
|
|
expect(body.items[0]).toHaveProperty("created_at");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("API Config", () => {
|
|
|
|
|
it("GET /config returns 200", async () => {
|
|
|
|
|
const { status } = await api("/config");
|
|
|
|
|
expect(status).toBe(200);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("API Voice", () => {
|
|
|
|
|
it("GET /guilds returns 200", async () => {
|
|
|
|
|
const { status } = await api("/guilds");
|
|
|
|
|
expect(status).toBe(200);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("API Negative", () => {
|
|
|
|
|
it("GET /nonexistent returns 404", async () => {
|
|
|
|
|
const { status } = await api("/nonexistent");
|
|
|
|
|
expect(status).toBe(404);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("GET /messages without channelId returns 400", async () => {
|
|
|
|
|
const { status } = await api("/messages?limit=3");
|
|
|
|
|
expect(status).toBe(400);
|
|
|
|
|
});
|
|
|
|
|
});
|