aboutsummaryrefslogtreecommitdiffstats
path: root/packages/web
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-03-02 09:53:24 +0000
committerMohamedBassem <me@mbassem.com>2024-03-02 09:53:24 +0000
commitb7b5306619cc4420bd0ae494881ee7161391dec4 (patch)
tree3e284af6160373b4c6873301ea57ab43e36130dd /packages/web
parenta942e69c9a830142c7ead9cbf7c1aa67fd87bf38 (diff)
downloadkarakeep-b7b5306619cc4420bd0ae494881ee7161391dec4.tar.zst
feature: Make the first user an admin automatically
Diffstat (limited to 'packages/web')
-rw-r--r--packages/web/lib/testUtils.ts31
-rw-r--r--packages/web/server/api/routers/bookmarks.test.ts2
-rw-r--r--packages/web/server/api/routers/users.test.ts57
-rw-r--r--packages/web/server/api/routers/users.ts7
4 files changed, 86 insertions, 11 deletions
diff --git a/packages/web/lib/testUtils.ts b/packages/web/lib/testUtils.ts
index 142ad844..bad78463 100644
--- a/packages/web/lib/testUtils.ts
+++ b/packages/web/lib/testUtils.ts
@@ -25,13 +25,15 @@ export async function seedUsers(db: TestDB) {
.returning();
}
-export function getApiCaller(db: TestDB, userId: string) {
+export function getApiCaller(db: TestDB, userId?: string) {
const createCaller = createCallerFactory(appRouter);
return createCaller({
- user: {
- id: userId,
- role: "user",
- },
+ user: userId
+ ? {
+ id: userId,
+ role: "user",
+ }
+ : null,
db,
});
}
@@ -40,20 +42,29 @@ export type APICallerType = ReturnType<typeof getApiCaller>;
export interface CustomTestContext {
apiCallers: APICallerType[];
+ unauthedAPICaller: APICallerType;
db: TestDB;
}
-export async function buildTestContext(): Promise<CustomTestContext> {
+export async function buildTestContext(
+ seedDB: boolean,
+): Promise<CustomTestContext> {
const db = getTestDB();
- const users = await seedUsers(db);
+ let users: Awaited<ReturnType<typeof seedUsers>> = [];
+ if (seedDB) {
+ users = await seedUsers(db);
+ }
const callers = users.map((u) => getApiCaller(db, u.id));
return {
apiCallers: callers,
+ unauthedAPICaller: getApiCaller(db),
db,
};
}
-export const defaultBeforeEach = async (context: object) => {
- Object.assign(context, await buildTestContext());
-};
+export function defaultBeforeEach(seedDB: boolean = true) {
+ return async (context: object) => {
+ Object.assign(context, await buildTestContext(seedDB));
+ };
+}
diff --git a/packages/web/server/api/routers/bookmarks.test.ts b/packages/web/server/api/routers/bookmarks.test.ts
index 603a173e..626a7250 100644
--- a/packages/web/server/api/routers/bookmarks.test.ts
+++ b/packages/web/server/api/routers/bookmarks.test.ts
@@ -1,7 +1,7 @@
import { CustomTestContext, defaultBeforeEach } from "@/lib/testUtils";
import { expect, describe, test, beforeEach, assert } from "vitest";
-beforeEach<CustomTestContext>(defaultBeforeEach);
+beforeEach<CustomTestContext>(defaultBeforeEach(true));
describe("Bookmark Routes", () => {
test<CustomTestContext>("create bookmark", async ({ apiCallers }) => {
diff --git a/packages/web/server/api/routers/users.test.ts b/packages/web/server/api/routers/users.test.ts
new file mode 100644
index 00000000..b188d3a0
--- /dev/null
+++ b/packages/web/server/api/routers/users.test.ts
@@ -0,0 +1,57 @@
+import { CustomTestContext, defaultBeforeEach } from "@/lib/testUtils";
+import { expect, describe, test, beforeEach } from "vitest";
+
+beforeEach<CustomTestContext>(defaultBeforeEach(false));
+
+describe("User Routes", () => {
+ test<CustomTestContext>("create user", async ({ unauthedAPICaller }) => {
+ const user = await unauthedAPICaller.users.create({
+ name: "Test User",
+ email: "test123@test.com",
+ password: "pass1234",
+ confirmPassword: "pass1234",
+ });
+
+ expect(user.name).toEqual("Test User");
+ expect(user.email).toEqual("test123@test.com");
+ });
+
+ test<CustomTestContext>("first user is admin", async ({
+ unauthedAPICaller,
+ }) => {
+ const user1 = await unauthedAPICaller.users.create({
+ name: "Test User",
+ email: "test123@test.com",
+ password: "pass1234",
+ confirmPassword: "pass1234",
+ });
+
+ const user2 = await unauthedAPICaller.users.create({
+ name: "Test User",
+ email: "test124@test.com",
+ password: "pass1234",
+ confirmPassword: "pass1234",
+ });
+
+ expect(user1.role).toEqual("admin");
+ expect(user2.role).toEqual("user");
+ });
+
+ test<CustomTestContext>("unique emails", async ({ unauthedAPICaller }) => {
+ await unauthedAPICaller.users.create({
+ name: "Test User",
+ email: "test123@test.com",
+ password: "pass1234",
+ confirmPassword: "pass1234",
+ });
+
+ await expect(() =>
+ unauthedAPICaller.users.create({
+ name: "Test User",
+ email: "test123@test.com",
+ password: "pass1234",
+ confirmPassword: "pass1234",
+ }),
+ ).rejects.toThrow(/Email is already taken/);
+ });
+});
diff --git a/packages/web/server/api/routers/users.ts b/packages/web/server/api/routers/users.ts
index 3078a42a..3d5d982d 100644
--- a/packages/web/server/api/routers/users.ts
+++ b/packages/web/server/api/routers/users.ts
@@ -5,6 +5,7 @@ import { z } from "zod";
import { hashPassword } from "@/server/auth";
import { TRPCError } from "@trpc/server";
import { users } from "@hoarder/db/schema";
+import { count } from "drizzle-orm";
export const usersAppRouter = router({
create: publicProcedure
@@ -13,9 +14,13 @@ export const usersAppRouter = router({
z.object({
name: z.string(),
email: z.string(),
+ role: z.enum(["user", "admin"]).nullable(),
}),
)
.mutation(async ({ input, ctx }) => {
+ const [{ count: userCount }] = await ctx.db
+ .select({ count: count() })
+ .from(users);
try {
const result = await ctx.db
.insert(users)
@@ -23,10 +28,12 @@ export const usersAppRouter = router({
name: input.name,
email: input.email,
password: await hashPassword(input.password),
+ role: userCount == 0 ? "admin" : "user",
})
.returning({
name: users.name,
email: users.email,
+ role: users.role,
});
return result[0];
} catch (e) {