Skip to content

Commit 5271560

Browse files
committed
test(api): cover federation jsonld document routes
1 parent 3ed2eb8 commit 5271560

3 files changed

Lines changed: 97 additions & 36 deletions

File tree

packages/api/src/http.ts

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,34 @@ export const federationActorDocumentResponse = () =>
602602
return yield* _(jsonLdResponse(makeFederationActorDocument(context), 200))
603603
}).pipe(Effect.catchAll(errorResponse))
604604

605+
export const federationOutboxDocumentResponse = () =>
606+
Effect.gen(function*(_) {
607+
const request = yield* _(HttpServerRequest.HttpServerRequest)
608+
const context = yield* _(resolveFederationContext(request))
609+
return yield* _(jsonLdResponse(makeFederationOutboxCollection(context), 200))
610+
}).pipe(Effect.catchAll(errorResponse))
611+
612+
export const federationFollowersDocumentResponse = () =>
613+
Effect.gen(function*(_) {
614+
const request = yield* _(HttpServerRequest.HttpServerRequest)
615+
const context = yield* _(resolveFederationContext(request))
616+
return yield* _(jsonLdResponse(makeFederationFollowersCollection(context), 200))
617+
}).pipe(Effect.catchAll(errorResponse))
618+
619+
export const federationFollowingDocumentResponse = () =>
620+
Effect.gen(function*(_) {
621+
const request = yield* _(HttpServerRequest.HttpServerRequest)
622+
const context = yield* _(resolveFederationContext(request))
623+
return yield* _(jsonLdResponse(makeFederationFollowingCollection(context), 200))
624+
}).pipe(Effect.catchAll(errorResponse))
625+
626+
export const federationLikedDocumentResponse = () =>
627+
Effect.gen(function*(_) {
628+
const request = yield* _(HttpServerRequest.HttpServerRequest)
629+
const context = yield* _(resolveFederationContext(request))
630+
return yield* _(jsonLdResponse(makeFederationLikedCollection(context), 200))
631+
}).pipe(Effect.catchAll(errorResponse))
632+
605633
const terminalWebSocketUpgradeResponse = Effect.gen(function*(_) {
606634
const request = yield* _(HttpServerRequest.HttpServerRequest)
607635
const upgrade = readHeader(request, "upgrade")?.toLowerCase()
@@ -870,35 +898,19 @@ export const makeRouter = () => {
870898
),
871899
HttpRouter.get(
872900
"/federation/outbox",
873-
Effect.gen(function*(_) {
874-
const request = yield* _(HttpServerRequest.HttpServerRequest)
875-
const context = yield* _(resolveFederationContext(request))
876-
return yield* _(jsonLdResponse(makeFederationOutboxCollection(context), 200))
877-
}).pipe(Effect.catchAll(errorResponse))
901+
federationOutboxDocumentResponse()
878902
),
879903
HttpRouter.get(
880904
"/federation/followers",
881-
Effect.gen(function*(_) {
882-
const request = yield* _(HttpServerRequest.HttpServerRequest)
883-
const context = yield* _(resolveFederationContext(request))
884-
return yield* _(jsonLdResponse(makeFederationFollowersCollection(context), 200))
885-
}).pipe(Effect.catchAll(errorResponse))
905+
federationFollowersDocumentResponse()
886906
),
887907
HttpRouter.get(
888908
"/federation/following",
889-
Effect.gen(function*(_) {
890-
const request = yield* _(HttpServerRequest.HttpServerRequest)
891-
const context = yield* _(resolveFederationContext(request))
892-
return yield* _(jsonLdResponse(makeFederationFollowingCollection(context), 200))
893-
}).pipe(Effect.catchAll(errorResponse))
909+
federationFollowingDocumentResponse()
894910
),
895911
HttpRouter.get(
896912
"/federation/liked",
897-
Effect.gen(function*(_) {
898-
const request = yield* _(HttpServerRequest.HttpServerRequest)
899-
const context = yield* _(resolveFederationContext(request))
900-
return yield* _(jsonLdResponse(makeFederationLikedCollection(context), 200))
901-
}).pipe(Effect.catchAll(errorResponse))
913+
federationLikedDocumentResponse()
902914
),
903915
HttpRouter.get(
904916
"/federation/status",

packages/api/src/services/federation.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ const defaultActorUsername = "docker-git"
106106
const activityJsonContentType = "application/activity+json"
107107
const activityAcceptHeader = `${federationJsonLdContentType}, ${activityJsonContentType}, application/json`
108108
const defaultExchangeQueue = "code"
109-
const stateVersion = 1 as const
110109
const exchangeEventLimit = 100
111110

112111
const issueStore: Map<string, FederationIssueRecord> = new Map()
@@ -380,7 +379,7 @@ const normalizeHttpUrl = (
380379
})
381380

382381
const serializeState = (): StoredFederationState => ({
383-
version: stateVersion,
382+
version: 1,
384383
issues: [...issueStore.values()],
385384
follows: [...followStore.values()],
386385
processedOutboxItems: [...processedOutboxItems],
@@ -1494,13 +1493,13 @@ const fetchOutbox = (
14941493
fetchJson(url, "Exchange outbox").pipe(
14951494
Effect.flatMap((record) =>
14961495
requireFederationJsonLdContext(record, "Exchange outbox").pipe(
1497-
Effect.as({
1496+
Effect.map((): ActivityPubOrderedCollection => ({
14981497
"@context": activityForgeFedJsonLdContext,
1499-
type: "OrderedCollection" as const,
1498+
type: "OrderedCollection",
15001499
id: readOptionalString(record, "id") ?? url,
15011500
totalItems: typeof record["totalItems"] === "number" ? record["totalItems"] : 0,
15021501
orderedItems: Array.isArray(record["orderedItems"]) ? record["orderedItems"] : []
1503-
})
1502+
}))
15041503
)
15051504
)
15061505
)

packages/api/tests/http-config.test.ts

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ import { Effect } from "effect"
55
import fc from "fast-check"
66

77
import {
8+
activityForgeFedJsonLdContext,
89
actorJsonLdContext,
910
federationJsonLdResponseContentType
1011
} from "../src/api/contracts.js"
1112
import {
1213
federationActorDocumentResponse,
1314
federationExchangeStatusResponse,
15+
federationFollowersDocumentResponse,
16+
federationFollowingDocumentResponse,
17+
federationLikedDocumentResponse,
18+
federationOutboxDocumentResponse,
1419
resolveConfiguredFederationPublicOrigin
1520
} from "../src/http.js"
1621
import { clearFederationState } from "../src/services/federation.js"
@@ -52,7 +57,11 @@ const federationDocumentHandler = HttpApp.toWebHandler(
5257
Effect.flatten(
5358
HttpRouter.toHttpApp(
5459
HttpRouter.empty.pipe(
55-
HttpRouter.get("/federation/actor", federationActorDocumentResponse())
60+
HttpRouter.get("/federation/actor", federationActorDocumentResponse()),
61+
HttpRouter.get("/federation/outbox", federationOutboxDocumentResponse()),
62+
HttpRouter.get("/federation/followers", federationFollowersDocumentResponse()),
63+
HttpRouter.get("/federation/following", federationFollowingDocumentResponse()),
64+
HttpRouter.get("/federation/liked", federationLikedDocumentResponse())
5665
)
5766
)
5867
)
@@ -118,6 +127,44 @@ const parseJsonObject = (raw: string): object | null => {
118127
const readField = (value: object | null, key: string): unknown =>
119128
value === null ? undefined : Reflect.get(value, key)
120129

130+
const federationDocumentCases: ReadonlyArray<{
131+
readonly path: string
132+
readonly expectedContext: unknown
133+
readonly expectedId: string
134+
readonly expectedType: string
135+
}> = [
136+
{
137+
path: "/federation/actor",
138+
expectedContext: actorJsonLdContext,
139+
expectedId: "https://public.example.test/federation/actor",
140+
expectedType: "Person"
141+
},
142+
{
143+
path: "/federation/outbox",
144+
expectedContext: activityForgeFedJsonLdContext,
145+
expectedId: "https://public.example.test/federation/outbox",
146+
expectedType: "OrderedCollection"
147+
},
148+
{
149+
path: "/federation/followers",
150+
expectedContext: activityForgeFedJsonLdContext,
151+
expectedId: "https://public.example.test/federation/followers",
152+
expectedType: "OrderedCollection"
153+
},
154+
{
155+
path: "/federation/following",
156+
expectedContext: activityForgeFedJsonLdContext,
157+
expectedId: "https://public.example.test/federation/following",
158+
expectedType: "OrderedCollection"
159+
},
160+
{
161+
path: "/federation/liked",
162+
expectedContext: activityForgeFedJsonLdContext,
163+
expectedId: "https://public.example.test/federation/liked",
164+
expectedType: "OrderedCollection"
165+
}
166+
]
167+
121168
describe("api http config", () => {
122169
it.effect("ignores empty federation public origin values", () =>
123170
Effect.sync(() => {
@@ -180,16 +227,19 @@ describe("api http config", () => {
180227
expect(Array.isArray(readField(payload, "recentEvents"))).toBe(true)
181228
}))
182229

183-
it.effect("serves federation actor documents as JSON-LD", () =>
184-
Effect.gen(function*(_) {
185-
yield* _(Effect.sync(() => clearFederationState()))
230+
for (const documentCase of federationDocumentCases) {
231+
it.effect(`serves ${documentCase.path} as JSON-LD`, () =>
232+
Effect.gen(function*(_) {
233+
yield* _(Effect.sync(() => clearFederationState()))
186234

187-
const actor = yield* _(readFederationDocumentRoute("/federation/actor"))
188-
const payload = parseJsonObject(actor.body)
235+
const document = yield* _(readFederationDocumentRoute(documentCase.path))
236+
const payload = parseJsonObject(document.body)
189237

190-
expect(actor.status).toBe(200)
191-
expect(actor.contentType).toBe(federationJsonLdResponseContentType)
192-
expect(readField(payload, "@context")).toEqual(actorJsonLdContext)
193-
expect(readField(payload, "id")).toBe("https://public.example.test/federation/actor")
194-
}))
238+
expect(document.status).toBe(200)
239+
expect(document.contentType).toBe(federationJsonLdResponseContentType)
240+
expect(readField(payload, "@context")).toEqual(documentCase.expectedContext)
241+
expect(readField(payload, "type")).toBe(documentCase.expectedType)
242+
expect(readField(payload, "id")).toBe(documentCase.expectedId)
243+
}))
244+
}
195245
})

0 commit comments

Comments
 (0)