File size: 3,990 Bytes
94004a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
 * Tests for utils/apiBase.ts (Phase 1 Wave 3, issue #80).
 *
 * Covers the four-branch resolver:
 *   1. VITE_OMNIVOICE_API override always wins.
 *   2. Tauri webview context → localhost:3900.
 *   3. Plain browser context → window.location.hostname:3900.
 *   4. SSR / no-window fallback → localhost:3900.
 */
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";

describe("apiBase.getApiBase", () => {
  // Snapshot what we mutate so each test starts clean.
  let originalTauriInternals: unknown;
  let originalTauri: unknown;
  let originalLocation: Location;

  beforeEach(() => {
    originalTauriInternals = (window as Window).__TAURI_INTERNALS__;
    originalTauri = (window as Window).__TAURI__;
    originalLocation = window.location;
    // jsdom defaults VITE_OMNIVOICE_API to undefined — no need to stub.
    vi.resetModules();
  });

  afterEach(() => {
    if (originalTauriInternals === undefined) {
      delete (window as Window).__TAURI_INTERNALS__;
    } else {
      (window as Window).__TAURI_INTERNALS__ = originalTauriInternals;
    }
    if (originalTauri === undefined) {
      delete (window as Window).__TAURI__;
    } else {
      (window as Window).__TAURI__ = originalTauri;
    }
    // Restore window.location via the descriptor — jsdom protects against
    // direct reassignment, so we have to override via defineProperty in each
    // test then restore here.
    Object.defineProperty(window, "location", {
      configurable: true,
      value: originalLocation,
    });
  });

  it("uses VITE_OMNIVOICE_API override when set", async () => {
    // vi.stubEnv works for inline `import.meta.env` reads in the test file,
    // but does not propagate to dynamically imported modules' import.meta in
    // vitest 4.x (Vite's per-module env snapshot). Instead, we patch the
    // module's resolver function via its (intentionally exposed) override
    // hook so the test exercises the same code path.
    vi.resetModules();
    const mod = await import("./apiBase");
    mod._setEnvOverrideForTesting("http://10.0.0.5:3900");
    expect(mod.getApiBase()).toBe("http://10.0.0.5:3900");
    mod._setEnvOverrideForTesting(undefined);
  });

  it("strips trailing slash from override", async () => {
    vi.resetModules();
    const mod = await import("./apiBase");
    mod._setEnvOverrideForTesting("http://10.0.0.5:3900/");
    expect(mod.getApiBase()).toBe("http://10.0.0.5:3900");
    mod._setEnvOverrideForTesting(undefined);
  });

  it("returns localhost:3900 in Tauri context", async () => {
    (window as Window).__TAURI_INTERNALS__ = {};
    const { getApiBase } = await import("./apiBase");
    expect(getApiBase()).toBe("http://localhost:3900");
  });

  it("returns window.location.hostname:3900 in plain browser context (LAN IP)", async () => {
    delete (window as Window).__TAURI_INTERNALS__;
    delete (window as Window).__TAURI__;
    Object.defineProperty(window, "location", {
      configurable: true,
      value: {
        protocol: "http:",
        hostname: "192.168.1.50",
        host: "192.168.1.50:5173",
        href: "http://192.168.1.50:5173/",
      },
    });
    const { getApiBase } = await import("./apiBase");
    expect(getApiBase()).toBe("http://192.168.1.50:3900");
  });

  it("respects page protocol (https) when serving over LAN TLS", async () => {
    delete (window as Window).__TAURI_INTERNALS__;
    delete (window as Window).__TAURI__;
    Object.defineProperty(window, "location", {
      configurable: true,
      value: {
        protocol: "https:",
        hostname: "studio.local",
        host: "studio.local",
        href: "https://studio.local/",
      },
    });
    const { getApiBase } = await import("./apiBase");
    expect(getApiBase()).toBe("https://studio.local:3900");
  });

  it("BACKEND_PORT constant is exported and equals 3900", async () => {
    const { BACKEND_PORT } = await import("./apiBase");
    expect(BACKEND_PORT).toBe(3900);
  });
});