mirror of
https://github.com/Quad4-Software/MeshChatX.git
synced 2026-08-18 09:49:09 -04:00
99 lines
3.6 KiB
JavaScript
99 lines
3.6 KiB
JavaScript
// SPDX-License-Identifier: 0BSD
|
|
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import GlobalState from "../../meshchatx/src/frontend/js/GlobalState.js";
|
|
import {
|
|
AUTH_STATUS_TIMEOUT_MS,
|
|
applyAuthStatusToGlobalState,
|
|
authNavigationTargetForStatus,
|
|
fetchAuthStatus,
|
|
resolveAuthNavigation,
|
|
} from "../../meshchatx/src/frontend/js/authSessionSync.js";
|
|
|
|
describe("authSessionSync", () => {
|
|
beforeEach(() => {
|
|
GlobalState.authSessionResolved = false;
|
|
GlobalState.authEnabled = false;
|
|
GlobalState.authenticated = false;
|
|
GlobalState.demoMode = false;
|
|
GlobalState.isLoopbackBind = true;
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("applies auth status into GlobalState", () => {
|
|
applyAuthStatusToGlobalState({
|
|
auth_enabled: true,
|
|
authenticated: true,
|
|
demo_mode: true,
|
|
is_loopback_bind: false,
|
|
});
|
|
expect(GlobalState.authEnabled).toBe(true);
|
|
expect(GlobalState.authenticated).toBe(true);
|
|
expect(GlobalState.demoMode).toBe(true);
|
|
expect(GlobalState.isLoopbackBind).toBe(false);
|
|
expect(GlobalState.authSessionResolved).toBe(true);
|
|
});
|
|
|
|
it("routes authenticated users away from auth", () => {
|
|
const target = authNavigationTargetForStatus({ name: "auth" }, { auth_enabled: true, authenticated: true });
|
|
expect(target).toEqual({ redirect: "/" });
|
|
});
|
|
|
|
it("routes unauthenticated users to auth", () => {
|
|
const target = authNavigationTargetForStatus(
|
|
{ name: "messages" },
|
|
{ auth_enabled: true, authenticated: false }
|
|
);
|
|
expect(target).toEqual({ redirect: "/auth" });
|
|
});
|
|
|
|
it("aborts auth status fetch after timeout so navigation guards cannot hang", async () => {
|
|
vi.useFakeTimers();
|
|
const api = {
|
|
get: vi.fn((_path, config) => {
|
|
return new Promise((resolve, reject) => {
|
|
if (config?.signal?.aborted) {
|
|
reject(new DOMException("Aborted", "AbortError"));
|
|
return;
|
|
}
|
|
config?.signal?.addEventListener("abort", () => {
|
|
reject(new DOMException("Aborted", "AbortError"));
|
|
});
|
|
});
|
|
}),
|
|
};
|
|
|
|
const pending = fetchAuthStatus(api);
|
|
const assertion = expect(pending).rejects.toMatchObject({ name: "AbortError" });
|
|
await vi.advanceTimersByTimeAsync(AUTH_STATUS_TIMEOUT_MS + 1);
|
|
await assertion;
|
|
expect(api.get).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("allows navigation when auth status fetch times out during backend outage", async () => {
|
|
vi.useFakeTimers();
|
|
const api = {
|
|
get: vi.fn((_path, config) => {
|
|
return new Promise((resolve, reject) => {
|
|
if (config?.signal?.aborted) {
|
|
reject(new DOMException("Aborted", "AbortError"));
|
|
return;
|
|
}
|
|
config?.signal?.addEventListener("abort", () => {
|
|
reject(new DOMException("Aborted", "AbortError"));
|
|
});
|
|
});
|
|
}),
|
|
};
|
|
|
|
const pending = resolveAuthNavigation({ name: "messages" }, api);
|
|
await vi.advanceTimersByTimeAsync(AUTH_STATUS_TIMEOUT_MS + 1);
|
|
const decision = await pending;
|
|
expect(decision).toEqual({ allow: true });
|
|
expect(GlobalState.authSessionResolved).toBe(true);
|
|
});
|
|
});
|