-
-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathmiddleware.test.ts
More file actions
149 lines (128 loc) · 4.41 KB
/
Copy pathmiddleware.test.ts
File metadata and controls
149 lines (128 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { Hono } from 'hono'
import { describe, expect, expectTypeOf, it } from 'vitest'
import type { SupabaseContext } from '../../types.js'
import { withSupabase } from './middleware.js'
type Env = { Variables: { supabaseContext: SupabaseContext } }
type Database = {
public: {
Tables: {
todos: {
Row: { id: number; title: string }
Insert: { id?: number; title: string }
Update: { id?: number; title?: string }
Relationships: []
}
}
}
}
type TypedEnv = {
Variables: {
supabaseContext: SupabaseContext<Database>
}
}
describe('hono supabase middleware', () => {
const env = {
url: 'https://test.supabase.co',
publishableKeys: { default: 'sb_publishable_xyz' },
secretKeys: { default: 'sb_publishable_xyz' },
jwks: null,
}
it('sets supabase context on successful auth', async () => {
const app = new Hono<Env>()
app.use('*', withSupabase({ auth: 'none', env }))
app.get('/', (c) => {
const ctx = c.get('supabaseContext')
return c.json({
authMode: ctx.authMode,
hasSupabase: !!ctx.supabase,
hasAdmin: !!ctx.supabaseAdmin,
})
})
const res = await app.request('/')
expect(res.status).toBe(200)
const body = await res.json()
expect(body.authMode).toBe('none')
expect(body.hasSupabase).toBe(true)
expect(body.hasAdmin).toBe(true)
})
it('uses the Hono app env to type the Supabase context', async () => {
const app = new Hono<TypedEnv>()
app.use('*', withSupabase({ auth: 'none', env }))
app.get('/', (c) => {
// Hono provides two ways to get typed Supabse Context
const getCtx = c.get('supabaseContext')
const varCtx = c.var.supabaseContext
expect(getCtx).toBe(varCtx)
expectTypeOf(getCtx).toEqualTypeOf<SupabaseContext<Database>>()
return c.json({ authMode: getCtx.authMode })
})
const res = await app.request('/')
expect(res.status).toBe(200)
})
it('uses the Hono adapter to type the Supabase context', async () => {
// match docs example in typescript-generics.md
const app = new Hono()
const rootApp = new Hono()
.use(withSupabase<Database>({ auth: 'none', env }))
.get('/', (c) => {
const ctx = c.var.supabaseContext
expectTypeOf(ctx).toEqualTypeOf<SupabaseContext<Database>>()
return c.json({ authMode: ctx.authMode })
})
app.route('/', rootApp)
const res = await app.request('/')
expect(res.status).toBe(200)
})
it('throws HTTPException on auth failure', async () => {
const app = new Hono()
app.use('*', withSupabase({ auth: 'user', env }))
app.get('/', (c) => c.json({ ok: true }))
const res = await app.request('/')
expect(res.status).toBe(401)
const body = await res.text()
expect(body).toBeTruthy()
})
it('exposes AuthError via cause in app.onError', async () => {
const app = new Hono()
app.use('*', withSupabase({ auth: 'user', env }))
app.get('/', (c) => c.json({ ok: true }))
app.onError((err, c) => {
const cause = (err as Error).cause as
| { code?: string; status?: number }
| undefined
return c.json(
{ error: err.message, code: cause?.code },
(cause?.status as 401) ?? 500,
)
})
const res = await app.request('/')
expect(res.status).toBe(401)
const body = await res.json()
expect(body.error).toBeDefined()
expect(body.code).toBeDefined()
})
it('skips if context is already set by prior middleware', async () => {
const app = new Hono<Env>()
// First middleware sets context with 'none' auth
app.use('*', withSupabase({ auth: 'none', env }))
// Second middleware would require 'secret' — but should skip
app.use('*', withSupabase({ auth: 'secret', env }))
app.get('/', (c) => {
const ctx = c.get('supabaseContext')
return c.json({ authMode: ctx.authMode })
})
// No apikey header — would fail 'secret' if it ran
const res = await app.request('/')
expect(res.status).toBe(200)
const body = await res.json()
// First middleware's auth type is preserved
expect(body.authMode).toBe('none')
})
it('does not add CORS headers', async () => {
const app = new Hono()
app.use('*', withSupabase({ auth: 'none', env }))
app.get('/', (c) => c.json({ ok: true }))
const res = await app.request('/')
expect(res.headers.get('Access-Control-Allow-Origin')).toBeNull()
})
})