11/// <reference types="bun" />
2- import { beforeAll , expect , test } from 'bun:test'
2+ import { beforeAll , beforeEach , expect , test } from 'bun:test'
33import { RequestContext } from 'remix/fetch-router'
4+ import { setAppDb } from '../app-env.ts'
45import { setAuthSessionSecret } from '../auth-session.ts'
6+ import { createPasswordHash } from '../password-hash.ts'
57import auth from './auth.ts'
68
79function createAuthRequest ( body : unknown , url : string ) {
@@ -17,10 +19,106 @@ function createAuthRequest(body: unknown, url: string) {
1719 }
1820}
1921
22+ type TestUser = {
23+ id : number
24+ email : string
25+ username : string
26+ password_hash : string
27+ }
28+
29+ function createTestDb ( ) {
30+ let nextId = 1
31+ const users = new Map < string , TestUser > ( )
32+ const db = {
33+ prepare ( query : string ) {
34+ return {
35+ bind ( ...params : Array < unknown > ) {
36+ const normalizedQuery = query
37+ . replace ( / \s + / g, ' ' )
38+ . trim ( )
39+ . toLowerCase ( )
40+ return {
41+ async first ( ) {
42+ if ( normalizedQuery . startsWith ( 'select id from users' ) ) {
43+ const email = String ( params [ 0 ] ?? '' ) . toLowerCase ( )
44+ const user = users . get ( email )
45+ return user ? { id : user . id } : null
46+ }
47+ if (
48+ normalizedQuery . startsWith (
49+ 'select password_hash from users where email = ?' ,
50+ )
51+ ) {
52+ const email = String ( params [ 0 ] ?? '' ) . toLowerCase ( )
53+ const user = users . get ( email )
54+ return user ? { password_hash : user . password_hash } : null
55+ }
56+ return null
57+ } ,
58+ async run ( ) {
59+ if ( normalizedQuery . startsWith ( 'insert into users' ) ) {
60+ const [ username , email , passwordHash ] = params as Array < string >
61+ const normalizedEmail = String ( email ) . toLowerCase ( )
62+ if ( users . has ( normalizedEmail ) ) {
63+ throw new Error ( 'UNIQUE constraint failed: users.email' )
64+ }
65+ const user : TestUser = {
66+ id : nextId ,
67+ email : String ( email ) ,
68+ username : String ( username ) ,
69+ password_hash : String ( passwordHash ) ,
70+ }
71+ nextId += 1
72+ users . set ( normalizedEmail , user )
73+ return { success : true }
74+ }
75+ if (
76+ normalizedQuery . startsWith (
77+ 'update users set password_hash = ? where email = ?' ,
78+ )
79+ ) {
80+ const [ passwordHash , email ] = params as Array < string >
81+ const user = users . get ( String ( email ) . toLowerCase ( ) )
82+ if ( user ) {
83+ user . password_hash = String ( passwordHash )
84+ }
85+ return { success : true }
86+ }
87+ return { success : true }
88+ } ,
89+ }
90+ } ,
91+ }
92+ } ,
93+ } as unknown as D1Database
94+
95+ async function addUser ( email : string , password : string ) {
96+ const passwordHash = await createPasswordHash ( password )
97+ const user : TestUser = {
98+ id : nextId ,
99+ email,
100+ username : email ,
101+ password_hash : passwordHash ,
102+ }
103+ nextId += 1
104+ users . set ( email . toLowerCase ( ) , user )
105+ return user
106+ }
107+
108+ return { db, users, addUser }
109+ }
110+
111+ let testDb : ReturnType < typeof createTestDb >
112+
20113beforeAll ( ( ) => {
21114 setAuthSessionSecret ( 'test-cookie-secret' )
22115} )
23116
117+ beforeEach ( ( ) => {
118+ testDb = createTestDb ( )
119+ setAppDb ( testDb . db )
120+ } )
121+
24122test ( 'auth handler returns 400 for invalid JSON' , async ( ) => {
25123 const authRequest = createAuthRequest ( '{' , 'http://example.com/auth' )
26124 const response = await authRequest . run ( )
@@ -42,7 +140,33 @@ test('auth handler returns 400 for missing fields', async () => {
42140 } )
43141} )
44142
143+ test ( 'auth handler rejects login with unknown user' , async ( ) => {
144+ const authRequest = createAuthRequest (
145+ { email : 'a@b.com' , password : 'secret' , mode : 'login' } ,
146+ 'http://example.com/auth' ,
147+ )
148+ const response = await authRequest . run ( )
149+ expect ( response . status ) . toBe ( 401 )
150+ const payload = await response . json ( )
151+ expect ( payload ) . toEqual ( { error : 'Invalid email or password.' } )
152+ } )
153+
154+ test ( 'auth handler creates a user and cookie for signup' , async ( ) => {
155+ const authRequest = createAuthRequest (
156+ { email : 'new@b.com' , password : 'secret' , mode : 'signup' } ,
157+ 'http://example.com/auth' ,
158+ )
159+ const response = await authRequest . run ( )
160+ expect ( response . status ) . toBe ( 200 )
161+ const payload = await response . json ( )
162+ expect ( payload ) . toEqual ( { ok : true , mode : 'signup' } )
163+ expect ( testDb . users . has ( 'new@b.com' ) ) . toBe ( true )
164+ const setCookie = response . headers . get ( 'Set-Cookie' ) ?? ''
165+ expect ( setCookie ) . toContain ( 'epicflare_session=' )
166+ } )
167+
45168test ( 'auth handler returns ok with a session cookie for login' , async ( ) => {
169+ await testDb . addUser ( 'a@b.com' , 'secret' )
46170 const authRequest = createAuthRequest (
47171 { email : 'a@b.com' , password : 'secret' , mode : 'login' } ,
48172 'http://example.com/auth' ,
0 commit comments