Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
182 lines
6.3 KiB
TypeScript
182 lines
6.3 KiB
TypeScript
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");
|
|
});
|
|
});
|
|
});
|