From 027847f996c1732ee798a14b28c44e66f92da0aa Mon Sep 17 00:00:00 2001 From: Sanjib Sen Date: Mon, 20 Jan 2025 21:20:04 +0600 Subject: [PATCH] added tests --- bun.lockb | Bin 191850 -> 194074 bytes ...monster.sql => 0000_ambitious_jocasta.sql} | 7 ++ drizzle/meta/0000_snapshot.json | 39 ++++++- drizzle/meta/_journal.json | 4 +- package.json | 17 +-- src/api/routes/note/note.controller.ts | 6 +- src/api/routes/note/note.route.ts | 6 +- src/api/routes/note/note.test.ts | 102 ++++++++++++++++++ src/db/schema/auth.ts | 7 ++ src/index.ts | 3 +- src/lib/auth/auth.ts | 11 +- test/client.ts | 27 +++++ 12 files changed, 211 insertions(+), 18 deletions(-) rename drizzle/{0000_uneven_professor_monster.sql => 0000_ambitious_jocasta.sql} (92%) create mode 100644 src/api/routes/note/note.test.ts create mode 100644 test/client.ts diff --git a/bun.lockb b/bun.lockb index 1872bc585141c17455afd9ce4081442285c997f0..c7a287aab8af8d883f5606d858159c924f4e471b 100755 GIT binary patch delta 37624 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 W=1Xtz^EE7b9dExi=^KXWdE0MuqM%a6XJm-3 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 diff --git a/drizzle/0000_uneven_professor_monster.sql b/drizzle/0000_ambitious_jocasta.sql similarity index 92% rename from drizzle/0000_uneven_professor_monster.sql rename to drizzle/0000_ambitious_jocasta.sql index 776ae33..e532564 100644 --- a/drizzle/0000_uneven_professor_monster.sql +++ b/drizzle/0000_ambitious_jocasta.sql @@ -16,6 +16,13 @@ CREATE TABLE "auth"."account" ( "updated_at" timestamp NOT NULL ); --> statement-breakpoint +CREATE TABLE "auth"."jwks" ( + "id" text PRIMARY KEY NOT NULL, + "public_key" text NOT NULL, + "private_key" text NOT NULL, + "created_at" timestamp NOT NULL +); +--> statement-breakpoint CREATE TABLE "auth"."rate_limit" ( "id" text PRIMARY KEY NOT NULL, "key" text, diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index e268da5..5e1d8bc 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -1,5 +1,5 @@ { - "id": "50bd2c27-8d45-478f-a894-b2f7cd4a718b", + "id": "38b37b1d-93e4-4f9a-8d7e-a77b0632afb4", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", @@ -110,6 +110,43 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "auth.jwks": { + "name": "jwks", + "schema": "auth", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "auth.rate_limit": { "name": "rate_limit", "schema": "auth", diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 729beee..9fdfc2b 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "7", - "when": 1737019435130, - "tag": "0000_uneven_professor_monster", + "when": 1737386379330, + "tag": "0000_ambitious_jocasta", "breakpoints": true } ] diff --git a/package.json b/package.json index d9984d9..be978da 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "app", "version": "1.0.50", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "bun test api", "dev": "bun run --watch src/index.ts", "email": "email dev --dir src/emails", "auth:generate": "bun x @better-auth/cli generate --config src/lib/auth/auth.ts --output src/db/schema/auth.ts && drizzle-kit migrate", @@ -16,14 +16,15 @@ }, "dependencies": { "@elysiajs/cors": "^1.2.0", + "@elysiajs/eden": "^1.2.0", "@elysiajs/opentelemetry": "^1.2.0", "@elysiajs/server-timing": "^1.2.0", "@elysiajs/swagger": "^1.2.0", "@paralleldrive/cuid2": "^2.2.2", - "@react-email/components": "^0.0.31", - "better-auth": "^1.1.10", + "@react-email/components": "^0.0.32", + "better-auth": "^1.1.14", "dotenv": "^16.4.7", - "drizzle-orm": "^0.38.3", + "drizzle-orm": "^0.38.4", "drizzle-typebox": "^0.2.1", "elysia": "latest", "minio": "^8.0.3", @@ -38,11 +39,11 @@ "devDependencies": { "@types/nodemailer": "^6.4.17", "@types/pg": "^8.11.10", - "@types/react": "^19.0.3", - "@types/react-dom": "^19.0.2", + "@types/react": "^19.0.7", + "@types/react-dom": "^19.0.3", "bun-types": "latest", - "drizzle-kit": "^0.30.1", - "react-email": "^3.0.4", + "drizzle-kit": "^0.30.2", + "react-email": "^3.0.6", "tsx": "^4.19.2" }, "module": "src/index.js" diff --git a/src/api/routes/note/note.controller.ts b/src/api/routes/note/note.controller.ts index 50b75a4..e0b1060 100644 --- a/src/api/routes/note/note.controller.ts +++ b/src/api/routes/note/note.controller.ts @@ -64,8 +64,12 @@ export class NoteController { ) ) .execute(); + let successStatus = true; + if(result.length===0){ + successStatus = false + }; return { - success: true, + success: successStatus, data: result, message: "", error: null, diff --git a/src/api/routes/note/note.route.ts b/src/api/routes/note/note.route.ts index 3863e6d..839dacf 100644 --- a/src/api/routes/note/note.route.ts +++ b/src/api/routes/note/note.route.ts @@ -44,7 +44,7 @@ export const noteRouter = new Elysia({ } ) .get( - ":id", + "/:id", async ({ note, user, params:{id} }) => { return await note.getNoteById(id, user.id); }, @@ -73,7 +73,7 @@ export const noteRouter = new Elysia({ } } ).patch( - ":id", + "/:id", async ({ body, note, user, params:{id} }) => { return await note.updateNoteById(id, body, user.id); }, @@ -89,7 +89,7 @@ export const noteRouter = new Elysia({ } } ).delete( - ":id", + "/:id", async ({ note, user, params:{id} }) => { return await note.deleteNoteById(id, user.id); }, diff --git a/src/api/routes/note/note.test.ts b/src/api/routes/note/note.test.ts new file mode 100644 index 0000000..304d241 --- /dev/null +++ b/src/api/routes/note/note.test.ts @@ -0,0 +1,102 @@ +import { describe, expect, it } from 'bun:test' +import { testClientApp } from '../../../../test/client'; + +let noteId: string; + +describe('Note', () => { + // Create a note before tests + it('Create Note', async () => { + const { data } = await testClientApp.api.note.post({ + "title": "test note", + "content": "description", + }) + if (!data?.data) { + throw new Error('create note api did not return data'); + } + noteId = data.data[0].id + expect(data.data[0].title).toBe('test note') + }) + + // Get all notes + it('Get All Notes', async () => { + const { data } = await testClientApp.api.note.get({ + query: { limit: 1, offset: 0 } + }) + expect(data?.data[0].id).toBe(noteId) + }) + + // Get single note + it('Get Created Note', async () => { + const { data, error } = await testClientApp.api.note({ id: noteId }).get() + expect(data?.data[0].id).toBe(noteId) + }) + + // Update note + it('Update Note', async () => { + const updatedTitle = "updated test note" + const updatedContent = "updated description" + + const { data } = await testClientApp.api.note({ id: noteId }).patch({ + title: updatedTitle, + content: updatedContent, + }) + + expect(data?.success).toBe(true) + expect(data?.data[0].title).toBe(updatedTitle) + expect(data?.data[0].content).toBe(updatedContent) + }) + + // Delete single note + it('Delete Single Note', async () => { + // First create a new note to delete + const { data: createData } = await testClientApp.api.note.post({ + "title": "note to delete", + "content": "this note will be deleted", + }) + const deleteNoteId = createData?.data[0].id + + + if (!deleteNoteId) { + throw new Error('Failed to receive noteId in delete note test'); + } + + // Delete the note + const { data: deleteData } = await testClientApp.api.note({ id: deleteNoteId }).delete() + expect(deleteData?.success).toBe(true) + + // Verify note is deleted by trying to fetch it + const { data: verifyData } = await testClientApp.api.note({ id: deleteNoteId }).get() + expect(verifyData?.data).toHaveLength(0) + }) + + // Delete all notes + it('Delete All Notes', async () => { + // First create multiple notes + await testClientApp.api.note.post({ + "title": "note 1", + "content": "content 1", + }) + await testClientApp.api.note.post({ + "title": "note 2", + "content": "content 2", + }) + + // Delete all notes + const { data: deleteData } = await testClientApp.api.note.delete() + expect(deleteData?.success).toBe(true) + + // Verify all notes are deleted + const { data: verifyData } = await testClientApp.api.note.get({ + query: { limit: 10, offset: 0 } + }) + expect(verifyData?.data).toHaveLength(0) + }) + + // Error cases + it('Should handle invalid note ID', async () => { + const invalidId = 'invalid-id' + const { data } = await testClientApp.api.note({ id: invalidId }).get() + expect(data?.success).toBe(false) + expect(data?.data).toHaveLength(0) + }) +}) \ No newline at end of file diff --git a/src/db/schema/auth.ts b/src/db/schema/auth.ts index c68d842..bac513a 100644 --- a/src/db/schema/auth.ts +++ b/src/db/schema/auth.ts @@ -54,3 +54,10 @@ export const rateLimit = authSchema.table("rate_limit", { count: integer('count'), lastRequest: integer('last_request') }); + +export const jwks = authSchema.table("jwks", { + id: text("id").primaryKey(), + publicKey: text("public_key").notNull(), + privateKey: text("private_key").notNull(), + createdAt: timestamp("created_at").notNull(), +}); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 9c3cb33..98bccc2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,8 @@ import { api } from "./api"; const baseConfig = getBaseConfig(); validateEnv(); -const app = new Elysia() + +export const app = new Elysia() .use(cors()) .use( opentelemetry({ diff --git a/src/lib/auth/auth.ts b/src/lib/auth/auth.ts index bb5a527..2bd5698 100644 --- a/src/lib/auth/auth.ts +++ b/src/lib/auth/auth.ts @@ -1,8 +1,8 @@ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { db } from "../../db/index"; -import { openAPI } from "better-auth/plugins" -import { user, account, verification, session, rateLimit } from "../../db/schema/auth"; +import { jwt, openAPI } from "better-auth/plugins" +import { user, account, verification, session, rateLimit, jwks } from "../../db/schema/auth"; import { sendMail } from "../mail/mail"; import { renderToStaticMarkup } from "react-dom/server"; import { createElement } from "react"; @@ -17,8 +17,14 @@ export const auth = betterAuth({ account: account, verification: verification, rateLimit: rateLimit, + jwks: jwks } }), + user: { + deleteUser: { + enabled: true // [!Code Highlight] + } + }, rateLimit: { window: 60, max: 100, @@ -67,6 +73,7 @@ export const auth = betterAuth({ openAPI({ path: "/docs", }), + jwt() ], socialProviders: { /* diff --git a/test/client.ts b/test/client.ts new file mode 100644 index 0000000..72966db --- /dev/null +++ b/test/client.ts @@ -0,0 +1,27 @@ +import { treaty } from '@elysiajs/eden' +import { app } from '../src' +import { getAuthConfig } from '../src/lib/utils/env'; + +async function getAuthToken() { + const authUrl = getAuthConfig().BETTER_AUTH_URL + const response = await fetch(`${authUrl}/api/auth/sign-in/email`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + email: "test@test.com", + password: "testpass123" + }) + }); + const cookies = response.headers.getSetCookie()[0]; + const sessionToken = cookies.split(";")[0].split("=")[1] + return sessionToken; +} + +const token = await getAuthToken(); +export const testClientApp = treaty(app,{ + headers: { + Cookie: `better-auth.session_token=${token}` + } +}) \ No newline at end of file