120 lines
5.2 KiB
TypeScript
120 lines
5.2 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import { RelayClient } from "../src/relay/client.js";
|
|
|
|
// Minimal Response stand-in: the client only uses res.ok and res.text().
|
|
function resp(body: unknown, ok = true, status = 200) {
|
|
return {
|
|
ok,
|
|
status,
|
|
text: async () => (typeof body === "string" ? body : JSON.stringify(body)),
|
|
};
|
|
}
|
|
|
|
const cfg = { url: "https://relay.test", apiKey: "key123", clientId: "clientA" };
|
|
let calls: { method: string; url: string; body?: unknown }[] = [];
|
|
const realFetch = globalThis.fetch;
|
|
|
|
beforeEach(() => {
|
|
calls = [];
|
|
globalThis.fetch = vi.fn(async (url: string, init?: RequestInit) => {
|
|
calls.push({ method: init?.method ?? "GET", url, body: init?.body ? JSON.parse(String(init.body)) : undefined });
|
|
return resp({ data: { name: "fetched", _id: "x", flags: { "campaign-codex": { type: "npc" } } } });
|
|
}) as unknown as typeof fetch;
|
|
});
|
|
afterEach(() => { globalThis.fetch = realFetch; });
|
|
|
|
describe("RelayClient auth + routing", () => {
|
|
it("requires an api key", () => {
|
|
expect(() => new RelayClient({ url: cfg.url, apiKey: "", clientId: cfg.clientId })).toThrow(/RELAY_API_KEY/);
|
|
});
|
|
|
|
it("sends x-api-key header and clientId as a query param", async () => {
|
|
const c = new RelayClient(cfg);
|
|
await c.getEntry("JournalEntry.aaa");
|
|
const call = calls[0];
|
|
expect(call.method).toBe("GET");
|
|
expect(call.url).toContain("https://relay.test/get");
|
|
expect(call.url).toContain("clientId=clientA");
|
|
});
|
|
|
|
it("omits clientId when unset", async () => {
|
|
const c = new RelayClient({ url: cfg.url, apiKey: "k", clientId: "" });
|
|
await c.getEntry("JournalEntry.aaa");
|
|
expect(calls[0].url).not.toContain("clientId");
|
|
});
|
|
});
|
|
|
|
describe("RelayClient /get", () => {
|
|
it("parses the { data } envelope", async () => {
|
|
const c = new RelayClient(cfg);
|
|
const entry = await c.getEntry("JournalEntry.aaa");
|
|
expect(entry.name).toBe("fetched");
|
|
expect(entry.flags?.["campaign-codex"]?.type).toBe("npc");
|
|
expect(calls[0].url).toContain("uuid=JournalEntry.aaa");
|
|
});
|
|
|
|
it("throws when the envelope has no data", async () => {
|
|
globalThis.fetch = vi.fn(async () => resp({})) as unknown as typeof fetch;
|
|
const c = new RelayClient(cfg);
|
|
await expect(c.getEntry("JournalEntry.aaa")).rejects.toThrow(/no data/);
|
|
});
|
|
|
|
it("surfaces relay-level { error } on non-2xx", async () => {
|
|
globalThis.fetch = vi.fn(async () => resp({ error: "No connected Foundry clients found" }, false, 404)) as unknown as typeof fetch;
|
|
const c = new RelayClient(cfg);
|
|
await expect(c.getEntry("JournalEntry.aaa")).rejects.toThrow(/404.*No connected Foundry clients/);
|
|
});
|
|
});
|
|
|
|
describe("RelayClient /update", () => {
|
|
it("PUTs { data } and parses the { entity: [...] } envelope", async () => {
|
|
globalThis.fetch = vi.fn(async (_u: string, init?: RequestInit) => {
|
|
calls.push({ method: init?.method ?? "GET", url: _u, body: init?.body ? JSON.parse(String(init.body)) : undefined });
|
|
return resp({ entity: [{ name: "Updated", _id: "x" }] });
|
|
}) as unknown as typeof fetch;
|
|
const c = new RelayClient(cfg);
|
|
const updated = await c.updateEntry("JournalEntry.aaa", { name: "Updated", "flags.campaign-codex": { type: "npc" } });
|
|
expect(updated.name).toBe("Updated");
|
|
expect(calls[0].method).toBe("PUT");
|
|
expect(calls[0].url).toContain("/update");
|
|
expect(calls[0].url).toContain("uuid=JournalEntry.aaa");
|
|
expect((calls[0].body as Record<string, unknown>).data).toEqual({ name: "Updated", "flags.campaign-codex": { type: "npc" } });
|
|
});
|
|
|
|
it("throws when entity is missing", async () => {
|
|
globalThis.fetch = vi.fn(async () => resp({})) as unknown as typeof fetch;
|
|
const c = new RelayClient(cfg);
|
|
await expect(c.updateEntry("JournalEntry.aaa", { name: "x" })).rejects.toThrow(/no entity/);
|
|
});
|
|
});
|
|
|
|
describe("RelayClient /search", () => {
|
|
it("lists journal entries with the right filter params", async () => {
|
|
globalThis.fetch = vi.fn(async (u: string) => {
|
|
calls.push({ method: "GET", url: u });
|
|
return resp({ results: [{ uuid: "JournalEntry.aaa", id: "aaa", name: "Fenris", documentType: "JournalEntry" }] });
|
|
}) as unknown as typeof fetch;
|
|
const c = new RelayClient(cfg);
|
|
const results = await c.searchJournalEntries();
|
|
expect(results[0].name).toBe("Fenris");
|
|
const url = calls[0].url;
|
|
expect(url).toContain("filter=documentType%3AJournalEntry");
|
|
expect(url).toContain("excludeCompendiums=true");
|
|
expect(url).toContain("minified=true");
|
|
});
|
|
});
|
|
|
|
describe("RelayClient /create", () => {
|
|
it("POSTs entityType + data and parses { uuid, data }", async () => {
|
|
globalThis.fetch = vi.fn(async (_u: string, init?: RequestInit) => {
|
|
calls.push({ method: init?.method ?? "GET", url: _u, body: init?.body ? JSON.parse(String(init.body)) : undefined });
|
|
return resp({ uuid: "JournalEntry.new", data: { name: "My Entry", _id: "new" } });
|
|
}) as unknown as typeof fetch;
|
|
const c = new RelayClient(cfg);
|
|
const created = await c.createEntry("JournalEntry", { name: "My Entry" });
|
|
expect(created.uuid).toBe("JournalEntry.new");
|
|
expect(created.data.name).toBe("My Entry");
|
|
expect(calls[0].method).toBe("POST");
|
|
expect((calls[0].body as Record<string, unknown>).entityType).toBe("JournalEntry");
|
|
});
|
|
}); |