186 lines
6.2 KiB
JavaScript
186 lines
6.2 KiB
JavaScript
// @vitest-environment jsdom
|
|
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
authenticatedRequest: vi.fn(),
|
|
parseError: vi.fn(),
|
|
pingApiServer: vi.fn(),
|
|
swalFire: vi.fn(() => Promise.resolve({ isConfirmed: true })),
|
|
loginRedirect: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/components/session/authenticatedRequest.vue", () => ({
|
|
authenticatedRequest: mocks.authenticatedRequest,
|
|
}));
|
|
|
|
vi.mock("@/components/request/HandleGlobalError.vue", () => ({
|
|
parseError: mocks.parseError,
|
|
}));
|
|
|
|
vi.mock("@/services/apiHealth.js", () => ({
|
|
pingApiServer: mocks.pingApiServer,
|
|
}));
|
|
|
|
vi.mock("sweetalert2", () => ({
|
|
default: {
|
|
fire: mocks.swalFire,
|
|
},
|
|
}));
|
|
|
|
import { getSessionData, SessionUser } from "@/components/session/token/SessionUser.vue";
|
|
import { EDGE_GATEWAY_WORKSPACE_CACHE_KEY } from "@/services/edgeGateways.js";
|
|
import {
|
|
buildPeriodCacheKey,
|
|
getCachedPeriodPage,
|
|
setCachedPeriodPage,
|
|
} from "@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/imports/InvoicingBillingPeriodImportPaging.js";
|
|
import {
|
|
getCachedXlvaskUsageAmount,
|
|
setCachedXlvaskUsageAmount,
|
|
} from "@/components/displays/department/pos/sync/xlvaskUsageAmountCache.js";
|
|
|
|
const seedStoredSession = (token = "stored-token") => {
|
|
localStorage.setItem("token", token);
|
|
SessionUser.token.value = token;
|
|
SessionUser.authenticated.value = true;
|
|
SessionUser.isSubuser.value = false;
|
|
SessionUser.initiated.value = false;
|
|
};
|
|
|
|
const invalidSessionError = () => ({
|
|
response: {
|
|
status: 401,
|
|
data: {
|
|
message: "Unauthenticated",
|
|
},
|
|
},
|
|
});
|
|
|
|
describe("session bootstrap failure handling", () => {
|
|
beforeEach(() => {
|
|
SessionUser.auth.forceClearSession();
|
|
SessionUser.functions.redirectTo.pages.login = mocks.loginRedirect;
|
|
mocks.authenticatedRequest.mockReset();
|
|
mocks.parseError.mockReset();
|
|
mocks.pingApiServer.mockReset();
|
|
mocks.swalFire.mockClear();
|
|
mocks.loginRedirect.mockReset();
|
|
window.history.replaceState({}, "", "/user");
|
|
});
|
|
|
|
it("clears privileged session caches when forcing the session closed", () => {
|
|
seedStoredSession("cached-session-token");
|
|
const periodCacheKey = buildPeriodCacheKey({
|
|
dateFrom: "2026-05-01",
|
|
dateTo: "2026-05-31",
|
|
periodView: "all",
|
|
page: 1,
|
|
limit: 100,
|
|
search: "",
|
|
flagTab: "all",
|
|
includeRequiresAction: true,
|
|
includeBooked: true,
|
|
});
|
|
|
|
setCachedPeriodPage(
|
|
periodCacheKey,
|
|
{ types: { all: [{ customer_name: "Sensitive Customer" }] } },
|
|
{ page: 1, per_page: 100, total: 1 }
|
|
);
|
|
setCachedXlvaskUsageAmount(8101, {
|
|
order_items: [{ license_plate: "PII-123" }],
|
|
potential_duplicates: [],
|
|
});
|
|
window.sessionStorage.setItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY, JSON.stringify({ details: { 1: "gateway" } }));
|
|
window.localStorage.setItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY, JSON.stringify({ details: { 1: "legacy" } }));
|
|
|
|
expect(getCachedPeriodPage(periodCacheKey)?.data?.types?.all?.[0]?.customer_name).toBe("Sensitive Customer");
|
|
expect(getCachedXlvaskUsageAmount(8101)?.order_items?.[0]?.license_plate).toBe("PII-123");
|
|
expect(window.sessionStorage.getItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY)).toContain("gateway");
|
|
expect(window.localStorage.getItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY)).toContain("legacy");
|
|
|
|
SessionUser.auth.forceClearSession();
|
|
|
|
expect(getCachedPeriodPage(periodCacheKey)).toBeNull();
|
|
expect(getCachedXlvaskUsageAmount(8101)).toBeNull();
|
|
expect(Object.keys(window.sessionStorage).filter((key) => key.startsWith("invoicing-period-page:")).length).toBe(0);
|
|
expect(Object.keys(window.sessionStorage).filter((key) => key.startsWith("xlvask-usage-amount:")).length).toBe(0);
|
|
expect(window.sessionStorage.getItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY)).toBeNull();
|
|
expect(window.localStorage.getItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY)).toBeNull();
|
|
});
|
|
|
|
it("clears an invalid stored session after ping confirms the API is reachable", async () => {
|
|
seedStoredSession("invalid-token");
|
|
mocks.authenticatedRequest.mockRejectedValueOnce(invalidSessionError());
|
|
mocks.pingApiServer.mockResolvedValueOnce({
|
|
ok: true,
|
|
status: 200,
|
|
url: "/api/ping",
|
|
error: null,
|
|
});
|
|
|
|
const result = await getSessionData();
|
|
|
|
expect(mocks.pingApiServer).toHaveBeenCalledTimes(1);
|
|
expect(localStorage.getItem("token")).toBeNull();
|
|
expect(SessionUser.authenticated.value).toBe(false);
|
|
expect(mocks.loginRedirect).toHaveBeenCalledTimes(1);
|
|
expect(result).toMatchObject({
|
|
cleared: true,
|
|
apiReachable: true,
|
|
redirectedTo: "/login",
|
|
});
|
|
});
|
|
|
|
it("preserves an invalid stored session and routes to connectivity when ping fails", async () => {
|
|
seedStoredSession("invalid-token");
|
|
mocks.authenticatedRequest.mockRejectedValueOnce(invalidSessionError());
|
|
mocks.pingApiServer.mockResolvedValueOnce({
|
|
ok: false,
|
|
status: null,
|
|
url: "/api/ping",
|
|
error: new Error("Network unavailable"),
|
|
});
|
|
|
|
const result = await getSessionData();
|
|
|
|
expect(mocks.pingApiServer).toHaveBeenCalledTimes(1);
|
|
expect(localStorage.getItem("token")).toBe("invalid-token");
|
|
expect(SessionUser.authenticated.value).toBe(true);
|
|
expect(mocks.loginRedirect).not.toHaveBeenCalled();
|
|
expect(result).toMatchObject({
|
|
cleared: false,
|
|
apiReachable: false,
|
|
redirectedTo: "/connectivity-issue",
|
|
});
|
|
});
|
|
|
|
it("does not clear the stored session for generic server bootstrap errors", async () => {
|
|
seedStoredSession("still-valid-token");
|
|
const serverError = {
|
|
response: {
|
|
status: 500,
|
|
data: {
|
|
message: "Internal server error",
|
|
},
|
|
},
|
|
};
|
|
mocks.authenticatedRequest.mockRejectedValueOnce(serverError);
|
|
|
|
const result = await getSessionData();
|
|
|
|
expect(mocks.pingApiServer).not.toHaveBeenCalled();
|
|
expect(localStorage.getItem("token")).toBe("still-valid-token");
|
|
expect(SessionUser.authenticated.value).toBe(true);
|
|
expect(mocks.loginRedirect).not.toHaveBeenCalled();
|
|
expect(mocks.parseError).toHaveBeenCalledWith(serverError, "auth");
|
|
expect(mocks.swalFire).toHaveBeenCalledTimes(1);
|
|
expect(result).toMatchObject({
|
|
cleared: false,
|
|
apiReachable: null,
|
|
redirectedTo: null,
|
|
});
|
|
});
|
|
});
|