import { describe, it, expect } from "vitest";
import {
hostingConfig,
requestOwner,
createPreviewLimiter,
} from "../server/hosting";
import { keepDraft, getDraft, keepReport, getReport } from "../server/sessions";
import { extractLocal, assess } from "../server/context";
import { CASES } from "../server/cases";
const reviewer = "12345678-1234-1234-1234-123456789abc";
const env = {
CONTEXT_HOSTED: "true",
PORT: "8080",
WEBSITE_SITE_NAME: "context-preview",
CONTEXT_AUTH_PROVIDER: "azure-easyauth",
CONTEXT_REVIEWER_IDS: reviewer,
CONTEXT_ALLOWED_ORIGINS:
"https://context.marmar.life,https://preview.azurewebsites.net",
};
describe("restricted hosted preview", () => {
it("fails closed with incomplete hosting configuration", () => {
expect(() => hostingConfig({ CONTEXT_HOSTED: "true" })).toThrow();
expect(() =>
hostingConfig({
...env,
CONTEXT_ALLOWED_ORIGINS: "http://context.marmar.life",
})
).toThrow();
expect(() => hostingConfig({ ...env, PORT: "oops" })).toThrow();
});
it("keeps local mode loopback only", () => {
const config = hostingConfig({});
expect(config.bind).toBe("127.0.0.1");
expect(
requestOwner(config, {
host: "localhost:4191",
origin: "http://localhost:4191",
})
).toBe("local");
expect(() => requestOwner(config, { host: "evil.example" })).toThrow();
expect(() =>
requestOwner(config, {
host: "localhost:4191",
origin: "https://evil.example",
})
).toThrow();
});
it("requires the exact allowed host, origin and approved principal", () => {
const config = hostingConfig(env);
const headers = {
host: "context.marmar.life",
origin: "https://context.marmar.life",
"x-ms-client-principal-id": reviewer,
};
expect(config.bind).toBe("0.0.0.0");
expect(requestOwner(config, headers)).toBe(reviewer);
expect(() =>
requestOwner(config, { ...headers, host: "other.azurewebsites.net" })
).toThrow();
expect(() =>
requestOwner(config, { ...headers, origin: "https://evil.example" })
).toThrow();
expect(() =>
requestOwner(config, {
...headers,
"x-ms-client-principal-id": "unknown",
})
).toThrow();
expect(() => requestOwner(config, { host: headers.host })).toThrow();
});
it("bounds repeated extractions separately for each reviewer", () => {
const limit = createPreviewLimiter();
for (let i = 0; i < 6; i++) limit(reviewer, 1000);
expect(() => limit(reviewer, 1000)).toThrow(/limit/);
expect(() => limit("other", 1000)).not.toThrow();
expect(() => limit(reviewer, 61_000)).not.toThrow();
});
it("bounds daily calls across minute windows", () => {
const limit = createPreviewLimiter();
for (let i = 0; i < 100; i++) limit(reviewer, i * 60_000);
expect(() => limit(reviewer, 100 * 60_000)).toThrow(/limit/);
expect(() => limit(reviewer, 86_400_000)).not.toThrow();
});
it("isolates drafts and reports between authenticated reviewers", () => {
const sources = CASES[0].sources;
const draft = keepDraft(extractLocal(sources), sources, reviewer);
expect(() => getDraft(draft.id!, sources, "other")).toThrow();
expect(getDraft(draft.id!, sources, reviewer).id).toBe(draft.id);
const review = Object.fromEntries(
draft.facts.map((f) => [f.id, "include" as const])
);
const assessment = assess(sources, review, draft.fingerprint, true, draft);
expect(() => keepReport(draft.id!, assessment, review, "other")).toThrow();
const saved = keepReport(draft.id!, assessment, review, reviewer);
expect(() => getReport(saved.reportId!, "other")).toThrow();
expect(getReport(saved.reportId!, reviewer).synthetic).toBe(true);
});
});