@fragno-dev/test 0.1.12 → 0.1.13
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/.turbo/turbo-build.log +11 -7
- package/CHANGELOG.md +27 -0
- package/dist/db-test.d.ts +129 -0
- package/dist/db-test.d.ts.map +1 -0
- package/dist/db-test.js +214 -0
- package/dist/db-test.js.map +1 -0
- package/dist/index.d.ts +5 -82
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -143
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
- package/src/db-test.test.ts +352 -0
- package/src/db-test.ts +574 -0
- package/src/index.test.ts +94 -94
- package/src/index.ts +10 -398
package/src/index.test.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { describe, expect, expectTypeOf, it } from "vitest";
|
|
2
2
|
import { column, idColumn, schema } from "@fragno-dev/db/schema";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
3
|
+
import { withDatabase } from "@fragno-dev/db";
|
|
4
|
+
import { defineFragment } from "@fragno-dev/core";
|
|
5
|
+
import { instantiate } from "@fragno-dev/core";
|
|
6
|
+
import { buildDatabaseFragmentsTest } from "./db-test";
|
|
7
|
+
import type { ExtractFragmentServices } from "@fragno-dev/core/route";
|
|
6
8
|
|
|
7
9
|
// Test schema with multiple versions
|
|
8
10
|
const testSchema = schema((s) => {
|
|
@@ -20,31 +22,32 @@ const testSchema = schema((s) => {
|
|
|
20
22
|
});
|
|
21
23
|
|
|
22
24
|
// Test fragment definition
|
|
23
|
-
const testFragmentDef =
|
|
24
|
-
.withDatabase(testSchema)
|
|
25
|
-
.
|
|
25
|
+
const testFragmentDef = defineFragment<{}>("test-fragment")
|
|
26
|
+
.extend(withDatabase(testSchema))
|
|
27
|
+
.providesBaseService(({ deps }) => {
|
|
26
28
|
return {
|
|
27
29
|
createUser: async (data: { name: string; email: string; age?: number | null }) => {
|
|
28
|
-
const id = await db.create("users", data);
|
|
30
|
+
const id = await deps.db.create("users", data);
|
|
29
31
|
return { ...data, id: id.valueOf() };
|
|
30
32
|
},
|
|
31
33
|
getUsers: async () => {
|
|
32
|
-
const users = await db.find("users", (b) =>
|
|
34
|
+
const users = await deps.db.find("users", (b) =>
|
|
33
35
|
b.whereIndex("idx_users_all", (eb) => eb("id", "!=", "")),
|
|
34
36
|
);
|
|
35
37
|
return users.map((u) => ({ ...u, id: u.id.valueOf() }));
|
|
36
38
|
},
|
|
37
39
|
};
|
|
38
|
-
})
|
|
40
|
+
})
|
|
41
|
+
.build();
|
|
39
42
|
|
|
40
|
-
describe("
|
|
43
|
+
describe("buildDatabaseFragmentsTest", () => {
|
|
41
44
|
it("should create and use a database fragment", async () => {
|
|
42
|
-
const {
|
|
43
|
-
{
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const { fragments, test } = await buildDatabaseFragmentsTest()
|
|
46
|
+
.withTestAdapter({ type: "kysely-sqlite" })
|
|
47
|
+
.withFragment("test", instantiate(testFragmentDef).withConfig({}).withRoutes([]))
|
|
48
|
+
.build();
|
|
49
|
+
|
|
50
|
+
const fragment = fragments.test;
|
|
48
51
|
|
|
49
52
|
// Should be able to create and query users
|
|
50
53
|
const user = await fragment.services.createUser({
|
|
@@ -68,32 +71,25 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
68
71
|
});
|
|
69
72
|
|
|
70
73
|
it("should throw error for non-database fragment", async () => {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
additionalContext: {},
|
|
75
|
-
},
|
|
76
|
-
$requiredOptions: {},
|
|
77
|
-
};
|
|
74
|
+
const nonDbFragmentDef = defineFragment<{}>("non-db-fragment")
|
|
75
|
+
.providesBaseService(() => ({}))
|
|
76
|
+
.build();
|
|
78
77
|
|
|
79
78
|
await expect(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
adapter: { type: "kysely-sqlite" },
|
|
85
|
-
},
|
|
86
|
-
),
|
|
79
|
+
buildDatabaseFragmentsTest()
|
|
80
|
+
.withTestAdapter({ type: "kysely-sqlite" })
|
|
81
|
+
.withFragment("nonDb", instantiate(nonDbFragmentDef).withConfig({}).withRoutes([]))
|
|
82
|
+
.build(),
|
|
87
83
|
).rejects.toThrow("Fragment 'non-db-fragment' does not have a database schema");
|
|
88
84
|
});
|
|
89
85
|
|
|
90
86
|
it("should reset database by truncating tables", async () => {
|
|
91
|
-
const {
|
|
92
|
-
{
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
87
|
+
const { fragments, test } = await buildDatabaseFragmentsTest()
|
|
88
|
+
.withTestAdapter({ type: "kysely-sqlite" })
|
|
89
|
+
.withFragment("test", instantiate(testFragmentDef).withConfig({}).withRoutes([]))
|
|
90
|
+
.build();
|
|
91
|
+
|
|
92
|
+
const fragment = fragments.test;
|
|
97
93
|
|
|
98
94
|
// Create some users
|
|
99
95
|
await fragment.services.createUser({
|
|
@@ -118,12 +114,12 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
118
114
|
});
|
|
119
115
|
|
|
120
116
|
it("should expose db property for direct ORM queries", async () => {
|
|
121
|
-
const {
|
|
122
|
-
{
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
117
|
+
const { fragments, test } = await buildDatabaseFragmentsTest()
|
|
118
|
+
.withTestAdapter({ type: "kysely-sqlite" })
|
|
119
|
+
.withFragment("test", instantiate(testFragmentDef).withConfig({}).withRoutes([]))
|
|
120
|
+
.build();
|
|
121
|
+
|
|
122
|
+
const fragment = fragments.test;
|
|
127
123
|
|
|
128
124
|
// Test creating a record directly using test.db
|
|
129
125
|
const userId = await fragment.db.create("users", {
|
|
@@ -171,29 +167,30 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
171
167
|
});
|
|
172
168
|
});
|
|
173
169
|
|
|
174
|
-
const authFragmentDef =
|
|
175
|
-
.withDatabase(authSchema)
|
|
176
|
-
.
|
|
170
|
+
const authFragmentDef = defineFragment<{}>("auth-test")
|
|
171
|
+
.extend(withDatabase(authSchema))
|
|
172
|
+
.providesBaseService(({ deps }) => {
|
|
177
173
|
return {
|
|
178
174
|
createUser: async (email: string, passwordHash: string) => {
|
|
179
|
-
const id = await db.create("user", { email, passwordHash });
|
|
175
|
+
const id = await deps.db.create("user", { email, passwordHash });
|
|
180
176
|
return { id: id.valueOf(), email, passwordHash };
|
|
181
177
|
},
|
|
182
178
|
createSession: async (userId: string) => {
|
|
183
179
|
const expiresAt = new Date();
|
|
184
180
|
expiresAt.setDate(expiresAt.getDate() + 30);
|
|
185
|
-
const id = await db.create("session", { userId, expiresAt });
|
|
181
|
+
const id = await deps.db.create("session", { userId, expiresAt });
|
|
186
182
|
return { id: id.valueOf(), userId, expiresAt };
|
|
187
183
|
},
|
|
188
184
|
};
|
|
189
|
-
})
|
|
185
|
+
})
|
|
186
|
+
.build();
|
|
190
187
|
|
|
191
|
-
const {
|
|
192
|
-
{
|
|
193
|
-
{
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
188
|
+
const { fragments, test } = await buildDatabaseFragmentsTest()
|
|
189
|
+
.withTestAdapter({ type: "kysely-sqlite" })
|
|
190
|
+
.withFragment("auth", instantiate(authFragmentDef).withConfig({}).withRoutes([]))
|
|
191
|
+
.build();
|
|
192
|
+
|
|
193
|
+
const fragment = fragments.auth;
|
|
197
194
|
|
|
198
195
|
// Create a user
|
|
199
196
|
const user = await fragment.services.createUser("test@test.com", "hashed-password");
|
|
@@ -215,7 +212,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
215
212
|
});
|
|
216
213
|
});
|
|
217
214
|
|
|
218
|
-
describe("
|
|
215
|
+
describe("multi-fragment tests", () => {
|
|
219
216
|
// Create two different schemas
|
|
220
217
|
const userSchema = schema((s) => {
|
|
221
218
|
return s.addTable("user", (t) => {
|
|
@@ -237,39 +234,41 @@ describe("createDatabaseFragmentsForTest", () => {
|
|
|
237
234
|
});
|
|
238
235
|
});
|
|
239
236
|
|
|
240
|
-
const userFragmentDef =
|
|
241
|
-
.withDatabase(userSchema)
|
|
242
|
-
.
|
|
237
|
+
const userFragmentDef = defineFragment<{}>("user-fragment")
|
|
238
|
+
.extend(withDatabase(userSchema))
|
|
239
|
+
.providesBaseService(({ deps }) => {
|
|
243
240
|
return {
|
|
244
241
|
createUser: async (data: { name: string; email: string }) => {
|
|
245
|
-
const id = await db.create("user", data);
|
|
242
|
+
const id = await deps.db.create("user", data);
|
|
246
243
|
return { ...data, id: id.valueOf() };
|
|
247
244
|
},
|
|
248
245
|
getUsers: async () => {
|
|
249
|
-
const users = await db.find("user", (b) =>
|
|
246
|
+
const users = await deps.db.find("user", (b) =>
|
|
250
247
|
b.whereIndex("idx_user_all", (eb) => eb("id", "!=", "")),
|
|
251
248
|
);
|
|
252
249
|
return users.map((u) => ({ ...u, id: u.id.valueOf() }));
|
|
253
250
|
},
|
|
254
251
|
};
|
|
255
|
-
})
|
|
252
|
+
})
|
|
253
|
+
.build();
|
|
256
254
|
|
|
257
|
-
const postFragmentDef =
|
|
258
|
-
.withDatabase(postSchema)
|
|
259
|
-
.
|
|
255
|
+
const postFragmentDef = defineFragment<{}>("post-fragment")
|
|
256
|
+
.extend(withDatabase(postSchema))
|
|
257
|
+
.providesBaseService(({ deps }) => {
|
|
260
258
|
return {
|
|
261
259
|
createPost: async (data: { title: string; userId: string }) => {
|
|
262
|
-
const id = await db.create("post", data);
|
|
260
|
+
const id = await deps.db.create("post", data);
|
|
263
261
|
return { ...data, id: id.valueOf() };
|
|
264
262
|
},
|
|
265
263
|
getPosts: async () => {
|
|
266
|
-
const posts = await db.find("post", (b) =>
|
|
264
|
+
const posts = await deps.db.find("post", (b) =>
|
|
267
265
|
b.whereIndex("idx_post_all", (eb) => eb("id", "!=", "")),
|
|
268
266
|
);
|
|
269
267
|
return posts.map((p) => ({ ...p, id: p.id.valueOf() }));
|
|
270
268
|
},
|
|
271
269
|
};
|
|
272
|
-
})
|
|
270
|
+
})
|
|
271
|
+
.build();
|
|
273
272
|
|
|
274
273
|
const adapters = [
|
|
275
274
|
{ name: "Kysely SQLite", adapter: { type: "kysely-sqlite" as const } },
|
|
@@ -279,19 +278,15 @@ describe("createDatabaseFragmentsForTest", () => {
|
|
|
279
278
|
|
|
280
279
|
for (const { name, adapter } of adapters) {
|
|
281
280
|
it(`should allow multiple fragments to share the same database adapter - ${name}`, async () => {
|
|
282
|
-
// Create both fragments with shared adapter
|
|
283
|
-
const { fragments, test } = await
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
{ adapter },
|
|
289
|
-
);
|
|
290
|
-
|
|
291
|
-
const [userFragment, postFragment] = fragments;
|
|
281
|
+
// Create both fragments with shared adapter
|
|
282
|
+
const { fragments, test } = await buildDatabaseFragmentsTest()
|
|
283
|
+
.withTestAdapter(adapter)
|
|
284
|
+
.withFragment("user", instantiate(userFragmentDef).withConfig({}).withRoutes([]))
|
|
285
|
+
.withFragment("post", instantiate(postFragmentDef).withConfig({}).withRoutes([]))
|
|
286
|
+
.build();
|
|
292
287
|
|
|
293
288
|
// Create a user
|
|
294
|
-
const user = await
|
|
289
|
+
const user = await fragments.user.services.createUser({
|
|
295
290
|
name: "John Doe",
|
|
296
291
|
email: "john@example.com",
|
|
297
292
|
});
|
|
@@ -303,7 +298,7 @@ describe("createDatabaseFragmentsForTest", () => {
|
|
|
303
298
|
});
|
|
304
299
|
|
|
305
300
|
// Create a post with the user's ID
|
|
306
|
-
const post = await
|
|
301
|
+
const post = await fragments.post.services.createPost({
|
|
307
302
|
title: "My First Post",
|
|
308
303
|
userId: user.id,
|
|
309
304
|
});
|
|
@@ -315,10 +310,10 @@ describe("createDatabaseFragmentsForTest", () => {
|
|
|
315
310
|
});
|
|
316
311
|
|
|
317
312
|
// Verify data exists
|
|
318
|
-
const users = await
|
|
313
|
+
const users = await fragments.user.services.getUsers();
|
|
319
314
|
expect(users).toHaveLength(1);
|
|
320
315
|
|
|
321
|
-
const posts = await
|
|
316
|
+
const posts = await fragments.post.services.getPosts();
|
|
322
317
|
expect(posts).toHaveLength(1);
|
|
323
318
|
expect(posts[0]!.userId).toBe(user.id);
|
|
324
319
|
|
|
@@ -329,7 +324,7 @@ describe("createDatabaseFragmentsForTest", () => {
|
|
|
329
324
|
});
|
|
330
325
|
|
|
331
326
|
describe("ExtractFragmentServices", () => {
|
|
332
|
-
it("extracts provided services from database fragment with
|
|
327
|
+
it("extracts provided services from database fragment with new API", () => {
|
|
333
328
|
const testSchema = schema((s) => s);
|
|
334
329
|
|
|
335
330
|
interface ITestService {
|
|
@@ -337,12 +332,16 @@ describe("ExtractFragmentServices", () => {
|
|
|
337
332
|
doSomethingElse: (input: number) => Promise<number>;
|
|
338
333
|
}
|
|
339
334
|
|
|
340
|
-
const fragment =
|
|
341
|
-
.withDatabase(testSchema)
|
|
342
|
-
.providesService(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
335
|
+
const fragment = defineFragment<{}>("test-db-fragment")
|
|
336
|
+
.extend(withDatabase(testSchema))
|
|
337
|
+
.providesService(
|
|
338
|
+
"test",
|
|
339
|
+
(): ITestService => ({
|
|
340
|
+
doSomething: async (input: string) => input.toUpperCase(),
|
|
341
|
+
doSomethingElse: async (input: number) => input * 2,
|
|
342
|
+
}),
|
|
343
|
+
)
|
|
344
|
+
.build();
|
|
346
345
|
|
|
347
346
|
type Services = ExtractFragmentServices<typeof fragment>;
|
|
348
347
|
|
|
@@ -352,21 +351,22 @@ describe("ExtractFragmentServices", () => {
|
|
|
352
351
|
}>();
|
|
353
352
|
});
|
|
354
353
|
|
|
355
|
-
it("merges
|
|
354
|
+
it("merges base services and provided services in database fragment", () => {
|
|
356
355
|
const testSchema = schema((s) => s);
|
|
357
356
|
|
|
358
|
-
const fragment =
|
|
359
|
-
.withDatabase(testSchema)
|
|
360
|
-
.
|
|
357
|
+
const fragment = defineFragment<{}>("test-db-fragment")
|
|
358
|
+
.extend(withDatabase(testSchema))
|
|
359
|
+
.providesBaseService(() => ({
|
|
361
360
|
internalService: async () => "internal",
|
|
362
361
|
}))
|
|
363
362
|
.providesService("externalService", () => ({
|
|
364
363
|
publicMethod: async () => "public",
|
|
365
|
-
}))
|
|
364
|
+
}))
|
|
365
|
+
.build();
|
|
366
366
|
|
|
367
367
|
type Services = ExtractFragmentServices<typeof fragment>;
|
|
368
368
|
|
|
369
|
-
// Should include both
|
|
369
|
+
// Should include both base services and provided services
|
|
370
370
|
expectTypeOf<Services>().toMatchObjectType<{
|
|
371
371
|
internalService: () => Promise<string>;
|
|
372
372
|
externalService: {
|