added tests

This commit is contained in:
Sanjib Sen 2025-01-20 21:20:04 +06:00
parent 21308fa5bd
commit 027847f996
12 changed files with 211 additions and 18 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -16,6 +16,13 @@ CREATE TABLE "auth"."account" (
"updated_at" timestamp NOT NULL "updated_at" timestamp NOT NULL
); );
--> statement-breakpoint --> 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" ( CREATE TABLE "auth"."rate_limit" (
"id" text PRIMARY KEY NOT NULL, "id" text PRIMARY KEY NOT NULL,
"key" text, "key" text,

View file

@ -1,5 +1,5 @@
{ {
"id": "50bd2c27-8d45-478f-a894-b2f7cd4a718b", "id": "38b37b1d-93e4-4f9a-8d7e-a77b0632afb4",
"prevId": "00000000-0000-0000-0000-000000000000", "prevId": "00000000-0000-0000-0000-000000000000",
"version": "7", "version": "7",
"dialect": "postgresql", "dialect": "postgresql",
@ -110,6 +110,43 @@
"checkConstraints": {}, "checkConstraints": {},
"isRLSEnabled": false "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": { "auth.rate_limit": {
"name": "rate_limit", "name": "rate_limit",
"schema": "auth", "schema": "auth",

View file

@ -5,8 +5,8 @@
{ {
"idx": 0, "idx": 0,
"version": "7", "version": "7",
"when": 1737019435130, "when": 1737386379330,
"tag": "0000_uneven_professor_monster", "tag": "0000_ambitious_jocasta",
"breakpoints": true "breakpoints": true
} }
] ]

View file

@ -2,7 +2,7 @@
"name": "app", "name": "app",
"version": "1.0.50", "version": "1.0.50",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "bun test api",
"dev": "bun run --watch src/index.ts", "dev": "bun run --watch src/index.ts",
"email": "email dev --dir src/emails", "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", "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": { "dependencies": {
"@elysiajs/cors": "^1.2.0", "@elysiajs/cors": "^1.2.0",
"@elysiajs/eden": "^1.2.0",
"@elysiajs/opentelemetry": "^1.2.0", "@elysiajs/opentelemetry": "^1.2.0",
"@elysiajs/server-timing": "^1.2.0", "@elysiajs/server-timing": "^1.2.0",
"@elysiajs/swagger": "^1.2.0", "@elysiajs/swagger": "^1.2.0",
"@paralleldrive/cuid2": "^2.2.2", "@paralleldrive/cuid2": "^2.2.2",
"@react-email/components": "^0.0.31", "@react-email/components": "^0.0.32",
"better-auth": "^1.1.10", "better-auth": "^1.1.14",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"drizzle-orm": "^0.38.3", "drizzle-orm": "^0.38.4",
"drizzle-typebox": "^0.2.1", "drizzle-typebox": "^0.2.1",
"elysia": "latest", "elysia": "latest",
"minio": "^8.0.3", "minio": "^8.0.3",
@ -38,11 +39,11 @@
"devDependencies": { "devDependencies": {
"@types/nodemailer": "^6.4.17", "@types/nodemailer": "^6.4.17",
"@types/pg": "^8.11.10", "@types/pg": "^8.11.10",
"@types/react": "^19.0.3", "@types/react": "^19.0.7",
"@types/react-dom": "^19.0.2", "@types/react-dom": "^19.0.3",
"bun-types": "latest", "bun-types": "latest",
"drizzle-kit": "^0.30.1", "drizzle-kit": "^0.30.2",
"react-email": "^3.0.4", "react-email": "^3.0.6",
"tsx": "^4.19.2" "tsx": "^4.19.2"
}, },
"module": "src/index.js" "module": "src/index.js"

View file

@ -64,8 +64,12 @@ export class NoteController {
) )
) )
.execute(); .execute();
let successStatus = true;
if(result.length===0){
successStatus = false
};
return { return {
success: true, success: successStatus,
data: result, data: result,
message: "", message: "",
error: null, error: null,

View file

@ -44,7 +44,7 @@ export const noteRouter = new Elysia({
} }
) )
.get( .get(
":id", "/:id",
async ({ note, user, params:{id} }) => { async ({ note, user, params:{id} }) => {
return await note.getNoteById(id, user.id); return await note.getNoteById(id, user.id);
}, },
@ -73,7 +73,7 @@ export const noteRouter = new Elysia({
} }
} }
).patch( ).patch(
":id", "/:id",
async ({ body, note, user, params:{id} }) => { async ({ body, note, user, params:{id} }) => {
return await note.updateNoteById(id, body, user.id); return await note.updateNoteById(id, body, user.id);
}, },
@ -89,7 +89,7 @@ export const noteRouter = new Elysia({
} }
} }
).delete( ).delete(
":id", "/:id",
async ({ note, user, params:{id} }) => { async ({ note, user, params:{id} }) => {
return await note.deleteNoteById(id, user.id); return await note.deleteNoteById(id, user.id);
}, },

View file

@ -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)
})
})

View file

@ -54,3 +54,10 @@ export const rateLimit = authSchema.table("rate_limit", {
count: integer('count'), count: integer('count'),
lastRequest: integer('last_request') 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(),
});

View file

@ -9,7 +9,8 @@ import { api } from "./api";
const baseConfig = getBaseConfig(); const baseConfig = getBaseConfig();
validateEnv(); validateEnv();
const app = new Elysia()
export const app = new Elysia()
.use(cors()) .use(cors())
.use( .use(
opentelemetry({ opentelemetry({

View file

@ -1,8 +1,8 @@
import { betterAuth } from "better-auth"; import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "../../db/index"; import { db } from "../../db/index";
import { openAPI } from "better-auth/plugins" import { jwt, openAPI } from "better-auth/plugins"
import { user, account, verification, session, rateLimit } from "../../db/schema/auth"; import { user, account, verification, session, rateLimit, jwks } from "../../db/schema/auth";
import { sendMail } from "../mail/mail"; import { sendMail } from "../mail/mail";
import { renderToStaticMarkup } from "react-dom/server"; import { renderToStaticMarkup } from "react-dom/server";
import { createElement } from "react"; import { createElement } from "react";
@ -17,8 +17,14 @@ export const auth = betterAuth({
account: account, account: account,
verification: verification, verification: verification,
rateLimit: rateLimit, rateLimit: rateLimit,
jwks: jwks
} }
}), }),
user: {
deleteUser: {
enabled: true // [!Code Highlight]
}
},
rateLimit: { rateLimit: {
window: 60, window: 60,
max: 100, max: 100,
@ -67,6 +73,7 @@ export const auth = betterAuth({
openAPI({ openAPI({
path: "/docs", path: "/docs",
}), }),
jwt()
], ],
socialProviders: { socialProviders: {
/* /*

27
test/client.ts Normal file
View file

@ -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}`
}
})