bxo 0.0.5-dev.50 โ†’ 0.0.5-dev.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,413 @@
1
+ import { describe, it, expect, beforeAll, afterAll, afterEach } from 'bun:test';
2
+ import BXO, { z } from '../../src/index';
3
+
4
+ describe('BXO Framework Integration', () => {
5
+ let app: BXO;
6
+ let baseUrl: string;
7
+
8
+ beforeAll(() => {
9
+ app = new BXO();
10
+ baseUrl = 'http://localhost:3001';
11
+ });
12
+
13
+ afterEach(async () => {
14
+ if (app.isServerRunning()) {
15
+ await app.stop();
16
+ }
17
+ });
18
+
19
+ afterAll(async () => {
20
+ if (app.isServerRunning()) {
21
+ await app.stop();
22
+ }
23
+ });
24
+
25
+ describe('Basic Routing', () => {
26
+ it('should handle GET requests', async () => {
27
+ app.get('/test-get', () => {
28
+ return { message: 'GET successful' };
29
+ });
30
+
31
+ await app.start(3001);
32
+
33
+ const response = await fetch(`${baseUrl}/test-get`);
34
+ const data = await response.json() as { message: string };
35
+
36
+ expect(response.status).toBe(200);
37
+ expect(data.message).toBe('GET successful');
38
+ });
39
+
40
+ it('should handle POST requests', async () => {
41
+ app.post('/test-post', (ctx) => {
42
+ return { message: 'POST successful', body: ctx.body };
43
+ });
44
+
45
+ const response = await fetch(`${baseUrl}/test-post`, {
46
+ method: 'POST',
47
+ headers: { 'Content-Type': 'application/json' },
48
+ body: JSON.stringify({ test: 'data' })
49
+ });
50
+
51
+ const data = await response.json() as { message: string, body: { test: string } };
52
+
53
+ expect(response.status).toBe(200);
54
+ expect(data.message).toBe('POST successful');
55
+ expect(data.body.test).toBe('data');
56
+ });
57
+
58
+ it('should handle PUT requests', async () => {
59
+ app.put('/test-put', (ctx) => {
60
+ return { message: 'PUT successful', body: ctx.body };
61
+ });
62
+
63
+ const response = await fetch(`${baseUrl}/test-put`, {
64
+ method: 'PUT',
65
+ headers: { 'Content-Type': 'application/json' },
66
+ body: JSON.stringify({ test: 'data' })
67
+ });
68
+
69
+ const data = await response.json() as { message: string };
70
+
71
+ expect(response.status).toBe(200);
72
+ expect(data.message).toBe('PUT successful');
73
+ });
74
+
75
+ it('should handle DELETE requests', async () => {
76
+ app.delete('/test-delete', () => {
77
+ return { message: 'DELETE successful' };
78
+ });
79
+
80
+ const response = await fetch(`${baseUrl}/test-delete`, {
81
+ method: 'DELETE'
82
+ });
83
+
84
+ const data = await response.json() as { message: string };
85
+
86
+ expect(response.status).toBe(200);
87
+ expect(data.message).toBe('DELETE successful');
88
+ });
89
+
90
+ it('should handle PATCH requests', async () => {
91
+ app.patch('/test-patch', (ctx) => {
92
+ return { message: 'PATCH successful', body: ctx.body };
93
+ });
94
+
95
+ const response = await fetch(`${baseUrl}/test-patch`, {
96
+ method: 'PATCH',
97
+ headers: { 'Content-Type': 'application/json' },
98
+ body: JSON.stringify({ test: 'data' })
99
+ });
100
+
101
+ const data = await response.json() as { message: string };
102
+
103
+ expect(response.status).toBe(200);
104
+ expect(data.message).toBe('PATCH successful');
105
+ });
106
+ });
107
+
108
+ describe('Route Parameters', () => {
109
+ it('should handle route parameters', async () => {
110
+ app.get('/users/:id', (ctx) => {
111
+ return { userId: ctx.params.id, message: 'User found' };
112
+ });
113
+
114
+ const response = await fetch(`${baseUrl}/users/123`);
115
+ const data = await response.json() as { userId: string, message: string };
116
+
117
+ expect(response.status).toBe(200);
118
+ expect(data.userId).toBe('123');
119
+ expect(data.message).toBe('User found');
120
+ });
121
+
122
+ it('should handle nested route parameters', async () => {
123
+ app.get('/users/:id/posts/:postId', (ctx) => {
124
+ return {
125
+ userId: ctx.params.id,
126
+ postId: ctx.params.postId,
127
+ message: 'Post found'
128
+ };
129
+ });
130
+
131
+ const response = await fetch(`${baseUrl}/users/123/posts/456`);
132
+ const data = await response.json() as { userId: string, postId: string, message: string };
133
+
134
+ expect(response.status).toBe(200);
135
+ expect(data.userId).toBe('123');
136
+ expect(data.postId).toBe('456');
137
+ expect(data.message).toBe('Post found');
138
+ });
139
+ });
140
+
141
+ describe('Query Parameters', () => {
142
+ it('should handle query parameters', async () => {
143
+ app.get('/search', (ctx) => {
144
+ return { query: ctx.query, message: 'Search completed' };
145
+ });
146
+
147
+ const response = await fetch(`${baseUrl}/search?q=test&page=1`);
148
+ const data = await response.json() as { query: { q: string, page: string }, message: string };
149
+
150
+ expect(response.status).toBe(200);
151
+ expect(data.query.q).toBe('test');
152
+ expect(data.query.page).toBe('1');
153
+ expect(data.message).toBe('Search completed');
154
+ });
155
+ });
156
+
157
+ describe('Request Body', () => {
158
+ it('should handle JSON body', async () => {
159
+ app.post('/api/users', (ctx) => {
160
+ return { user: ctx.body, message: 'User created' };
161
+ });
162
+
163
+ const userData = { name: 'John', email: 'john@example.com' };
164
+ const response = await fetch(`${baseUrl}/api/users`, {
165
+ method: 'POST',
166
+ headers: { 'Content-Type': 'application/json' },
167
+ body: JSON.stringify(userData)
168
+ });
169
+
170
+ const data = await response.json() as { user: { name: string, email: string }, message: string };
171
+
172
+ expect(response.status).toBe(200);
173
+ expect(data.user.name).toBe('John');
174
+ expect(data.user.email).toBe('john@example.com');
175
+ expect(data.message).toBe('User created');
176
+ });
177
+
178
+ it('should handle form data', async () => {
179
+ app.post('/api/contact', (ctx) => {
180
+ return { formData: ctx.body, message: 'Form submitted' };
181
+ });
182
+
183
+ const formData = new FormData();
184
+ formData.append('name', 'John');
185
+ formData.append('email', 'john@example.com');
186
+
187
+ const response = await fetch(`${baseUrl}/api/contact`, {
188
+ method: 'POST',
189
+ body: formData
190
+ });
191
+
192
+ const data = await response.json() as { formData: { name: string, email: string }, message: string };
193
+
194
+ expect(response.status).toBe(200);
195
+ expect(data.formData.name).toBe('John');
196
+ expect(data.formData.email).toBe('john@example.com');
197
+ expect(data.message).toBe('Form submitted');
198
+ });
199
+ });
200
+
201
+ describe('Response Handling', () => {
202
+ it('should handle string responses', async () => {
203
+ app.get('/text', () => {
204
+ return 'Hello World';
205
+ });
206
+
207
+ const response = await fetch(`${baseUrl}/text`);
208
+ const text = await response.text();
209
+
210
+ expect(response.status).toBe(200);
211
+ expect(text).toBe('Hello World');
212
+ expect(response.headers.get('content-type')).toBe('text/plain');
213
+ });
214
+
215
+ it('should handle object responses', async () => {
216
+ app.get('/json', () => {
217
+ return { message: 'Hello World', status: 'success' };
218
+ });
219
+
220
+ const response = await fetch(`${baseUrl}/json`);
221
+ const data = await response.json() as { message: string, status: string };
222
+
223
+ expect(response.status).toBe(200);
224
+ expect(data.message).toBe('Hello World');
225
+ expect(data.status).toBe('success');
226
+ expect(response.headers.get('content-type')).toBe('application/json');
227
+ });
228
+
229
+ it('should handle Response objects', async () => {
230
+ app.get('/custom-response', () => {
231
+ return new Response('Custom response', {
232
+ status: 201,
233
+ headers: { 'x-custom': 'value' }
234
+ });
235
+ });
236
+
237
+ const response = await fetch(`${baseUrl}/custom-response`);
238
+ const text = await response.text();
239
+
240
+ expect(response.status).toBe(201);
241
+ expect(text).toBe('Custom response');
242
+ expect(response.headers.get('x-custom')).toBe('value');
243
+ });
244
+ });
245
+
246
+ describe('Status and Headers', () => {
247
+ it('should handle custom status codes', async () => {
248
+ app.get('/not-found', (ctx) => {
249
+ ctx.status(404, 'Not Found');
250
+ return { error: 'Resource not found' };
251
+ });
252
+
253
+ const response = await fetch(`${baseUrl}/not-found`);
254
+ const data = await response.json() as { error: string };
255
+
256
+ expect(response.status).toBe(404);
257
+ expect(data.error).toBe('Resource not found');
258
+ });
259
+
260
+ it('should handle custom headers', async () => {
261
+ app.get('/custom-headers', (ctx) => {
262
+ ctx.set.headers['x-custom'] = 'value';
263
+ ctx.set.headers['authorization'] = 'Bearer token';
264
+ return { message: 'Headers set' };
265
+ });
266
+
267
+ const response = await fetch(`${baseUrl}/custom-headers`);
268
+ const data = await response.json() as { message: string };
269
+
270
+ expect(response.status).toBe(200);
271
+ expect(response.headers.get('x-custom')).toBe('value');
272
+ expect(response.headers.get('authorization')).toBe('Bearer token');
273
+ expect(data.message).toBe('Headers set');
274
+ });
275
+ });
276
+
277
+ describe('Cookies', () => {
278
+ it('should handle setting cookies', async () => {
279
+ app.get('/set-cookie', (ctx) => {
280
+ ctx.set.cookies('session', 'abc123', {
281
+ httpOnly: true,
282
+ secure: false
283
+ });
284
+ return { message: 'Cookie set' };
285
+ });
286
+
287
+ const response = await fetch(`${baseUrl}/set-cookie`);
288
+ const data = await response.json() as { message: string };
289
+
290
+ expect(response.status).toBe(200);
291
+ expect(response.headers.get('set-cookie')).toContain('session=abc123');
292
+ expect(response.headers.get('set-cookie')).toContain('HttpOnly');
293
+ expect(data.message).toBe('Cookie set');
294
+ });
295
+ });
296
+
297
+ describe('Redirects', () => {
298
+ it('should handle redirects', async () => {
299
+ app.get('/redirect', (ctx) => {
300
+ return ctx.redirect('/target', 301);
301
+ });
302
+
303
+ const response = await fetch(`${baseUrl}/redirect`, {
304
+ redirect: 'manual'
305
+ });
306
+
307
+ expect(response.status).toBe(301);
308
+ expect(response.headers.get('location')).toBe('/target');
309
+ });
310
+
311
+ it('should handle implicit redirects', async () => {
312
+ app.get('/implicit-redirect', (ctx) => {
313
+ ctx.set.headers['Location'] = '/target';
314
+ ctx.set.status = 302;
315
+ // Don't return anything - should create redirect automatically
316
+ });
317
+
318
+ const response = await fetch(`${baseUrl}/implicit-redirect`, {
319
+ redirect: 'manual'
320
+ });
321
+
322
+ expect(response.status).toBe(302);
323
+ expect(response.headers.get('location')).toBe('/target');
324
+ });
325
+ });
326
+
327
+ describe('Error Handling', () => {
328
+ it('should handle 404 for non-existent routes', async () => {
329
+ const response = await fetch(`${baseUrl}/non-existent`);
330
+
331
+ expect(response.status).toBe(404);
332
+ expect(await response.text()).toBe('Not Found');
333
+ });
334
+
335
+ it('should handle validation errors', async () => {
336
+ app.post('/validate', (ctx) => {
337
+ return { data: ctx.body };
338
+ }, {
339
+ body: z.object({
340
+ name: z.string().min(1),
341
+ email: z.string().email()
342
+ })
343
+ });
344
+
345
+ const response = await fetch(`${baseUrl}/validate`, {
346
+ method: 'POST',
347
+ headers: { 'Content-Type': 'application/json' },
348
+ body: JSON.stringify({ name: '', email: 'invalid' })
349
+ });
350
+
351
+ expect(response.status).toBe(400);
352
+ const data = await response.json() as { error: string };
353
+ expect(data.error).toContain('Validation failed');
354
+ });
355
+ });
356
+
357
+ describe('WebSocket Support', () => {
358
+ it('should handle WebSocket routes', async () => {
359
+ app.ws('/ws', {
360
+ onOpen: (ws) => {
361
+ ws.send('Connected');
362
+ },
363
+ onMessage: (ws, message) => {
364
+ ws.send(`Echo: ${message}`);
365
+ }
366
+ });
367
+
368
+ // WebSocket testing would require a WebSocket client
369
+ // For now, just verify the route is registered
370
+ expect(app.wsRoutes).toHaveLength(1);
371
+ expect(app.wsRoutes[0]?.path).toBe('/ws');
372
+ });
373
+ });
374
+
375
+ describe('Lifecycle Hooks', () => {
376
+ it('should execute lifecycle hooks', async () => {
377
+ let beforeStartCalled = false;
378
+ let afterStartCalled = false;
379
+
380
+ app.onBeforeStart(() => {
381
+ beforeStartCalled = true;
382
+ });
383
+
384
+ app.onAfterStart(() => {
385
+ afterStartCalled = true;
386
+ });
387
+
388
+ await app.start(3002);
389
+
390
+ expect(beforeStartCalled).toBe(true);
391
+ expect(afterStartCalled).toBe(true);
392
+
393
+ await app.stop();
394
+ });
395
+ });
396
+
397
+ describe('Server Information', () => {
398
+ it('should provide server information', async () => {
399
+ await app.start(3003);
400
+
401
+ const info = app.info;
402
+
403
+ expect(info.running).toBe(true);
404
+ expect(info.server).toBe('Bun');
405
+ expect(info.port).toBe(3003);
406
+ expect(info.hostname).toBe('localhost');
407
+ expect(info.runtime).toBe('Bun');
408
+ expect(info.totalRoutes).toBeGreaterThan(0);
409
+
410
+ await app.stop();
411
+ });
412
+ });
413
+ });
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { spawn } from 'child_process';
4
+
5
+ console.log('๐Ÿงช Running BXO Framework Tests...\n');
6
+
7
+ // Run unit tests
8
+ console.log('๐Ÿ“‹ Running Unit Tests...');
9
+ const unitTests = spawn('bun', ['test', 'tests/unit/'], {
10
+ stdio: 'inherit',
11
+ shell: true
12
+ });
13
+
14
+ unitTests.on('close', (code) => {
15
+ if (code === 0) {
16
+ console.log('\nโœ… Unit tests passed!\n');
17
+
18
+ // Run integration tests
19
+ console.log('๐Ÿ”— Running Integration Tests...');
20
+ const integrationTests = spawn('bun', ['test', 'tests/integration/'], {
21
+ stdio: 'inherit',
22
+ shell: true
23
+ });
24
+
25
+ integrationTests.on('close', (integrationCode) => {
26
+ if (integrationCode === 0) {
27
+ console.log('\nโœ… Integration tests passed!');
28
+ console.log('\n๐ŸŽ‰ All tests completed successfully!');
29
+ process.exit(0);
30
+ } else {
31
+ console.log('\nโŒ Integration tests failed!');
32
+ process.exit(integrationCode);
33
+ }
34
+ });
35
+ } else {
36
+ console.log('\nโŒ Unit tests failed!');
37
+ process.exit(code);
38
+ }
39
+ });
40
+
41
+ unitTests.on('error', (error) => {
42
+ console.error('โŒ Error running unit tests:', error);
43
+ process.exit(1);
44
+ });