Import tests: backend/tests/e2e/tier1

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:09:36 +03:30
parent 200b9b3981
commit 21d7ae477d
+464
View File
@@ -0,0 +1,464 @@
import { describe, expect, it } from "vitest";
import { apiFetch, clearCookieJar } from "./setup.js";
import type { User, Website, Domain, DnsRecord } from "../../../src/api/contracts/models.js";
describe("Tier 1 E2E - Happy Paths", () => {
// --- Feature 1: Authentication ---
describe("Auth Happy Paths", () => {
it("should successfully log in standard credentials", async () => {
const sessionId = "tier1_auth_1";
clearCookieJar(sessionId);
const loginRes = await apiFetch<{ user: User; challenge: string | null; sessionExpiresAt: string }>(
"/api/auth/login",
{
method: "POST",
sessionId,
body: JSON.stringify({
email: "owner@example.com",
password: "correct-password",
}),
}
);
expect(loginRes.$status).toBe(200);
expect(loginRes.error).toBeNull();
expect(loginRes.data).toBeDefined();
expect(loginRes.data?.user.email).toBe("owner@example.com");
expect(loginRes.data?.user.role).toBe("owner");
expect(loginRes.data?.challenge).toBeNull();
expect(loginRes.data?.sessionExpiresAt).toBeDefined();
});
it("should trigger a 2FA challenge when logging in with 2FA email", async () => {
const sessionId = "tier1_auth_2";
clearCookieJar(sessionId);
const loginRes = await apiFetch<{ user: User | null; challenge: string | null }>(
"/api/auth/login",
{
method: "POST",
sessionId,
body: JSON.stringify({
email: "owner-2fa@example.com",
password: "2fa-password",
}),
}
);
expect(loginRes.$status).toBe(200);
expect(loginRes.error).toBeNull();
expect(loginRes.data?.challenge).toBe("totp");
expect(loginRes.data?.user).toBeNull();
});
it("should complete login when providing valid 2FA code", async () => {
const sessionId = "tier1_auth_3";
clearCookieJar(sessionId);
const loginRes = await apiFetch<{ user: User; challenge: string | null }>(
"/api/auth/login",
{
method: "POST",
sessionId,
body: JSON.stringify({
email: "owner-2fa@example.com",
password: "2fa-password",
code: "123456",
}),
}
);
expect(loginRes.$status).toBe(200);
expect(loginRes.error).toBeNull();
expect(loginRes.data?.user).toBeDefined();
expect(loginRes.data?.user.twoFactorEnabled).toBe(true);
});
it("should successfully fetch profile details via /api/auth/me", async () => {
const sessionId = "tier1_auth_4";
clearCookieJar(sessionId);
// Login first
await apiFetch("/api/auth/login", {
method: "POST",
sessionId,
body: JSON.stringify({ email: "owner@example.com", password: "correct-password" }),
});
// Get profile
const meRes = await apiFetch<User>("/api/auth/me", {
method: "GET",
sessionId,
});
expect(meRes.$status).toBe(200);
expect(meRes.error).toBeNull();
expect(meRes.data?.email).toBe("owner@example.com");
expect(meRes.data?.role).toBe("owner");
});
it("should successfully log out and invalidate session cookie", async () => {
const sessionId = "tier1_auth_5";
clearCookieJar(sessionId);
// Login
await apiFetch("/api/auth/login", {
method: "POST",
sessionId,
body: JSON.stringify({ email: "owner@example.com", password: "correct-password" }),
});
// Logout
const logoutRes = await apiFetch<{ ok: boolean }>("/api/auth/logout", {
method: "POST",
sessionId,
});
expect(logoutRes.$status).toBe(200);
expect(logoutRes.error).toBeNull();
expect(logoutRes.data?.ok).toBe(true);
// Subsequent /api/auth/me should fail
const meRes = await apiFetch("/api/auth/me", {
method: "GET",
sessionId,
});
expect(meRes.$status).toBe(401);
expect(meRes.error?.code).toBe("PERMISSION_DENIED");
});
});
// --- Feature 2: Websites CRUD ---
describe("Websites Happy Paths", () => {
const sessionId = "tier1_websites";
it("should successfully list websites", async () => {
clearCookieJar(sessionId);
await apiFetch("/api/auth/login", {
method: "POST",
sessionId,
body: JSON.stringify({ email: "owner@example.com", password: "correct-password" }),
});
const listRes = await apiFetch<Website[]>("/api/websites", {
method: "GET",
sessionId,
});
expect(listRes.$status).toBe(200);
expect(listRes.error).toBeNull();
expect(listRes.data).toBeInstanceOf(Array);
expect(listRes.data!.length).toBeGreaterThan(0);
});
it("should successfully create a new website", async () => {
const createRes = await apiFetch<Website>("/api/websites", {
method: "POST",
sessionId,
body: JSON.stringify({
name: "Test Happy Site",
domain: "happy.com",
runtime: "Node.js",
environment: "production",
}),
});
expect(createRes.$status).toBe(201);
expect(createRes.error).toBeNull();
expect(createRes.data?.id).toBeDefined();
expect(createRes.data?.name).toBe("Test Happy Site");
expect(createRes.data?.domain).toBe("happy.com");
});
it("should successfully retrieve website details by ID", async () => {
// List to get the ID we just created
const listRes = await apiFetch<Website[]>("/api/websites", {
method: "GET",
sessionId,
});
const created = listRes.data!.find((w) => w.name === "Test Happy Site");
expect(created).toBeDefined();
const getRes = await apiFetch<Website>(`/api/websites/${created!.id}`, {
method: "GET",
sessionId,
});
expect(getRes.$status).toBe(200);
expect(getRes.error).toBeNull();
expect(getRes.data?.name).toBe("Test Happy Site");
});
it("should successfully update website settings", async () => {
const listRes = await apiFetch<Website[]>("/api/websites", {
method: "GET",
sessionId,
});
const created = listRes.data!.find((w) => w.name === "Test Happy Site");
const updateRes = await apiFetch<Website>(`/api/websites/${created!.id}`, {
method: "PUT",
sessionId,
body: JSON.stringify({
runtime: "Docker",
environment: "staging",
}),
});
expect(updateRes.$status).toBe(200);
expect(updateRes.error).toBeNull();
expect(updateRes.data?.runtime).toBe("Docker");
expect(updateRes.data?.environment).toBe("staging");
});
it("should successfully delete a website", async () => {
const listRes = await apiFetch<Website[]>("/api/websites", {
method: "GET",
sessionId,
});
const created = listRes.data!.find((w) => w.name === "Test Happy Site");
const deleteRes = await apiFetch<{ deleted: string }>(`/api/websites/${created!.id}`, {
method: "DELETE",
sessionId,
});
expect(deleteRes.$status).toBe(200);
expect(deleteRes.error).toBeNull();
expect(deleteRes.data?.deleted).toBe(created!.id);
// Confirm deletion
const confirmRes = await apiFetch(`/api/websites/${created!.id}`, {
method: "GET",
sessionId,
});
expect(confirmRes.$status).toBe(404);
});
});
// --- Feature 3: Domains CRUD ---
describe("Domains Happy Paths", () => {
const sessionId = "tier1_domains";
it("should successfully list domains", async () => {
clearCookieJar(sessionId);
await apiFetch("/api/auth/login", {
method: "POST",
sessionId,
body: JSON.stringify({ email: "owner@example.com", password: "correct-password" }),
});
const listRes = await apiFetch<Domain[]>("/api/domains", {
method: "GET",
sessionId,
});
expect(listRes.$status).toBe(200);
expect(listRes.error).toBeNull();
expect(listRes.data).toBeInstanceOf(Array);
expect(listRes.data!.length).toBeGreaterThan(0);
});
it("should successfully create a new domain", async () => {
const createRes = await apiFetch<Domain>("/api/domains", {
method: "POST",
sessionId,
body: JSON.stringify({
name: "happydomain.com",
type: "primary",
documentRoot: "/home/coaxys/happydomain.com",
}),
});
expect(createRes.$status).toBe(201);
expect(createRes.error).toBeNull();
expect(createRes.data?.id).toBeDefined();
expect(createRes.data?.name).toBe("happydomain.com");
});
it("should successfully update domain properties", async () => {
const listRes = await apiFetch<Domain[]>("/api/domains", {
method: "GET",
sessionId,
});
const created = listRes.data!.find((d) => d.name === "happydomain.com");
const updateRes = await apiFetch<Domain>(`/api/domains/${created!.id}`, {
method: "PUT",
sessionId,
body: JSON.stringify({
documentRoot: "/home/coaxys/happydomain.com/public",
dnssec: true,
}),
});
expect(updateRes.$status).toBe(200);
expect(updateRes.error).toBeNull();
expect(updateRes.data?.documentRoot).toBe("/home/coaxys/happydomain.com/public");
expect(updateRes.data?.dnssec).toBe(true);
});
it("should successfully list DNS records for a domain", async () => {
const listRes = await apiFetch<Domain[]>("/api/domains", {
method: "GET",
sessionId,
});
const domain = listRes.data!.find((d) => d.name === "happydomain.com");
const dnsRes = await apiFetch<DnsRecord[]>(`/api/domains/${domain!.id}/dns`, {
method: "GET",
sessionId,
});
expect(dnsRes.$status).toBe(200);
expect(dnsRes.error).toBeNull();
expect(dnsRes.data).toBeInstanceOf(Array);
});
it("should successfully create, update, and delete a DNS record on a domain", async () => {
const listRes = await apiFetch<Domain[]>("/api/domains", {
method: "GET",
sessionId,
});
const domain = listRes.data!.find((d) => d.name === "happydomain.com");
// 1. Create
const createDns = await apiFetch<DnsRecord>(`/api/domains/${domain!.id}/dns`, {
method: "POST",
sessionId,
body: JSON.stringify({
name: "web",
ttl: 3600,
type: "A",
record: "192.0.2.55",
}),
});
expect(createDns.$status).toBe(201);
expect(createDns.data?.id).toBeDefined();
expect(createDns.data?.record).toBe("192.0.2.55");
// 2. Update
const updateDns = await apiFetch<DnsRecord>(
`/api/domains/${domain!.id}/dns/${createDns.data!.id}`,
{
method: "PUT",
sessionId,
body: JSON.stringify({
record: "192.0.2.56",
ttl: 7200,
}),
}
);
expect(updateDns.$status).toBe(200);
expect(updateDns.data?.record).toBe("192.0.2.56");
expect(updateDns.data?.ttl).toBe(7200);
// 3. Delete
const deleteDns = await apiFetch<{ deleted: string }>(
`/api/domains/${domain!.id}/dns/${createDns.data!.id}`,
{
method: "DELETE",
sessionId,
}
);
expect(deleteDns.$status).toBe(200);
expect(deleteDns.data?.deleted).toBe(createDns.data!.id);
});
it("should successfully delete a domain", async () => {
const listRes = await apiFetch<Domain[]>("/api/domains", {
method: "GET",
sessionId,
});
const created = listRes.data!.find((d) => d.name === "happydomain.com");
const deleteRes = await apiFetch<{ deleted: string }>(`/api/domains/${created!.id}`, {
method: "DELETE",
sessionId,
});
expect(deleteRes.$status).toBe(200);
expect(deleteRes.error).toBeNull();
// Confirm deletion
const confirmRes = await apiFetch(`/api/domains/${created!.id}`, {
method: "GET",
sessionId,
});
expect(confirmRes.$status).toBe(404);
});
});
// --- Feature 4: Envelope & Metadata Validation ---
describe("Envelope & Metadata Happy Paths", () => {
const sessionId = "tier1_envelope";
it("should conform to strict envelope formatting on success", async () => {
clearCookieJar(sessionId);
await apiFetch("/api/auth/login", {
method: "POST",
sessionId,
body: JSON.stringify({ email: "owner@example.com", password: "correct-password" }),
});
const res = await apiFetch("/api/auth/me", { method: "GET", sessionId });
// Envelope layout validation
expect(res).toBeDefined();
expect(res).toHaveProperty("data");
expect(res).toHaveProperty("meta");
expect(res).toHaveProperty("error");
expect(res.error).toBeNull();
expect(res.data).not.toBeNull();
expect(typeof res.meta.requestId).toBe("string");
expect(res.meta.requestId.length).toBeGreaterThan(0);
});
it("should conform to strict envelope formatting on failure", async () => {
// Bad credentials login
const res = await apiFetch("/api/auth/login", {
method: "POST",
sessionId,
body: JSON.stringify({ email: "owner@example.com", password: "wrong-password" }),
});
// Envelope layout validation
expect(res).toBeDefined();
expect(res).toHaveProperty("data");
expect(res).toHaveProperty("meta");
expect(res).toHaveProperty("error");
expect(res.data).toBeNull();
expect(res.error).not.toBeNull();
expect(typeof res.error!.code).toBe("string");
expect(typeof res.error!.message).toBe("string");
expect(typeof res.meta.requestId).toBe("string");
});
it("should provide pagination metadata on list endpoints", async () => {
const res = await apiFetch<Website[]>("/api/websites?page=1&pageSize=1", {
method: "GET",
sessionId,
});
expect(res.meta.pagination).toBeDefined();
expect(res.meta.pagination?.page).toBe(1);
expect(res.meta.pagination?.pageSize).toBe(1);
expect(typeof res.meta.pagination?.total).toBe("number");
expect(typeof res.meta.pagination?.pageCount).toBe("number");
});
it("should include rate limit metadata when appropriate", async () => {
// Perform request to rate limited simulator or trigger rate limit headers
const res = await apiFetch("/api/websites", {
method: "GET",
sessionId,
headers: { "x-test-rate-limit": "true" },
});
expect(res.$status).toBe(429);
expect(res.error?.code).toBe("RATE_LIMIT_ERROR");
expect((res.meta as any).rateLimit).toBeDefined();
});
});
});