Initial commit: full-stack Bun + Elysia + React template

Elysia.js API with session-based auth (email/password + Google OAuth),
role system (USER/ADMIN/SUPER_ADMIN), Prisma + PostgreSQL, React 19
with Mantine UI, TanStack Router, dark theme, and comprehensive test
suite (unit, integration, E2E with Lightpanda).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
bipproduction
2026-04-01 10:12:19 +08:00
commit 08a1054e3c
57 changed files with 3732 additions and 0 deletions

23
tests/unit/db.test.ts Normal file
View File

@@ -0,0 +1,23 @@
import { test, expect, describe, afterAll } from 'bun:test'
import { prisma } from '../helpers'
afterAll(async () => {
await prisma.$disconnect()
})
describe('Prisma database connection', () => {
test('connects to database', async () => {
const result = await prisma.$queryRaw`SELECT 1 as ok`
expect(result).toEqual([{ ok: 1 }])
})
test('user table exists', async () => {
const count = await prisma.user.count()
expect(typeof count).toBe('number')
})
test('session table exists', async () => {
const count = await prisma.session.count()
expect(typeof count).toBe('number')
})
})

34
tests/unit/env.test.ts Normal file
View File

@@ -0,0 +1,34 @@
import { test, expect, describe } from 'bun:test'
describe('env', () => {
test('PORT defaults to 3000 when not set', () => {
const original = process.env.PORT
delete process.env.PORT
// Re-import to test default
// Since modules are cached, we test the logic directly
const value = parseInt(process.env.PORT ?? '3000', 10)
expect(value).toBe(3000)
if (original) process.env.PORT = original
})
test('PORT parses from env', () => {
const value = parseInt(process.env.PORT ?? '3000', 10)
expect(typeof value).toBe('number')
expect(value).toBeGreaterThan(0)
})
test('DATABASE_URL is set', () => {
expect(process.env.DATABASE_URL).toBeDefined()
expect(process.env.DATABASE_URL).toContain('postgresql://')
})
test('GOOGLE_CLIENT_ID is set', () => {
expect(process.env.GOOGLE_CLIENT_ID).toBeDefined()
expect(process.env.GOOGLE_CLIENT_ID!.length).toBeGreaterThan(0)
})
test('GOOGLE_CLIENT_SECRET is set', () => {
expect(process.env.GOOGLE_CLIENT_SECRET).toBeDefined()
expect(process.env.GOOGLE_CLIENT_SECRET!.length).toBeGreaterThan(0)
})
})

View File

@@ -0,0 +1,24 @@
import { test, expect, describe } from 'bun:test'
describe('Bun.password (bcrypt)', () => {
test('hash and verify correct password', async () => {
const hash = await Bun.password.hash('mypassword', { algorithm: 'bcrypt' })
expect(hash).toStartWith('$2')
const valid = await Bun.password.verify('mypassword', hash)
expect(valid).toBe(true)
})
test('reject wrong password', async () => {
const hash = await Bun.password.hash('mypassword', { algorithm: 'bcrypt' })
const valid = await Bun.password.verify('wrongpassword', hash)
expect(valid).toBe(false)
})
test('different hashes for same password', async () => {
const hash1 = await Bun.password.hash('same', { algorithm: 'bcrypt' })
const hash2 = await Bun.password.hash('same', { algorithm: 'bcrypt' })
expect(hash1).not.toBe(hash2) // bcrypt salt differs
expect(await Bun.password.verify('same', hash1)).toBe(true)
expect(await Bun.password.verify('same', hash2)).toBe(true)
})
})