Import tests: backend/tests/e2e/tier3
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { apiFetch, clearCookieJar } from "./setup.js";
|
||||
import type { Website, Domain } from "../../../src/api/contracts/models.js";
|
||||
|
||||
describe("Tier 3 E2E - Cross-Feature Logic", () => {
|
||||
// --- 1. Session Expiration vs CRUD ---
|
||||
describe("Session Expiration vs CRUD", () => {
|
||||
it("should prevent CRUD operations once the session has expired or was cleared", async () => {
|
||||
const sessionId = "tier3_expiration";
|
||||
clearCookieJar(sessionId);
|
||||
|
||||
// Log in
|
||||
await apiFetch("/api/auth/login", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ email: "owner@example.com", password: "correct-password" }),
|
||||
});
|
||||
|
||||
// Verify CRUD works while logged in
|
||||
const listBefore = await apiFetch<Website[]>("/api/websites", { method: "GET", sessionId });
|
||||
expect(listBefore.$status).toBe(200);
|
||||
|
||||
// Simulate session expiration by clearing the cookies
|
||||
clearCookieJar(sessionId);
|
||||
|
||||
// Attempt GET should fail
|
||||
const listAfter = await apiFetch("/api/websites", { method: "GET", sessionId });
|
||||
expect(listAfter.$status).toBe(401);
|
||||
expect(listAfter.error?.code).toBe("PERMISSION_DENIED");
|
||||
|
||||
// Attempt POST should fail
|
||||
const createAfter = await apiFetch("/api/websites", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ name: "ExpiredSite", domain: "expired.com" }),
|
||||
});
|
||||
expect(createAfter.$status).toBe(401);
|
||||
|
||||
// Attempt DELETE should fail
|
||||
const deleteAfter = await apiFetch("/api/websites/site-1", {
|
||||
method: "DELETE",
|
||||
sessionId,
|
||||
});
|
||||
expect(deleteAfter.$status).toBe(401);
|
||||
});
|
||||
});
|
||||
|
||||
// --- 2. Role Scope Comparisons ---
|
||||
describe("Role Scope Comparisons", () => {
|
||||
it("should allow Owner full read/write privileges", async () => {
|
||||
const sessionId = "tier3_role_owner";
|
||||
clearCookieJar(sessionId);
|
||||
|
||||
await apiFetch("/api/auth/login", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ email: "owner@example.com", password: "correct-password" }),
|
||||
});
|
||||
|
||||
// Owner creates website
|
||||
const createRes = await apiFetch<Website>("/api/websites", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ name: "OwnerSite", domain: "owner.com" }),
|
||||
});
|
||||
expect(createRes.$status).toBe(201);
|
||||
|
||||
// Owner deletes website
|
||||
const deleteRes = await apiFetch(`/api/websites/${createRes.data!.id}`, {
|
||||
method: "DELETE",
|
||||
sessionId,
|
||||
});
|
||||
expect(deleteRes.$status).toBe(200);
|
||||
});
|
||||
|
||||
it("should restrict Auditor role to read-only actions and block mutations", async () => {
|
||||
const sessionId = "tier3_role_auditor";
|
||||
clearCookieJar(sessionId);
|
||||
|
||||
await apiFetch("/api/auth/login", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ email: "auditor@example.com", password: "correct-password" }),
|
||||
});
|
||||
|
||||
// Read actions should pass
|
||||
const listRes = await apiFetch<Website[]>("/api/websites", { method: "GET", sessionId });
|
||||
expect(listRes.$status).toBe(200);
|
||||
|
||||
// Write action (Create) should fail with 403 Forbidden/Permission Denied
|
||||
const createRes = await apiFetch("/api/websites", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ name: "AuditorSite", domain: "auditor.com" }),
|
||||
});
|
||||
expect(createRes.$status).toBe(403);
|
||||
expect(createRes.error?.code).toBe("PERMISSION_DENIED");
|
||||
|
||||
// Mutation action (Delete) should fail with 403
|
||||
const deleteRes = await apiFetch("/api/websites/site-1", {
|
||||
method: "DELETE",
|
||||
sessionId,
|
||||
});
|
||||
expect(deleteRes.$status).toBe(403);
|
||||
expect(deleteRes.error?.code).toBe("PERMISSION_DENIED");
|
||||
});
|
||||
|
||||
it("should allow Developer to create/update websites but restrict them from deleting websites", async () => {
|
||||
const sessionId = "tier3_role_developer";
|
||||
clearCookieJar(sessionId);
|
||||
|
||||
await apiFetch("/api/auth/login", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ email: "developer@example.com", password: "correct-password" }),
|
||||
});
|
||||
|
||||
// Developer can create website
|
||||
const createRes = await apiFetch<Website>("/api/websites", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ name: "DevSite", domain: "dev.com" }),
|
||||
});
|
||||
expect(createRes.$status).toBe(201);
|
||||
|
||||
// Developer cannot delete website (returns 403)
|
||||
const deleteRes = await apiFetch(`/api/websites/${createRes.data!.id}`, {
|
||||
method: "DELETE",
|
||||
sessionId,
|
||||
});
|
||||
expect(deleteRes.$status).toBe(403);
|
||||
expect(deleteRes.error?.code).toBe("PERMISSION_DENIED");
|
||||
});
|
||||
});
|
||||
|
||||
// --- 3. Domain Mapping to Website CRUD ---
|
||||
describe("Domain Mapping to Website CRUD", () => {
|
||||
it("should update dependent website state when its mapped domain is deleted", async () => {
|
||||
const sessionId = "tier3_mapping";
|
||||
clearCookieJar(sessionId);
|
||||
|
||||
await apiFetch("/api/auth/login", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ email: "owner@example.com", password: "correct-password" }),
|
||||
});
|
||||
|
||||
// 1. Create a domain
|
||||
const domainRes = await apiFetch<Domain>("/api/domains", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ name: "mapped-domain.com", type: "primary" }),
|
||||
});
|
||||
expect(domainRes.$status).toBe(201);
|
||||
|
||||
// 2. Create a website mapped to that domain
|
||||
const siteRes = await apiFetch<Website>("/api/websites", {
|
||||
method: "POST",
|
||||
sessionId,
|
||||
body: JSON.stringify({ name: "MappedSite", domain: "mapped-domain.com" }),
|
||||
});
|
||||
expect(siteRes.$status).toBe(201);
|
||||
expect(siteRes.data?.status).toBe("healthy");
|
||||
|
||||
// 3. Delete the domain
|
||||
const deleteDomRes = await apiFetch(`/api/domains/${domainRes.data!.id}`, {
|
||||
method: "DELETE",
|
||||
sessionId,
|
||||
});
|
||||
expect(deleteDomRes.$status).toBe(200);
|
||||
|
||||
// 4. Retrieve website details and check if status degraded/warning due to severed domain
|
||||
const siteDetailRes = await apiFetch<Website>(`/api/websites/${siteRes.data!.id}`, {
|
||||
method: "GET",
|
||||
sessionId,
|
||||
});
|
||||
expect(siteDetailRes.$status).toBe(200);
|
||||
expect(siteDetailRes.data?.status).toBe("warning");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user