Skip to content

Commit e720d1a

Browse files
committed
test(netlify): add handler tests
1 parent bcc81b1 commit e720d1a

File tree

1 file changed

+282
-0
lines changed

1 file changed

+282
-0
lines changed
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
import { Hono } from '../../hono'
2+
import { handle } from './handler'
3+
4+
describe('Netlify Adapter', () => {
5+
// Mock Netlify context
6+
const createMockContext = (overrides = {}) => ({
7+
geo: {
8+
city: 'San Francisco',
9+
country: { code: 'US', name: 'United States' },
10+
},
11+
ip: '192.168.1.1',
12+
requestId: 'test-request-id',
13+
...overrides,
14+
})
15+
16+
describe('Basic Request Handling', () => {
17+
it('Should return 200 response for GET request', async () => {
18+
const app = new Hono()
19+
app.get('/', (c) => c.text('Hello Netlify'))
20+
21+
const handler = handle(app)
22+
const req = new Request('http://localhost/')
23+
const ctx = createMockContext()
24+
25+
const res = await handler(req, ctx)
26+
27+
expect(res.status).toBe(200)
28+
expect(await res.text()).toBe('Hello Netlify')
29+
})
30+
31+
it('Should handle POST request with JSON body', async () => {
32+
const app = new Hono()
33+
app.post('/api/users', async (c) => {
34+
const body = await c.req.json()
35+
return c.json({ received: body })
36+
})
37+
38+
const handler = handle(app)
39+
const req = new Request('http://localhost/api/users', {
40+
method: 'POST',
41+
headers: { 'Content-Type': 'application/json' },
42+
body: JSON.stringify({ name: 'John' }),
43+
})
44+
const ctx = createMockContext()
45+
46+
const res = await handler(req, ctx)
47+
48+
expect(res.status).toBe(200)
49+
expect(await res.json()).toEqual({
50+
received: { name: 'John' },
51+
})
52+
})
53+
54+
it('Should handle path parameters', async () => {
55+
const app = new Hono()
56+
app.get('/users/:id', (c) => {
57+
const id = c.req.param('id')
58+
return c.json({ userId: id })
59+
})
60+
61+
const handler = handle(app)
62+
const req = new Request('http://localhost/users/123')
63+
const ctx = createMockContext()
64+
65+
const res = await handler(req, ctx)
66+
67+
expect(res.status).toBe(200)
68+
expect(await res.json()).toEqual({ userId: '123' })
69+
})
70+
71+
it('Should handle query parameters', async () => {
72+
const app = new Hono()
73+
app.get('/search', (c) => {
74+
const query = c.req.query('q')
75+
return c.json({ query })
76+
})
77+
78+
const handler = handle(app)
79+
const req = new Request('http://localhost/search?q=hono')
80+
const ctx = createMockContext()
81+
82+
const res = await handler(req, ctx)
83+
84+
expect(res.status).toBe(200)
85+
expect(await res.json()).toEqual({ query: 'hono' })
86+
})
87+
})
88+
89+
describe('Context Handling', () => {
90+
it('Should pass Netlify context to Hono env', async () => {
91+
const app = new Hono()
92+
app.get('/', (c) => {
93+
const netlifyCtx = c.env.context
94+
return c.json({
95+
ip: netlifyCtx.ip,
96+
requestId: netlifyCtx.requestId,
97+
})
98+
})
99+
100+
const handler = handle(app)
101+
const req = new Request('http://localhost/')
102+
const ctx = createMockContext({ ip: '10.0.0.1', requestId: 'req-abc' })
103+
104+
const res = await handler(req, ctx)
105+
106+
expect(res.status).toBe(200)
107+
expect(await res.json()).toEqual({
108+
ip: '10.0.0.1',
109+
requestId: 'req-abc',
110+
})
111+
})
112+
113+
it('Should access geo information from context', async () => {
114+
const app = new Hono()
115+
app.get('/geo', (c) => {
116+
const { geo } = c.env.context
117+
return c.json({
118+
city: geo.city,
119+
country: geo.country?.code,
120+
})
121+
})
122+
123+
const handler = handle(app)
124+
const req = new Request('http://localhost/geo')
125+
const ctx = createMockContext({
126+
geo: {
127+
city: 'Tokyo',
128+
country: { code: 'JP', name: 'Japan' },
129+
},
130+
})
131+
132+
const res = await handler(req, ctx)
133+
134+
expect(res.status).toBe(200)
135+
expect(await res.json()).toEqual({
136+
city: 'Tokyo',
137+
country: 'JP',
138+
})
139+
})
140+
})
141+
142+
describe('Response Handling', () => {
143+
it('Should return JSON response with correct content-type', async () => {
144+
const app = new Hono()
145+
app.get('/api', (c) => c.json({ message: 'Hello' }))
146+
147+
const handler = handle(app)
148+
const req = new Request('http://localhost/api')
149+
const ctx = createMockContext()
150+
151+
const res = await handler(req, ctx)
152+
153+
expect(res.status).toBe(200)
154+
expect(res.headers.get('Content-Type')).toContain('application/json')
155+
})
156+
157+
it('Should return custom status codes', async () => {
158+
const app = new Hono()
159+
app.post('/created', (c) => c.json({ id: 1 }, 201))
160+
161+
const handler = handle(app)
162+
const req = new Request('http://localhost/created', { method: 'POST' })
163+
const ctx = createMockContext()
164+
165+
const res = await handler(req, ctx)
166+
167+
expect(res.status).toBe(201)
168+
})
169+
170+
it('Should return custom headers', async () => {
171+
const app = new Hono()
172+
app.get('/headers', (c) => {
173+
c.header('X-Custom-Header', 'custom-value')
174+
return c.text('OK')
175+
})
176+
177+
const handler = handle(app)
178+
const req = new Request('http://localhost/headers')
179+
const ctx = createMockContext()
180+
181+
const res = await handler(req, ctx)
182+
183+
expect(res.headers.get('X-Custom-Header')).toBe('custom-value')
184+
})
185+
})
186+
187+
describe('Error Handling', () => {
188+
it('Should return 404 for unmatched routes', async () => {
189+
const app = new Hono()
190+
app.get('/exists', (c) => c.text('Found'))
191+
192+
const handler = handle(app)
193+
const req = new Request('http://localhost/not-exists')
194+
const ctx = createMockContext()
195+
196+
const res = await handler(req, ctx)
197+
198+
expect(res.status).toBe(404)
199+
})
200+
201+
it('Should handle custom error handler', async () => {
202+
const app = new Hono()
203+
app.get('/error', () => {
204+
throw new Error('Something went wrong')
205+
})
206+
app.onError((err, c) => {
207+
return c.json({ error: err.message }, 500)
208+
})
209+
210+
const handler = handle(app)
211+
const req = new Request('http://localhost/error')
212+
const ctx = createMockContext()
213+
214+
const res = await handler(req, ctx)
215+
216+
expect(res.status).toBe(500)
217+
expect(await res.json()).toEqual({ error: 'Something went wrong' })
218+
})
219+
})
220+
221+
describe('Middleware', () => {
222+
it('Should execute middleware', async () => {
223+
const app = new Hono()
224+
225+
app.use('*', async (c, next) => {
226+
c.header('X-Middleware', 'executed')
227+
await next()
228+
})
229+
230+
app.get('/', (c) => c.text('Hello'))
231+
232+
const handler = handle(app)
233+
const req = new Request('http://localhost/')
234+
const ctx = createMockContext()
235+
236+
const res = await handler(req, ctx)
237+
238+
expect(res.status).toBe(200)
239+
expect(res.headers.get('X-Middleware')).toBe('executed')
240+
})
241+
})
242+
243+
describe('Compatibility', () => {
244+
it('Should work with basePath', async () => {
245+
const app = new Hono().basePath('/api')
246+
app.get('/users', (c) => c.json({ users: [] }))
247+
248+
const handler = handle(app)
249+
const req = new Request('http://localhost/api/users')
250+
const ctx = createMockContext()
251+
252+
const res = await handler(req, ctx)
253+
254+
expect(res.status).toBe(200)
255+
expect(await res.json()).toEqual({ users: [] })
256+
})
257+
258+
it('Should propagate errors when onError re-throws', async () => {
259+
const app = new Hono().basePath('/api')
260+
261+
app.onError((e) => {
262+
throw e
263+
})
264+
app.get('/error', () => {
265+
throw new Error('Custom Error')
266+
})
267+
268+
const handler = handle(app)
269+
const req = new Request('http://localhost/api/error')
270+
const ctx = createMockContext()
271+
272+
let error: Error | null = null
273+
try {
274+
await handler(req, ctx)
275+
} catch (e) {
276+
error = e as Error
277+
}
278+
expect(error).not.toBeNull()
279+
expect(error?.message).toBe('Custom Error')
280+
})
281+
})
282+
})

0 commit comments

Comments
 (0)