diff --git a/api/api/v1/accounts/:id/roles/:role_id/index.test.ts b/api/api/v1/accounts/:id/roles/:role_id/index.test.ts index 2bf596d2..3cb9aa0e 100644 --- a/api/api/v1/accounts/:id/roles/:role_id/index.test.ts +++ b/api/api/v1/accounts/:id/roles/:role_id/index.test.ts @@ -1,9 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { Role } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(2); +const { users, deleteUsers } = await getTestUsers(2); let role: Role; let higherPriorityRole: Role; @@ -44,56 +44,44 @@ afterAll(async () => { // /api/v1/accounts/:id/roles/:role_id describe("/api/v1/accounts/:id/roles/:role_id", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest( - `/api/v1/accounts/${users[1].id}/roles/${role.id}`, - { - method: "POST", - }, - ); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.assignRole(users[1].id, role.id); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return 404 if role does not exist", async () => { - const response = await fakeRequest( - `/api/v1/accounts/${users[1].id}/roles/00000000-0000-0000-0000-000000000000`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.assignRole( + users[1].id, + "00000000-0000-0000-0000-000000000000", ); - expect(response.status).toBe(404); + expect(ok).toBe(false); + expect(raw.status).toBe(404); }); test("should return 404 if user does not exist", async () => { - const response = await fakeRequest( - `/api/v1/accounts/00000000-0000-0000-0000-000000000000/roles/${role.id}`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.assignRole( + "00000000-0000-0000-0000-000000000000", + role.id, ); - expect(response.status).toBe(404); + expect(ok).toBe(false); + expect(raw.status).toBe(404); }); test("should assign role to user", async () => { - const response = await fakeRequest( - `/api/v1/accounts/${users[1].id}/roles/${role.id}`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[0]); - expect(response.status).toBe(204); + const { ok } = await client.assignRole(users[1].id, role.id); + + expect(ok).toBe(true); // Check if role was assigned const userRoles = await Role.getUserRoles(users[1].id, false); @@ -103,19 +91,16 @@ describe("/api/v1/accounts/:id/roles/:role_id", () => { }); test("should return 403 if user tries to assign role with higher priority", async () => { - const response = await fakeRequest( - `/api/v1/accounts/${users[1].id}/roles/${higherPriorityRole.id}`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, + await using client = await generateClient(users[0]); + + const { data, ok, raw } = await client.assignRole( + users[1].id, + higherPriorityRole.id, ); - expect(response.status).toBe(403); - const output = await response.json(); - expect(output).toMatchObject({ + expect(ok).toBe(false); + expect(raw.status).toBe(403); + expect(data).toMatchObject({ error: "Forbidden", details: "User with highest role priority 2 cannot assign role with priority 3", @@ -123,17 +108,11 @@ describe("/api/v1/accounts/:id/roles/:role_id", () => { }); test("should remove role from user", async () => { - const response = await fakeRequest( - `/api/v1/accounts/${users[1].id}/roles/${role.id}`, - { - method: "DELETE", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[0]); - expect(response.status).toBe(204); + const { ok } = await client.unassignRole(users[1].id, role.id); + + expect(ok).toBe(true); // Check if role was removed const userRoles = await Role.getUserRoles(users[1].id, false); @@ -145,19 +124,16 @@ describe("/api/v1/accounts/:id/roles/:role_id", () => { test("should return 403 if user tries to remove role with higher priority", async () => { await higherPriorityRole.linkUser(users[1].id); - const response = await fakeRequest( - `/api/v1/accounts/${users[1].id}/roles/${higherPriorityRole.id}`, - { - method: "DELETE", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, + await using client = await generateClient(users[0]); + + const { data, ok, raw } = await client.unassignRole( + users[1].id, + higherPriorityRole.id, ); - expect(response.status).toBe(403); - const output = await response.json(); - expect(output).toMatchObject({ + expect(ok).toBe(false); + expect(raw.status).toBe(403); + expect(data).toMatchObject({ error: "Forbidden", details: "User with highest role priority 2 cannot remove role with priority 3", diff --git a/api/api/v1/accounts/:id/roles/index.test.ts b/api/api/v1/accounts/:id/roles/index.test.ts index 574caafc..a3047dc7 100644 --- a/api/api/v1/accounts/:id/roles/index.test.ts +++ b/api/api/v1/accounts/:id/roles/index.test.ts @@ -1,9 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { Role } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(2); +const { users, deleteUsers } = await getTestUsers(2); let role: Role; beforeAll(async () => { @@ -29,37 +29,27 @@ afterAll(async () => { describe("/api/v1/accounts/:id/roles", () => { test("should return 404 if user does not exist", async () => { - const response = await fakeRequest( - "/api/v1/accounts/00000000-0000-0000-0000-000000000000/roles", - { - method: "GET", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, + await using client = await generateClient(users[0]); + + const { data, ok, raw } = await client.getAccountRoles( + "00000000-0000-0000-0000-000000000000", ); - expect(response.status).toBe(404); - const output = await response.json(); - expect(output).toMatchObject({ + expect(ok).toBe(false); + expect(raw.status).toBe(404); + expect(data).toMatchObject({ error: "User not found", }); }); test("should return a list of roles for the user", async () => { - const response = await fakeRequest( - `/api/v1/accounts/${users[0].id}/roles`, - { - method: "GET", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[0]); - expect(response.ok).toBe(true); - const roles = await response.json(); - expect(roles).toContainEqual({ + const { data, ok } = await client.getAccountRoles(users[0].id); + + expect(ok).toBe(true); + expect(data).toBeArray(); + expect(data).toContainEqual({ id: role.id, name: "test", permissions: [RolePermissions.ManageRoles], diff --git a/api/api/v1/emojis/:id/index.test.ts b/api/api/v1/emojis/:id/index.test.ts index 95ab2747..098633bd 100644 --- a/api/api/v1/emojis/:id/index.test.ts +++ b/api/api/v1/emojis/:id/index.test.ts @@ -2,9 +2,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { db } from "@versia/kit/db"; import { Emojis } from "@versia/kit/tables"; import { inArray } from "drizzle-orm"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(2); +const { users, deleteUsers } = await getTestUsers(2); let id = ""; // Make user 2 an admin @@ -12,22 +12,14 @@ beforeAll(async () => { await users[1].update({ isAdmin: true }); // Create an emoji - const response = await fakeRequest("/api/v1/emojis", { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify({ - shortcode: "test", - element: "https://cdn.versia.social/logo.webp", - global: true, - }), - }); + await using client = await generateClient(users[1]); + const { data, ok } = await client.uploadEmoji( + "test", + new URL("https://cdn.versia.social/logo.webp"), + ); - expect(response.ok).toBe(true); - const emoji = await response.json(); - id = emoji.id; + expect(ok).toBe(true); + id = data.id; }); afterAll(async () => { @@ -41,124 +33,95 @@ afterAll(async () => { // /api/v1/emojis/:id (PATCH, DELETE, GET) describe("/api/v1/emojis/:id", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest(`/api/v1/emojis/${id}`, { - method: "GET", - }); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.getEmoji(id); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return 404 if emoji does not exist", async () => { - const response = await fakeRequest( - "/api/v1/emojis/00000000-0000-0000-0000-000000000000", - { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - method: "GET", - }, + await using client = await generateClient(users[1]); + + const { ok, raw } = await client.getEmoji( + "00000000-0000-0000-0000-000000000000", ); - expect(response.status).toBe(404); + expect(ok).toBe(false); + expect(raw.status).toBe(404); }); test("should not work if the user is trying to update an emoji they don't own", async () => { - const response = await fakeRequest(`/api/v1/emojis/${id}`, { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - method: "PATCH", - body: JSON.stringify({ - shortcode: "test2", - }), + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.updateEmoji(id, { + shortcode: "test2", }); - expect(response.status).toBe(403); + expect(ok).toBe(false); + expect(raw.status).toBe(403); }); test("should return the emoji", async () => { - const response = await fakeRequest(`/api/v1/emojis/${id}`, { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - method: "GET", - }); + await using client = await generateClient(users[1]); - expect(response.ok).toBe(true); - const emoji = await response.json(); - expect(emoji.shortcode).toBe("test"); + const { data, ok } = await client.getEmoji(id); + + expect(ok).toBe(true); + expect(data.shortcode).toBe("test"); }); test("should update the emoji", async () => { - const response = await fakeRequest(`/api/v1/emojis/${id}`, { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - method: "PATCH", - body: JSON.stringify({ - shortcode: "test2", - }), + await using client = await generateClient(users[1]); + + const { data, ok } = await client.updateEmoji(id, { + shortcode: "test2", }); - expect(response.ok).toBe(true); - const emoji = await response.json(); - expect(emoji.shortcode).toBe("test2"); + expect(ok).toBe(true); + expect(data.shortcode).toBe("test2"); }); test("should update the emoji with another url, but keep the shortcode", async () => { - const response = await fakeRequest(`/api/v1/emojis/${id}`, { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - method: "PATCH", - body: JSON.stringify({ - element: "https://avatars.githubusercontent.com/u/30842467?v=4", - }), + await using client = await generateClient(users[1]); + + const { data, ok } = await client.updateEmoji(id, { + image: new URL( + "https://avatars.githubusercontent.com/u/30842467?v=4", + ), }); - expect(response.ok).toBe(true); - const emoji = await response.json(); - expect(emoji.shortcode).toBe("test2"); + expect(ok).toBe(true); + expect(data.shortcode).toBe("test2"); + expect(data.url).toContain("/media/proxy/"); }); test("should update the emoji to be non-global", async () => { - const response = await fakeRequest(`/api/v1/emojis/${id}`, { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - method: "PATCH", - body: JSON.stringify({ - global: false, - }), + await using client = await generateClient(users[1]); + + const { data, ok } = await client.updateEmoji(id, { + global: false, }); - expect(response.ok).toBe(true); + expect(ok).toBe(true); + expect(data.global).toBe(false); // Check if the other user can see it - const response2 = await fakeRequest("/api/v1/custom_emojis", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - method: "GET", - }); + await using client2 = await generateClient(users[0]); - expect(response2.ok).toBe(true); - const emojis = await response2.json(); - expect(emojis).not.toContainEqual(expect.objectContaining({ id })); + const { data: data2, ok: ok2 } = + await client2.getInstanceCustomEmojis(); + + expect(ok2).toBe(true); + expect(data2).not.toContainEqual(expect.objectContaining({ id })); }); test("should delete the emoji", async () => { - const response = await fakeRequest(`/api/v1/emojis/${id}`, { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - method: "DELETE", - }); + await using client = await generateClient(users[1]); - expect(response.status).toBe(204); + const { ok } = await client.deleteEmoji(id); + + expect(ok).toBe(true); }); }); diff --git a/api/api/v1/emojis/index.test.ts b/api/api/v1/emojis/index.test.ts index 22575b7f..e2848f4a 100644 --- a/api/api/v1/emojis/index.test.ts +++ b/api/api/v1/emojis/index.test.ts @@ -3,9 +3,9 @@ import { db } from "@versia/kit/db"; import { Emojis } from "@versia/kit/tables"; import { inArray } from "drizzle-orm"; import sharp from "sharp"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(3); +const { users, deleteUsers } = await getTestUsers(3); // Make user 2 an admin beforeAll(async () => { @@ -39,146 +39,109 @@ const createImage = async (name: string): Promise => { describe("/api/v1/emojis", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest("/api/v1/emojis", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - shortcode: "test", - element: "https://cdn.versia.social/logo.webp", - }), - }); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.uploadEmoji( + "test", + new URL("https://cdn.versia.social/logo.webp"), + ); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); describe("Admin tests", () => { test("should upload a file and create an emoji", async () => { - const formData = new FormData(); - formData.append("shortcode", "test1"); - formData.append("element", await createImage("test.png")); - formData.append("global", "true"); + await using client = await generateClient(users[1]); - const response = await fakeRequest("/api/v1/emojis", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, + const { data, ok } = await client.uploadEmoji( + "test1", + await createImage("test.png"), + { + global: true, }, - body: formData, - }); + ); - expect(response.ok).toBe(true); - const emoji = await response.json(); - expect(emoji.shortcode).toBe("test1"); - expect(emoji.url).toContain("/media/proxy"); + expect(ok).toBe(true); + expect(data.shortcode).toBe("test1"); + expect(data.url).toContain("/media/proxy"); }); test("should try to upload a non-image", async () => { - const formData = new FormData(); - formData.append("shortcode", "test2"); - formData.append("element", new File(["test"], "test.txt")); + await using client = await generateClient(users[1]); - const response = await fakeRequest("/api/v1/emojis", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - body: formData, - }); + const { ok, raw } = await client.uploadEmoji( + "test2", + new File(["test"], "test.txt"), + ); - expect(response.status).toBe(422); + expect(ok).toBe(false); + expect(raw.status).toBe(422); }); test("should upload an emoji by url", async () => { - const response = await fakeRequest("/api/v1/emojis", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - shortcode: "test3", - element: "https://cdn.versia.social/logo.webp", - }), - }); + await using client = await generateClient(users[1]); - expect(response.ok).toBe(true); - const emoji = await response.json(); - expect(emoji.shortcode).toBe("test3"); - expect(emoji.url).toContain("/media/proxy/"); + const { data, ok } = await client.uploadEmoji( + "test3", + new URL("https://cdn.versia.social/logo.webp"), + ); + + expect(ok).toBe(true); + expect(data.shortcode).toBe("test3"); + expect(data.url).toContain("/media/proxy"); }); test("should fail when uploading an already existing emoji", async () => { - const formData = new FormData(); - formData.append("shortcode", "test1"); - formData.append("element", await createImage("test-image.png")); + await using client = await generateClient(users[1]); - const response = await fakeRequest("/api/v1/emojis", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - body: formData, - }); + const { ok, raw } = await client.uploadEmoji( + "test1", + await createImage("test-image.png"), + ); - expect(response.status).toBe(422); + expect(ok).toBe(false); + expect(raw.status).toBe(422); }); }); describe("User tests", () => { test("should upload a file and create an emoji", async () => { - const formData = new FormData(); - formData.append("shortcode", "test4"); - formData.append("element", await createImage("test-image.png")); + await using client = await generateClient(users[0]); - const response = await fakeRequest("/api/v1/emojis", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: formData, - }); + const { data, ok } = await client.uploadEmoji( + "test4", + await createImage("test-image.png"), + ); - expect(response.ok).toBe(true); - const emoji = await response.json(); - expect(emoji.shortcode).toBe("test4"); - expect(emoji.url).toContain("/media/proxy/"); + expect(ok).toBe(true); + expect(data.shortcode).toBe("test4"); + expect(data.url).toContain("/media/proxy"); }); test("should fail when uploading an already existing global emoji", async () => { - const formData = new FormData(); - formData.append("shortcode", "test1"); - formData.append("element", await createImage("test-image.png")); + await using client = await generateClient(users[0]); - const response = await fakeRequest("/api/v1/emojis", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: formData, - }); + const { ok, raw } = await client.uploadEmoji( + "test1", + await createImage("test-image.png"), + ); - expect(response.status).toBe(422); + expect(ok).toBe(false); + expect(raw.status).toBe(422); }); test("should create an emoji as another user with the same shortcode", async () => { - const formData = new FormData(); - formData.append("shortcode", "test4"); - formData.append("element", await createImage("test-image.png")); + await using client = await generateClient(users[2]); - const response = await fakeRequest("/api/v1/emojis", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[2].data.accessToken}`, - }, - body: formData, - }); + const { data, ok } = await client.uploadEmoji( + "test4", + await createImage("test-image.png"), + ); - expect(response.ok).toBe(true); - const emoji = await response.json(); - expect(emoji.shortcode).toBe("test4"); - expect(emoji.url).toContain("/media/proxy/"); + expect(ok).toBe(true); + expect(data.shortcode).toBe("test4"); + expect(data.url).toContain("/media/proxy/"); }); }); }); diff --git a/api/api/v1/instance/extended_description.test.ts b/api/api/v1/instance/extended_description.test.ts index 67db3eb2..c5b847ce 100644 --- a/api/api/v1/instance/extended_description.test.ts +++ b/api/api/v1/instance/extended_description.test.ts @@ -1,17 +1,15 @@ import { describe, expect, test } from "bun:test"; -import { fakeRequest } from "~/tests/utils"; +import { generateClient } from "~/tests/utils"; // /api/v1/instance/extended_description describe("/api/v1/instance/extended_description", () => { test("should return extended description", async () => { - const response = await fakeRequest( - "/api/v1/instance/extended_description", - ); + await using client = await generateClient(); - expect(response.status).toBe(200); + const { data, ok } = await client.getInstanceExtendedDescription(); - const json = await response.json(); - expect(json).toEqual({ + expect(ok).toBe(true); + expect(data).toEqual({ updated_at: new Date(0).toISOString(), content: '

This is a Versia server with the default extended description.

\n', diff --git a/api/api/v1/instance/privacy_policy.test.ts b/api/api/v1/instance/privacy_policy.test.ts index 6cfff924..396f982c 100644 --- a/api/api/v1/instance/privacy_policy.test.ts +++ b/api/api/v1/instance/privacy_policy.test.ts @@ -1,15 +1,15 @@ import { describe, expect, test } from "bun:test"; -import { fakeRequest } from "~/tests/utils"; +import { generateClient } from "~/tests/utils"; // /api/v1/instance/privacy_policy describe("/api/v1/instance/privacy_policy", () => { test("should return privacy policy", async () => { - const response = await fakeRequest("/api/v1/instance/privacy_policy"); + await using client = await generateClient(); - expect(response.status).toBe(200); + const { data, ok } = await client.getInstancePrivacyPolicy(); - const json = await response.json(); - expect(json).toEqual({ + expect(ok).toBe(true); + expect(data).toEqual({ updated_at: new Date(0).toISOString(), // This instance has not provided any privacy policy. content: diff --git a/api/api/v1/instance/rules.test.ts b/api/api/v1/instance/rules.test.ts index 07c5ab23..bdd84da9 100644 --- a/api/api/v1/instance/rules.test.ts +++ b/api/api/v1/instance/rules.test.ts @@ -1,16 +1,16 @@ import { describe, expect, test } from "bun:test"; import { config } from "~/config.ts"; -import { fakeRequest } from "~/tests/utils"; +import { generateClient } from "~/tests/utils"; // /api/v1/instance/rules describe("/api/v1/instance/rules", () => { test("should return rules", async () => { - const response = await fakeRequest("/api/v1/instance/rules"); + await using client = await generateClient(); - expect(response.status).toBe(200); + const { data, ok } = await client.getRules(); - const json = await response.json(); - expect(json).toEqual( + expect(ok).toBe(true); + expect(data).toEqual( config.instance.rules.map((r, index) => ({ id: String(index), text: r.text, diff --git a/api/api/v1/instance/terms_of_service.test.ts b/api/api/v1/instance/terms_of_service.test.ts index fb3f5baf..eb529365 100644 --- a/api/api/v1/instance/terms_of_service.test.ts +++ b/api/api/v1/instance/terms_of_service.test.ts @@ -1,15 +1,15 @@ import { describe, expect, test } from "bun:test"; -import { fakeRequest } from "~/tests/utils"; +import { generateClient } from "~/tests/utils"; // /api/v1/instance/terms_of_service describe("/api/v1/instance/terms_of_service", () => { test("should return terms of service", async () => { - const response = await fakeRequest("/api/v1/instance/terms_of_service"); + await using client = await generateClient(); - expect(response.status).toBe(200); + const { data, ok } = await client.getInstanceTermsOfService(); - const json = await response.json(); - expect(json).toEqual({ + expect(ok).toBe(true); + expect(data).toEqual({ updated_at: new Date(0).toISOString(), // This instance has not provided any terms of service. content: diff --git a/api/api/v1/markers/index.test.ts b/api/api/v1/markers/index.test.ts index ebd1a34b..0180a623 100644 --- a/api/api/v1/markers/index.test.ts +++ b/api/api/v1/markers/index.test.ts @@ -1,7 +1,7 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { fakeRequest, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(1); +const { users, deleteUsers } = await getTestUsers(1); const timeline = await getTestStatuses(10, users[0]); afterAll(async () => { @@ -11,46 +11,34 @@ afterAll(async () => { // /api/v1/markers describe("/api/v1/markers", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest("/api/v1/markers", { - method: "GET", - }); - expect(response.status).toBe(401); + await using client = await generateClient(); + + const { ok, raw } = await client.getMarkers(["home", "notifications"]); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return empty markers", async () => { - const response = await fakeRequest( - `/api/v1/markers?${new URLSearchParams([ - ["timeline[]", "home"], - ["timeline[]", "notifications"], - ])}`, - { - method: "GET", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); - expect(await response.json()).toEqual({}); + const { data, ok } = await client.getMarkers(["home", "notifications"]); + + expect(ok).toBe(true); + expect(data).toEqual({}); }); test("should create markers", async () => { - const response = await fakeRequest( - `/api/v1/markers?${new URLSearchParams({ - "home[last_read_id]": timeline[0].id, - })}`, + await using client = await generateClient(users[0]); - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, + const { data, ok } = await client.saveMarkers({ + home: { + last_read_id: timeline[0].id, }, - ); + }); - expect(response.status).toBe(200); - expect(await response.json()).toEqual({ + expect(ok).toBe(true); + expect(data).toEqual({ home: { last_read_id: timeline[0].id, updated_at: expect.any(String), @@ -60,21 +48,12 @@ describe("/api/v1/markers", () => { }); test("should return markers", async () => { - const response = await fakeRequest( - `/api/v1/markers?${new URLSearchParams([ - ["timeline[]", "home"], - ["timeline[]", "notifications"], - ])}`, - { - method: "GET", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); - expect(await response.json()).toEqual({ + const { data, ok } = await client.getMarkers(["home", "notifications"]); + + expect(ok).toBe(true); + expect(data).toEqual({ home: { last_read_id: timeline[0].id, updated_at: expect.any(String), diff --git a/api/api/v1/mutes/index.test.ts b/api/api/v1/mutes/index.test.ts index 43faecd4..5206d3be 100644 --- a/api/api/v1/mutes/index.test.ts +++ b/api/api/v1/mutes/index.test.ts @@ -1,76 +1,54 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(3); +const { users, deleteUsers } = await getTestUsers(3); afterAll(async () => { await deleteUsers(); }); beforeAll(async () => { - const response = await fakeRequest(`/api/v1/accounts/${users[1].id}/mute`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }); - expect(response.status).toBe(200); + await using client = await generateClient(users[0]); + + const { ok } = await client.muteAccount(users[1].id); + + expect(ok).toBe(true); }); // /api/v1/mutes describe("/api/v1/mutes", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest( - "/api/v1/mutes".replace(":id", users[1].id), - { - method: "GET", - }, - ); - expect(response.status).toBe(401); + await using client = await generateClient(); + + const { ok, raw } = await client.getMutes(); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return mutes", async () => { - const response = await fakeRequest("/api/v1/mutes", { - method: "GET", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); - expect(response.status).toBe(200); - const body = await response.json(); - expect(body).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: users[1].id, - }), - ]), - ); + await using client = await generateClient(users[0]); + + const { data, ok } = await client.getMutes(); + + expect(ok).toBe(true); + expect(data).toEqual([ + expect.objectContaining({ + id: users[1].id, + }), + ]); }); test("should return mutes after unmute", async () => { - const response = await fakeRequest( - `/api/v1/accounts/${users[1].id}/unmute`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }, - ); - expect(response.status).toBe(200); + await using client = await generateClient(users[0]); - const response2 = await fakeRequest("/api/v1/mutes", { - method: "GET", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); - expect(response2.status).toBe(200); - const body = await response2.json(); - expect(body).toEqual([]); + const { ok } = await client.unmuteAccount(users[1].id); + + expect(ok).toBe(true); + + const { data, ok: ok2 } = await client.getMutes(); + + expect(ok2).toBe(true); + expect(data).toEqual([]); }); }); diff --git a/api/api/v1/notifications/:id/dismiss.test.ts b/api/api/v1/notifications/:id/dismiss.test.ts index ae24eb87..31180516 100644 --- a/api/api/v1/notifications/:id/dismiss.test.ts +++ b/api/api/v1/notifications/:id/dismiss.test.ts @@ -1,28 +1,24 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import type { Notification as ApiNotification } from "@versia/client/types"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import type { z } from "@hono/zod-openapi"; +import type { Notification } from "@versia/client-ng/schemas"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(2); -let notifications: ApiNotification[] = []; +const { users, deleteUsers } = await getTestUsers(2); +let notifications: z.infer[] = []; // Create some test notifications: follow, favourite, reblog, mention beforeAll(async () => { - await fakeRequest(`/api/v1/accounts/${users[0].id}/follow`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }); + await using client0 = await generateClient(users[0]); + await using client1 = await generateClient(users[1]); - notifications = await fakeRequest("/api/v1/notifications", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }).then((r) => r.json()); + const { ok } = await client1.followAccount(users[0].id); - expect(notifications.length).toBe(1); + expect(ok).toBe(true); + + const { data } = await client0.getNotifications(); + + expect(data).toBeArrayOfSize(1); + notifications = data; }); afterAll(async () => { @@ -31,55 +27,41 @@ afterAll(async () => { describe("/api/v1/notifications/:id/dismiss", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest( - `/api/v1/notifications/${notifications[0].id}/dismiss`, - { - method: "POST", - }, + await using client = await generateClient(); + + const { ok, raw } = await client.dismissNotification( + notifications[0].id, ); - expect(response.status).toBe(401); + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should dismiss notification", async () => { - const response = await fakeRequest( - `/api/v1/notifications/${notifications[0].id}/dismiss`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); + const { ok } = await client.dismissNotification(notifications[0].id); + + expect(ok).toBe(true); }); test("should not display dismissed notification", async () => { - const response = await fakeRequest("/api/v1/notifications", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); + const { data, ok } = await client.getNotifications(); - const output = await response.json(); - - expect(output.length).toBe(0); + expect(ok).toBe(true); + expect(data).toBeArrayOfSize(0); }); test("should not be able to dismiss other user's notifications", async () => { - const response = await fakeRequest( - `/api/v1/notifications/${notifications[0].id}/dismiss`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }, + await using client = await generateClient(users[1]); + + const { ok, raw } = await client.dismissNotification( + notifications[0].id, ); - expect(response.status).toBe(404); + expect(ok).toBe(false); + expect(raw.status).toBe(404); }); }); diff --git a/api/api/v1/notifications/:id/index.test.ts b/api/api/v1/notifications/:id/index.test.ts index b403e802..b4e96639 100644 --- a/api/api/v1/notifications/:id/index.test.ts +++ b/api/api/v1/notifications/:id/index.test.ts @@ -1,28 +1,23 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import type { Notification as ApiNotification } from "@versia/client/types"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import type { z } from "@hono/zod-openapi"; +import type { Notification } from "@versia/client-ng/schemas"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(2); -let notifications: ApiNotification[] = []; +const { users, deleteUsers } = await getTestUsers(2); +let notifications: z.infer[] = []; -// Create some test notifications: follow, favourite, reblog, mention beforeAll(async () => { - await fakeRequest(`/api/v1/accounts/${users[0].id}/follow`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }); + await using client0 = await generateClient(users[0]); + await using client1 = await generateClient(users[1]); - notifications = await fakeRequest("/api/v1/notifications", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }).then((r) => r.json()); + const { ok } = await client1.followAccount(users[0].id); - expect(notifications.length).toBe(1); + expect(ok).toBe(true); + + const { data } = await client0.getNotifications(); + + expect(data).toBeArrayOfSize(1); + notifications = data; }); afterAll(async () => { @@ -32,66 +27,54 @@ afterAll(async () => { // /api/v1/notifications/:id describe("/api/v1/notifications/:id", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest( - "/api/v1/notifications/00000000-0000-0000-0000-000000000000", - ); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.getNotification(notifications[0].id); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return 422 if ID is invalid", async () => { - const response = await fakeRequest("/api/v1/notifications/invalid", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); - expect(response.status).toBe(422); + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.getNotification("invalid"); + + expect(ok).toBe(false); + expect(raw.status).toBe(422); }); test("should return 404 if notification not found", async () => { - const response = await fakeRequest( - "/api/v1/notifications/00000000-0000-0000-0000-000000000000", - { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.getNotification( + "00000000-0000-0000-0000-000000000000", ); - expect(response.status).toBe(404); + expect(ok).toBe(false); + expect(raw.status).toBe(404); }); test("should return notification", async () => { - const response = await fakeRequest( - `/api/v1/notifications/${notifications[0].id}`, - { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); + const { ok, data } = await client.getNotification(notifications[0].id); - const notification = (await response.json()) as ApiNotification; + expect(ok).toBe(true); - expect(notification).toBeDefined(); - expect(notification.id).toBe(notifications[0].id); - expect(notification.type).toBe("follow"); - expect(notification.account).toBeDefined(); - expect(notification.account?.id).toBe(users[1].id); + expect(data).toBeDefined(); + expect(data.id).toBe(notifications[0].id); + expect(data.type).toBe("follow"); + expect(data.account).toBeDefined(); + expect(data.account?.id).toBe(users[1].id); }); test("should not be able to view other user's notifications", async () => { - const response = await fakeRequest( - `/api/v1/notifications/${notifications[0].id}`, - { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[1]); - expect(response.status).toBe(404); + const { ok, raw } = await client.getNotification(notifications[0].id); + + expect(ok).toBe(false); + expect(raw.status).toBe(404); }); }); diff --git a/api/api/v1/notifications/clear/index.test.ts b/api/api/v1/notifications/clear/index.test.ts index 8f010433..7950cdbd 100644 --- a/api/api/v1/notifications/clear/index.test.ts +++ b/api/api/v1/notifications/clear/index.test.ts @@ -1,28 +1,21 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import type { Notification as ApiNotification } from "@versia/client/types"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(2); -let notifications: ApiNotification[] = []; +const { users, deleteUsers } = await getTestUsers(2); // Create some test notifications: follow, favourite, reblog, mention beforeAll(async () => { - await fakeRequest(`/api/v1/accounts/${users[0].id}/follow`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }); + await using client1 = await generateClient(users[1]); + await using client0 = await generateClient(users[0]); - notifications = await fakeRequest("/api/v1/notifications", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }).then((r) => r.json()); + const { ok } = await client1.followAccount(users[0].id); - expect(notifications.length).toBe(1); + expect(ok).toBe(true); + + const { data, ok: ok2 } = await client0.getNotifications(); + + expect(ok2).toBe(true); + expect(data).toBeArrayOfSize(1); }); afterAll(async () => { @@ -32,29 +25,23 @@ afterAll(async () => { // /api/v1/notifications/clear describe("/api/v1/notifications/clear", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest("/api/v1/notifications/clear", { - method: "POST", - }); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.dismissNotifications(); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should clear notifications", async () => { - const response = await fakeRequest("/api/v1/notifications/clear", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); + const { ok } = await client.dismissNotifications(); - const newNotifications = await fakeRequest("/api/v1/notifications", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }).then((r) => r.json()); + expect(ok).toBe(true); - expect(newNotifications.length).toBe(0); + const { data: newNotifications } = await client.getNotifications(); + + expect(newNotifications).toBeArrayOfSize(0); }); }); diff --git a/api/api/v1/notifications/destroy_multiple/index.test.ts b/api/api/v1/notifications/destroy_multiple/index.test.ts index b205e33f..3f301e32 100644 --- a/api/api/v1/notifications/destroy_multiple/index.test.ts +++ b/api/api/v1/notifications/destroy_multiple/index.test.ts @@ -1,40 +1,29 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import type { Notification as ApiNotification } from "@versia/client/types"; -import { fakeRequest, getTestStatuses, getTestUsers } from "~/tests/utils"; +import type { z } from "@hono/zod-openapi"; +import type { Notification } from "@versia/client-ng/schemas"; +import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(2); -const statuses = await getTestStatuses(40, users[0]); -let notifications: ApiNotification[] = []; +const { users, deleteUsers } = await getTestUsers(2); +const statuses = await getTestStatuses(5, users[0]); +let notifications: z.infer[] = []; // Create some test notifications beforeAll(async () => { - await fakeRequest(`/api/v1/accounts/${users[0].id}/follow`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }); + await using client0 = await generateClient(users[0]); + await using client1 = await generateClient(users[1]); + + const { ok } = await client1.followAccount(users[0].id); + + expect(ok).toBe(true); for (const i of [0, 1, 2, 3]) { - await fakeRequest(`/api/v1/statuses/${statuses[i].id}/favourite`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }); + await client1.favouriteStatus(statuses[i].id); } - notifications = await fakeRequest("/api/v1/notifications", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }).then((r) => r.json()); + const { data } = await client0.getNotifications(); - expect(notifications.length).toBe(5); + expect(data).toBeArrayOfSize(5); + notifications = data; }); afterAll(async () => { @@ -44,45 +33,33 @@ afterAll(async () => { // /api/v1/notifications/destroy_multiple describe("/api/v1/notifications/destroy_multiple", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest( - `/api/v1/notifications/destroy_multiple?${new URLSearchParams( - notifications.slice(1).map((n) => ["ids[]", n.id]), - ).toString()}`, - { - method: "DELETE", - }, + await using client = await generateClient(); + + const { ok, raw } = await client.dismissMultipleNotifications( + notifications.slice(1).map((n) => n.id), ); - expect(response.status).toBe(401); + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should dismiss notifications", async () => { - const response = await fakeRequest( - `/api/v1/notifications/destroy_multiple?${new URLSearchParams( - notifications.slice(1).map((n) => ["ids[]", n.id]), - ).toString()}`, - { - method: "DELETE", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.dismissMultipleNotifications( + notifications.slice(1).map((n) => n.id), ); - expect(response.status).toBe(200); + expect(ok).toBe(true); + expect(raw.status).toBe(200); }); - test("should not display dismissed notification", async () => { - const response = await fakeRequest("/api/v1/notifications", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); + test("should not display dismissed notifications", async () => { + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); + const { data } = await client.getNotifications(); - const output = await response.json(); - - expect(output.length).toBe(1); + expect(data).toBeArrayOfSize(1); + expect(data[0].id).toBe(notifications[0].id); }); }); diff --git a/api/api/v1/notifications/index.test.ts b/api/api/v1/notifications/index.test.ts index 9f9ea7d0..d03ebb89 100644 --- a/api/api/v1/notifications/index.test.ts +++ b/api/api/v1/notifications/index.test.ts @@ -1,70 +1,33 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import type { Notification as ApiNotification } from "@versia/client/types"; -import { fakeRequest, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; -const getFormData = ( - object: Record, -): FormData => - Object.keys(object).reduce((formData, key) => { - formData.append(key, String(object[key])); - return formData; - }, new FormData()); - -const { users, tokens, deleteUsers } = await getTestUsers(2); -const timeline = (await getTestStatuses(40, users[0])).toReversed(); +const { users, deleteUsers } = await getTestUsers(2); +const timeline = (await getTestStatuses(5, users[0])).toReversed(); // Create some test notifications: follow, favourite, reblog, mention beforeAll(async () => { - const res1 = await fakeRequest(`/api/v1/accounts/${users[0].id}/follow`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }); + await using client = await generateClient(users[1]); - expect(res1.status).toBe(200); + const { ok } = await client.followAccount(users[0].id); - const res2 = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}/favourite`, + expect(ok).toBe(true); + + const { ok: ok2 } = await client.favouriteStatus(timeline[0].id); + + expect(ok2).toBe(true); + + const { ok: ok3 } = await client.reblogStatus(timeline[0].id); + + expect(ok3).toBe(true); + + const { ok: ok4 } = await client.postStatus( + `@${users[0].data.username} test mention`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }, - ); - - expect(res2.status).toBe(200); - - const res3 = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}/reblog`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - body: getFormData({}), - }, - ); - - expect(res3.status).toBe(200); - - const res4 = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - body: new URLSearchParams({ - status: `@${users[0].data.username} test mention`, visibility: "direct", - local_only: "true", - }), - }); + local_only: true, + }, + ); - expect(res4.status).toBe(200); + expect(ok4).toBe(true); }); afterAll(async () => { @@ -74,27 +37,22 @@ afterAll(async () => { // /api/v1/notifications describe("/api/v1/notifications", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest("/api/v1/notifications"); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.getNotifications(); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return 200 with notifications", async () => { - const response = await fakeRequest("/api/v1/notifications", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", - ); + const { data, ok } = await client.getNotifications(); - const objects = (await response.json()) as ApiNotification[]; - - expect(objects.length).toBe(4); - for (const [index, notification] of objects.entries()) { + expect(ok).toBe(true); + expect(data.length).toBe(4); + for (const [index, notification] of data.entries()) { expect(notification.account).toBeDefined(); expect(notification.account?.id).toBe(users[1].id); expect(notification.created_at).toBeDefined(); @@ -103,64 +61,43 @@ describe("/api/v1/notifications", () => { expect(notification.type).toBe( ["follow", "favourite", "reblog", "mention"].toReversed()[ index - ], + ] as "follow" | "favourite" | "reblog" | "mention", ); } }); test("should not return notifications with filtered keywords", async () => { - const filterResponse = await fakeRequest("/api/v2/filters", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/x-www-form-urlencoded", + await using client = await generateClient(users[0]); + + const { data: filter, ok } = await client.createFilter( + ["notifications"], + "Test Filter", + "hide", + { + keywords_attributes: [ + { + keyword: timeline[0].content.slice(4, 20), + whole_word: false, + }, + ], }, - body: new URLSearchParams({ - title: "Test Filter", - "context[]": "notifications", - filter_action: "hide", - "keywords_attributes[0][keyword]": timeline[0].content.slice( - 4, - 20, - ), - "keywords_attributes[0][whole_word]": "false", - }), - }); - - expect(filterResponse.status).toBe(200); - - const response = await fakeRequest("/api/v1/notifications?limit=20", { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", ); - const objects = (await response.json()) as ApiNotification[]; + expect(ok).toBe(true); - expect(objects.length).toBe(3); + const { data: notifications } = await client.getNotifications(); + + expect(notifications.length).toBe(3); // There should be no element with a status with id of timeline[0].id - expect(objects).not.toContainEqual( + expect(notifications).not.toContainEqual( expect.objectContaining({ status: expect.objectContaining({ id: timeline[0].id }), }), ); // Delete filter - const filterDeleteResponse = await fakeRequest( - `/api/v2/filters/${(await filterResponse.json()).id}`, - { - method: "DELETE", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, - ); + const { ok: ok2 } = await client.deleteFilter(filter.id); - expect(filterDeleteResponse.status).toBe(204); + expect(ok2).toBe(true); }); }); diff --git a/api/api/v1/push/subscription/index.test.ts b/api/api/v1/push/subscription/index.test.ts index e6acddac..29344980 100644 --- a/api/api/v1/push/subscription/index.test.ts +++ b/api/api/v1/push/subscription/index.test.ts @@ -1,6 +1,6 @@ import { afterAll, beforeEach, describe, expect, test } from "bun:test"; import { PushSubscription } from "@versia/kit/db"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "~/tests/utils"; const { users, tokens, deleteUsers } = await getTestUsers(2); @@ -15,42 +15,34 @@ beforeEach(async () => { describe("/api/v1/push/subscriptions", () => { test("should create a push subscription", async () => { - const res = await fakeRequest("/api/v1/push/subscription", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", + await using client = await generateClient(users[0]); + + const { ok, data } = await client.subscribePushNotifications( + { + endpoint: "https://example.com", + keys: { + auth: "test", + p256dh: "test", + }, }, - body: JSON.stringify({ - data: { - alerts: { - update: true, - }, + { + alerts: { + update: true, }, policy: "all", - subscription: { - endpoint: "https://example.com", - keys: { - p256dh: "test", - auth: "testthatis24charactersha", - }, - }, - }), - }); - - expect(res.status).toBe(200); - - const body = await res.json(); - - expect(body).toMatchObject({ - endpoint: "https://example.com", - alerts: { - update: true, }, + ); + + expect(ok).toBe(true); + expect(data.endpoint).toBe("https://example.com"); + expect(data.alerts).toMatchObject({ + update: true, }); }); test("should retrieve the same push subscription", async () => { + await using client = await generateClient(users[1]); + await PushSubscription.insert({ endpoint: "https://example.com", alerts: { @@ -68,28 +60,21 @@ describe("/api/v1/push/subscriptions", () => { policy: "all", authSecret: "test", publicKey: "test", - tokenId: tokens[1].id, + tokenId: client.dbToken.id, }); - const res = await fakeRequest("/api/v1/push/subscription", { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }); + const { ok, data } = await client.getPushSubscription(); - expect(res.status).toBe(200); - - const body = await res.json(); - - expect(body).toMatchObject({ - endpoint: "https://example.com", - alerts: { - update: true, - }, + expect(ok).toBe(true); + expect(data.endpoint).toBe("https://example.com"); + expect(data.alerts).toMatchObject({ + update: true, }); }); test("should update a push subscription", async () => { + await using client = await generateClient(users[0]); + await PushSubscription.insert({ endpoint: "https://example.com", alerts: { @@ -107,65 +92,49 @@ describe("/api/v1/push/subscriptions", () => { policy: "all", authSecret: "test", publicKey: "test", - tokenId: tokens[0].id, + tokenId: client.dbToken.id, }); - const res = await fakeRequest("/api/v1/push/subscription", { - method: "PUT", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - data: { - alerts: { - update: false, - favourite: true, - }, + const { ok, data } = await client.updatePushSubscription( + { + alerts: { + update: false, + favourite: true, }, - policy: "follower", - }), - }); - - expect(res.status).toBe(200); - expect(await res.json()).toMatchObject({ - alerts: { - update: false, - favourite: true, }, + "follower", + ); + + expect(ok).toBe(true); + expect(data.alerts).toMatchObject({ + update: false, + favourite: true, }); }); describe("permissions", () => { test("should not allow watching admin reports without permissions", async () => { - const res = await fakeRequest("/api/v1/push/subscription", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", + await using client = await generateClient(users[0]); + + const { data, ok } = await client.subscribePushNotifications( + { + endpoint: "https://example.com", + keys: { + auth: "testthatis24charactersha", + p256dh: "test", + }, }, - body: JSON.stringify({ - data: { - alerts: { - "admin.report": true, - }, + { + alerts: { + "admin.report": true, }, policy: "all", - subscription: { - endpoint: "https://example.com", - keys: { - p256dh: "test", - auth: "testthatis24charactersha", - }, - }, - }), - }); - - expect(res.status).toBe(200); - expect(await res.json()).toMatchObject({ - alerts: { - "admin.report": false, }, + ); + + expect(ok).toBe(true); + expect(data.alerts).toMatchObject({ + "admin.report": false, }); }); @@ -174,30 +143,28 @@ describe("/api/v1/push/subscriptions", () => { isAdmin: true, }); - const res = await fakeRequest("/api/v1/push/subscription", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", + await using client = await generateClient(users[0]); + + const { ok, data } = await client.subscribePushNotifications( + { + endpoint: "https://example.com", + keys: { + auth: "testthatis24charactersha", + p256dh: "test", + }, }, - body: JSON.stringify({ - data: { - alerts: { - "admin.report": true, - }, + { + alerts: { + "admin.report": true, }, policy: "all", - subscription: { - endpoint: "https://example.com", - keys: { - p256dh: "test", - auth: "testthatis24charactersha", - }, - }, - }), - }); + }, + ); - expect(res.status).toBe(200); + expect(ok).toBe(true); + expect(data.alerts).toMatchObject({ + "admin.report": true, + }); await users[0].update({ isAdmin: false, @@ -205,6 +172,8 @@ describe("/api/v1/push/subscriptions", () => { }); test("should not allow editing to add admin reports without permissions", async () => { + await using client = await generateClient(users[0]); + await PushSubscription.insert({ endpoint: "https://example.com", alerts: { @@ -222,30 +191,21 @@ describe("/api/v1/push/subscriptions", () => { policy: "all", authSecret: "test", publicKey: "test", - tokenId: tokens[0].id, + tokenId: client.dbToken.id, }); - const res = await fakeRequest("/api/v1/push/subscription", { - method: "PUT", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - data: { - alerts: { - "admin.report": true, - }, + const { ok, data } = await client.updatePushSubscription( + { + alerts: { + "admin.report": true, }, - policy: "all", - }), - }); - - expect(res.status).toBe(200); - expect(await res.json()).toMatchObject({ - alerts: { - "admin.report": false, }, + "all", + ); + + expect(ok).toBe(true); + expect(data.alerts).toMatchObject({ + "admin.report": false, }); }); @@ -254,6 +214,8 @@ describe("/api/v1/push/subscriptions", () => { isAdmin: true, }); + await using client = await generateClient(users[0]); + await PushSubscription.insert({ endpoint: "https://example.com", alerts: { @@ -271,39 +233,30 @@ describe("/api/v1/push/subscriptions", () => { policy: "all", authSecret: "test", publicKey: "test", - tokenId: tokens[0].id, + tokenId: client.dbToken.id, }); - const res = await fakeRequest("/api/v1/push/subscription", { - method: "PUT", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - data: { - alerts: { - "admin.report": true, - }, + const { ok, data } = await client.updatePushSubscription( + { + alerts: { + "admin.report": true, }, - policy: "all", - }), - }); - - expect(res.status).toBe(200); - expect(await res.json()).toMatchObject({ - alerts: { - update: true, - "admin.report": true, - "admin.sign_up": false, - favourite: false, - follow: false, - follow_request: false, - mention: false, - poll: false, - reblog: false, - status: false, }, + "all", + ); + + expect(ok).toBe(true); + expect(data.alerts).toMatchObject({ + update: true, + "admin.report": true, + "admin.sign_up": false, + favourite: false, + follow: false, + follow_request: false, + mention: false, + poll: false, + reblog: false, + status: false, }); await users[0].update({ diff --git a/api/api/v1/roles/:id/index.test.ts b/api/api/v1/roles/:id/index.test.ts index 5bff9a9e..50e9c81e 100644 --- a/api/api/v1/roles/:id/index.test.ts +++ b/api/api/v1/roles/:id/index.test.ts @@ -1,9 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { Role } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(2); +const { users, deleteUsers } = await getTestUsers(2); let role: Role; let higherPriorityRole: Role; @@ -44,38 +44,32 @@ afterAll(async () => { // /api/v1/roles/:id describe("/api/v1/roles/:id", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest(`/api/v1/roles/${role.id}`, { - method: "GET", - }); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.getRole(role.id); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return 404 if role does not exist", async () => { - const response = await fakeRequest( - "/api/v1/roles/00000000-0000-0000-0000-000000000000", - { - method: "GET", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.getRole( + "00000000-0000-0000-0000-000000000000", ); - expect(response.status).toBe(404); + expect(ok).toBe(false); + expect(raw.status).toBe(404); }); test("should return role data", async () => { - const response = await fakeRequest(`/api/v1/roles/${role.id}`, { - method: "GET", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); - const responseData = await response.json(); - expect(responseData).toMatchObject({ + const { ok, data } = await client.getRole(role.id); + + expect(ok).toBe(true); + expect(data).toMatchObject({ id: role.id, name: role.data.name, permissions: role.data.permissions, @@ -87,63 +81,56 @@ describe("/api/v1/roles/:id", () => { }); test("should return 401 if not authenticated", async () => { - const response = await fakeRequest(`/api/v1/roles/${role.id}`, { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ name: "updatedName" }), + await using client = await generateClient(); + + const { ok, raw } = await client.updateRole(role.id, { + name: "updatedName", }); - expect(response.status).toBe(401); + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return 404 if role does not exist", async () => { - const response = await fakeRequest( - "/api/v1/roles/00000000-0000-0000-0000-000000000000", + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.updateRole( + "00000000-0000-0000-0000-000000000000", { - method: "PATCH", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ name: "updatedName" }), + name: "updatedName", }, ); - expect(response.status).toBe(404); + expect(ok).toBe(false); + expect(raw.status).toBe(404); }); test("should update role data", async () => { - const response = await fakeRequest(`/api/v1/roles/${role.id}`, { - method: "PATCH", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ name: "updatedName" }), + await using client = await generateClient(users[0]); + + const { ok } = await client.updateRole(role.id, { + name: "updatedName", }); - expect(response.status).toBe(204); + expect(ok).toBe(true); const updatedRole = await Role.fromId(role.id); expect(updatedRole?.data.name).toBe("updatedName"); }); test("should return 403 if user tries to update role with higher priority", async () => { - const response = await fakeRequest( - `/api/v1/roles/${higherPriorityRole.id}`, + await using client = await generateClient(users[0]); + + const { data, ok, raw } = await client.updateRole( + higherPriorityRole.id, { - method: "PATCH", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ name: "updatedName" }), + name: "updatedName", }, ); - expect(response.status).toBe(403); - const output = await response.json(); - expect(output).toMatchObject({ + expect(ok).toBe(false); + expect(raw.status).toBe(403); + expect(data).toMatchObject({ error: "Forbidden", details: "User with highest role priority 2 cannot edit role with priority 3", @@ -151,45 +138,38 @@ describe("/api/v1/roles/:id", () => { }); test("should return 403 if user tries to update role with permissions they do not have", async () => { - const response = await fakeRequest(`/api/v1/roles/${role.id}`, { - method: "PATCH", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - permissions: [RolePermissions.Impersonate], - }), + await using client = await generateClient(users[0]); + + const { data, ok, raw } = await client.updateRole(role.id, { + permissions: [RolePermissions.Impersonate], }); - expect(response.status).toBe(403); - const output = await response.json(); - expect(output).toMatchObject({ + expect(ok).toBe(false); + expect(raw.status).toBe(403); + expect(data).toMatchObject({ error: "Forbidden", details: "User cannot add or remove permissions they do not have", }); }); test("should return 401 if not authenticated", async () => { - const response = await fakeRequest(`/api/v1/roles/${role.id}`, { - method: "DELETE", - }); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.deleteRole(role.id); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return 404 if role does not exist", async () => { - const response = await fakeRequest( - "/api/v1/roles/00000000-0000-0000-0000-000000000000", - { - method: "DELETE", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.deleteRole( + "00000000-0000-0000-0000-000000000000", ); - expect(response.status).toBe(404); + expect(ok).toBe(false); + expect(raw.status).toBe(404); }); test("should delete role", async () => { @@ -202,33 +182,26 @@ describe("/api/v1/roles/:id", () => { icon: "test", }); - const response = await fakeRequest(`/api/v1/roles/${newRole.id}`, { - method: "DELETE", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); + await using client = await generateClient(users[0]); - expect(response.status).toBe(204); + const { ok } = await client.deleteRole(newRole.id); + + expect(ok).toBe(true); const deletedRole = await Role.fromId(newRole.id); expect(deletedRole).toBeNull(); }); test("should return 403 if user tries to delete role with higher priority", async () => { - const response = await fakeRequest( - `/api/v1/roles/${higherPriorityRole.id}`, - { - method: "DELETE", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, + await using client = await generateClient(users[0]); + + const { data, ok, raw } = await client.deleteRole( + higherPriorityRole.id, ); - expect(response.status).toBe(403); - const output = await response.json(); - expect(output).toMatchObject({ + expect(ok).toBe(false); + expect(raw.status).toBe(403); + expect(data).toMatchObject({ error: "Forbidden", details: "User with highest role priority 2 cannot delete role with priority 3", diff --git a/api/api/v1/roles/index.test.ts b/api/api/v1/roles/index.test.ts index 12997104..1f859d22 100644 --- a/api/api/v1/roles/index.test.ts +++ b/api/api/v1/roles/index.test.ts @@ -2,9 +2,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { Role } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { config } from "~/config.ts"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, deleteUsers, tokens } = await getTestUsers(1); +const { users, deleteUsers } = await getTestUsers(1); let role: Role; beforeAll(async () => { @@ -32,24 +32,21 @@ afterAll(async () => { // /api/v1/roles describe("/api/v1/roles", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest("/api/v1/roles", { - method: "GET", - }); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.getRoles(); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return a list of roles", async () => { - const response = await fakeRequest("/api/v1/roles", { - method: "GET", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }); + await using client = await generateClient(users[0]); - expect(response.ok).toBe(true); - const roles = await response.json(); - expect(roles).toContainEqual({ + const { ok, data } = await client.getRoles(); + + expect(ok).toBe(true); + expect(data).toContainEqual({ name: "test", permissions: [RolePermissions.ManageRoles], priority: 10, @@ -59,7 +56,7 @@ describe("/api/v1/roles", () => { id: role.id, }); - expect(roles).toContainEqual({ + expect(data).toContainEqual({ id: "default", name: "Default", permissions: config.permissions.default, @@ -70,25 +67,18 @@ describe("/api/v1/roles", () => { }); test("should create a new role", async () => { - const response = await fakeRequest("/api/v1/roles", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - name: "newRole", - permissions: [RolePermissions.ManageRoles], - priority: 1, - description: "newRole", - visible: true, - icon: "https://example.com/icon.png", - }), + await using client = await generateClient(users[0]); + + const { ok, data } = await client.createRole("newRole", { + permissions: [RolePermissions.ManageRoles], + priority: 1, + description: "newRole", + visible: true, + icon: "https://example.com/icon.png", }); - expect(response.ok).toBe(true); - const newRole = await response.json(); - expect(newRole).toMatchObject({ + expect(ok).toBe(true); + expect(data).toMatchObject({ name: "newRole", permissions: [RolePermissions.ManageRoles], priority: 1, @@ -98,7 +88,7 @@ describe("/api/v1/roles", () => { }); // Cleanup - const createdRole = await Role.fromId(newRole.id); + const createdRole = await Role.fromId(data.id); expect(createdRole).toBeDefined(); @@ -106,49 +96,37 @@ describe("/api/v1/roles", () => { }); test("should return 403 if user tries to create a role with higher priority", async () => { - const response = await fakeRequest("/api/v1/roles", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - name: "newRole", - permissions: [RolePermissions.ManageBlocks], - priority: 11, - description: "newRole", - visible: true, - icon: "https://example.com/icon.png", - }), + await using client = await generateClient(users[0]); + + const { data, ok, raw } = await client.createRole("newRole", { + permissions: [RolePermissions.ManageBlocks], + priority: 11, + description: "newRole", + visible: true, + icon: "https://example.com/icon.png", }); - expect(response.status).toBe(403); - const output = await response.json(); - expect(output).toMatchObject({ + expect(ok).toBe(false); + expect(raw.status).toBe(403); + expect(data).toMatchObject({ error: "Cannot create role with higher priority than your own", }); }); test("should return 403 if user tries to create a role with permissions they do not have", async () => { - const response = await fakeRequest("/api/v1/roles", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - name: "newRole", - permissions: [RolePermissions.Impersonate], - priority: 1, - description: "newRole", - visible: true, - icon: "https://example.com/icon.png", - }), + await using client = await generateClient(users[0]); + + const { data, ok, raw } = await client.createRole("newRole", { + permissions: [RolePermissions.Impersonate], + priority: 1, + description: "newRole", + visible: true, + icon: "https://example.com/icon.png", }); - expect(response.status).toBe(403); - const output = await response.json(); - expect(output).toMatchObject({ + expect(ok).toBe(false); + expect(raw.status).toBe(403); + expect(data).toMatchObject({ error: "Cannot create role with permissions you do not have", details: "Forbidden permissions: impersonate", }); diff --git a/api/api/v1/statuses/:id/favourite.test.ts b/api/api/v1/statuses/:id/favourite.test.ts index 1c097343..84a30665 100644 --- a/api/api/v1/statuses/:id/favourite.test.ts +++ b/api/api/v1/statuses/:id/favourite.test.ts @@ -1,60 +1,43 @@ import { afterAll, describe, expect, test } from "bun:test"; -import type { Status as ApiStatus } from "@versia/client/types"; -import { fakeRequest, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(5); +const { users, deleteUsers } = await getTestUsers(5); const timeline = (await getTestStatuses(2, users[0])).toReversed(); afterAll(async () => { await deleteUsers(); }); -// /api/v1/statuses/:id/favourite describe("/api/v1/statuses/:id/favourite", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}/favourite`, - { - method: "POST", - }, - ); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.favouriteStatus(timeline[0].id); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should favourite post", async () => { - const response = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}/favourite`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }, - ); - expect(response.status).toBe(200); + await using client = await generateClient(users[1]); - const json = (await response.json()) as ApiStatus; + const { data, ok } = await client.favouriteStatus(timeline[0].id); - expect(json.favourited).toBe(true); - expect(json.favourites_count).toBe(1); + expect(ok).toBe(true); + expect(data).toMatchObject({ + favourited: true, + favourites_count: 1, + }); }); test("post should be favourited when fetched", async () => { - const response = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}`, - { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[1]); - expect(response.status).toBe(200); + const { data } = await client.getStatus(timeline[0].id); - const json = (await response.json()) as ApiStatus; - - expect(json.favourited).toBe(true); - expect(json.favourites_count).toBe(1); + expect(data).toMatchObject({ + favourited: true, + favourites_count: 1, + }); }); }); diff --git a/api/api/v1/statuses/:id/favourited_by.test.ts b/api/api/v1/statuses/:id/favourited_by.test.ts index 37b8b30f..f8521545 100644 --- a/api/api/v1/statuses/:id/favourited_by.test.ts +++ b/api/api/v1/statuses/:id/favourited_by.test.ts @@ -1,56 +1,42 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import type { Account as ApiAccount } from "@versia/client/types"; -import { fakeRequest, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(5); -const timeline = (await getTestStatuses(40, users[0])).toReversed(); +const { users, deleteUsers } = await getTestUsers(5); +const timeline = (await getTestStatuses(5, users[0])).toReversed(); afterAll(async () => { await deleteUsers(); }); beforeAll(async () => { + await using client = await generateClient(users[1]); + for (const status of timeline) { - await fakeRequest(`/api/v1/statuses/${status.id}/favourite`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }); + await client.favouriteStatus(status.id); } }); // /api/v1/statuses/:id/favourited_by describe("/api/v1/statuses/:id/favourited_by", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}/favourited_by`, - ); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.getStatusFavouritedBy(timeline[0].id); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return 200 with users", async () => { - const response = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}/favourited_by`, - { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", - ); + const { data, ok } = await client.getStatusFavouritedBy(timeline[0].id); - const objects = (await response.json()) as ApiAccount[]; + expect(ok).toBe(true); + expect(data).toBeArrayOfSize(1); - expect(objects.length).toBe(1); - for (const [, status] of objects.entries()) { - expect(status.id).toBe(users[1].id); - expect(status.username).toBe(users[1].data.username); - } + expect(data.length).toBe(1); + expect(data[0].id).toBe(users[1].id); + expect(data[0].username).toBe(users[1].data.username); }); }); diff --git a/api/api/v1/statuses/:id/reblogged_by.test.ts b/api/api/v1/statuses/:id/reblogged_by.test.ts index 45167bbc..6d1e0f78 100644 --- a/api/api/v1/statuses/:id/reblogged_by.test.ts +++ b/api/api/v1/statuses/:id/reblogged_by.test.ts @@ -1,56 +1,41 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import type { Account as ApiAccount } from "@versia/client/types"; -import { fakeRequest, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(5); -const timeline = (await getTestStatuses(40, users[0])).toReversed(); +const { users, deleteUsers } = await getTestUsers(5); +const timeline = (await getTestStatuses(5, users[0])).toReversed(); afterAll(async () => { await deleteUsers(); }); beforeAll(async () => { + await using client = await generateClient(users[1]); + for (const status of timeline) { - await fakeRequest(`/api/v1/statuses/${status.id}/reblog`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }); + await client.reblogStatus(status.id); } }); -// /api/v1/statuses/:id/reblogged_by describe("/api/v1/statuses/:id/reblogged_by", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}/reblogged_by`, - ); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.getStatusRebloggedBy(timeline[0].id); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return 200 with users", async () => { - const response = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}/reblogged_by`, - { - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", - ); + const { data, ok } = await client.getStatusRebloggedBy(timeline[0].id); - const objects = (await response.json()) as ApiAccount[]; + expect(ok).toBe(true); + expect(data).toBeArrayOfSize(1); - expect(objects.length).toBe(1); - for (const [, status] of objects.entries()) { - expect(status.id).toBe(users[1].id); - expect(status.username).toBe(users[1].data.username); - } + expect(data.length).toBe(1); + expect(data[0].id).toBe(users[1].id); + expect(data[0].username).toBe(users[1].data.username); }); }); diff --git a/api/api/v1/statuses/:id/unfavourite.test.ts b/api/api/v1/statuses/:id/unfavourite.test.ts index 185e91fd..7b13db21 100644 --- a/api/api/v1/statuses/:id/unfavourite.test.ts +++ b/api/api/v1/statuses/:id/unfavourite.test.ts @@ -1,84 +1,58 @@ -import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import type { Status as ApiStatus } from "@versia/client/types"; -import { fakeRequest, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { afterAll, describe, expect, test } from "bun:test"; +import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(5); +const { users, deleteUsers } = await getTestUsers(5); const timeline = (await getTestStatuses(2, users[0])).toReversed(); afterAll(async () => { await deleteUsers(); }); -// /api/v1/statuses/:id/unfavourite describe("/api/v1/statuses/:id/unfavourite", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}/unfavourite`, - { - method: "POST", - }, - ); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.unfavouriteStatus(timeline[0].id); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should be able to unfavourite post that is not favourited", async () => { - const response = await fakeRequest( - `/api/v1/statuses/${timeline[0].id}/unfavourite`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[1]); - expect(response.status).toBe(200); + const { ok } = await client.unfavouriteStatus(timeline[0].id); + + expect(ok).toBe(true); }); test("should unfavourite post", async () => { - beforeAll(async () => { - await fakeRequest(`/api/v1/statuses/${timeline[1].id}/favourite`, { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }); + await using client = await generateClient(users[1]); + + await client.favouriteStatus(timeline[1].id); + + const { ok } = await client.unfavouriteStatus(timeline[1].id); + + expect(ok).toBe(true); + + const { ok: ok2, data } = await client.getStatus(timeline[1].id); + + expect(ok2).toBe(true); + expect(data).toMatchObject({ + favourited: false, + favourites_count: 0, }); - - const response = await fakeRequest( - `/api/v1/statuses/${timeline[1].id}/unfavourite`, - { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }, - ); - - expect(response.status).toBe(200); - - const json = (await response.json()) as ApiStatus; - - expect(json.favourited).toBe(false); - expect(json.favourites_count).toBe(0); }); test("post should not be favourited when fetched", async () => { - const response = await fakeRequest( - `/api/v1/statuses/${timeline[1].id}`, - { - headers: { - Authorization: `Bearer ${tokens[1].data.accessToken}`, - }, - }, - ); + await using client = await generateClient(users[1]); - expect(response.status).toBe(200); + const { ok, data } = await client.getStatus(timeline[1].id); - const json = (await response.json()) as ApiStatus; - - expect(json.favourited).toBe(false); - expect(json.favourites_count).toBe(0); + expect(ok).toBe(true); + expect(data).toMatchObject({ + favourited: false, + favourites_count: 0, + }); }); }); diff --git a/api/api/v1/statuses/index.test.ts b/api/api/v1/statuses/index.test.ts index f3aafe8e..acce02d5 100644 --- a/api/api/v1/statuses/index.test.ts +++ b/api/api/v1/statuses/index.test.ts @@ -1,12 +1,13 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import type { Status as ApiStatus } from "@versia/client/types"; +import type { z } from "@hono/zod-openapi"; +import type { Status } from "@versia/client-ng/schemas"; import { Media, db } from "@versia/kit/db"; import { Emojis } from "@versia/kit/tables"; import { eq } from "drizzle-orm"; import { config } from "~/config.ts"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, tokens, deleteUsers } = await getTestUsers(5); +const { users, deleteUsers } = await getTestUsers(5); let media: Media; afterAll(async () => { @@ -32,266 +33,172 @@ beforeAll(async () => { describe("/api/v1/statuses", () => { test("should return 401 if not authenticated", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - body: new URLSearchParams({ - status: "Hello, world!", - }), - }); + await using client = await generateClient(); - expect(response.status).toBe(401); + const { ok, raw } = await client.postStatus("Hello, world!"); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); }); test("should return 422 is status is empty", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams(), - }); + await using client = await generateClient(users[0]); - expect(response.status).toBe(422); + const { ok, raw } = await client.postStatus(""); + + expect(ok).toBe(false); + expect(raw.status).toBe(422); }); test("should return 422 is status is too long", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "a".repeat(config.validation.notes.max_characters + 1), - local_only: "true", - }), - }); + await using client = await generateClient(users[0]); - expect(response.status).toBe(422); + const { ok, raw } = await client.postStatus( + "a".repeat(config.validation.notes.max_characters + 1), + ); + + expect(ok).toBe(false); + expect(raw.status).toBe(422); }); test("should return 422 is visibility is invalid", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hello, world!", - visibility: "invalid", - local_only: "true", - }), + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.postStatus("Hello, world!", { + visibility: "invalid" as z.infer["visibility"], }); - expect(response.status).toBe(422); + expect(ok).toBe(false); + expect(raw.status).toBe(422); }); test("should return 422 if scheduled_at is invalid", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hello, world!", - scheduled_at: "invalid", - local_only: "true", - }), + await using client = await generateClient(users[0]); + + const { data, ok, raw } = await client.postStatus("Hello, world!", { + scheduled_at: new Date(Date.now() - 1000), }); - expect(response.status).toBe(422); + expect(ok).toBe(false); + expect(raw.status).toBe(422); + expect(data).toMatchObject({ + error: expect.stringContaining( + "must be at least 5 minutes in the future", + ), + }); }); test("should return 422 is in_reply_to_id is invalid", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hello, world!", - in_reply_to_id: "invalid", - local_only: "true", - }), + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.postStatus("Hello, world!", { + in_reply_to_id: "invalid", }); - expect(response.status).toBe(422); + expect(ok).toBe(false); + expect(raw.status).toBe(422); }); test("should return 422 is quote_id is invalid", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hello, world!", - quote_id: "invalid", - local_only: "true", - }), + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.postStatus("Hello, world!", { + quote_id: "invalid", }); - expect(response.status).toBe(422); + expect(ok).toBe(false); + expect(raw.status).toBe(422); }); test("should return 422 is media_ids is invalid", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hello, world!", - "media_ids[]": "invalid", - local_only: "true", - }), + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.postStatus("Hello, world!", { + media_ids: ["invalid"], }); - expect(response.status).toBe(422); + expect(ok).toBe(false); + expect(raw.status).toBe(422); }); test("should create a post", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hello, world!", - local_only: "true", - }), + await using client = await generateClient(users[0]); + + const { data, ok } = await client.postStatus("Hello, world!"); + + expect(ok).toBe(true); + expect(data).toMatchObject({ + content: "

Hello, world!

", }); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", - ); - - const object = (await response.json()) as ApiStatus; - - expect(object.content).toBe("

Hello, world!

"); }); test("should create a post with visibility", async () => { - // This one uses JSON to test the interop - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - status: "Hello, world!", - visibility: "unlisted", - local_only: "true", - }), + await using client = await generateClient(users[0]); + + const { data, ok } = await client.postStatus("Hello, world!", { + visibility: "unlisted", }); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", - ); - - const object = (await response.json()) as ApiStatus; - - expect(object.content).toBe("

Hello, world!

"); - expect(object.visibility).toBe("unlisted"); + expect(ok).toBe(true); + expect(data).toMatchObject({ + visibility: "unlisted", + content: "

Hello, world!

", + }); }); test("should create a post with a reply", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, + await using client = await generateClient(users[0]); + + const { data, ok } = await client.postStatus("Hello, world!"); + + expect(ok).toBe(true); + + const { data: data2, ok: ok2 } = await client.postStatus( + "Hello, world again!", + { + in_reply_to_id: data.id, }, - body: new URLSearchParams({ - status: "Hello, world!", - local_only: "true", - }), - }); - - const object = (await response.json()) as ApiStatus; - - const response2 = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hello, world again!", - in_reply_to_id: object.id, - local_only: "true", - }), - }); - - expect(response2.status).toBe(200); - expect(response2.headers.get("content-type")).toContain( - "application/json", ); - const object2 = (await response2.json()) as ApiStatus; - - expect(object2.content).toBe("

Hello, world again!

"); - expect(object2.in_reply_to_id).toBe(object.id); + expect(ok2).toBe(true); + expect(data2).toMatchObject({ + content: "

Hello, world again!

", + in_reply_to_id: data.id, + }); }); test("should create a post with a quote", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, + await using client = await generateClient(users[0]); + + const { data, ok } = await client.postStatus("Hello, world!"); + + expect(ok).toBe(true); + + const { data: data2, ok: ok2 } = await client.postStatus( + "Hello, world again!", + { + quote_id: data.id, }, - body: new URLSearchParams({ - status: "Hello, world!", - local_only: "true", - }), - }); - - const object = (await response.json()) as ApiStatus; - - const response2 = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hello, world again!", - quote_id: object.id, - local_only: "true", - }), - }); - - expect(response2.status).toBe(200); - expect(response2.headers.get("content-type")).toContain( - "application/json", ); - const object2 = (await response2.json()) as ApiStatus; - - expect(object2.content).toBe("

Hello, world again!

"); - expect(object2.quote?.id).toBe(object.id); + expect(ok2).toBe(true); + expect(data2).toMatchObject({ + content: "

Hello, world again!

", + quote: expect.objectContaining({ + id: data.id, + }), + }); }); test("should correctly parse emojis", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hello, :test:!", - local_only: "true", - }), - }); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", - ); + const { data, ok } = await client.postStatus("Hello, :test:!"); - const object = (await response.json()) as ApiStatus; + expect(ok).toBe(true); - expect(object.emojis).toBeArrayOfSize(1); - expect(object.emojis[0]).toMatchObject({ + expect((data as z.infer).emojis).toBeArrayOfSize(1); + expect((data as z.infer).emojis[0]).toMatchObject({ shortcode: "test", url: expect.stringContaining("/media/proxy/"), }); @@ -299,29 +206,20 @@ describe("/api/v1/statuses", () => { describe("mentions testing", () => { test("should correctly parse @mentions", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: `Hello, @${users[1].data.username}!`, - local_only: "true", - }), + await using client = await generateClient(users[0]); + + const { data, ok } = await client.postStatus( + `Hello, @${users[1].data.username}!`, + ); + + expect(ok).toBe(true); + expect(data).toMatchObject({ + content: `

