Files
MeshChatX/tests/frontend/mapTileNetwork.test.js
T

104 lines
4.1 KiB
JavaScript

import { describe, it, expect, vi } from "vitest";
import {
buildNominatimSearchUrl,
normalizeHttpBaseUrl,
fetchTileBlobWithRetry,
fetchJsonWithRetry,
TILE_FETCH_TIMEOUT_MS,
} from "@/js/mapTileNetwork";
describe("mapTileNetwork", () => {
describe("normalizeHttpBaseUrl", () => {
it("strips trailing slash", () => {
expect(normalizeHttpBaseUrl("http://127.0.0.1:8080/nominatim/")).toBe("http://127.0.0.1:8080/nominatim");
});
});
describe("buildNominatimSearchUrl", () => {
it("builds search URL for public nominatim", () => {
const u = buildNominatimSearchUrl("https://nominatim.openstreetmap.org", "Paris", 5);
expect(u).toBe("https://nominatim.openstreetmap.org/search?format=json&q=Paris&limit=5&addressdetails=1");
});
it("supports custom local mesh nominatim base", () => {
const u = buildNominatimSearchUrl("http://192.168.44.1:3000/", "camp", 10);
expect(u.startsWith("http://192.168.44.1:3000/search?")).toBe(true);
expect(u).toContain(encodeURIComponent("camp"));
});
it("encodes query parameters safely", () => {
const u = buildNominatimSearchUrl("http://localhost/nom", "a&b=c", 10);
expect(u).toContain(encodeURIComponent("a&b=c"));
expect(u).not.toContain("a&b=c&");
});
});
describe("fetchTileBlobWithRetry", () => {
it("returns blob on first successful response", async () => {
const blob = new Blob([new Uint8Array([1, 2, 3])], { type: "image/png" });
global.fetch = vi.fn().mockResolvedValue({
ok: true,
blob: () => Promise.resolve(blob),
});
const r = await fetchTileBlobWithRetry("https://tile.example/0/0/0.png", {}, { retries: 0 });
expect(r.ok).toBe(true);
expect(r.blob).toBe(blob);
expect(global.fetch).toHaveBeenCalledTimes(1);
delete global.fetch;
});
it("retries after failure then succeeds", async () => {
const blob = new Blob([], { type: "image/png" });
global.fetch = vi
.fn()
.mockRejectedValueOnce(new TypeError("Failed to fetch"))
.mockResolvedValueOnce({
ok: true,
blob: () => Promise.resolve(blob),
});
const r = await fetchTileBlobWithRetry(
"https://tile.example/1/1/1.png",
{},
{ retries: 2, retryBaseDelayMs: 0 }
);
expect(r.ok).toBe(true);
expect(global.fetch).toHaveBeenCalledTimes(2);
delete global.fetch;
});
it("returns ok false after exhausting retries", async () => {
global.fetch = vi.fn().mockRejectedValue(new TypeError("network"));
const r = await fetchTileBlobWithRetry(
"https://tile.example/z/x/y.png",
{},
{ retries: 1, retryBaseDelayMs: 0, timeoutMs: TILE_FETCH_TIMEOUT_MS }
);
expect(r.ok).toBe(false);
expect(r.error).toBeDefined();
delete global.fetch;
});
});
describe("fetchJsonWithRetry", () => {
it("returns response on HTTP 200", async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve([]),
});
const r = await fetchJsonWithRetry("http://127.0.0.1:9999/search?q=x", {}, { retries: 0 });
expect(r.ok).toBe(true);
expect(r.response.ok).toBe(true);
delete global.fetch;
});
it("does not retry on HTTP 404", async () => {
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 404 });
const r = await fetchJsonWithRetry("http://localhost/n/search", {}, { retries: 2, retryBaseDelayMs: 0 });
expect(r.ok).toBe(false);
expect(r.status).toBe(404);
expect(global.fetch).toHaveBeenCalledTimes(1);
delete global.fetch;
});
});
});