Skip to content

Commit fc2d1ee

Browse files
committed
Cover timeline quote compatibility
Add a regression test for home timelines returning quote posts in the Mastodon Quote entity shape expected by current Mastodon clients. #421 Assisted-by: OpenCode:gpt-5.5
1 parent 4fae3db commit fc2d1ee

1 file changed

Lines changed: 92 additions & 1 deletion

File tree

src/api/v1/timelines.test.ts

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ describe.sequential("/api/v1/timelines/list/:list_id", () => {
142142
expect(json[0].media_attachments[0].type).toBe("unknown");
143143
});
144144
});
145-
146145
describe.sequential("/api/v1/timelines/home", () => {
147146
let owner: Awaited<ReturnType<typeof createAccount>>;
148147
let approvedAuthor: Awaited<ReturnType<typeof createAccount>>;
@@ -222,3 +221,95 @@ describe.sequential("/api/v1/timelines/home", () => {
222221
expect(ids).toEqual([approvedPostId]);
223222
});
224223
});
224+
225+
describe.sequential("/api/v1/timelines/home", () => {
226+
let owner: Awaited<ReturnType<typeof createAccount>>;
227+
let client: Awaited<ReturnType<typeof createOAuthApplication>>;
228+
let accessToken: Awaited<ReturnType<typeof getAccessToken>>;
229+
230+
beforeEach(async () => {
231+
await cleanDatabase();
232+
233+
owner = await createAccount();
234+
client = await createOAuthApplication({
235+
scopes: ["read:statuses"],
236+
});
237+
accessToken = await getAccessToken(client, owner, ["read:statuses"]);
238+
});
239+
240+
it("serializes quotes using the Mastodon Quote entity format", async () => {
241+
expect.assertions(7);
242+
243+
const authorId = crypto.randomUUID() as Uuid;
244+
const quotedPostId = uuidv7();
245+
const quotePostId = uuidv7();
246+
247+
await db
248+
.insert(instances)
249+
.values({ host: "remote.test" })
250+
.onConflictDoNothing();
251+
252+
await db.insert(accounts).values({
253+
id: authorId,
254+
iri: "https://remote.test/users/author",
255+
instanceHost: "remote.test",
256+
type: "Person",
257+
name: "Remote author",
258+
emojis: {},
259+
handle: "@author@remote.test",
260+
bioHtml: "",
261+
url: "https://remote.test/@author",
262+
protected: false,
263+
inboxUrl: "https://remote.test/users/author/inbox",
264+
});
265+
266+
await db.insert(follows).values({
267+
iri: "https://hollo.test/follows/author",
268+
followingId: authorId,
269+
followerId: owner.id,
270+
approved: new Date(),
271+
});
272+
273+
await db.insert(posts).values([
274+
{
275+
id: quotedPostId,
276+
iri: `https://remote.test/notes/${quotedPostId}`,
277+
type: "Note",
278+
accountId: authorId,
279+
visibility: "public",
280+
content: "Quoted post",
281+
contentHtml: "<p>Quoted post</p>",
282+
published: new Date(),
283+
},
284+
{
285+
id: quotePostId,
286+
iri: `https://remote.test/notes/${quotePostId}`,
287+
type: "Note",
288+
accountId: authorId,
289+
quoteTargetId: quotedPostId,
290+
visibility: "public",
291+
content: "Quote post",
292+
contentHtml: "<p>Quote post</p>",
293+
published: new Date(),
294+
},
295+
]);
296+
297+
const response = await app.request("/api/v1/timelines/home", {
298+
method: "GET",
299+
headers: {
300+
authorization: bearerAuthorization(accessToken),
301+
},
302+
});
303+
304+
expect(response.status).toBe(200);
305+
expect(response.headers.get("content-type")).toBe("application/json");
306+
307+
const json = await response.json();
308+
309+
expect(Array.isArray(json)).toBe(true);
310+
expect(json[0].id).toBe(quotePostId);
311+
expect(json[0].quote_id).toBe(quotedPostId);
312+
expect(json[0].quote.state).toBe("accepted");
313+
expect(json[0].quote.quoted_status.id).toBe(quotedPostId);
314+
});
315+
});

0 commit comments

Comments
 (0)