UNPKG

@fedify/fedify

Version:

An ActivityPub server framework

208 lines (207 loc) • 9.44 kB
import "@js-temporal/polyfill"; import "urlpattern-polyfill"; globalThis.addEventListener = () => {}; import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs"; import "../std__assert-CRDpx_HF.mjs"; import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-DGu1NFwu.mjs"; import { r as signObject } from "../proof-DLhLRv3m.mjs"; import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs"; import { o as createFederation } from "../middleware-D9k0Knum.mjs"; import { mockDocumentLoader, test } from "@fedify/fixture"; import { Create, Follow, Person } from "@fedify/vocab"; //#region src/federation/idempotency.test.ts const kv = new MemoryKvStore(); const federationOptions = { kv, documentLoaderFactory: () => mockDocumentLoader, authenticatedDocumentLoaderFactory: () => mockDocumentLoader }; function createTestFederation() { const federation = createFederation(federationOptions); federation.setActorDispatcher("/users/{identifier}", (_, identifier) => identifier === "john" ? new Person({}) : null).setKeyPairsDispatcher(() => [{ privateKey: ed25519PrivateKey, publicKey: ed25519PublicKey.publicKey }]); return federation; } test("Federation.setInboxListeners().withIdempotency() - per-origin strategy", async () => { const federation = createTestFederation(); const processedActivities = []; federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").withIdempotency("per-origin").on(Create, (ctx, activity) => { processedActivities.push([ctx.recipient, activity]); }); const signedActivity = await signObject(new Create({ id: new URL("https://example.com/activities/1"), actor: new URL("https://example.com/person2") }), ed25519PrivateKey, ed25519Multikey.id); const body = JSON.stringify(await signedActivity.toJsonLd({ contextLoader: mockDocumentLoader })); let response = await federation.fetch(new Request("https://example.com/users/john/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 1); assertEquals(processedActivities[0][0], "john"); response = await federation.fetch(new Request("https://example.com/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 1); await kv.delete([ "_fedify", "activityIdempotence", "https://example.com:https://example.com/activities/1" ]); }); test("Federation.setInboxListeners().withIdempotency() - per-inbox strategy", async () => { const federation = createTestFederation(); const processedActivities = []; federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").withIdempotency("per-inbox").on(Create, (ctx, activity) => { processedActivities.push([ctx.recipient, activity]); }); const signedActivity = await signObject(new Create({ id: new URL("https://example.com/activities/2"), actor: new URL("https://example.com/person2") }), ed25519PrivateKey, ed25519Multikey.id); const body = JSON.stringify(await signedActivity.toJsonLd({ contextLoader: mockDocumentLoader })); let response = await federation.fetch(new Request("https://example.com/users/john/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 1); assertEquals(processedActivities[0][0], "john"); response = await federation.fetch(new Request("https://example.com/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 2); assertEquals(processedActivities[1][0], null); response = await federation.fetch(new Request("https://example.com/users/john/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 2); }); test("Federation.setInboxListeners().withIdempotency() - global strategy", async () => { const federation = createTestFederation(); const processedActivities = []; federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").withIdempotency("global").on(Create, (ctx, activity) => { processedActivities.push([ctx.recipient, activity]); }); const signedActivity = await signObject(new Create({ id: new URL("https://example.com/activities/3"), actor: new URL("https://example.com/person2") }), ed25519PrivateKey, ed25519Multikey.id); const body = JSON.stringify(await signedActivity.toJsonLd({ contextLoader: mockDocumentLoader })); let response = await federation.fetch(new Request("https://example.com/users/john/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 1); response = await federation.fetch(new Request("https://example.com/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 1); }); test("Federation.setInboxListeners().withIdempotency() - custom callback", async () => { const federation = createTestFederation(); const processedActivities = []; federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").withIdempotency((ctx, activity) => { if (activity instanceof Follow) return null; const inboxId = ctx.recipient ?? "shared"; return `${ctx.origin}:${activity.id?.href}:${inboxId}`; }).on(Create, (ctx, activity) => { processedActivities.push([ctx.recipient, activity]); }).on(Follow, (ctx, activity) => { processedActivities.push([ctx.recipient, activity]); }); const signedCreate = await signObject(new Create({ id: new URL("https://example.com/activities/4"), actor: new URL("https://example.com/person2") }), ed25519PrivateKey, ed25519Multikey.id); const createBody = JSON.stringify(await signedCreate.toJsonLd({ contextLoader: mockDocumentLoader })); let response = await federation.fetch(new Request("https://example.com/users/john/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body: createBody }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 1); response = await federation.fetch(new Request("https://example.com/users/john/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body: createBody }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 1); const signedFollow = await signObject(new Follow({ id: new URL("https://example.com/activities/5"), actor: new URL("https://example.com/person2") }), ed25519PrivateKey, ed25519Multikey.id); const followBody = JSON.stringify(await signedFollow.toJsonLd({ contextLoader: mockDocumentLoader })); response = await federation.fetch(new Request("https://example.com/users/john/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body: followBody }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 2); response = await federation.fetch(new Request("https://example.com/users/john/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body: followBody }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 3); }); test("Federation.setInboxListeners() - default strategy is per-inbox", async () => { const federation = createTestFederation(); const processedActivities = []; federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, (ctx, activity) => { processedActivities.push([ctx.recipient, activity]); }); const signedActivity = await signObject(new Create({ id: new URL("https://example.com/activities/6"), actor: new URL("https://example.com/person2") }), ed25519PrivateKey, ed25519Multikey.id); const body = JSON.stringify(await signedActivity.toJsonLd({ contextLoader: mockDocumentLoader })); let response = await federation.fetch(new Request("https://example.com/users/john/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 1); assertEquals(processedActivities[0][0], "john"); response = await federation.fetch(new Request("https://example.com/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 2); assertEquals(processedActivities[1][0], null); response = await federation.fetch(new Request("https://example.com/users/john/inbox", { method: "POST", headers: { "Content-Type": "application/activity+json" }, body }), { contextData: void 0 }); assertEquals(response.status, 202); assertEquals(processedActivities.length, 2); }); //#endregion export {};