File size: 3,822 Bytes
9f21d0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { afterEach, describe, expect, test, vi } from "vitest";

import { fetchGpGroup, fetchGpIndex, resetGpSource } from "./gpSource";

type RouteMap = Record<string, () => Response>;

function json(body: unknown): () => Response {
  return () => new Response(JSON.stringify(body), { headers: { "Content-Type": "application/json" } });
}

function installFetch(routes: RouteMap): string[] {
  const requested: string[] = [];
  vi.stubGlobal(
    "fetch",
    vi.fn(async (input: RequestInfo | URL) => {
      const url = String(input);
      requested.push(url);
      const route = routes[url];
      if (!route) {
        return new Response("Not Found", { status: 404 });
      }
      return route();
    }),
  );
  return requested;
}

const WORKER_ROUTES: RouteMap = {
  "/api/groups.json": json({ updated: "", groups: [{ name: "weather", count: 2 }] }),
};

const STATIC_ROUTES: RouteMap = {
  "data/gp/index.json": json({ updated: "", groups: [{ name: "weather", count: 1 }] }),
};

afterEach(() => {
  resetGpSource();
  vi.unstubAllGlobals();
});

describe("GpSource probe", () => {
  test("uses the worker API when the probe answers with JSON", async () => {
    const requested = installFetch({
      ...WORKER_ROUTES,
      "/api/gp/weather.json": () => new Response("[]"),
    });
    expect(await fetchGpIndex()).toEqual([{ name: "weather", count: 2 }]);
    await fetchGpGroup("weather");
    expect(requested).toContain("/api/gp/weather.json");
  });

  test("falls back to the static snapshot when the probe fails", async () => {
    const requested = installFetch({
      ...STATIC_ROUTES,
      "data/gp/weather.json": () => new Response("[]"),
    });
    expect(await fetchGpIndex()).toEqual([{ name: "weather", count: 1 }]);
    await fetchGpGroup("weather");
    expect(requested).toContain("data/gp/weather.json");
    expect(requested).not.toContain("/api/gp/weather.json");
  });

  test("treats an HTML probe answer (worker-less SPA fallthrough) as no worker", async () => {
    installFetch({
      "/api/groups.json": () => new Response("<!doctype html><html></html>", { status: 200, headers: { "Content-Type": "text/html" } }),
      ...STATIC_ROUTES,
    });
    expect(await fetchGpIndex()).toEqual([{ name: "weather", count: 1 }]);
  });

  test("probes only once per session", async () => {
    const requested = installFetch(WORKER_ROUTES);
    await fetchGpIndex();
    await fetchGpGroup("weather").catch(() => undefined);
    await fetchGpGroup("stations").catch(() => undefined);
    expect(requested.filter((url) => url === "/api/groups.json")).toHaveLength(1);
  });
});

describe("fetchGpGroup", () => {
  test("retries a failed API group against the static snapshot", async () => {
    installFetch({
      ...WORKER_ROUTES,
      "/api/gp/weather.json": () => new Response("KV miss", { status: 404 }),
      "data/gp/weather.json": () => new Response('[{"OBJECT_NAME":"METEO-1","NORAD_CAT_ID":1}]'),
    });
    const payload = await fetchGpGroup("weather");
    expect(JSON.parse(payload)).toHaveLength(1);
  });

  test("passes explicit URL sources through without a fallback", async () => {
    const requested = installFetch({
      ...WORKER_ROUTES,
      "data/tle/custom.txt": () => new Response("CUSTOM\n1 ...\n2 ..."),
    });
    const payload = await fetchGpGroup("data/tle/custom.txt");
    expect(payload).toContain("CUSTOM");
    expect(requested).not.toContain("/api/gp/data/tle/custom.txt.json");
  });

  test("rethrows when both the API and the static snapshot fail", async () => {
    installFetch({
      ...WORKER_ROUTES,
      "/api/gp/weather.json": () => new Response("boom", { status: 500 }),
      "data/gp/weather.json": () => new Response("missing", { status: 404 }),
    });
    await expect(fetchGpGroup("weather")).rejects.toThrow();
  });
});