Files
openwork/apps/app/tests/den-session-offline-resilience.test.ts

384 lines
12 KiB
TypeScript

import { afterEach, describe, expect, spyOn, test } from "bun:test";
import {
clearDenSession,
DenApiError,
ensureDenActiveOrganization,
isDenSessionRevokedError,
mergePassiveDenSettings,
readDenSettings,
writeDenSettings,
} from "../src/app/lib/den";
import { resolveDenAuthFailureStatus } from "../src/react-app/domains/cloud/den-auth-provider";
const originalWindow = globalThis.window;
const originalFetch = globalThis.fetch;
const originalDev = process.env.DEV;
function memoryStorage(): Storage {
const map = new Map<string, string>();
return {
get length() {
return map.size;
},
clear() {
map.clear();
},
getItem(key: string) {
return map.get(key) ?? null;
},
key(index: number) {
return Array.from(map.keys())[index] ?? null;
},
removeItem(key: string) {
map.delete(key);
},
setItem(key: string, value: string) {
map.set(key, value);
},
};
}
afterEach(() => {
Object.defineProperty(globalThis, "window", {
configurable: true,
value: originalWindow,
});
Object.defineProperty(globalThis, "fetch", {
configurable: true,
value: originalFetch,
});
if (originalDev === undefined) {
delete process.env.DEV;
} else {
process.env.DEV = originalDev;
}
});
function stubWindow() {
Object.defineProperty(globalThis, "window", {
configurable: true,
value: {
localStorage: memoryStorage(),
dispatchEvent: () => true,
},
});
}
describe("active organization drop tripwire", () => {
test("warns with a recorded stack for an unflagged drop", () => {
process.env.DEV = "true";
stubWindow();
const warn = spyOn(console, "warn").mockImplementation(() => {});
writeDenSettings({ baseUrl: "https://den.test", activeOrgId: "org_stored" }, { persistBootstrap: false });
writeDenSettings({ baseUrl: "https://den.test", activeOrgId: null }, { persistBootstrap: false });
expect(warn).toHaveBeenCalledTimes(1);
expect(window.__openworkOrgDropWarnings).toHaveLength(1);
expect(window.__openworkOrgDropWarnings?.[0]).toContain("activeOrgId dropped unexpectedly from org_stored");
expect(window.__openworkOrgDropWarnings?.[0]).toContain("\n");
warn.mockRestore();
});
test("stays silent for opted-out clears and sign-out", () => {
process.env.DEV = "true";
stubWindow();
const warn = spyOn(console, "warn").mockImplementation(() => {});
writeDenSettings({ baseUrl: "https://den.test", activeOrgId: "org_stored" }, { persistBootstrap: false });
writeDenSettings(
{ baseUrl: "https://den.test", activeOrgId: null },
{ persistBootstrap: false, intentionalActiveOrgClear: true },
);
writeDenSettings({ baseUrl: "https://den.test", activeOrgId: "org_stored" }, { persistBootstrap: false });
clearDenSession();
expect(warn).not.toHaveBeenCalled();
expect(window.__openworkOrgDropWarnings).toBeUndefined();
warn.mockRestore();
});
test("stays silent when no organization was set", () => {
process.env.DEV = "true";
stubWindow();
const warn = spyOn(console, "warn").mockImplementation(() => {});
writeDenSettings({ baseUrl: "https://den.test", activeOrgId: null }, { persistBootstrap: false });
expect(warn).not.toHaveBeenCalled();
expect(window.__openworkOrgDropWarnings).toBeUndefined();
warn.mockRestore();
});
test("is inert outside development builds", () => {
delete process.env.DEV;
stubWindow();
const warn = spyOn(console, "warn").mockImplementation(() => {});
writeDenSettings({ baseUrl: "https://den.test", activeOrgId: "org_stored" }, { persistBootstrap: false });
writeDenSettings({ baseUrl: "https://den.test", activeOrgId: null }, { persistBootstrap: false });
expect(warn).not.toHaveBeenCalled();
expect(window.__openworkOrgDropWarnings).toBeUndefined();
warn.mockRestore();
});
});
describe("mergePassiveDenSettings", () => {
test("preserves stored credentials and org when in-memory state is empty", () => {
const result = mergePassiveDenSettings(
{
baseUrl: "https://stored.example.com",
authToken: "tok_stored",
activeOrgId: "org_stored",
activeOrgSlug: "stored-org",
activeOrgName: "Stored Org",
},
{
baseUrl: "https://next.example.com",
authToken: null,
activeOrgId: null,
activeOrgSlug: null,
activeOrgName: null,
},
);
expect(result).toEqual({
baseUrl: "https://next.example.com",
apiBaseUrl: "https://next.example.com/api/den",
authToken: "tok_stored",
activeOrgId: "org_stored",
activeOrgSlug: "stored-org",
activeOrgName: "Stored Org",
});
});
test("uses fresh in-memory values when they are present", () => {
const result = mergePassiveDenSettings(
{
baseUrl: "https://stored.example.com",
authToken: "tok_stored",
activeOrgId: "org_stored",
activeOrgSlug: "stored-org",
activeOrgName: "Stored Org",
},
{
baseUrl: "https://next.example.com",
authToken: " tok_fresh ",
activeOrgId: " org_fresh ",
activeOrgSlug: " fresh-org ",
activeOrgName: " Fresh Org ",
},
);
expect(result.authToken).toBe("tok_fresh");
expect(result.activeOrgId).toBe("org_fresh");
expect(result.activeOrgSlug).toBe("fresh-org");
expect(result.activeOrgName).toBe("Fresh Org");
});
test("keeps empty storage empty when in-memory state is empty", () => {
const result = mergePassiveDenSettings(
{
baseUrl: "https://stored.example.com",
authToken: null,
activeOrgId: null,
activeOrgSlug: null,
activeOrgName: null,
},
{
baseUrl: "https://next.example.com",
authToken: null,
activeOrgId: null,
activeOrgSlug: null,
activeOrgName: null,
},
);
expect(result.authToken).toBeNull();
expect(result.activeOrgId).toBeNull();
expect(result.activeOrgSlug).toBeNull();
expect(result.activeOrgName).toBeNull();
});
test("passive write leaves stored session keys intact", () => {
Object.defineProperty(globalThis, "window", {
configurable: true,
value: {
localStorage: memoryStorage(),
dispatchEvent: () => true,
},
});
window.localStorage.setItem("openwork.den.authToken", "tok_stored");
window.localStorage.setItem("openwork.den.activeOrgId", "org_stored");
window.localStorage.setItem("openwork.den.activeOrgSlug", "stored-org");
window.localStorage.setItem("openwork.den.activeOrgName", "Stored Org");
writeDenSettings(
mergePassiveDenSettings(readDenSettings(), {
baseUrl: "https://next.example.com",
authToken: null,
activeOrgId: null,
activeOrgSlug: null,
activeOrgName: null,
}),
);
expect(window.localStorage.getItem("openwork.den.authToken")).toBe("tok_stored");
expect(window.localStorage.getItem("openwork.den.activeOrgId")).toBe("org_stored");
expect(window.localStorage.getItem("openwork.den.activeOrgSlug")).toBe("stored-org");
expect(window.localStorage.getItem("openwork.den.activeOrgName")).toBe("Stored Org");
});
});
describe("ensureDenActiveOrganization", () => {
test("preserves the selected organization when the server temporarily returns none", async () => {
Object.defineProperty(globalThis, "window", {
configurable: true,
value: {
localStorage: memoryStorage(),
dispatchEvent: () => true,
},
});
writeDenSettings({
baseUrl: "https://den.test",
authToken: "tok_stored",
activeOrgId: "org_stored",
activeOrgSlug: "stored-org",
activeOrgName: "Stored Org",
});
Object.defineProperty(globalThis, "fetch", {
configurable: true,
value: (async () => new Response(JSON.stringify({
orgs: [],
activeOrgId: null,
activeOrgSlug: null,
}), {
status: 200,
headers: { "Content-Type": "application/json" },
})) satisfies typeof fetch,
});
await expect(ensureDenActiveOrganization()).resolves.toBeNull();
expect(readDenSettings()).toMatchObject({
activeOrgId: "org_stored",
activeOrgSlug: "stored-org",
activeOrgName: "Stored Org",
});
});
});
describe("isDenSessionRevokedError", () => {
test("treats only explicit invalid/expired/revoked session codes as revoked", () => {
expect(isDenSessionRevokedError(new DenApiError(401, "unauthorized", "Unauthorized"))).toBe(
true,
);
expect(
isDenSessionRevokedError(new DenApiError(401, "session_expired", "Session expired")),
).toBe(true);
expect(
isDenSessionRevokedError(new DenApiError(401, "session_revoked", "Session revoked")),
).toBe(true);
expect(isDenSessionRevokedError(new DenApiError(401, "invalid_token", "Invalid token"))).toBe(
true,
);
expect(isDenSessionRevokedError(new DenApiError(401, "request_failed", "Proxy 401"))).toBe(
false,
);
expect(isDenSessionRevokedError(new DenApiError(500, "server_error", "Server error"))).toBe(
false,
);
expect(isDenSessionRevokedError(new Error("Request timed out."))).toBe(false);
});
test("keeps the session for structured 401s minted by infrastructure", () => {
// Deployment platforms, proxies, and misrouted edges answer with their
// own JSON 401 envelopes while the control plane is unreachable. None of
// them prove the stored session is invalid.
expect(
isDenSessionRevokedError(
new DenApiError(401, "base_url_not_present", "Base URL not present"),
),
).toBe(false);
expect(
isDenSessionRevokedError(
new DenApiError(401, "deployment_not_found", "Deployment not found"),
),
).toBe(false);
expect(
isDenSessionRevokedError(new DenApiError(401, "proxy_authentication", "Proxy auth")),
).toBe(false);
});
});
describe("resolveDenAuthFailureStatus", () => {
test("keeps proxy-shaped 401s unavailable while Den-shaped 401s sign out", () => {
expect(resolveDenAuthFailureStatus(new DenApiError(401, "request_failed", "Proxy 401"))).toBe(
"unavailable",
);
expect(resolveDenAuthFailureStatus(new DenApiError(401, "unauthorized", "Unauthorized"))).toBe(
"signed_out",
);
expect(
resolveDenAuthFailureStatus(new DenApiError(401, "session_expired", "Session expired")),
).toBe("signed_out");
expect(resolveDenAuthFailureStatus(new Error("Request timed out."))).toBe("unavailable");
});
test("reports transient structured 401s as unavailable, retaining the session", () => {
expect(
resolveDenAuthFailureStatus(
new DenApiError(401, "base_url_not_present", "Base URL not present"),
),
).toBe("unavailable");
expect(
resolveDenAuthFailureStatus(new DenApiError(401, "deployment_not_found", "Deployment not found")),
).toBe("unavailable");
});
});
describe("explicit sign-out", () => {
test("clears the stored session and notifies listeners with signed_out", () => {
const dispatched: Event[] = [];
Object.defineProperty(globalThis, "window", {
configurable: true,
value: {
localStorage: memoryStorage(),
dispatchEvent: (event: Event) => {
dispatched.push(event);
return true;
},
},
});
writeDenSettings(
{
baseUrl: "https://den.test",
authToken: "tok_stored",
activeOrgId: "org_stored",
activeOrgSlug: "stored-org",
activeOrgName: "Stored Org",
},
{ persistBootstrap: false },
);
dispatched.length = 0;
clearDenSession();
expect(window.localStorage.getItem("openwork.den.authToken")).toBeNull();
expect(window.localStorage.getItem("openwork.den.activeOrgId")).toBeNull();
expect(window.localStorage.getItem("openwork.den.activeOrgSlug")).toBeNull();
expect(window.localStorage.getItem("openwork.den.activeOrgName")).toBeNull();
// The provider store listens for this signed_out notification to remove
// organization and imported Cloud providers; explicit logout must keep
// emitting it.
const sessionUpdated = dispatched.find(
(event) => event.type === "openwork-den-session-updated",
) as CustomEvent<{ status?: string }> | undefined;
expect(sessionUpdated?.detail.status).toBe("signed_out");
});
});