File size: 2,415 Bytes
37211b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { installApiAuthResponseMonitor, onApiUnauthorized } from "./apiAuthEvents";

const FETCH_PATCH_FLAG = "__mastersToolkitAuthFetchPatched";

async function flushAsync(): Promise<void> {
  await new Promise<void>((resolve) => window.setTimeout(resolve, 0));
}

describe("apiAuthEvents", () => {
  let originalFetch: typeof window.fetch;

  beforeEach(() => {
    originalFetch = window.fetch.bind(window);
    delete (window as Window & { [FETCH_PATCH_FLAG]?: boolean })[FETCH_PATCH_FLAG];
  });

  afterEach(() => {
    window.fetch = originalFetch;
    delete (window as Window & { [FETCH_PATCH_FLAG]?: boolean })[FETCH_PATCH_FLAG];
  });

  it("ignores 401 responses without Authorization header", async () => {
    const events: Array<{ status: number; message: string }> = [];
    const dispose = onApiUnauthorized((detail) => {
      events.push({ status: detail.status, message: detail.message });
    });

    window.fetch = vi.fn(async () => {
      return new Response(JSON.stringify({ detail: "Missing Authorization header." }), {
        status: 401,
        headers: {
          "content-type": "application/json",
          "x-masters-auth-error": "1",
        },
      });
    }) as unknown as typeof window.fetch;

    installApiAuthResponseMonitor();
    await window.fetch("/api/ui/tabs");
    await flushAsync();

    dispose();
    expect(events).toHaveLength(0);
  });

  it("emits unauthorized event for protected 401 responses with Authorization header", async () => {
    const events: Array<{ status: number; message: string }> = [];
    const dispose = onApiUnauthorized((detail) => {
      events.push({ status: detail.status, message: detail.message });
    });

    window.fetch = vi.fn(async () => {
      return new Response(JSON.stringify({ detail: "Invalid or expired token." }), {
        status: 401,
        headers: {
          "content-type": "application/json",
          "x-masters-auth-error": "1",
        },
      });
    }) as unknown as typeof window.fetch;

    installApiAuthResponseMonitor();
    await window.fetch("/api/health", {
      headers: { Authorization: "Bearer token" },
    });
    await flushAsync();

    dispose();
    expect(events).toHaveLength(1);
    expect(events[0]?.status).toBe(401);
    expect(events[0]?.message).toContain("Invalid or expired token");
  });
});