diff --git a/runtime/docloader.test.ts b/runtime/docloader.test.ts index c8e0d669..971c7a01 100644 --- a/runtime/docloader.test.ts +++ b/runtime/docloader.test.ts @@ -1,7 +1,12 @@ +import * as mf from "https://deno.land/x/mock_fetch@0.3.0/mod.ts"; +import { + assertEquals, + assertRejects, + assertThrows, +} from "jsr:@std/assert@^0.218.2"; import { Temporal } from "npm:@js-temporal/polyfill@^0.4.4"; -import { assertEquals, assertThrows } from "jsr:@std/assert@^0.218.2"; import { mockDocumentLoader } from "../testing/docloader.ts"; -import { FetchError, kvCache } from "./docloader.ts"; +import { fetchDocumentLoader, FetchError, kvCache } from "./docloader.ts"; Deno.test("new FetchError()", () => { const e = new FetchError("https://example.com/", "An error message."); @@ -14,6 +19,46 @@ Deno.test("new FetchError()", () => { assertEquals(e2.message, "https://example.org/"); }); +Deno.test("fetchDocumentLoader()", async (t) => { + mf.install(); + + mf.mock("GET@/object", (_req) => + new Response( + JSON.stringify({ + "@context": "https://www.w3.org/ns/activitystreams", + id: "https://example.com/object", + name: "Fetched object", + type: "Object", + }), + { status: 200 }, + )); + + await t.step("ok", async () => { + assertEquals(await fetchDocumentLoader("https://example.com/object"), { + contextUrl: null, + documentUrl: "https://example.com/object", + document: { + "@context": "https://www.w3.org/ns/activitystreams", + id: "https://example.com/object", + name: "Fetched object", + type: "Object", + }, + }); + }); + + mf.mock("GET@/404", (_req) => new Response("", { status: 404 })); + + await t.step("not ok", async () => { + await assertRejects( + () => fetchDocumentLoader("https://example.com/404"), + FetchError, + "HTTP 404: https://example.com/404", + ); + }); + + mf.uninstall(); +}); + Deno.test("kvCache()", async (t) => { const kv = await Deno.openKv(":memory:"); @@ -29,7 +74,7 @@ Deno.test("kvCache()", async (t) => { Temporal.Duration.from({ days: 30 }), ], ], - prefix: ["_test"], + prefix: ["_test", "cached"], }); const result = await loader("https://example.com/object"); assertEquals(result, { @@ -42,29 +87,78 @@ Deno.test("kvCache()", async (t) => { type: "Object", }, }); - const cache = await kv.get(["_test", "https://example.com/object"]); + const cache = await kv.get([ + "_test", + "cached", + "https://example.com/object", + ]); assertEquals(cache.value, result); await kv.set( - ["_test", "https://example.com/mock"], + ["_test", "cached", "https://example.org/"], { contextUrl: null, - documentUrl: "https://example.com/mock", + documentUrl: "https://example.org/", document: { - "id": "https://example.com/mock", + "id": "https://example.org/", }, }, ); - const result2 = await loader("https://example.com/mock"); + const result2 = await loader("https://example.org/"); assertEquals(result2, { contextUrl: null, - documentUrl: "https://example.com/mock", + documentUrl: "https://example.org/", + document: { + "id": "https://example.org/", + }, + }); + + await kv.set( + ["_test", "cached", "https://example.net/"], + { + contextUrl: null, + documentUrl: "https://example.net/", + document: { + "id": "https://example.net/", + }, + }, + ); + const result3 = await loader("https://example.net/"); + assertEquals(result3, { + contextUrl: null, + documentUrl: "https://example.net/", document: { - "id": "https://example.com/mock", + "id": "https://example.net/", }, }); }); + await t.step("not cached", async () => { + const loader = kvCache({ + kv, + loader: mockDocumentLoader, + rules: [], + prefix: ["_test", "not cached"], + }); + const result = await loader("https://example.com/object"); + assertEquals(result, { + contextUrl: null, + documentUrl: "https://example.com/object", + document: { + "@context": "https://www.w3.org/ns/activitystreams", + id: "https://example.com/object", + name: "Fetched object", + type: "Object", + }, + }); + const cache = await kv.get([ + "test2", + "not cached", + "https://example.com/object", + ]); + assertEquals(cache.value, null); + }); + await t.step("maximum cache duration", () => { assertThrows( () => @@ -73,7 +167,22 @@ Deno.test("kvCache()", async (t) => { loader: mockDocumentLoader, rules: [ [ - "https://example.com", + "https://example.com/", + Temporal.Duration.from({ days: 30, seconds: 1 }), + ], + ], + }), + TypeError, + "The maximum cache duration is 30 days", + ); + assertThrows( + () => + kvCache({ + kv, + loader: mockDocumentLoader, + rules: [ + [ + new URLPattern("https://example.com/*"), Temporal.Duration.from({ days: 30, seconds: 1 }), ], ], diff --git a/runtime/docloader.ts b/runtime/docloader.ts index f9b96561..42b1e920 100644 --- a/runtime/docloader.ts +++ b/runtime/docloader.ts @@ -51,16 +51,17 @@ export async function fetchDocumentLoader( }, redirect: "follow", }); + const documentUrl = response.url === "" ? url : response.url; if (!response.ok) { throw new FetchError( - response.url, - `HTTP ${response.status}: ${response.url}`, + documentUrl, + `HTTP ${response.status}: ${documentUrl}`, ); } return { contextUrl: null, document: await response.json(), - documentUrl: response.url, + documentUrl, }; } diff --git a/runtime/langstr.test.ts b/runtime/langstr.test.ts index 31c3fab2..228e78d8 100644 --- a/runtime/langstr.test.ts +++ b/runtime/langstr.test.ts @@ -1,6 +1,15 @@ import { assertEquals } from "jsr:@std/assert@^0.218.2"; +import { parseLanguageTag } from "npm:@phensley/language-tag@1.8.0"; import { LanguageString } from "./langstr.ts"; +Deno.test("new LanguageString()", () => { + const langStr = new LanguageString("Hello", "en"); + assertEquals(langStr.toString(), "Hello"); + assertEquals(langStr.language, parseLanguageTag("en")); + + assertEquals(new LanguageString("Hello", parseLanguageTag("en")), langStr); +}); + Deno.test("Deno.inspect(LanguageString)", () => { const langStr = new LanguageString("Hello", "en"); assertEquals(Deno.inspect(langStr, { colors: false }), ' "Hello"');