Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/providers/piefed/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,30 @@ const piefedMiddleware: Middleware = {
/** Canonical search types PieFed can actually serve (its enum has no "All") */
type SearchableType = Exclude<types.SearchType, "all">;

/**
* PieFed counts `max_depth` from *below* top-level, Lemmy counts from the
* post, so the same request reaches a level deeper on PieFed — verified
* live: with `max_depth=1` and no `parent_id`, Lemmy returns top-level
* comments while PieFed returns those plus their children. Requesting one
* less keeps the canonical meaning ("levels of comments to return")
* identical on both. With a `parent_id` the two agree, so it passes
* through untouched.
*
* Requests for zero levels never reach here (getComments answers those
* directly), so the adjusted value can't go negative — and wire `0`
* unambiguously means canonical `1`, which is what lets the fake's decoder
* invert this.
*/
function toPiefedMaxDepth(
payload: Parameters<BaseClient["getComments"]>[0],
): number | undefined {
const { max_depth, parent_id } = payload;

if (max_depth === undefined || parent_id !== undefined) return max_depth;

return max_depth - 1;
}

const PIEFED_SEARCH_TYPE = {
comments: "Comments",
communities: "Communities",
Expand Down Expand Up @@ -396,9 +420,21 @@ export class UnsafePiefedClient implements BaseClient {
`Connected to piefed, ${payload.mode} is not supported`,
);

// PieFed's shallowest response still contains top-level comments, so a
// canonical request for zero levels has no PieFed equivalent — answer
// it directly rather than asking for something else and returning more
// than the caller wanted.
if (
payload.max_depth !== undefined &&
payload.max_depth <= 0 &&
payload.parent_id === undefined
)
return { ...compat.toPageResponse(payload, { items: 0 }), data: [] };

const { type_, ...rest } = compat.fromPageParams(payload);
const query = {
...rest,
max_depth: toPiefedMaxDepth(payload),
...(type_ && { type_: compat.fromListingType(type_) }),
} satisfies paths["/api/alpha/comment/list"]["get"]["parameters"]["query"];

Expand Down
12 changes: 10 additions & 2 deletions src/testing/piefed/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,20 @@ const PIEFED_OPERATIONS = {
getComments: {
decode: (call: RecordedCall): Payload<"getComments"> => {
const q = query(call);
const parentId = numberish(q.parent_id);
const wireDepth = numberish(q.max_depth);

return {
limit: numberish(q.limit),
max_depth: numberish(q.max_depth),
// Invert the adapter's piefed depth adjustment (see
// toPiefedMaxDepth) so the decoded payload is canonical
max_depth:
wireDepth === undefined || parentId !== undefined
? wireDepth
: wireDepth + 1,
// piefed pages with numbers; canonical page_cursor is the string
page_cursor: q.page,
parent_id: numberish(q.parent_id),
parent_id: parentId,
post_id: numberish(q.post_id),
sort: q.sort,
} as Payload<"getComments">;
Expand Down
26 changes: 26 additions & 0 deletions test/live-smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,32 @@ describe.runIf(process.env.LIVE_SMOKE)("live smoke", () => {
expect(data.length).toBeGreaterThan(0);
});

it(
"max_depth means the same depth on every provider",
OPTIONS,
async () => {
// The adapters absorb each server's own base (PieFed counts from
// below top-level, Lemmy from the post). If a server changes that,
// this catches it before consumers do.
const { data: posts } = await client.getPosts({
limit: 20,
type_: "local",
});
const post = posts.find((view) => view.post.comments > 0);
expect(post, "no post with comments to probe").toBeDefined();

const { data } = await client.getComments({
limit: 50,
max_depth: 1,
post_id: post!.post.id,
});

// Depth 1 is top-level only: paths look like `0.<id>`
for (const view of data)
expect(view.comment.path.split(".")).toHaveLength(2);
},
);

it("all-type search passes canonical validation", OPTIONS, async () => {
// PieFed has no all-type search endpoint — the adapter fans out and
// merges, so an unspecified type_ must work everywhere
Expand Down
15 changes: 15 additions & 0 deletions test/testing-request-decoders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ const SCENARIOS = [
c.getComments({ limit: 5, parent_id: 7, post_id: 42 }),
operation: "getComments",
},
{
// No parent: piefed's wire depth is adjusted, so the decoder has to
// undo it to report what the caller asked for
expected: { max_depth: 3, post_id: 42 },
invoke: (c: ThreadiverseClient) =>
c.getComments({ max_depth: 3, post_id: 42 }),
operation: "getComments",
},
{
// With a parent the providers agree, so depth passes through untouched
expected: { max_depth: 2, parent_id: 7, post_id: 42 },
invoke: (c: ThreadiverseClient) =>
c.getComments({ max_depth: 2, parent_id: 7, post_id: 42 }),
operation: "getComments",
},
{
expected: { search_term: "cats", type_: "communities" },
invoke: (c: ThreadiverseClient) =>
Expand Down
28 changes: 24 additions & 4 deletions test/testing-seed-matrix.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,27 @@ describe.each([
expect(second.data).toHaveLength(0);
});

it("returns no comments when asked for zero levels", async () => {
const { client, post } = setup();

const { data } = await client.getComments({
max_depth: 0,
post_id: post.id,
});

expect(data).toEqual([]);
});

it("asks for the same comment depth regardless of provider", async () => {
const { client, fake, post } = setup();

await client.getComments({ max_depth: 3, post_id: post.id });

// Canonical payloads stay canonical even where the wire request had to
// be adjusted for the provider
expect(fake.callsTo("getComments")[0]).toMatchObject({ max_depth: 3 });
});

it("honors max_depth relative to the requested parent", async () => {
const { client, fake, post } = setup();

Expand All @@ -322,11 +343,10 @@ describe.each([
post,
});

// Shallowest depth = top-level only. The providers count differently
// without a parent (verified against live servers): Lemmy counts from
// the post, PieFed counts levels below top-level.
// max_depth means the same thing on every provider: the piefed adapter
// absorbs that server's different base (see toPiefedMaxDepth)
const shallow = await client.getComments({
max_depth: mode === "piefed" ? 0 : 1,
max_depth: 1,
post_id: post.id,
});
expect(shallow.data.map((view) => view.comment.content)).toEqual([
Expand Down