mirror of
https://github.com/saltbo/zpan.git
synced 2026-08-28 15:51:29 +08:00
310e4950b3
Replace pnpm workspace monorepo (packages/server, packages/web, packages/shared) with a flat single-package layout following Hono's pages-stack pattern. Switch from pnpm to npm and from Workers+Assets to CF Pages Functions deployment model. - Move source: packages/server/src/ → server/, packages/web/src/ → src/, packages/shared/src/ → shared/ - Add functions/api/[[route]].ts as CF Pages Functions entry (replaces entry-cloudflare.ts) - Update 22 import paths: server uses relative, web uses @shared/@server aliases - Merge three package.json into one, switch to npm - Update wrangler.toml: remove main/assets (Pages auto-detects functions/ dir) - Add per-directory tsconfig.json for VS Code type resolution - Simplify Dockerfile for flat layout - Fix react-pdf CSS import path (dist/esm/ → dist/) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
153 lines
6.1 KiB
TypeScript
153 lines
6.1 KiB
TypeScript
import { eq } from 'drizzle-orm'
|
|
import { describe, expect, it } from 'vitest'
|
|
import * as authSchema from '../db/auth-schema.js'
|
|
import { createTestApp } from '../test/setup.js'
|
|
|
|
describe('Auth API', () => {
|
|
it('POST /api/auth/sign-up/email creates user', async () => {
|
|
const { app } = createTestApp()
|
|
const res = await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'Test', email: 'test@example.com', password: 'password123456' }),
|
|
})
|
|
expect(res.status).toBe(200)
|
|
const body = (await res.json()) as { user: { email: string } }
|
|
expect(body.user.email).toBe('test@example.com')
|
|
})
|
|
|
|
it('POST /api/auth/sign-in/email signs in', async () => {
|
|
const { app } = createTestApp()
|
|
// First sign up
|
|
await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'Test', email: 'login@example.com', password: 'password123456' }),
|
|
})
|
|
// Then sign in
|
|
const res = await app.request('/api/auth/sign-in/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ email: 'login@example.com', password: 'password123456' }),
|
|
})
|
|
expect(res.status).toBe(200)
|
|
expect(res.headers.get('set-cookie')).toBeTruthy()
|
|
})
|
|
|
|
it('POST /api/auth/sign-in/email rejects wrong password', async () => {
|
|
const { app } = createTestApp()
|
|
await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'Test', email: 'wrong@example.com', password: 'password123456' }),
|
|
})
|
|
const res = await app.request('/api/auth/sign-in/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ email: 'wrong@example.com', password: 'wrongpassword' }),
|
|
})
|
|
expect(res.status).not.toBe(200)
|
|
})
|
|
|
|
it('first user gets admin role after signup', async () => {
|
|
const { app, db } = createTestApp()
|
|
await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'Admin User', email: 'admin@example.com', password: 'password123456' }),
|
|
})
|
|
const users = await db.select().from(authSchema.user).where(eq(authSchema.user.email, 'admin@example.com'))
|
|
expect(users[0].role).toBe('admin')
|
|
})
|
|
|
|
it('first user gets a personal organization created after signup', async () => {
|
|
const { app, db } = createTestApp()
|
|
const signUpRes = await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'Admin User', email: 'admin@example.com', password: 'password123456' }),
|
|
})
|
|
const body = (await signUpRes.json()) as { user: { id: string } }
|
|
const userId = body.user.id
|
|
|
|
const orgs = await db
|
|
.select()
|
|
.from(authSchema.organization)
|
|
.where(eq(authSchema.organization.slug, `personal-${userId}`))
|
|
expect(orgs).toHaveLength(1)
|
|
expect(JSON.parse(orgs[0].metadata!)).toEqual({ type: 'personal' })
|
|
})
|
|
|
|
it('second user does NOT get admin role', async () => {
|
|
const { app, db } = createTestApp()
|
|
// First user
|
|
await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'First', email: 'first@example.com', password: 'password123456' }),
|
|
})
|
|
// Second user
|
|
await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'Second', email: 'second@example.com', password: 'password123456' }),
|
|
})
|
|
const users = await db.select().from(authSchema.user).where(eq(authSchema.user.email, 'second@example.com'))
|
|
expect(users[0].role).not.toBe('admin')
|
|
})
|
|
|
|
it('second user also gets a personal organization created', async () => {
|
|
const { app, db } = createTestApp()
|
|
// First user
|
|
await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'First', email: 'first@example.com', password: 'password123456' }),
|
|
})
|
|
// Second user
|
|
const signUpRes = await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'Second', email: 'second@example.com', password: 'password123456' }),
|
|
})
|
|
const body = (await signUpRes.json()) as { user: { id: string } }
|
|
const userId = body.user.id
|
|
|
|
const orgs = await db
|
|
.select()
|
|
.from(authSchema.organization)
|
|
.where(eq(authSchema.organization.slug, `personal-${userId}`))
|
|
expect(orgs).toHaveLength(1)
|
|
expect(JSON.parse(orgs[0].metadata!)).toEqual({ type: 'personal' })
|
|
})
|
|
|
|
it('second user gets a member record with owner role in their personal org', async () => {
|
|
const { app, db } = createTestApp()
|
|
// First user
|
|
await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'First', email: 'first@example.com', password: 'password123456' }),
|
|
})
|
|
// Second user
|
|
const signUpRes = await app.request('/api/auth/sign-up/email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: 'Second', email: 'second@example.com', password: 'password123456' }),
|
|
})
|
|
const body = (await signUpRes.json()) as { user: { id: string } }
|
|
const userId = body.user.id
|
|
|
|
const orgs = await db
|
|
.select()
|
|
.from(authSchema.organization)
|
|
.where(eq(authSchema.organization.slug, `personal-${userId}`))
|
|
const orgId = orgs[0].id
|
|
|
|
const members = await db.select().from(authSchema.member).where(eq(authSchema.member.organizationId, orgId))
|
|
expect(members).toHaveLength(1)
|
|
expect(members[0].userId).toBe(userId)
|
|
expect(members[0].role).toBe('owner')
|
|
})
|
|
})
|