@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/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 { defineFragmentWithDatabase } from "@fragno-dev/db/fragment";
4
- import { createDatabaseFragmentForTest, createDatabaseFragmentsForTest } from "./index";
5
- import type { ExtractFragmentServices } from "@fragno-dev/core/api/route";
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 = defineFragmentWithDatabase<{}>("test-fragment")
24
- .withDatabase(testSchema)
25
- .providesService(({ db }) => {
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("createDatabaseFragmentForTest", () => {
43
+ describe("buildDatabaseFragmentsTest", () => {
41
44
  it("should create and use a database fragment", async () => {
42
- const { fragment, test } = await createDatabaseFragmentForTest(
43
- { definition: testFragmentDef, routes: [] },
44
- {
45
- adapter: { type: "kysely-sqlite" },
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 nonDbFragment = {
72
- definition: {
73
- name: "non-db-fragment",
74
- additionalContext: {},
75
- },
76
- $requiredOptions: {},
77
- };
74
+ const nonDbFragmentDef = defineFragment<{}>("non-db-fragment")
75
+ .providesBaseService(() => ({}))
76
+ .build();
78
77
 
79
78
  await expect(
80
- createDatabaseFragmentForTest(
81
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
- { definition: nonDbFragment as any, routes: [] },
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 { fragment, test } = await createDatabaseFragmentForTest(
92
- { definition: testFragmentDef, routes: [] },
93
- {
94
- adapter: { type: "kysely-sqlite" },
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 { fragment, test } = await createDatabaseFragmentForTest(
122
- { definition: testFragmentDef, routes: [] },
123
- {
124
- adapter: { type: "kysely-sqlite" },
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 = defineFragmentWithDatabase<{}>("auth-test")
175
- .withDatabase(authSchema)
176
- .providesService(({ db }) => {
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 { fragment, test } = await createDatabaseFragmentForTest(
192
- { definition: authFragmentDef, routes: [] },
193
- {
194
- adapter: { type: "kysely-sqlite" },
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("createDatabaseFragmentsForTest", () => {
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 = defineFragmentWithDatabase<{}>("user-fragment")
241
- .withDatabase(userSchema)
242
- .providesService(({ db }) => {
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 = defineFragmentWithDatabase<{}>("post-fragment")
258
- .withDatabase(postSchema)
259
- .providesService(({ db }) => {
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 using array input
283
- const { fragments, test } = await createDatabaseFragmentsForTest(
284
- [
285
- { definition: userFragmentDef, routes: [] },
286
- { definition: postFragmentDef, routes: [] },
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 userFragment!.services.createUser({
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 postFragment!.services.createPost({
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 userFragment!.services.getUsers();
313
+ const users = await fragments.user.services.getUsers();
319
314
  expect(users).toHaveLength(1);
320
315
 
321
- const posts = await postFragment!.services.getPosts();
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 .providesService()", () => {
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 = defineFragmentWithDatabase<{}>("test-db-fragment")
341
- .withDatabase(testSchema)
342
- .providesService("test", {
343
- doSomething: async (input: string) => input.toUpperCase(),
344
- doSomethingElse: async (input: number) => input * 2,
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 unnamed .providesService() and named .providesService() in database fragment", () => {
354
+ it("merges base services and provided services in database fragment", () => {
356
355
  const testSchema = schema((s) => s);
357
356
 
358
- const fragment = defineFragmentWithDatabase<{}>("test-db-fragment")
359
- .withDatabase(testSchema)
360
- .providesService(() => ({
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 internal services and provided services
369
+ // Should include both base services and provided services
370
370
  expectTypeOf<Services>().toMatchObjectType<{
371
371
  internalService: () => Promise<string>;
372
372
  externalService: {