128 lines
3.8 KiB
TypeScript
128 lines
3.8 KiB
TypeScript
// @vitest-environment node
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import type { SourceAssetItem } from "@/lib/furni/source-assets";
|
|
import { inspectSourceAssets, probeAsset } from "./furniture-source-assets";
|
|
|
|
const source: SourceAssetItem = {
|
|
id: 1,
|
|
classname: "h25_monkeybzn_pole",
|
|
name: "Jungle flags",
|
|
description: "Flags",
|
|
type: "flooritem",
|
|
revision: 73253,
|
|
category: "other",
|
|
};
|
|
const alternative = {
|
|
...source,
|
|
id: 2,
|
|
classname: "br_c25_monkeybzn_pole",
|
|
revision: 73257,
|
|
};
|
|
afterEach(() => vi.unstubAllGlobals());
|
|
describe("source asset preflight", () => {
|
|
it("keeps missing furniture unchanged even when a similar asset is available", async () => {
|
|
const fetchMock = vi.fn(
|
|
async (url: string) =>
|
|
new Response(null, { status: url.includes("/73257/") ? 200 : 404 }),
|
|
);
|
|
vi.stubGlobal("fetch", fetchMock);
|
|
const result = await inspectSourceAssets(source, [source, alternative]);
|
|
expect(result.state).toBe("missing");
|
|
expect(result.classname).toBe(source.classname);
|
|
expect(result.alternatives).toEqual([]);
|
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
});
|
|
it("does not describe blocked or unreachable servers as missing files", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async () => new Response(null, { status: 403 })),
|
|
);
|
|
const result = await inspectSourceAssets(
|
|
{ ...source, classname: "blocked_test" },
|
|
[alternative],
|
|
);
|
|
expect(result.state).toBe("unknown");
|
|
expect(result.alternatives).toEqual([]);
|
|
});
|
|
it("accepts an available configured Nitro source", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(
|
|
async (url: string) =>
|
|
new Response(null, { status: url.endsWith(".nitro") ? 200 : 404 }),
|
|
),
|
|
);
|
|
const result = await inspectSourceAssets(
|
|
{ ...source, classname: "custom_test" },
|
|
[],
|
|
{
|
|
id: "custom",
|
|
name: "Custom",
|
|
hotel: "",
|
|
furnidataUrl: "https://source.example/data.json",
|
|
nitroBaseUrl: "https://source.example/furniture",
|
|
iconBaseUrl: "",
|
|
},
|
|
);
|
|
expect(result.state).toBe("available");
|
|
});
|
|
it("caches a probe to avoid repeating CDN requests", async () => {
|
|
const fetchMock = vi.fn(async () => new Response(null, { status: 200 }));
|
|
vi.stubGlobal("fetch", fetchMock);
|
|
await probeAsset("https://example.test/cache.nitro");
|
|
await probeAsset("https://example.test/cache.nitro");
|
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
const item = {
|
|
id: 1,
|
|
classname: "chair",
|
|
name: "Chair",
|
|
description: "",
|
|
type: "flooritem",
|
|
revision: 123,
|
|
category: "other",
|
|
};
|
|
|
|
it("reports every missing filename and HTTP reason without credentials", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn().mockResolvedValue(new Response(null, { status: 404 })),
|
|
);
|
|
const result = await inspectSourceAssets(item, [], {
|
|
nitroBaseUrl: "https://user:[email protected]/private?token=secret",
|
|
} as never);
|
|
expect(result.state).toBe("missing");
|
|
expect(result.attempts).toHaveLength(2);
|
|
expect(result.attempts?.[1]).toMatchObject({
|
|
file: "chair.swf",
|
|
reason: "http",
|
|
status: 404,
|
|
});
|
|
expect(JSON.stringify(result)).not.toContain("secret");
|
|
});
|
|
it("keeps network and blocked sources unknown rather than calling them missing", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn().mockRejectedValue(new Error("https://secret.test/?token=secret")),
|
|
);
|
|
const result = await inspectSourceAssets({ ...item, revision: 124 }, []);
|
|
expect(result.state).toBe("unknown");
|
|
expect(result.attempts?.[0].reason).toBe("network");
|
|
expect(JSON.stringify(result)).not.toContain("secret");
|
|
});
|
|
it("distinguishes HTML responses from valid assets", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi
|
|
.fn()
|
|
.mockResolvedValue(
|
|
new Response(null, { headers: { "content-type": "text/html" } }),
|
|
),
|
|
);
|
|
const result = await inspectSourceAssets({ ...item, revision: 125 }, []);
|
|
expect(result.attempts?.[0].reason).toBe("invalidResponse");
|
|
expect(result.state).toBe("unknown");
|
|
});
|