From d05b077df13f08f1a9ef9799e9a39545036d5573 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Sun, 22 Oct 2023 15:47:04 -1000 Subject: [PATCH] More API tests, fixes --- README.md | 1 + database/entities/Status.ts | 29 +++++++++--- database/entities/User.ts | 6 +-- server/api/api/v1/accounts/[id]/statuses.ts | 4 +- server/api/api/v1/statuses/[id]/context.ts | 9 ++-- server/api/api/v1/statuses/[id]/index.ts | 4 +- server/api/api/v1/statuses/index.ts | 6 +-- server/api/api/v1/timelines/home.ts | 4 +- server/api/api/v1/timelines/public.ts | 4 +- tests/api.test.ts | 51 ++++++++++++++++++++- 10 files changed, 90 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 25f78b79..f27b7b98 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ Working endpoints are: - `/api/v1/accounts/verify_credentials` - `/api/v1/accounts/familiar_followers` - `/api/v1/statuses/:id` (`GET`, `DELETE`) +- `/api/v1/statuses/:id/context` - `/api/v1/statuses` - `/api/v1/timelines/public` - `/api/v1/apps` diff --git a/database/entities/Status.ts b/database/entities/Status.ts index 0952472f..7714b25c 100644 --- a/database/entities/Status.ts +++ b/database/entities/Status.ts @@ -21,6 +21,21 @@ import { Instance } from "./Instance"; const config = getConfig(); +export const statusRelations = [ + "account", + "reblog", + "object", + "in_reply_to_post", + "instance", + "in_reply_to_account", + "in_reply_to_post.account", + "application", + "emojis", + "mentions", + "likes", + "announces", +]; + /** * Represents a status (i.e. a post) */ @@ -57,8 +72,9 @@ export class Status extends BaseEntity { */ @ManyToOne(() => Status, status => status.id, { nullable: true, + onDelete: "SET NULL", }) - reblog?: Status; + reblog?: Status | null; /** * The raw object associated with this status. @@ -94,6 +110,7 @@ export class Status extends BaseEntity { */ @ManyToOne(() => Status, { nullable: true, + onDelete: "SET NULL", }) in_reply_to_post!: Status | null; @@ -229,9 +246,7 @@ export class Status extends BaseEntity { where: { id: id, }, - relations: { - in_reply_to_post: true, - }, + relations: statusRelations, }); if (currentStatus) { @@ -279,9 +294,7 @@ export class Status extends BaseEntity { id: status.id, }, }, - relations: { - in_reply_to_post: true, - }, + relations: statusRelations, }); for (const status of currentStatus) { @@ -443,6 +456,8 @@ export class Status extends BaseEntity { return { ...(await this.object.toAPI()), id: this.id, + in_reply_to_id: this.in_reply_to_post?.id || null, + in_reply_to_account_id: this.in_reply_to_post?.account.id || null, }; } } diff --git a/database/entities/User.ts b/database/entities/User.ts index cff03197..4778c86d 100644 --- a/database/entities/User.ts +++ b/database/entities/User.ts @@ -21,7 +21,7 @@ import { } from "activitypub-types"; import { RawObject } from "./RawObject"; import { Token } from "./Token"; -import { Status } from "./Status"; +import { Status, statusRelations } from "./Status"; import { APISource } from "~types/entities/source"; import { Relationship } from "./Relationship"; import { Instance } from "./Instance"; @@ -368,9 +368,7 @@ export class User extends BaseEntity { id: this.id, }, }, - relations: { - object: true, - }, + relations: statusRelations, }); // Delete both diff --git a/server/api/api/v1/accounts/[id]/statuses.ts b/server/api/api/v1/accounts/[id]/statuses.ts index e9221f4a..6d08114c 100644 --- a/server/api/api/v1/accounts/[id]/statuses.ts +++ b/server/api/api/v1/accounts/[id]/statuses.ts @@ -1,6 +1,6 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { Status } from "~database/entities/Status"; +import { Status, statusRelations } from "~database/entities/Status"; import { User } from "~database/entities/User"; import { applyConfig } from "@api"; @@ -60,7 +60,7 @@ export default async ( }, isReblog: exclude_reblogs ? true : undefined, }, - relations: ["account", "emojis", "announces", "likes", "object"], + relations: statusRelations, order: { created_at: "DESC", }, diff --git a/server/api/api/v1/statuses/[id]/context.ts b/server/api/api/v1/statuses/[id]/context.ts index 52f63011..e83ca895 100644 --- a/server/api/api/v1/statuses/[id]/context.ts +++ b/server/api/api/v1/statuses/[id]/context.ts @@ -1,7 +1,7 @@ import { applyConfig } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { Status } from "~database/entities/Status"; +import { Status, statusRelations } from "~database/entities/Status"; import { User } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; @@ -32,8 +32,11 @@ export default async ( let foundStatus: Status | null; try { - foundStatus = await Status.findOneBy({ - id, + foundStatus = await Status.findOne({ + where: { + id, + }, + relations: statusRelations, }); } catch (e) { return errorResponse("Invalid ID", 404); diff --git a/server/api/api/v1/statuses/[id]/index.ts b/server/api/api/v1/statuses/[id]/index.ts index 46ca99da..768a156f 100644 --- a/server/api/api/v1/statuses/[id]/index.ts +++ b/server/api/api/v1/statuses/[id]/index.ts @@ -1,7 +1,7 @@ import { applyConfig } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { Status } from "~database/entities/Status"; +import { Status, statusRelations } from "~database/entities/Status"; import { User } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; @@ -35,7 +35,7 @@ export default async ( where: { id, }, - relations: ["account", "object"], + relations: statusRelations, }); } catch (e) { return errorResponse("Invalid ID", 404); diff --git a/server/api/api/v1/statuses/index.ts b/server/api/api/v1/statuses/index.ts index 276bda4c..97bdba08 100644 --- a/server/api/api/v1/statuses/index.ts +++ b/server/api/api/v1/statuses/index.ts @@ -11,7 +11,7 @@ import { sanitize } from "isomorphic-dompurify"; import { parse } from "marked"; import { Application } from "~database/entities/Application"; import { RawObject } from "~database/entities/RawObject"; -import { Status } from "~database/entities/Status"; +import { Status, statusRelations } from "~database/entities/Status"; import { User } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; @@ -132,9 +132,7 @@ export default async (req: Request): Promise => { where: { id: in_reply_to_id, }, - relations: { - account: true, - }, + relations: statusRelations, }); replyUser = replyStatus?.account || null; diff --git a/server/api/api/v1/timelines/home.ts b/server/api/api/v1/timelines/home.ts index 67bcb06e..cfebf005 100644 --- a/server/api/api/v1/timelines/home.ts +++ b/server/api/api/v1/timelines/home.ts @@ -3,7 +3,7 @@ import { applyConfig } from "@api"; import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { FindManyOptions } from "typeorm"; -import { Status } from "~database/entities/Status"; +import { Status, statusRelations } from "~database/entities/Status"; import { User } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; @@ -60,7 +60,7 @@ export default async (req: Request): Promise => { created_at: "DESC", }, take: limit, - relations: ["object"], + relations: statusRelations, }; if (max_id) { diff --git a/server/api/api/v1/timelines/public.ts b/server/api/api/v1/timelines/public.ts index b3fbdbd9..ad102cad 100644 --- a/server/api/api/v1/timelines/public.ts +++ b/server/api/api/v1/timelines/public.ts @@ -3,7 +3,7 @@ import { applyConfig } from "@api"; import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { FindManyOptions, IsNull, Not } from "typeorm"; -import { Status } from "~database/entities/Status"; +import { Status, statusRelations } from "~database/entities/Status"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -56,7 +56,7 @@ export default async (req: Request): Promise => { created_at: "DESC", }, take: limit, - relations: ["object"], + relations: statusRelations, }; if (max_id) { diff --git a/tests/api.test.ts b/tests/api.test.ts index 314b8ca4..a627760f 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -20,6 +20,7 @@ let token: Token; let user: User; let user2: User; let status: APIStatus | null = null; +let status2: APIStatus | null = null; describe("API Tests", () => { beforeAll(async () => { @@ -153,6 +154,52 @@ describe("API Tests", () => { expect(status.in_reply_to_id).toBeNull(); expect(status.in_reply_to_account_id).toBeNull(); }); + + test("should create a new status in reply to the previous one", async () => { + const response = await fetch( + `${config.http.base_url}/api/v1/statuses`, + { + method: "POST", + headers: { + Authorization: `Bearer ${token.access_token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + status: "This is a reply!", + visibility: "public", + in_reply_to_id: status?.id, + }), + } + ); + + expect(response.status).toBe(200); + expect(response.headers.get("content-type")).toBe( + "application/json" + ); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + status2 = (await response.json()) as APIStatus; + expect(status2.content).toBe("This is a reply!"); + expect(status2.visibility).toBe("public"); + expect(status2.account.id).toBe(user.id); + expect(status2.replies_count).toBe(0); + expect(status2.favourites_count).toBe(0); + expect(status2.reblogged).toBe(false); + expect(status2.favourited).toBe(false); + expect(status2.media_attachments).toEqual([]); + expect(status2.mentions).toEqual([]); + expect(status2.tags).toEqual([]); + expect(status2.sensitive).toBe(false); + expect(status2.spoiler_text).toBe(""); + expect(status2.language).toBeNull(); + expect(status2.pinned).toBe(false); + expect(status2.visibility).toBe("public"); + expect(status2.card).toBeNull(); + expect(status2.poll).toBeNull(); + expect(status2.emojis).toEqual([]); + expect(status2.in_reply_to_id).toEqual(status?.id); + expect(status2.in_reply_to_account_id).toEqual(user.id); + }); }); describe("GET /api/v1/timelines/public", () => { @@ -270,9 +317,9 @@ describe("API Tests", () => { const statuses = (await response.json()) as APIStatus[]; - expect(statuses.length).toBe(1); + expect(statuses.length).toBe(2); - const status1 = statuses[0]; + const status1 = statuses[1]; // Basic validation expect(status1.content).toBe("Hello, world!");