@buenojs/bueno 0.8.0
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.
- package/.env.example +109 -0
- package/.github/workflows/ci.yml +31 -0
- package/LICENSE +21 -0
- package/README.md +892 -0
- package/architecture.md +652 -0
- package/bun.lock +70 -0
- package/dist/cli/index.js +3233 -0
- package/dist/index.js +9014 -0
- package/package.json +77 -0
- package/src/cache/index.ts +795 -0
- package/src/cli/ARCHITECTURE.md +837 -0
- package/src/cli/bin.ts +10 -0
- package/src/cli/commands/build.ts +425 -0
- package/src/cli/commands/dev.ts +248 -0
- package/src/cli/commands/generate.ts +541 -0
- package/src/cli/commands/help.ts +55 -0
- package/src/cli/commands/index.ts +112 -0
- package/src/cli/commands/migration.ts +355 -0
- package/src/cli/commands/new.ts +804 -0
- package/src/cli/commands/start.ts +208 -0
- package/src/cli/core/args.ts +283 -0
- package/src/cli/core/console.ts +349 -0
- package/src/cli/core/index.ts +60 -0
- package/src/cli/core/prompt.ts +424 -0
- package/src/cli/core/spinner.ts +265 -0
- package/src/cli/index.ts +135 -0
- package/src/cli/templates/deploy.ts +295 -0
- package/src/cli/templates/docker.ts +307 -0
- package/src/cli/templates/index.ts +24 -0
- package/src/cli/utils/fs.ts +428 -0
- package/src/cli/utils/index.ts +8 -0
- package/src/cli/utils/strings.ts +197 -0
- package/src/config/env.ts +408 -0
- package/src/config/index.ts +506 -0
- package/src/config/loader.ts +329 -0
- package/src/config/merge.ts +285 -0
- package/src/config/types.ts +320 -0
- package/src/config/validation.ts +441 -0
- package/src/container/forward-ref.ts +143 -0
- package/src/container/index.ts +386 -0
- package/src/context/index.ts +360 -0
- package/src/database/index.ts +1142 -0
- package/src/database/migrations/index.ts +371 -0
- package/src/database/schema/index.ts +619 -0
- package/src/frontend/api-routes.ts +640 -0
- package/src/frontend/bundler.ts +643 -0
- package/src/frontend/console-client.ts +419 -0
- package/src/frontend/console-stream.ts +587 -0
- package/src/frontend/dev-server.ts +846 -0
- package/src/frontend/file-router.ts +611 -0
- package/src/frontend/frameworks/index.ts +106 -0
- package/src/frontend/frameworks/react.ts +85 -0
- package/src/frontend/frameworks/solid.ts +104 -0
- package/src/frontend/frameworks/svelte.ts +110 -0
- package/src/frontend/frameworks/vue.ts +92 -0
- package/src/frontend/hmr-client.ts +663 -0
- package/src/frontend/hmr.ts +728 -0
- package/src/frontend/index.ts +342 -0
- package/src/frontend/islands.ts +552 -0
- package/src/frontend/isr.ts +555 -0
- package/src/frontend/layout.ts +475 -0
- package/src/frontend/ssr/react.ts +446 -0
- package/src/frontend/ssr/solid.ts +523 -0
- package/src/frontend/ssr/svelte.ts +546 -0
- package/src/frontend/ssr/vue.ts +504 -0
- package/src/frontend/ssr.ts +699 -0
- package/src/frontend/types.ts +2274 -0
- package/src/health/index.ts +604 -0
- package/src/index.ts +410 -0
- package/src/lock/index.ts +587 -0
- package/src/logger/index.ts +444 -0
- package/src/logger/transports/index.ts +969 -0
- package/src/metrics/index.ts +494 -0
- package/src/middleware/built-in.ts +360 -0
- package/src/middleware/index.ts +94 -0
- package/src/modules/filters.ts +458 -0
- package/src/modules/guards.ts +405 -0
- package/src/modules/index.ts +1256 -0
- package/src/modules/interceptors.ts +574 -0
- package/src/modules/lazy.ts +418 -0
- package/src/modules/lifecycle.ts +478 -0
- package/src/modules/metadata.ts +90 -0
- package/src/modules/pipes.ts +626 -0
- package/src/router/index.ts +339 -0
- package/src/router/linear.ts +371 -0
- package/src/router/regex.ts +292 -0
- package/src/router/tree.ts +562 -0
- package/src/rpc/index.ts +1263 -0
- package/src/security/index.ts +436 -0
- package/src/ssg/index.ts +631 -0
- package/src/storage/index.ts +456 -0
- package/src/telemetry/index.ts +1097 -0
- package/src/testing/index.ts +1586 -0
- package/src/types/index.ts +236 -0
- package/src/types/optional-deps.d.ts +219 -0
- package/src/validation/index.ts +276 -0
- package/src/websocket/index.ts +1004 -0
- package/tests/integration/cli.test.ts +1016 -0
- package/tests/integration/fullstack.test.ts +234 -0
- package/tests/unit/cache.test.ts +174 -0
- package/tests/unit/cli-commands.test.ts +892 -0
- package/tests/unit/cli.test.ts +1258 -0
- package/tests/unit/container.test.ts +279 -0
- package/tests/unit/context.test.ts +221 -0
- package/tests/unit/database.test.ts +183 -0
- package/tests/unit/linear-router.test.ts +280 -0
- package/tests/unit/lock.test.ts +336 -0
- package/tests/unit/middleware.test.ts +184 -0
- package/tests/unit/modules.test.ts +142 -0
- package/tests/unit/pubsub.test.ts +257 -0
- package/tests/unit/regex-router.test.ts +265 -0
- package/tests/unit/router.test.ts +373 -0
- package/tests/unit/rpc.test.ts +1248 -0
- package/tests/unit/security.test.ts +174 -0
- package/tests/unit/telemetry.test.ts +371 -0
- package/tests/unit/test-cache.test.ts +110 -0
- package/tests/unit/test-database.test.ts +282 -0
- package/tests/unit/tree-router.test.ts +325 -0
- package/tests/unit/validation.test.ts +794 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import {
|
|
4
|
+
Router,
|
|
5
|
+
Context,
|
|
6
|
+
compose,
|
|
7
|
+
createValidator,
|
|
8
|
+
Password,
|
|
9
|
+
JWT,
|
|
10
|
+
createCache,
|
|
11
|
+
Database,
|
|
12
|
+
createServer,
|
|
13
|
+
} from '../../src';
|
|
14
|
+
|
|
15
|
+
// ============= Integration Tests =============
|
|
16
|
+
|
|
17
|
+
describe('Integration Tests', () => {
|
|
18
|
+
describe('Full Stack Application', () => {
|
|
19
|
+
test('should handle basic routing', async () => {
|
|
20
|
+
const router = new Router();
|
|
21
|
+
|
|
22
|
+
router.get('/users', (ctx) => ctx.json([{ id: 1, name: 'John' }]));
|
|
23
|
+
router.post('/users', async (ctx) => {
|
|
24
|
+
const body = await ctx.body<{ name: string }>();
|
|
25
|
+
return ctx.status(201).json({ id: 1, name: body?.name });
|
|
26
|
+
});
|
|
27
|
+
router.get('/users/:id', (ctx) => ctx.json({ id: parseInt(ctx.params.id), name: 'User' }));
|
|
28
|
+
|
|
29
|
+
// Test GET /users
|
|
30
|
+
const request1 = new Request('http://localhost:3000/users');
|
|
31
|
+
const match1 = router.match('GET', '/users');
|
|
32
|
+
expect(match1).toBeDefined();
|
|
33
|
+
const ctx1 = new Context(request1, match1!.params);
|
|
34
|
+
const response1 = await match1!.handler(ctx1);
|
|
35
|
+
expect(response1.status).toBe(200);
|
|
36
|
+
const users = await response1.json();
|
|
37
|
+
expect(Array.isArray(users)).toBe(true);
|
|
38
|
+
|
|
39
|
+
// Test POST /users
|
|
40
|
+
const request2 = new Request('http://localhost:3000/users', {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: { 'Content-Type': 'application/json' },
|
|
43
|
+
body: JSON.stringify({ name: 'Alice' }),
|
|
44
|
+
});
|
|
45
|
+
const match2 = router.match('POST', '/users');
|
|
46
|
+
expect(match2).toBeDefined();
|
|
47
|
+
const ctx2 = new Context(request2, match2!.params);
|
|
48
|
+
const response2 = await match2!.handler(ctx2);
|
|
49
|
+
expect(response2.status).toBe(201);
|
|
50
|
+
|
|
51
|
+
// Test GET /users/:id
|
|
52
|
+
const request3 = new Request('http://localhost:3000/users/123');
|
|
53
|
+
const match3 = router.match('GET', '/users/123');
|
|
54
|
+
expect(match3).toBeDefined();
|
|
55
|
+
const ctx3 = new Context(request3, match3!.params);
|
|
56
|
+
const response3 = await match3!.handler(ctx3);
|
|
57
|
+
const user = await response3.json();
|
|
58
|
+
expect(user.id).toBe(123);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('should handle 404 for missing routes', async () => {
|
|
62
|
+
const router = new Router();
|
|
63
|
+
router.get('/users', (ctx) => ctx.json([]));
|
|
64
|
+
|
|
65
|
+
const match = router.match('GET', '/nonexistent');
|
|
66
|
+
expect(match).toBeUndefined();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('Middleware Integration', () => {
|
|
71
|
+
test('should apply middleware to routes', async () => {
|
|
72
|
+
const router = new Router();
|
|
73
|
+
const order: string[] = [];
|
|
74
|
+
|
|
75
|
+
const middleware1 = async (ctx: Context, next: () => Promise<Response>) => {
|
|
76
|
+
order.push('before1');
|
|
77
|
+
const response = await next();
|
|
78
|
+
order.push('after1');
|
|
79
|
+
return response;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const middleware2 = async (ctx: Context, next: () => Promise<Response>) => {
|
|
83
|
+
order.push('before2');
|
|
84
|
+
const response = await next();
|
|
85
|
+
order.push('after2');
|
|
86
|
+
return response;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
router.get('/test', (ctx) => {
|
|
90
|
+
order.push('handler');
|
|
91
|
+
return ctx.json({ ok: true });
|
|
92
|
+
}, { middleware: [middleware1, middleware2] });
|
|
93
|
+
|
|
94
|
+
const match = router.match('GET', '/test');
|
|
95
|
+
expect(match).toBeDefined();
|
|
96
|
+
|
|
97
|
+
if (match && match.middleware) {
|
|
98
|
+
const request = new Request('http://localhost:3000/test');
|
|
99
|
+
const context = new Context(request, match.params);
|
|
100
|
+
const pipeline = compose(match.middleware);
|
|
101
|
+
await pipeline(context, match.handler);
|
|
102
|
+
|
|
103
|
+
expect(order).toEqual(['before1', 'before2', 'handler', 'after2', 'after1']);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('Validation Integration', () => {
|
|
109
|
+
test('should validate request body', async () => {
|
|
110
|
+
const schema = z.object({
|
|
111
|
+
email: z.string().email(),
|
|
112
|
+
age: z.number().min(0),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const validator = createValidator({ body: schema });
|
|
116
|
+
|
|
117
|
+
// Valid request
|
|
118
|
+
const validRequest = new Request('http://localhost:3000/test', {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: { 'Content-Type': 'application/json' },
|
|
121
|
+
body: JSON.stringify({ email: 'test@example.com', age: 25 }),
|
|
122
|
+
});
|
|
123
|
+
const validCtx = new Context(validRequest, {});
|
|
124
|
+
let nextCalled = false;
|
|
125
|
+
const validResponse = await validator(validCtx, async () => {
|
|
126
|
+
nextCalled = true;
|
|
127
|
+
return new Response('OK');
|
|
128
|
+
});
|
|
129
|
+
expect(nextCalled).toBe(true);
|
|
130
|
+
|
|
131
|
+
// Invalid request
|
|
132
|
+
const invalidRequest = new Request('http://localhost:3000/test', {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: { 'Content-Type': 'application/json' },
|
|
135
|
+
body: JSON.stringify({ email: 'invalid', age: -5 }),
|
|
136
|
+
});
|
|
137
|
+
const invalidCtx = new Context(invalidRequest, {});
|
|
138
|
+
const invalidResponse = await validator(invalidCtx, async () => new Response('OK'));
|
|
139
|
+
expect(invalidResponse.status).toBe(400);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('Database Integration', () => {
|
|
144
|
+
let db: Database;
|
|
145
|
+
|
|
146
|
+
beforeAll(async () => {
|
|
147
|
+
db = new Database({ url: ':memory:' });
|
|
148
|
+
await db.connect();
|
|
149
|
+
await db.raw(`
|
|
150
|
+
CREATE TABLE test_users (
|
|
151
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
152
|
+
name TEXT NOT NULL,
|
|
153
|
+
email TEXT UNIQUE
|
|
154
|
+
)
|
|
155
|
+
`);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
afterAll(async () => {
|
|
159
|
+
await db.close();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('should perform CRUD operations', async () => {
|
|
163
|
+
// Create
|
|
164
|
+
await db.raw("INSERT INTO test_users (name, email) VALUES ($1, $2)", ['Test User', 'test@example.com']);
|
|
165
|
+
|
|
166
|
+
// Read
|
|
167
|
+
const user = await db.queryOne<{ id: number; name: string; email: string }>`
|
|
168
|
+
SELECT * FROM test_users WHERE email = ${'test@example.com'}
|
|
169
|
+
`;
|
|
170
|
+
expect(user?.name).toBe('Test User');
|
|
171
|
+
|
|
172
|
+
// Update
|
|
173
|
+
await db.raw("UPDATE test_users SET name = $1 WHERE email = $2", ['Updated', 'test@example.com']);
|
|
174
|
+
const updated = await db.queryOne<{ name: string }>`
|
|
175
|
+
SELECT name FROM test_users WHERE email = ${'test@example.com'}
|
|
176
|
+
`;
|
|
177
|
+
expect(updated?.name).toBe('Updated');
|
|
178
|
+
|
|
179
|
+
// Delete
|
|
180
|
+
await db.raw("DELETE FROM test_users WHERE email = $1", ['test@example.com']);
|
|
181
|
+
const deleted = await db.queryOne`
|
|
182
|
+
SELECT * FROM test_users WHERE email = ${'test@example.com'}
|
|
183
|
+
`;
|
|
184
|
+
expect(deleted).toBeNull();
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('Security Integration', () => {
|
|
189
|
+
test('should hash and verify passwords', async () => {
|
|
190
|
+
const password = 'mySecurePassword123';
|
|
191
|
+
const hash = await Password.hash(password);
|
|
192
|
+
|
|
193
|
+
expect(await Password.verify(password, hash)).toBe(true);
|
|
194
|
+
expect(await Password.verify('wrongPassword', hash)).toBe(false);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('should sign and verify JWTs', async () => {
|
|
198
|
+
const jwt = new JWT('test-secret', { expiresIn: '1h' });
|
|
199
|
+
const payload = { userId: 123, role: 'admin' };
|
|
200
|
+
|
|
201
|
+
const token = await jwt.sign(payload);
|
|
202
|
+
const decoded = await jwt.verify(token);
|
|
203
|
+
|
|
204
|
+
expect(decoded?.userId).toBe(123);
|
|
205
|
+
expect(decoded?.role).toBe('admin');
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('Cache Integration', () => {
|
|
210
|
+
test('should cache values with TTL', async () => {
|
|
211
|
+
const cache = createCache({ driver: 'memory', ttl: 1 });
|
|
212
|
+
await cache.connect();
|
|
213
|
+
|
|
214
|
+
// Cache miss
|
|
215
|
+
let callCount = 0;
|
|
216
|
+
const value = await cache.getOrSet('key1', async () => {
|
|
217
|
+
callCount++;
|
|
218
|
+
return 'computed';
|
|
219
|
+
});
|
|
220
|
+
expect(value).toBe('computed');
|
|
221
|
+
expect(callCount).toBe(1);
|
|
222
|
+
|
|
223
|
+
// Cache hit
|
|
224
|
+
const cached = await cache.getOrSet('key1', async () => {
|
|
225
|
+
callCount++;
|
|
226
|
+
return 'computed again';
|
|
227
|
+
});
|
|
228
|
+
expect(cached).toBe('computed');
|
|
229
|
+
expect(callCount).toBe(1); // Factory not called again
|
|
230
|
+
|
|
231
|
+
await cache.disconnect();
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
});
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
|
|
2
|
+
import { Cache, createCache, SessionStore } from '../../src/cache';
|
|
3
|
+
|
|
4
|
+
describe('Cache', () => {
|
|
5
|
+
// Skip Redis tests if not available, use in-memory for testing
|
|
6
|
+
let cache: Cache;
|
|
7
|
+
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
// Use in-memory cache for testing
|
|
10
|
+
cache = createCache({
|
|
11
|
+
driver: 'memory',
|
|
12
|
+
ttl: 1, // 1 second for testing
|
|
13
|
+
});
|
|
14
|
+
await cache.connect();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(async () => {
|
|
18
|
+
await cache.disconnect();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('Basic Operations', () => {
|
|
22
|
+
test('should set and get value', async () => {
|
|
23
|
+
await cache.set('test-key', 'test-value');
|
|
24
|
+
const value = await cache.get('test-key');
|
|
25
|
+
|
|
26
|
+
expect(value).toBe('test-value');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('should return null for missing key', async () => {
|
|
30
|
+
const value = await cache.get('nonexistent');
|
|
31
|
+
expect(value).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('should delete value', async () => {
|
|
35
|
+
await cache.set('test-key', 'test-value');
|
|
36
|
+
await cache.delete('test-key');
|
|
37
|
+
|
|
38
|
+
const value = await cache.get('test-key');
|
|
39
|
+
expect(value).toBeNull();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('should check if key exists', async () => {
|
|
43
|
+
await cache.set('test-key', 'test-value');
|
|
44
|
+
|
|
45
|
+
expect(await cache.has('test-key')).toBe(true);
|
|
46
|
+
expect(await cache.has('nonexistent')).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('should set with TTL', async () => {
|
|
50
|
+
await cache.set('ttl-key', 'value', 0.5); // 0.5 seconds
|
|
51
|
+
|
|
52
|
+
expect(await cache.get('ttl-key')).toBe('value');
|
|
53
|
+
|
|
54
|
+
// Wait for TTL to expire
|
|
55
|
+
await new Promise(resolve => setTimeout(resolve, 600));
|
|
56
|
+
|
|
57
|
+
expect(await cache.get('ttl-key')).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Advanced Operations', () => {
|
|
62
|
+
test('should increment value', async () => {
|
|
63
|
+
await cache.set('counter', 0);
|
|
64
|
+
const newValue = await cache.increment('counter', 1);
|
|
65
|
+
|
|
66
|
+
expect(newValue).toBe(1);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('should decrement value', async () => {
|
|
70
|
+
await cache.set('counter', 10);
|
|
71
|
+
const newValue = await cache.decrement('counter', 3);
|
|
72
|
+
|
|
73
|
+
expect(newValue).toBe(7);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('should set multiple values', async () => {
|
|
77
|
+
await cache.mset({
|
|
78
|
+
'key1': 'value1',
|
|
79
|
+
'key2': 'value2',
|
|
80
|
+
'key3': 'value3',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(await cache.get('key1')).toBe('value1');
|
|
84
|
+
expect(await cache.get('key2')).toBe('value2');
|
|
85
|
+
expect(await cache.get('key3')).toBe('value3');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('should get multiple values', async () => {
|
|
89
|
+
await cache.set('key1', 'value1');
|
|
90
|
+
await cache.set('key2', 'value2');
|
|
91
|
+
|
|
92
|
+
const values = await cache.mget(['key1', 'key2', 'key3']);
|
|
93
|
+
|
|
94
|
+
expect(values).toEqual(['value1', 'value2', null]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('should clear all keys', async () => {
|
|
98
|
+
await cache.set('key1', 'value1');
|
|
99
|
+
await cache.set('key2', 'value2');
|
|
100
|
+
|
|
101
|
+
await cache.clear();
|
|
102
|
+
|
|
103
|
+
expect(await cache.get('key1')).toBeNull();
|
|
104
|
+
expect(await cache.get('key2')).toBeNull();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('JSON Operations', () => {
|
|
109
|
+
test('should store and retrieve JSON objects', async () => {
|
|
110
|
+
const obj = { name: 'John', age: 30 };
|
|
111
|
+
await cache.set('user:1', obj);
|
|
112
|
+
|
|
113
|
+
const retrieved = await cache.get<{ name: string; age: number }>('user:1');
|
|
114
|
+
|
|
115
|
+
expect(retrieved).toEqual(obj);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('should store arrays', async () => {
|
|
119
|
+
const arr = [1, 2, 3, 4, 5];
|
|
120
|
+
await cache.set('array', arr);
|
|
121
|
+
|
|
122
|
+
const retrieved = await cache.get<number[]>('array');
|
|
123
|
+
|
|
124
|
+
expect(retrieved).toEqual(arr);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('SessionStore', () => {
|
|
130
|
+
let store: SessionStore;
|
|
131
|
+
|
|
132
|
+
beforeEach(async () => {
|
|
133
|
+
store = new SessionStore({
|
|
134
|
+
ttl: 1, // 1 second
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('should create session', async () => {
|
|
139
|
+
const sessionId = await store.create({ userId: 1, name: 'John' });
|
|
140
|
+
|
|
141
|
+
expect(sessionId).toBeDefined();
|
|
142
|
+
expect(typeof sessionId).toBe('string');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('should get session', async () => {
|
|
146
|
+
const sessionId = await store.create({ userId: 1 });
|
|
147
|
+
const session = await store.get(sessionId);
|
|
148
|
+
|
|
149
|
+
expect(session).toBeDefined();
|
|
150
|
+
expect(session?.userId).toBe(1);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('should update session', async () => {
|
|
154
|
+
const sessionId = await store.create({ userId: 1 });
|
|
155
|
+
await store.update(sessionId, { userId: 2, name: 'Jane' });
|
|
156
|
+
|
|
157
|
+
const session = await store.get(sessionId);
|
|
158
|
+
expect(session?.userId).toBe(2);
|
|
159
|
+
expect(session?.name).toBe('Jane');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('should delete session', async () => {
|
|
163
|
+
const sessionId = await store.create({ userId: 1 });
|
|
164
|
+
await store.delete(sessionId);
|
|
165
|
+
|
|
166
|
+
const session = await store.get(sessionId);
|
|
167
|
+
expect(session).toBeNull();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('should return null for invalid session', async () => {
|
|
171
|
+
const session = await store.get('invalid-session-id');
|
|
172
|
+
expect(session).toBeNull();
|
|
173
|
+
});
|
|
174
|
+
});
|