Hello, @${users[1].data.username}!

`, }); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", + expect((data as z.infer).mentions).toBeArrayOfSize( + 1, ); - - const object = (await response.json()) as ApiStatus; - - expect(object.content).toBe( - `

Hello, @${users[1].data.username}!

`, - ); - expect(object.mentions).toBeArrayOfSize(1); - expect(object.mentions[0]).toMatchObject({ + expect((data as z.infer).mentions[0]).toMatchObject({ id: users[1].id, username: users[1].data.username, acct: users[1].data.username, @@ -329,28 +227,22 @@ describe("/api/v1/statuses", () => { }); test("should correctly parse @mentions@domain", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: `Hello, @${users[1].data.username}@${ - config.http.base_url.host - }!`, - local_only: "true", - }), - }); + await using client = await generateClient(users[0]); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", + const { data, ok } = await client.postStatus( + `Hello, @${users[1].data.username}@${ + config.http.base_url.host + }!`, ); - const object = (await response.json()) as ApiStatus; - - expect(object.mentions).toBeArrayOfSize(1); - expect(object.mentions[0]).toMatchObject({ + expect(ok).toBe(true); + expect(data).toMatchObject({ + content: `

Hello, @${users[1].data.username}!

`, + }); + expect((data as z.infer).mentions).toBeArrayOfSize( + 1, + ); + expect((data as z.infer).mentions[0]).toMatchObject({ id: users[1].id, username: users[1].data.username, acct: users[1].data.username, @@ -360,83 +252,51 @@ describe("/api/v1/statuses", () => { describe("HTML injection testing", () => { test("should not allow HTML injection", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hi! ", - local_only: "true", - }), + await using client = await generateClient(users[0]); + + const { data, ok } = await client.postStatus( + "Hi! ", + ); + + expect(ok).toBe(true); + expect(data).toMatchObject({ + content: + "

Hi! <script>alert('Hello, world!');</script>

", }); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", - ); - - const object = (await response.json()) as ApiStatus; - - expect(object.content).toBe( - "

Hi! <script>alert('Hello, world!');</script>

", - ); }); test("should not allow HTML injection in spoiler_text", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "Hello, world!", - spoiler_text: - "uwu ", - local_only: "true", - }), + await using client = await generateClient(users[0]); + + const { data, ok } = await client.postStatus("Hello, world!", { + spoiler_text: "uwu ", }); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", - ); - - const object = (await response.json()) as ApiStatus; - - expect(object.spoiler_text).toBe( - "uwu <script>alert('Hello, world!');</script>", - ); + expect(ok).toBe(true); + expect(data).toMatchObject({ + spoiler_text: + "uwu <script>alert('Hello, world!');</script>", + }); }); test("should rewrite all image and video src to go through proxy", async () => { - const response = await fakeRequest("/api/v1/statuses", { - method: "POST", - headers: { - Authorization: `Bearer ${tokens[0].data.accessToken}`, - }, - body: new URLSearchParams({ - status: "