Remove Prisma once and for all

This commit is contained in:
Jesse Wierzbinski 2024-04-13 14:46:33 -10:00
parent 90d522eaa3
commit a65249b79d
No known key found for this signature in database
18 changed files with 112 additions and 563 deletions

View file

@ -13,7 +13,6 @@ await $`rm -rf dist && mkdir dist`;
await Bun.build({ await Bun.build({
entrypoints: [ entrypoints: [
`${process.cwd()}/index.ts`, `${process.cwd()}/index.ts`,
`${process.cwd()}/prisma.ts`,
`${process.cwd()}/cli.ts`, `${process.cwd()}/cli.ts`,
// Force Bun to include endpoints // Force Bun to include endpoints
...Object.values(rawRoutes), ...Object.values(rawRoutes),
@ -22,7 +21,7 @@ await Bun.build({
target: "bun", target: "bun",
splitting: true, splitting: true,
minify: true, minify: true,
external: ["bullmq", "@prisma/client", "frontend"], external: ["bullmq", "frontend"],
}).then((output) => { }).then((output) => {
if (!output.success) { if (!output.success) {
console.log(output.logs); console.log(output.logs);
@ -33,14 +32,6 @@ await Bun.build({
// I apologize for this // I apologize for this
await $`sed -i 's|import("node_modules/|import("./node_modules/|g' dist/*.js`; await $`sed -i 's|import("node_modules/|import("./node_modules/|g' dist/*.js`;
// Copy generated Prisma client to dist
await $`mkdir -p dist/node_modules/@prisma`;
await $`cp -r ${process.cwd()}/node_modules/@prisma dist/node_modules/`;
await $`cp -r ${process.cwd()}/node_modules/.prisma dist/node_modules`;
await $`mkdir -p dist/node_modules/.bin`;
await $`cp -r ${process.cwd()}/node_modules/.bin/prisma dist/node_modules/.bin`;
await $`cp -r ${process.cwd()}/node_modules/prisma dist/node_modules/`;
// Copy Sharp to dist // Copy Sharp to dist
await $`mkdir -p dist/node_modules/@img`; await $`mkdir -p dist/node_modules/@img`;
await $`cp -r node_modules/@img/sharp-libvips-linux-* dist/node_modules/@img`; await $`cp -r node_modules/@img/sharp-libvips-linux-* dist/node_modules/@img`;

BIN
bun.lockb

Binary file not shown.

22
cli.ts
View file

@ -3,26 +3,25 @@ import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { Parser } from "@json2csv/plainjs"; import { Parser } from "@json2csv/plainjs";
import { MeiliIndexType, rebuildSearchIndexes } from "@meilisearch"; import { MeiliIndexType, rebuildSearchIndexes } from "@meilisearch";
import type { Prisma } from "@prisma/client";
import chalk from "chalk"; import chalk from "chalk";
import { CliBuilder, CliCommand } from "cli-parser"; import { CliBuilder, CliCommand } from "cli-parser";
import Table from "cli-table"; import Table from "cli-table";
import { type SQL, eq, inArray, isNotNull, isNull, like } from "drizzle-orm";
import extract from "extract-zip"; import extract from "extract-zip";
import { MediaBackend } from "media-manager"; import { MediaBackend } from "media-manager";
import { lookup } from "mime-types"; import { lookup } from "mime-types";
import { getUrl } from "~database/entities/Attachment"; import { getUrl } from "~database/entities/Attachment";
import { findFirstStatuses, findManyStatuses } from "~database/entities/Status";
import { import {
type User,
createNewLocalUser, createNewLocalUser,
findFirstUser, findFirstUser,
findManyUsers, findManyUsers,
type User,
} from "~database/entities/User"; } from "~database/entities/User";
import { CliParameterType } from "~packages/cli-parser/cli-builder.type";
import { config } from "~packages/config-manager";
import { db } from "~drizzle/db"; import { db } from "~drizzle/db";
import { emoji, openIdAccount, status, user } from "~drizzle/schema"; import { emoji, openIdAccount, status, user } from "~drizzle/schema";
import { type SQL, eq, inArray, isNotNull, isNull, like } from "drizzle-orm"; import { CliParameterType } from "~packages/cli-parser/cli-builder.type";
import { findFirstStatuses, findManyStatuses } from "~database/entities/Status"; import { config } from "~packages/config-manager";
const args = process.argv; const args = process.argv;
@ -949,17 +948,6 @@ const cliBuilder = new CliBuilder([
return 1; return 1;
} }
const queries: Prisma.StatusWhereInput[] = [];
for (const field of fields) {
queries.push({
[field]: {
contains: query,
mode: caseSensitive ? "default" : "insensitive",
},
});
}
let instanceQuery: SQL<unknown> | undefined = isNull( let instanceQuery: SQL<unknown> | undefined = isNull(
status.instanceId, status.instanceId,
); );

View file

@ -1,10 +1,4 @@
// import { Queue } from "bullmq"; // import { Queue } from "bullmq";
import { PrismaClient } from "@prisma/client";
import { config } from "config-manager";
const client = new PrismaClient({
datasourceUrl: `postgresql://${config.database.username}:${config.database.password}@${config.database.host}:${config.database.port}/${config.database.database}`,
});
/* const federationQueue = new Queue("federation", { /* const federationQueue = new Queue("federation", {
connection: { connection: {
@ -14,5 +8,3 @@ const client = new PrismaClient({
db: config.redis.queue.database || undefined, db: config.redis.queue.database || undefined,
}, },
}); */ }); */
export { client /* federationQueue */ };

View file

@ -1,111 +0,0 @@
import type { Prisma } from "@prisma/client";
export const userRelations: Prisma.UserInclude = {
emojis: true,
instance: true,
likes: true,
relationships: true,
relationshipSubjects: true,
pinnedNotes: true,
_count: {
select: {
statuses: true,
likes: true,
},
},
};
export const statusAndUserRelations: Prisma.StatusInclude = {
author: {
include: userRelations,
},
application: true,
emojis: true,
inReplyToPost: {
include: {
author: {
include: userRelations,
},
application: true,
emojis: true,
inReplyToPost: {
include: {
author: true,
},
},
instance: true,
mentions: true,
pinnedBy: true,
_count: {
select: {
replies: true,
},
},
},
},
reblogs: true,
attachments: true,
instance: true,
mentions: {
include: userRelations,
},
pinnedBy: true,
_count: {
select: {
replies: true,
likes: true,
reblogs: true,
},
},
reblog: {
include: {
author: {
include: userRelations,
},
application: true,
emojis: true,
inReplyToPost: {
include: {
author: true,
},
},
instance: true,
mentions: {
include: userRelations,
},
pinnedBy: true,
_count: {
select: {
replies: true,
},
},
},
},
quotingPost: {
include: {
author: {
include: userRelations,
},
application: true,
emojis: true,
inReplyToPost: {
include: {
author: true,
},
},
instance: true,
mentions: true,
pinnedBy: true,
_count: {
select: {
replies: true,
},
},
},
},
likes: {
include: {
liker: true,
},
},
};

View file

@ -2,7 +2,6 @@ import { exists, mkdir, writeFile } from "node:fs/promises";
import { dirname } from "node:path"; import { dirname } from "node:path";
import { connectMeili } from "@meilisearch"; import { connectMeili } from "@meilisearch";
import { moduleIsEntry } from "@module"; import { moduleIsEntry } from "@module";
import { initializeRedisCache } from "@redis";
import { config } from "config-manager"; import { config } from "config-manager";
import { count, sql } from "drizzle-orm"; import { count, sql } from "drizzle-orm";
import { LogLevel, LogManager, MultiLogManager } from "log-manager"; import { LogLevel, LogManager, MultiLogManager } from "log-manager";
@ -39,8 +38,6 @@ await dualLogger.log(LogLevel.INFO, "Lysand", "Starting Lysand...");
const isProd = const isProd =
process.env.NODE_ENV === "production" || process.argv.includes("--prod"); process.env.NODE_ENV === "production" || process.argv.includes("--prod");
const redisCache = await initializeRedisCache();
if (config.meilisearch.enabled) { if (config.meilisearch.enabled) {
await connectMeili(dualLogger); await connectMeili(dualLogger);
} }

View file

@ -43,7 +43,7 @@
"prisma": "DATABASE_URL=$(bun run prisma.ts) bunx prisma", "prisma": "DATABASE_URL=$(bun run prisma.ts) bunx prisma",
"generate": "bun prisma generate", "generate": "bun prisma generate",
"benchmark:timeline": "bun run benchmarks/timelines.ts", "benchmark:timeline": "bun run benchmarks/timelines.ts",
"cloc": "cloc . --exclude-dir node_modules,dist", "cloc": "cloc . --exclude-dir node_modules,dist,.output,.nuxt,meta,logs --exclude-ext sql,log",
"cli": "bun run cli.ts" "cli": "bun run cli.ts"
}, },
"trustedDependencies": [ "trustedDependencies": [
@ -51,14 +51,11 @@
"@fortawesome/fontawesome-common-types", "@fortawesome/fontawesome-common-types",
"@fortawesome/free-regular-svg-icons", "@fortawesome/free-regular-svg-icons",
"@fortawesome/free-solid-svg-icons", "@fortawesome/free-solid-svg-icons",
"@prisma/client",
"@prisma/engines",
"es5-ext", "es5-ext",
"esbuild", "esbuild",
"json-editor-vue", "json-editor-vue",
"msgpackr-extract", "msgpackr-extract",
"nuxt-app", "nuxt-app",
"prisma",
"sharp", "sharp",
"vue-demi" "vue-demi"
], ],
@ -99,7 +96,6 @@
"@aws-sdk/client-s3": "^3.461.0", "@aws-sdk/client-s3": "^3.461.0",
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"@json2csv/plainjs": "^7.0.6", "@json2csv/plainjs": "^7.0.6",
"@prisma/client": "^5.6.0",
"blurhash": "^2.0.5", "blurhash": "^2.0.5",
"bullmq": "latest", "bullmq": "latest",
"chalk": "^5.3.0", "chalk": "^5.3.0",
@ -128,9 +124,6 @@
"next-route-matcher": "^1.0.1", "next-route-matcher": "^1.0.1",
"oauth4webapi": "^2.4.0", "oauth4webapi": "^2.4.0",
"pg": "^8.11.5", "pg": "^8.11.5",
"prisma": "^5.6.0",
"prisma-json-types-generator": "^3.0.4",
"prisma-redis-middleware": "^4.8.0",
"request-parser": "workspace:*", "request-parser": "workspace:*",
"semver": "^7.5.4", "semver": "^7.5.4",
"sharp": "^0.33.3", "sharp": "^0.33.3",

View file

@ -14,9 +14,9 @@ import {
validateAuthResponse, validateAuthResponse,
} from "oauth4webapi"; } from "oauth4webapi";
import { TokenType } from "~database/entities/Token"; import { TokenType } from "~database/entities/Token";
import { findFirstUser } from "~database/entities/User";
import { db } from "~drizzle/db"; import { db } from "~drizzle/db";
import { token } from "~drizzle/schema"; import { token } from "~drizzle/schema";
import { findFirstUser } from "~database/entities/User";
export const meta = applyConfig({ export const meta = applyConfig({
allowedMethods: ["GET"], allowedMethods: ["GET"],

View file

@ -1,74 +1,19 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import type { Token } from "@prisma/client";
import { config } from "config-manager"; import { config } from "config-manager";
import { inArray } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { client } from "~database/datasource";
import { TokenType } from "~database/entities/Token";
import {
type UserWithRelations,
createNewLocalUser,
} from "~database/entities/User";
import { db } from "~drizzle/db"; import { db } from "~drizzle/db";
import { application, user } from "~drizzle/schema"; import { emoji } from "~drizzle/schema";
import type { APIEmoji } from "~types/entities/emoji"; import type { APIEmoji } from "~types/entities/emoji";
import type { APIInstance } from "~types/entities/instance"; import type { APIInstance } from "~types/entities/instance";
import { sendTestRequest, wrapRelativeUrl } from "./utils"; import { getTestUsers, sendTestRequest, wrapRelativeUrl } from "./utils";
const base_url = config.http.base_url; const base_url = config.http.base_url;
let token: Token; const { tokens, deleteUsers } = await getTestUsers(1);
let dummyUser: UserWithRelations;
describe("API Tests", () => { describe("API Tests", () => {
beforeAll(async () => {
await db.delete(user).where(inArray(user.username, ["test", "test2"]));
await db
.delete(application)
.where(inArray(application.clientId, ["test"]));
// Initialize test user
dummyUser = await createNewLocalUser({
email: "test@test.com",
username: "test",
password: "test",
display_name: "",
});
if (!dummyUser) {
throw new Error("Failed to create test user");
}
token = await client.token.create({
data: {
access_token: "test",
application: {
create: {
client_id: "test",
name: "Test Application",
redirect_uris: "https://example.com",
scopes: "read write",
secret: "test",
website: "https://example.com",
vapid_key: null,
},
},
code: "test",
scope: "read write",
token_type: TokenType.BEARER,
user: {
connect: {
id: dummyUser.id,
},
},
},
});
});
afterAll(async () => { afterAll(async () => {
await db.delete(user).where(inArray(user.username, ["test", "test2"])); await deleteUsers();
await db
.delete(application)
.where(inArray(application.clientId, ["test"]));
}); });
describe("GET /api/v1/instance", () => { describe("GET /api/v1/instance", () => {
@ -111,14 +56,11 @@ describe("API Tests", () => {
describe("GET /api/v1/custom_emojis", () => { describe("GET /api/v1/custom_emojis", () => {
beforeAll(async () => { beforeAll(async () => {
await client.emoji.create({ await db.insert(emoji).values({
data: { shortcode: "test",
instanceId: null, url: "https://example.com/test.png",
url: "https://example.com/test.png", contentType: "image/png",
content_type: "image/png", visibleInPicker: true,
shortcode: "test",
visible_in_picker: true,
},
}); });
}); });
@ -132,7 +74,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${tokens[0].accessToken}`,
}, },
}, },
), ),
@ -151,11 +93,7 @@ describe("API Tests", () => {
}); });
afterAll(async () => { afterAll(async () => {
await client.emoji.deleteMany({ await db.delete(emoji).where(eq(emoji.shortcode, "test"));
where: {
shortcode: "test",
},
});
}); });
}); });
}); });

View file

@ -1,86 +1,19 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import type { Token } from "@prisma/client";
import { config } from "config-manager"; import { config } from "config-manager";
import { client } from "~database/datasource"; import { getTestUsers, sendTestRequest, wrapRelativeUrl } from "~tests/utils";
import { TokenType } from "~database/entities/Token";
import {
type UserWithRelations,
createNewLocalUser,
} from "~database/entities/User";
import { sendTestRequest, wrapRelativeUrl } from "~tests/utils";
import type { APIAccount } from "~types/entities/account"; import type { APIAccount } from "~types/entities/account";
import type { APIRelationship } from "~types/entities/relationship"; import type { APIRelationship } from "~types/entities/relationship";
import type { APIStatus } from "~types/entities/status"; import type { APIStatus } from "~types/entities/status";
const base_url = config.http.base_url; const base_url = config.http.base_url;
let token: Token; const { users, tokens, deleteUsers } = await getTestUsers(2);
let user: UserWithRelations; const user = users[0];
let user2: UserWithRelations; const user2 = users[1];
const token = tokens[0];
beforeAll(async () => {
await client.user.deleteMany({
where: {
username: {
in: ["test", "test2"],
},
},
});
user = await createNewLocalUser({
email: "test@test.com",
username: "test",
password: "test",
display_name: "",
});
user2 = await createNewLocalUser({
email: "test2@test.com",
username: "test2",
password: "test2",
display_name: "",
});
token = await client.token.create({
data: {
access_token: "test",
application: {
create: {
client_id: "test",
name: "Test Application",
redirect_uris: "https://example.com",
scopes: "read write",
secret: "test",
website: "https://example.com",
vapid_key: null,
},
},
code: "test",
scope: "read write",
token_type: TokenType.BEARER,
user: {
connect: {
id: user.id,
},
},
},
});
});
afterAll(async () => { afterAll(async () => {
await client.user.deleteMany({ await deleteUsers();
where: {
username: {
in: ["test", "test2"],
},
},
});
await client.application.deleteMany({
where: {
client_id: "test",
},
});
}); });
describe("API Tests", () => { describe("API Tests", () => {
@ -92,7 +25,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -117,7 +50,7 @@ describe("API Tests", () => {
{ {
method: "PATCH", method: "PATCH",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
@ -149,7 +82,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -199,7 +132,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -228,7 +161,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({}), body: JSON.stringify({}),
@ -259,7 +192,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({}), body: JSON.stringify({}),
@ -290,7 +223,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({}), body: JSON.stringify({}),
@ -321,7 +254,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({}), body: JSON.stringify({}),
@ -347,7 +280,7 @@ describe("API Tests", () => {
new Request(wrapRelativeUrl("/api/v1/blocks", base_url), { new Request(wrapRelativeUrl("/api/v1/blocks", base_url), {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
}, },
}), }),
); );
@ -375,7 +308,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({}), body: JSON.stringify({}),
@ -406,7 +339,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ notifications: true }), body: JSON.stringify({ notifications: true }),
@ -436,7 +369,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ notifications: false }), body: JSON.stringify({ notifications: false }),
@ -463,7 +396,7 @@ describe("API Tests", () => {
new Request(wrapRelativeUrl("/api/v1/mutes", base_url), { new Request(wrapRelativeUrl("/api/v1/mutes", base_url), {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
}, },
}), }),
); );
@ -492,7 +425,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({}), body: JSON.stringify({}),
@ -523,7 +456,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({}), body: JSON.stringify({}),
@ -554,7 +487,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({}), body: JSON.stringify({}),
@ -585,7 +518,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ comment: "This is a new note" }), body: JSON.stringify({ comment: "This is a new note" }),
@ -616,7 +549,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
}, },
}, },
), ),
@ -651,7 +584,7 @@ describe("API Tests", () => {
{ {
method: "DELETE", method: "DELETE",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -678,7 +611,7 @@ describe("API Tests", () => {
{ {
method: "DELETE", method: "DELETE",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -708,7 +641,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({}), body: JSON.stringify({}),
@ -732,7 +665,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
}, },
}, },
), ),

View file

@ -1,13 +1,12 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import type { Token } from "@prisma/client";
import { config } from "config-manager"; import { config } from "config-manager";
import { client } from "~database/datasource"; import { statusToAPI } from "~database/entities/Status";
import { TokenType } from "~database/entities/Token";
import { import {
type UserWithRelations, getTestStatuses,
createNewLocalUser, getTestUsers,
} from "~database/entities/User"; sendTestRequest,
import { sendTestRequest, wrapRelativeUrl } from "~tests/utils"; wrapRelativeUrl,
} from "~tests/utils";
import type { APIAccount } from "~types/entities/account"; import type { APIAccount } from "~types/entities/account";
import type { APIAsyncAttachment } from "~types/entities/async_attachment"; import type { APIAsyncAttachment } from "~types/entities/async_attachment";
import type { APIContext } from "~types/entities/context"; import type { APIContext } from "~types/entities/context";
@ -15,69 +14,16 @@ import type { APIStatus } from "~types/entities/status";
const base_url = config.http.base_url; const base_url = config.http.base_url;
let token: Token; const { users, tokens, deleteUsers } = await getTestUsers(1);
let user: UserWithRelations; const user = users[0];
const token = tokens[0];
let status: APIStatus | null = null; let status: APIStatus | null = null;
let status2: APIStatus | null = null; let status2: APIStatus | null = null;
let media1: APIAsyncAttachment | null = null; let media1: APIAsyncAttachment | null = null;
describe("API Tests", () => { describe("API Tests", () => {
beforeAll(async () => {
await client.user.deleteMany({
where: {
username: {
in: ["test", "test2"],
},
},
});
user = await createNewLocalUser({
email: "test@test.com",
username: "test",
password: "test",
display_name: "",
});
token = await client.token.create({
data: {
access_token: "test",
application: {
create: {
client_id: "test",
name: "Test Application",
redirect_uris: "https://example.com",
scopes: "read write",
secret: "test",
website: "https://example.com",
vapid_key: null,
},
},
code: "test",
scope: "read write",
token_type: TokenType.BEARER,
user: {
connect: {
id: user.id,
},
},
},
});
});
afterAll(async () => { afterAll(async () => {
await client.user.deleteMany({ await deleteUsers();
where: {
username: {
in: ["test", "test2"],
},
},
});
await client.application.deleteMany({
where: {
client_id: "test",
},
});
}); });
describe("POST /api/v2/media", () => { describe("POST /api/v2/media", () => {
@ -91,7 +37,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
}, },
body: formData, body: formData,
}, },
@ -119,7 +65,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
@ -167,7 +113,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
@ -220,7 +166,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -271,7 +217,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -302,7 +248,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -332,7 +278,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -365,7 +311,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -394,7 +340,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -430,7 +376,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -452,7 +398,7 @@ describe("API Tests", () => {
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -483,7 +429,7 @@ describe("API Tests", () => {
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, },
@ -513,7 +459,7 @@ describe("API Tests", () => {
{ {
method: "DELETE", method: "DELETE",
headers: { headers: {
Authorization: `Bearer ${token.access_token}`, Authorization: `Bearer ${token.accessToken}`,
}, },
}, },
), ),

View file

@ -1,94 +0,0 @@
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
import { client } from "~database/datasource";
import { createNewLocalUser } from "~database/entities/User";
describe("cli.ts", () => {
describe("User creation", () => {
it("should execute user create command without admin flag", async () => {
afterAll(async () => {
await client.user.deleteMany({
where: {
username: "testuser297",
email: "testuser297@gmail.com",
},
});
});
// Run command and wait for it to finish
Bun.spawnSync([
"bun",
"run",
"cli.ts",
"user",
"create",
"testuser297",
"password123",
"testuser297@gmail.com",
]);
const createdUser = await client.user.findFirst({
where: {
username: "testuser297",
email: "testuser297@gmail.com",
},
});
expect(createdUser).toBeDefined();
});
it("should execute user create command with admin flag", async () => {
afterAll(async () => {
await client.user.deleteMany({
where: {
username: "testuser297",
email: "testuser297@gmail.com",
},
});
});
// Run command and wait for it to finish
Bun.spawnSync([
"bun",
"run",
"cli.ts",
"user",
"create",
"testuser297",
"password123",
"testuser297@gmail.com",
"--admin",
]);
const createdUser = await client.user.findFirst({
where: {
username: "testuser297",
email: "testuser297@gmail.com",
isAdmin: true,
},
});
expect(createdUser).toBeDefined();
});
});
it("should execute user delete command", async () => {
beforeAll(async () => {
await createNewLocalUser({
username: "bob124",
password: "jesus",
email: "bob124@bob124.com",
});
});
Bun.spawnSync(["bun", "run", "cli", "user", "delete", "bob124"]);
const userExists = await client.user.findFirst({
where: {
username: "bob124",
email: "bob124@bob124.com",
},
});
expect(!!userExists).toBe(false);
});
});

View file

@ -1,6 +1,6 @@
import { describe, expect, it } from "bun:test"; import { describe, expect, it } from "bun:test";
import { checkIfOauthIsValid } from "@oauth"; import { checkIfOauthIsValid } from "@oauth";
import type { Application } from "@prisma/client"; import type { Application } from "~database/entities/Application";
describe("checkIfOauthIsValid", () => { describe("checkIfOauthIsValid", () => {
it("should return true when routeScopes and application.scopes are empty", () => { it("should return true when routeScopes and application.scopes are empty", () => {

View file

@ -1,26 +1,25 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import type { Application, Token } from "@prisma/client"; import type { APIApplication } from "~types/entities/application";
import { client } from "~database/datasource"; import {
import { createNewLocalUser } from "~database/entities/User"; deleteOldTestUsers,
import { sendTestRequest, wrapRelativeUrl } from "./utils"; getTestUsers,
sendTestRequest,
wrapRelativeUrl,
} from "./utils";
import type { APIToken } from "~types/entities/token";
const base_url = "http://lysand.localhost:8080"; //config.http.base_url; const base_url = "http://lysand.localhost:8080"; //config.http.base_url;
let client_id: string; let client_id: string;
let client_secret: string; let client_secret: string;
let code: string; let code: string;
let token: Token; let token: APIToken;
const { users, passwords, deleteUsers } = await getTestUsers(1);
beforeAll(async () => { afterAll(async () => {
// Init test user await deleteUsers();
await createNewLocalUser({ await deleteOldTestUsers();
email: "test@test.com",
username: "test",
password: "test",
display_name: "",
});
}); });
describe("POST /api/v1/apps/", () => { describe("POST /api/v1/apps/", () => {
test("should create an application", async () => { test("should create an application", async () => {
const formData = new FormData(); const formData = new FormData();
@ -61,8 +60,10 @@ describe("POST /api/auth/login/", () => {
test("should get a code", async () => { test("should get a code", async () => {
const formData = new FormData(); const formData = new FormData();
formData.append("email", "test@test.com"); console.log(users[0]?.email ?? "");
formData.append("password", "test");
formData.append("email", users[0]?.email ?? "");
formData.append("password", passwords[0]);
const response = await sendTestRequest( const response = await sendTestRequest(
new Request( new Request(
@ -139,20 +140,9 @@ describe("GET /api/v1/apps/verify_credentials", () => {
expect(response.status).toBe(200); expect(response.status).toBe(200);
expect(response.headers.get("content-type")).toBe("application/json"); expect(response.headers.get("content-type")).toBe("application/json");
const credentials = (await response.json()) as Partial<Application>; const credentials = (await response.json()) as Partial<APIApplication>;
expect(credentials.name).toBe("Test Application"); expect(credentials.name).toBe("Test Application");
expect(credentials.website).toBe("https://example.com"); expect(credentials.website).toBe("https://example.com");
expect(credentials.redirect_uris).toBe("https://example.com");
expect(credentials.scopes).toBe("read write");
});
});
afterAll(async () => {
// Clean up user
await client.user.delete({
where: {
username: "test",
},
}); });
}); });

View file

@ -1,6 +1,6 @@
import { randomBytes } from "node:crypto"; import { randomBytes } from "node:crypto";
import { inArray, like } from "drizzle-orm"; import { inArray, like } from "drizzle-orm";
import type { Status } from "~database/entities/Status"; import { type Status, findManyStatuses } from "~database/entities/Status";
import { import {
type User, type User,
type UserWithRelations, type UserWithRelations,
@ -31,19 +31,22 @@ export const deleteOldTestUsers = async () => {
export const getTestUsers = async (count: number) => { export const getTestUsers = async (count: number) => {
const users: UserWithRelations[] = []; const users: UserWithRelations[] = [];
const passwords: string[] = [];
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const password = randomBytes(32).toString("hex");
const user = await createNewLocalUser({ const user = await createNewLocalUser({
username: `test-${randomBytes(32).toString("hex")}`, username: `test-${randomBytes(32).toString("hex")}`,
email: `${randomBytes(32).toString("hex")}@test.com`, email: `${randomBytes(32).toString("hex")}@test.com`,
password: randomBytes(32).toString("hex"), password,
skipPasswordHash: true,
}); });
if (!user) { if (!user) {
throw new Error("Failed to create test user"); throw new Error("Failed to create test user");
} }
passwords.push(password);
users.push(user); users.push(user);
} }
@ -64,6 +67,7 @@ export const getTestUsers = async (count: number) => {
return { return {
users, users,
tokens, tokens,
passwords,
deleteUsers: async () => { deleteUsers: async () => {
await db.delete(user).where( await db.delete(user).where(
inArray( inArray(
@ -104,5 +108,14 @@ export const getTestStatuses = async (
statuses.push(newStatus); statuses.push(newStatus);
} }
return statuses.toSorted((a, b) => a.id.localeCompare(b.id)); const statusesWithRelations = await findManyStatuses({
where: (status, { inArray }) =>
inArray(
status.id,
statuses.map((s) => s.id),
),
orderBy: (status, { asc }) => asc(status.id),
});
return statusesWithRelations;
}; };

28
types.d.ts vendored
View file

@ -1,28 +0,0 @@
import type { LysandObject } from "@prisma/client";
import type { APIAccount } from "~types/entities/account";
import type { APIField } from "~types/entities/field";
import type { ContentFormat } from "~types/lysand/Object";
declare namespace global {
namespace PrismaJson {
type InstanceLogo = ContentFormat[];
type ObjectData = LysandObject;
type ObjectExtensions = LysandObject["extensions"];
interface UserEndpoints {
inbox: string;
liked: string;
outbox: string;
disliked: string;
featured: string;
followers: string;
following: string;
}
interface UserSource {
note: string;
fields: APIField[];
privacy: APIAccount["privacy"];
language: string;
sensitive: boolean;
}
}
}

View file

@ -1,4 +1,4 @@
import type { Application } from "@prisma/client"; import type { Application } from "~database/entities/Application";
/** /**
* Check if an OAuth application is valid for a route * Check if an OAuth application is valid for a route

View file

@ -1,4 +1,4 @@
import type { Prisma } from "@prisma/client"; /* import type { Prisma } from "@prisma/client";
import chalk from "chalk"; import chalk from "chalk";
import { config } from "config-manager"; import { config } from "config-manager";
import Redis from "ioredis"; import Redis from "ioredis";
@ -56,3 +56,4 @@ export const initializeRedisCache = async () => {
return null; return null;
}; };
*/