Loading fedify/deno.json +0 −1 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ "@cfworker/json-schema": "npm:@cfworker/json-schema@^4.1.1", "@cloudflare/workers-types": "npm:@cloudflare/workers-types@^4.20250529.0", "@es-toolkit/es-toolkit": "jsr:@es-toolkit/es-toolkit@^1.38.0", "@hongminhee/deno-mock-fetch": "jsr:@hongminhee/deno-mock-fetch@^0.3.2", "@hugoalh/http-header-link": "jsr:@hugoalh/http-header-link@^1.0.2", "@multiformats/base-x": "npm:@multiformats/base-x@^4.0.1", "@opentelemetry/api": "npm:@opentelemetry/api@^1.9.0", Loading fedify/nodeinfo/client.test.ts +20 −34 Original line number Diff line number Diff line import * as mf from "@hongminhee/deno-mock-fetch"; import { assertEquals } from "@std/assert"; import fetchMock from "fetch-mock"; import { test } from "../testing/mod.ts"; import { getNodeInfo, Loading @@ -19,20 +19,17 @@ import type { } from "./types.ts"; test("getNodeInfo()", async (t) => { mf.install(); fetchMock.spyGlobal(); mf.mock("GET@/.well-known/nodeinfo", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response( JSON.stringify({ fetchMock.get("https://example.com/.well-known/nodeinfo", { body: { links: [ { rel: "http://nodeinfo.diaspora.software/ns/schema/2.1", href: "https://example.com/nodeinfo/2.1", }, ], }), ); }, }); const rawExpected = { Loading @@ -41,17 +38,8 @@ test("getNodeInfo()", async (t) => { usage: { users: {}, localPosts: 123, localComments: 456 }, }; mf.mock("GET@/nodeinfo/2.1", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response( JSON.stringify(rawExpected), ); }); mf.mock("GET@/404", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response(null, { status: 404 }); }); fetchMock.get("https://example.com/nodeinfo/2.1", { body: rawExpected }); fetchMock.get("https://example.com/404", { status: 404 }); const expected: NodeInfo = { software: { Loading @@ -78,11 +66,9 @@ test("getNodeInfo()", async (t) => { assertEquals(info, expected); }); mf.mock("GET@/.well-known/nodeinfo", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response(JSON.stringify({ links: [], })); fetchMock.removeRoutes(); fetchMock.get("https://example.com/.well-known/nodeinfo", { body: { links: [] }, }); await t.step("indirect: no links", async () => { Loading @@ -90,9 +76,9 @@ test("getNodeInfo()", async (t) => { assertEquals(info, undefined); }); mf.mock("GET@/.well-known/nodeinfo", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response(null, { status: 404 }); fetchMock.removeRoutes(); fetchMock.get("https://example.com/.well-known/nodeinfo", { status: 404, }); await t.step("indirect: 404", async () => { Loading @@ -113,7 +99,7 @@ test("getNodeInfo()", async (t) => { assertEquals(info2, undefined); }); mf.uninstall(); fetchMock.hardReset(); }); test("parseNodeInfo()", () => { Loading fedify/package.json +0 −1 Original line number Diff line number Diff line Loading @@ -104,7 +104,6 @@ }, "devDependencies": { "@cloudflare/workers-types": "^4.20250529.0", "@hongminhee/deno-mock-fetch": "jsr:^0.3.2", "@std/assert": "jsr:^0.226.0", "@std/path": "jsr:^1.0.9", "@std/url": "jsr:1.0.0-rc.3", Loading fedify/runtime/docloader.test.ts +87 −111 Original line number Diff line number Diff line import * as mf from "@hongminhee/deno-mock-fetch"; import { assertEquals, assertRejects, assertThrows } from "@std/assert"; import fetchMock from "fetch-mock"; import process from "node:process"; import metadata from "../deno.json" with { type: "json" }; import type { KvKey, KvStore, KvStoreSetOptions } from "../federation/kv.ts"; Loading Loading @@ -29,18 +29,16 @@ test("new FetchError()", () => { test("getDocumentLoader()", async (t) => { const fetchDocumentLoader = getDocumentLoader(); mf.install(); fetchMock.spyGlobal(); mf.mock("GET@/object", (_req) => new Response( JSON.stringify({ fetchMock.get("https://example.com/object", { body: { "@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"), { Loading @@ -55,67 +53,49 @@ test("getDocumentLoader()", async (t) => { }); }); mf.mock("GET@/link-ctx", (_req) => new Response( JSON.stringify({ fetchMock.get("https://example.com/link-ctx", { body: { id: "https://example.com/link-ctx", name: "Fetched object", type: "Object", }), { status: 200, }, headers: { "Content-Type": "application/activity+json", Link: "<https://www.w3.org/ns/activitystreams>; " + 'rel="http://www.w3.org/ns/json-ld#context"; ' + 'type="application/ld+json"', }, }, )); }); mf.mock("GET@/link-obj", (_req) => new Response( "", { status: 200, fetchMock.get("https://example.com/link-obj", { headers: { "Content-Type": "text/html; charset=utf-8", Link: '<https://example.com/object>; rel="alternate"; ' + 'type="application/activity+json"', }, }, )); }); mf.mock("GET@/link-obj-relative", (_req) => new Response( "", { status: 200, fetchMock.get("https://example.com/link-obj-relative", { headers: { "Content-Type": "text/html; charset=utf-8", Link: '</object>; rel="alternate"; ' + 'type="application/activity+json"', }, }, )); }); mf.mock("GET@/obj-w-wrong-link", (_req) => new Response( JSON.stringify({ fetchMock.get("https://example.com/obj-w-wrong-link", { body: { "@context": "https://www.w3.org/ns/activitystreams", id: "https://example.com/obj-w-wrong-link", name: "Fetched object", type: "Object", }), { status: 200, }, headers: { "Content-Type": "text/html; charset=utf-8", Link: '<https://example.com/object>; rel="alternate"; ' + 'type="application/ld+json; profile="https://www.w3.org/ns/activitystreams""', }, }, )); }); await t.step("Link header", async () => { assertEquals(await fetchDocumentLoader("https://example.com/link-ctx"), { Loading Loading @@ -182,9 +162,8 @@ test("getDocumentLoader()", async (t) => { ); }); mf.mock("GET@/html-link", (_req) => new Response( `<html> fetchMock.get("https://example.com/html-link", { body: `<html> <head> <meta charset=utf-8> <link Loading @@ -193,11 +172,8 @@ test("getDocumentLoader()", async (t) => { href="https://example.com/object"> </head> </html>`, { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" }, }, )); }); await t.step("HTML <link>", async () => { assertEquals(await fetchDocumentLoader("https://example.com/html-link"), { Loading @@ -212,9 +188,8 @@ test("getDocumentLoader()", async (t) => { }); }); mf.mock("GET@/html-a", (_req) => new Response( `<html> fetchMock.get("https://example.com/html-a", { body: `<html> <head> <meta charset=utf-8> </head> Loading @@ -225,11 +200,8 @@ test("getDocumentLoader()", async (t) => { href=https://example.com/object>test</a> </body> </html>`, { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" }, }, )); }); await t.step("HTML <a>", async () => { assertEquals(await fetchDocumentLoader("https://example.com/html-a"), { Loading @@ -244,16 +216,15 @@ test("getDocumentLoader()", async (t) => { }); }); mf.mock("GET@/wrong-content-type", (_req) => new Response( JSON.stringify({ fetchMock.get("https://example.com/wrong-content-type", { body: { "@context": "https://www.w3.org/ns/activitystreams", id: "https://example.com/wrong-content-type", name: "Fetched object", type: "Object", }), { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" } }, )); }, headers: { "Content-Type": "text/html; charset=utf-8" }, }); await t.step("Wrong Content-Type", async () => { assertEquals( Loading @@ -271,7 +242,7 @@ test("getDocumentLoader()", async (t) => { ); }); mf.mock("GET@/404", (_req) => new Response("", { status: 404 })); fetchMock.get("https://example.com/404", { status: 404 }); await t.step("not ok", async () => { await assertRejects( Loading @@ -298,14 +269,13 @@ test("getDocumentLoader()", async (t) => { ); }); mf.mock( "GET@/localhost-redirect", (_req) => Response.redirect("https://localhost/object", 302), ); fetchMock.get("https://example.com/localhost-redirect", { status: 302, headers: { Location: "https://localhost/object" }, }); mf.mock("GET@/localhost-link", (_req) => new Response( `<html> fetchMock.get("https://example.com/localhost-link", { body: `<html> <head> <meta charset=utf-8> <link Loading @@ -314,11 +284,17 @@ test("getDocumentLoader()", async (t) => { href="https://localhost/object"> </head> </html>`, { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" }, }); fetchMock.get("https://localhost/object", { body: { "@context": "https://www.w3.org/ns/activitystreams", id: "https://localhost/object", name: "Fetched object", type: "Object", }, )); }); await t.step("allowPrivateAddress: false", async () => { await assertRejects( Loading @@ -343,7 +319,7 @@ test("getDocumentLoader()", async (t) => { documentUrl: "https://localhost/object", document: { "@context": "https://www.w3.org/ns/activitystreams", id: "https://example.com/object", id: "https://localhost/object", name: "Fetched object", type: "Object", }, Loading @@ -362,7 +338,7 @@ test("getDocumentLoader()", async (t) => { ); }); mf.uninstall(); fetchMock.hardReset(); }); test("kvCache()", async (t) => { Loading fedify/sig/http.test.ts +37 −32 Original line number Diff line number Diff line import * as mf from "@hongminhee/deno-mock-fetch"; import { assert, assertEquals, Loading @@ -7,6 +6,7 @@ import { assertStringIncludes, } from "@std/assert"; import { encodeBase64 } from "byte-encodings/base64"; import fetchMock from "fetch-mock"; import { exportSpki } from "../runtime/key.ts"; import { mockDocumentLoader } from "../testing/docloader.ts"; import { Loading Loading @@ -1163,15 +1163,16 @@ test("verifyRequest() [rfc9421] test vector from Mastodon", async () => { test("doubleKnock() function with successful first attempt", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // A counter to track the number of times the endpoint is hit let requestCount = 0; let firstRequestSpec: string | null = null; // Mock an endpoint that accepts RFC 9421 signatures mf.mock("POST@/inbox-accepts-rfc9421", (req) => { fetchMock.post("https://example.com/inbox-accepts-rfc9421", (cl) => { requestCount++; const req = cl.request!; const signatureInputHeader = req.headers.get("Signature-Input"); const signatureHeader = req.headers.get("Signature"); Loading Loading @@ -1243,12 +1244,12 @@ test("doubleKnock() function with successful first attempt", async () => { "Logged request should have RFC 9421 Signature header", ); mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() function with fallback to draft-cavage", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts and specs used let requestCount = 0; Loading @@ -1256,7 +1257,8 @@ test("doubleKnock() function with fallback to draft-cavage", async () => { let secondSpec: string | null = null; // Mock an endpoint that only accepts draft-cavage signatures mf.mock("POST@/inbox-accepts-draft-cavage", (req) => { fetchMock.post("https://example.com/inbox-accepts-draft-cavage", (cl) => { const req = cl.request!; requestCount++; // Check which signature format was used Loading Loading @@ -1331,27 +1333,27 @@ test("doubleKnock() function with fallback to draft-cavage", async () => { "Successful spec should be remembered", ); mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() function with redirect handling", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts and redirects const requestedUrls: string[] = []; const responseCodes: number[] = []; // Mock an endpoint that redirects mf.mock("POST@/redirect-endpoint", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/redirect-endpoint", (cl) => { requestedUrls.push(cl.url); responseCodes.push(302); return Response.redirect("https://example.com/final-endpoint", 302); }); // Mock the destination endpoint mf.mock("POST@/final-endpoint", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/final-endpoint", (cl) => { requestedUrls.push(cl.url); responseCodes.push(202); return new Response("", { status: 202 }); }); Loading Loading @@ -1397,19 +1399,20 @@ test("doubleKnock() function with redirect handling", async () => { "Response status codes should match expected sequence", ); mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() function with both specs rejected", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts let requestCount = 0; const attempts: string[] = []; // Mock an endpoint that rejects all signatures mf.mock("POST@/inbox-rejects-all", (req) => { fetchMock.post("https://example.com/inbox-rejects-all", (cl) => { const req = cl.request!; requestCount++; if (req.headers.has("Signature-Input")) { Loading Loading @@ -1460,19 +1463,20 @@ test("doubleKnock() function with both specs rejected", async () => { "Second attempt should use draft-cavage", ); mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() function with specDeterminer choosing draft-cavage first", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts let requestCount = 0; let firstSpec: string | null = null; // Mock an endpoint that accepts draft-cavage signatures mf.mock("POST@/inbox-accepts-any", (req) => { fetchMock.post("https://example.com/inbox-accepts-any", (cl) => { const req = cl.request!; requestCount++; if (req.headers.has("Signature-Input")) { Loading Loading @@ -1525,34 +1529,34 @@ test("doubleKnock() function with specDeterminer choosing draft-cavage first", a "First attempt should use draft-cavage", ); mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() complex redirect chain test", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts const requestedUrls: string[] = []; // Create a redirect chain with 3 redirects mf.mock("POST@/redirect1", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/redirect1", (cl) => { requestedUrls.push(cl.url); return Response.redirect("https://example.com/redirect2", 302); }); mf.mock("POST@/redirect2", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/redirect2", (cl) => { requestedUrls.push(cl.url); return Response.redirect("https://example.com/redirect3", 307); }); mf.mock("POST@/redirect3", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/redirect3", (cl) => { requestedUrls.push(cl.url); return Response.redirect("https://example.com/final", 301); }); mf.mock("POST@/final", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/final", (cl) => { requestedUrls.push(cl.url); return new Response("Success", { status: 200 }); }); Loading Loading @@ -1622,19 +1626,20 @@ test("doubleKnock() complex redirect chain test", async () => { ); } mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() async specDeterminer test", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts let requestCount = 0; let specUsed: string | null = null; // Mock an endpoint that accepts both types of signatures mf.mock("POST@/inbox-async-determiner", (req) => { fetchMock.post("https://example.com/inbox-async-determiner", (cl) => { const req = cl.request!; requestCount++; if (req.headers.has("Signature-Input")) { Loading Loading @@ -1689,7 +1694,7 @@ test("doubleKnock() async specDeterminer test", async () => { "Should use spec from async determiner", ); mf.uninstall(); fetchMock.hardReset(); }); test("timingSafeEqual()", async (t) => { Loading Loading
fedify/deno.json +0 −1 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ "@cfworker/json-schema": "npm:@cfworker/json-schema@^4.1.1", "@cloudflare/workers-types": "npm:@cloudflare/workers-types@^4.20250529.0", "@es-toolkit/es-toolkit": "jsr:@es-toolkit/es-toolkit@^1.38.0", "@hongminhee/deno-mock-fetch": "jsr:@hongminhee/deno-mock-fetch@^0.3.2", "@hugoalh/http-header-link": "jsr:@hugoalh/http-header-link@^1.0.2", "@multiformats/base-x": "npm:@multiformats/base-x@^4.0.1", "@opentelemetry/api": "npm:@opentelemetry/api@^1.9.0", Loading
fedify/nodeinfo/client.test.ts +20 −34 Original line number Diff line number Diff line import * as mf from "@hongminhee/deno-mock-fetch"; import { assertEquals } from "@std/assert"; import fetchMock from "fetch-mock"; import { test } from "../testing/mod.ts"; import { getNodeInfo, Loading @@ -19,20 +19,17 @@ import type { } from "./types.ts"; test("getNodeInfo()", async (t) => { mf.install(); fetchMock.spyGlobal(); mf.mock("GET@/.well-known/nodeinfo", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response( JSON.stringify({ fetchMock.get("https://example.com/.well-known/nodeinfo", { body: { links: [ { rel: "http://nodeinfo.diaspora.software/ns/schema/2.1", href: "https://example.com/nodeinfo/2.1", }, ], }), ); }, }); const rawExpected = { Loading @@ -41,17 +38,8 @@ test("getNodeInfo()", async (t) => { usage: { users: {}, localPosts: 123, localComments: 456 }, }; mf.mock("GET@/nodeinfo/2.1", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response( JSON.stringify(rawExpected), ); }); mf.mock("GET@/404", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response(null, { status: 404 }); }); fetchMock.get("https://example.com/nodeinfo/2.1", { body: rawExpected }); fetchMock.get("https://example.com/404", { status: 404 }); const expected: NodeInfo = { software: { Loading @@ -78,11 +66,9 @@ test("getNodeInfo()", async (t) => { assertEquals(info, expected); }); mf.mock("GET@/.well-known/nodeinfo", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response(JSON.stringify({ links: [], })); fetchMock.removeRoutes(); fetchMock.get("https://example.com/.well-known/nodeinfo", { body: { links: [] }, }); await t.step("indirect: no links", async () => { Loading @@ -90,9 +76,9 @@ test("getNodeInfo()", async (t) => { assertEquals(info, undefined); }); mf.mock("GET@/.well-known/nodeinfo", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response(null, { status: 404 }); fetchMock.removeRoutes(); fetchMock.get("https://example.com/.well-known/nodeinfo", { status: 404, }); await t.step("indirect: 404", async () => { Loading @@ -113,7 +99,7 @@ test("getNodeInfo()", async (t) => { assertEquals(info2, undefined); }); mf.uninstall(); fetchMock.hardReset(); }); test("parseNodeInfo()", () => { Loading
fedify/package.json +0 −1 Original line number Diff line number Diff line Loading @@ -104,7 +104,6 @@ }, "devDependencies": { "@cloudflare/workers-types": "^4.20250529.0", "@hongminhee/deno-mock-fetch": "jsr:^0.3.2", "@std/assert": "jsr:^0.226.0", "@std/path": "jsr:^1.0.9", "@std/url": "jsr:1.0.0-rc.3", Loading
fedify/runtime/docloader.test.ts +87 −111 Original line number Diff line number Diff line import * as mf from "@hongminhee/deno-mock-fetch"; import { assertEquals, assertRejects, assertThrows } from "@std/assert"; import fetchMock from "fetch-mock"; import process from "node:process"; import metadata from "../deno.json" with { type: "json" }; import type { KvKey, KvStore, KvStoreSetOptions } from "../federation/kv.ts"; Loading Loading @@ -29,18 +29,16 @@ test("new FetchError()", () => { test("getDocumentLoader()", async (t) => { const fetchDocumentLoader = getDocumentLoader(); mf.install(); fetchMock.spyGlobal(); mf.mock("GET@/object", (_req) => new Response( JSON.stringify({ fetchMock.get("https://example.com/object", { body: { "@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"), { Loading @@ -55,67 +53,49 @@ test("getDocumentLoader()", async (t) => { }); }); mf.mock("GET@/link-ctx", (_req) => new Response( JSON.stringify({ fetchMock.get("https://example.com/link-ctx", { body: { id: "https://example.com/link-ctx", name: "Fetched object", type: "Object", }), { status: 200, }, headers: { "Content-Type": "application/activity+json", Link: "<https://www.w3.org/ns/activitystreams>; " + 'rel="http://www.w3.org/ns/json-ld#context"; ' + 'type="application/ld+json"', }, }, )); }); mf.mock("GET@/link-obj", (_req) => new Response( "", { status: 200, fetchMock.get("https://example.com/link-obj", { headers: { "Content-Type": "text/html; charset=utf-8", Link: '<https://example.com/object>; rel="alternate"; ' + 'type="application/activity+json"', }, }, )); }); mf.mock("GET@/link-obj-relative", (_req) => new Response( "", { status: 200, fetchMock.get("https://example.com/link-obj-relative", { headers: { "Content-Type": "text/html; charset=utf-8", Link: '</object>; rel="alternate"; ' + 'type="application/activity+json"', }, }, )); }); mf.mock("GET@/obj-w-wrong-link", (_req) => new Response( JSON.stringify({ fetchMock.get("https://example.com/obj-w-wrong-link", { body: { "@context": "https://www.w3.org/ns/activitystreams", id: "https://example.com/obj-w-wrong-link", name: "Fetched object", type: "Object", }), { status: 200, }, headers: { "Content-Type": "text/html; charset=utf-8", Link: '<https://example.com/object>; rel="alternate"; ' + 'type="application/ld+json; profile="https://www.w3.org/ns/activitystreams""', }, }, )); }); await t.step("Link header", async () => { assertEquals(await fetchDocumentLoader("https://example.com/link-ctx"), { Loading Loading @@ -182,9 +162,8 @@ test("getDocumentLoader()", async (t) => { ); }); mf.mock("GET@/html-link", (_req) => new Response( `<html> fetchMock.get("https://example.com/html-link", { body: `<html> <head> <meta charset=utf-8> <link Loading @@ -193,11 +172,8 @@ test("getDocumentLoader()", async (t) => { href="https://example.com/object"> </head> </html>`, { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" }, }, )); }); await t.step("HTML <link>", async () => { assertEquals(await fetchDocumentLoader("https://example.com/html-link"), { Loading @@ -212,9 +188,8 @@ test("getDocumentLoader()", async (t) => { }); }); mf.mock("GET@/html-a", (_req) => new Response( `<html> fetchMock.get("https://example.com/html-a", { body: `<html> <head> <meta charset=utf-8> </head> Loading @@ -225,11 +200,8 @@ test("getDocumentLoader()", async (t) => { href=https://example.com/object>test</a> </body> </html>`, { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" }, }, )); }); await t.step("HTML <a>", async () => { assertEquals(await fetchDocumentLoader("https://example.com/html-a"), { Loading @@ -244,16 +216,15 @@ test("getDocumentLoader()", async (t) => { }); }); mf.mock("GET@/wrong-content-type", (_req) => new Response( JSON.stringify({ fetchMock.get("https://example.com/wrong-content-type", { body: { "@context": "https://www.w3.org/ns/activitystreams", id: "https://example.com/wrong-content-type", name: "Fetched object", type: "Object", }), { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" } }, )); }, headers: { "Content-Type": "text/html; charset=utf-8" }, }); await t.step("Wrong Content-Type", async () => { assertEquals( Loading @@ -271,7 +242,7 @@ test("getDocumentLoader()", async (t) => { ); }); mf.mock("GET@/404", (_req) => new Response("", { status: 404 })); fetchMock.get("https://example.com/404", { status: 404 }); await t.step("not ok", async () => { await assertRejects( Loading @@ -298,14 +269,13 @@ test("getDocumentLoader()", async (t) => { ); }); mf.mock( "GET@/localhost-redirect", (_req) => Response.redirect("https://localhost/object", 302), ); fetchMock.get("https://example.com/localhost-redirect", { status: 302, headers: { Location: "https://localhost/object" }, }); mf.mock("GET@/localhost-link", (_req) => new Response( `<html> fetchMock.get("https://example.com/localhost-link", { body: `<html> <head> <meta charset=utf-8> <link Loading @@ -314,11 +284,17 @@ test("getDocumentLoader()", async (t) => { href="https://localhost/object"> </head> </html>`, { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" }, }); fetchMock.get("https://localhost/object", { body: { "@context": "https://www.w3.org/ns/activitystreams", id: "https://localhost/object", name: "Fetched object", type: "Object", }, )); }); await t.step("allowPrivateAddress: false", async () => { await assertRejects( Loading @@ -343,7 +319,7 @@ test("getDocumentLoader()", async (t) => { documentUrl: "https://localhost/object", document: { "@context": "https://www.w3.org/ns/activitystreams", id: "https://example.com/object", id: "https://localhost/object", name: "Fetched object", type: "Object", }, Loading @@ -362,7 +338,7 @@ test("getDocumentLoader()", async (t) => { ); }); mf.uninstall(); fetchMock.hardReset(); }); test("kvCache()", async (t) => { Loading
fedify/sig/http.test.ts +37 −32 Original line number Diff line number Diff line import * as mf from "@hongminhee/deno-mock-fetch"; import { assert, assertEquals, Loading @@ -7,6 +6,7 @@ import { assertStringIncludes, } from "@std/assert"; import { encodeBase64 } from "byte-encodings/base64"; import fetchMock from "fetch-mock"; import { exportSpki } from "../runtime/key.ts"; import { mockDocumentLoader } from "../testing/docloader.ts"; import { Loading Loading @@ -1163,15 +1163,16 @@ test("verifyRequest() [rfc9421] test vector from Mastodon", async () => { test("doubleKnock() function with successful first attempt", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // A counter to track the number of times the endpoint is hit let requestCount = 0; let firstRequestSpec: string | null = null; // Mock an endpoint that accepts RFC 9421 signatures mf.mock("POST@/inbox-accepts-rfc9421", (req) => { fetchMock.post("https://example.com/inbox-accepts-rfc9421", (cl) => { requestCount++; const req = cl.request!; const signatureInputHeader = req.headers.get("Signature-Input"); const signatureHeader = req.headers.get("Signature"); Loading Loading @@ -1243,12 +1244,12 @@ test("doubleKnock() function with successful first attempt", async () => { "Logged request should have RFC 9421 Signature header", ); mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() function with fallback to draft-cavage", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts and specs used let requestCount = 0; Loading @@ -1256,7 +1257,8 @@ test("doubleKnock() function with fallback to draft-cavage", async () => { let secondSpec: string | null = null; // Mock an endpoint that only accepts draft-cavage signatures mf.mock("POST@/inbox-accepts-draft-cavage", (req) => { fetchMock.post("https://example.com/inbox-accepts-draft-cavage", (cl) => { const req = cl.request!; requestCount++; // Check which signature format was used Loading Loading @@ -1331,27 +1333,27 @@ test("doubleKnock() function with fallback to draft-cavage", async () => { "Successful spec should be remembered", ); mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() function with redirect handling", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts and redirects const requestedUrls: string[] = []; const responseCodes: number[] = []; // Mock an endpoint that redirects mf.mock("POST@/redirect-endpoint", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/redirect-endpoint", (cl) => { requestedUrls.push(cl.url); responseCodes.push(302); return Response.redirect("https://example.com/final-endpoint", 302); }); // Mock the destination endpoint mf.mock("POST@/final-endpoint", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/final-endpoint", (cl) => { requestedUrls.push(cl.url); responseCodes.push(202); return new Response("", { status: 202 }); }); Loading Loading @@ -1397,19 +1399,20 @@ test("doubleKnock() function with redirect handling", async () => { "Response status codes should match expected sequence", ); mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() function with both specs rejected", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts let requestCount = 0; const attempts: string[] = []; // Mock an endpoint that rejects all signatures mf.mock("POST@/inbox-rejects-all", (req) => { fetchMock.post("https://example.com/inbox-rejects-all", (cl) => { const req = cl.request!; requestCount++; if (req.headers.has("Signature-Input")) { Loading Loading @@ -1460,19 +1463,20 @@ test("doubleKnock() function with both specs rejected", async () => { "Second attempt should use draft-cavage", ); mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() function with specDeterminer choosing draft-cavage first", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts let requestCount = 0; let firstSpec: string | null = null; // Mock an endpoint that accepts draft-cavage signatures mf.mock("POST@/inbox-accepts-any", (req) => { fetchMock.post("https://example.com/inbox-accepts-any", (cl) => { const req = cl.request!; requestCount++; if (req.headers.has("Signature-Input")) { Loading Loading @@ -1525,34 +1529,34 @@ test("doubleKnock() function with specDeterminer choosing draft-cavage first", a "First attempt should use draft-cavage", ); mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() complex redirect chain test", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts const requestedUrls: string[] = []; // Create a redirect chain with 3 redirects mf.mock("POST@/redirect1", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/redirect1", (cl) => { requestedUrls.push(cl.url); return Response.redirect("https://example.com/redirect2", 302); }); mf.mock("POST@/redirect2", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/redirect2", (cl) => { requestedUrls.push(cl.url); return Response.redirect("https://example.com/redirect3", 307); }); mf.mock("POST@/redirect3", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/redirect3", (cl) => { requestedUrls.push(cl.url); return Response.redirect("https://example.com/final", 301); }); mf.mock("POST@/final", (req) => { requestedUrls.push(req.url); fetchMock.post("https://example.com/final", (cl) => { requestedUrls.push(cl.url); return new Response("Success", { status: 200 }); }); Loading Loading @@ -1622,19 +1626,20 @@ test("doubleKnock() complex redirect chain test", async () => { ); } mf.uninstall(); fetchMock.hardReset(); }); test("doubleKnock() async specDeterminer test", async () => { // Install mock fetch handler mf.install(); fetchMock.spyGlobal(); // Track request attempts let requestCount = 0; let specUsed: string | null = null; // Mock an endpoint that accepts both types of signatures mf.mock("POST@/inbox-async-determiner", (req) => { fetchMock.post("https://example.com/inbox-async-determiner", (cl) => { const req = cl.request!; requestCount++; if (req.headers.has("Signature-Input")) { Loading Loading @@ -1689,7 +1694,7 @@ test("doubleKnock() async specDeterminer test", async () => { "Should use spec from async determiner", ); mf.uninstall(); fetchMock.hardReset(); }); test("timingSafeEqual()", async (t) => { Loading