@fragno-dev/test 1.0.2 → 2.0.2

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.
Files changed (63) hide show
  1. package/.turbo/turbo-build.log +41 -15
  2. package/CHANGELOG.md +112 -0
  3. package/dist/adapters.d.ts +20 -5
  4. package/dist/adapters.d.ts.map +1 -1
  5. package/dist/adapters.js +20 -210
  6. package/dist/adapters.js.map +1 -1
  7. package/dist/db-test.d.ts +120 -18
  8. package/dist/db-test.d.ts.map +1 -1
  9. package/dist/db-test.js +236 -57
  10. package/dist/db-test.js.map +1 -1
  11. package/dist/durable-hooks.d.ts +11 -0
  12. package/dist/durable-hooks.d.ts.map +1 -0
  13. package/dist/durable-hooks.js +17 -0
  14. package/dist/durable-hooks.js.map +1 -0
  15. package/dist/index.d.ts +9 -5
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +6 -2
  18. package/dist/index.js.map +1 -1
  19. package/dist/model-checker-actors.d.ts +41 -0
  20. package/dist/model-checker-actors.d.ts.map +1 -0
  21. package/dist/model-checker-actors.js +406 -0
  22. package/dist/model-checker-actors.js.map +1 -0
  23. package/dist/model-checker-adapter.d.ts +32 -0
  24. package/dist/model-checker-adapter.d.ts.map +1 -0
  25. package/dist/model-checker-adapter.js +109 -0
  26. package/dist/model-checker-adapter.js.map +1 -0
  27. package/dist/model-checker.d.ts +128 -0
  28. package/dist/model-checker.d.ts.map +1 -0
  29. package/dist/model-checker.js +443 -0
  30. package/dist/model-checker.js.map +1 -0
  31. package/dist/test-adapters/drizzle-pglite.js +116 -0
  32. package/dist/test-adapters/drizzle-pglite.js.map +1 -0
  33. package/dist/test-adapters/in-memory.js +39 -0
  34. package/dist/test-adapters/in-memory.js.map +1 -0
  35. package/dist/test-adapters/kysely-pglite.js +105 -0
  36. package/dist/test-adapters/kysely-pglite.js.map +1 -0
  37. package/dist/test-adapters/kysely-sqlite.js +87 -0
  38. package/dist/test-adapters/kysely-sqlite.js.map +1 -0
  39. package/dist/test-adapters/model-checker.js +41 -0
  40. package/dist/test-adapters/model-checker.js.map +1 -0
  41. package/dist/tsconfig.tsbuildinfo +1 -0
  42. package/package.json +34 -34
  43. package/src/adapter-conformance.test.ts +324 -0
  44. package/src/adapters.ts +52 -320
  45. package/src/db-roundtrip-guard.test.ts +206 -0
  46. package/src/db-test.test.ts +133 -79
  47. package/src/db-test.ts +583 -99
  48. package/src/durable-hooks.test.ts +58 -0
  49. package/src/durable-hooks.ts +28 -0
  50. package/src/index.test.ts +250 -89
  51. package/src/index.ts +45 -6
  52. package/src/model-checker-actors.test.ts +81 -0
  53. package/src/model-checker-actors.ts +643 -0
  54. package/src/model-checker-adapter.ts +201 -0
  55. package/src/model-checker.test.ts +402 -0
  56. package/src/model-checker.ts +800 -0
  57. package/src/test-adapters/drizzle-pglite.ts +162 -0
  58. package/src/test-adapters/in-memory.ts +56 -0
  59. package/src/test-adapters/kysely-pglite.ts +151 -0
  60. package/src/test-adapters/kysely-sqlite.ts +119 -0
  61. package/src/test-adapters/model-checker.ts +58 -0
  62. package/tsconfig.json +1 -1
  63. package/vitest.config.ts +1 -0
@@ -1,13 +1,16 @@
1
1
  import { assert, describe, expect, it } from "vitest";
2
- import { column, idColumn, schema } from "@fragno-dev/db/schema";
3
- import { withDatabase } from "@fragno-dev/db";
4
- import { defineFragment, instantiate } from "@fragno-dev/core";
2
+
5
3
  import { defineRoute } from "@fragno-dev/core/route";
4
+ import { column, idColumn, schema } from "@fragno-dev/db/schema";
6
5
  import { z } from "zod";
6
+
7
+ import { defineFragment, instantiate } from "@fragno-dev/core";
8
+ import { withDatabase } from "@fragno-dev/db";
9
+
7
10
  import { buildDatabaseFragmentsTest } from "./db-test";
8
11
 
9
12
  // Test schema with users table
10
- const userSchema = schema((s) => {
13
+ const userSchema = schema("user", (s) => {
11
14
  return s.addTable("users", (t) => {
12
15
  return t
13
16
  .addColumn("id", idColumn())
@@ -18,7 +21,7 @@ const userSchema = schema((s) => {
18
21
  });
19
22
 
20
23
  // Test schema with posts table
21
- const postSchema = schema((s) => {
24
+ const postSchema = schema("post", (s) => {
22
25
  return s.addTable("posts", (t) => {
23
26
  return t
24
27
  .addColumn("id", idColumn())
@@ -33,34 +36,46 @@ describe("buildDatabaseFragmentsTest", () => {
33
36
  // Define fragments using new API
34
37
  const userFragmentDef = defineFragment<{}>("user-fragment")
35
38
  .extend(withDatabase(userSchema))
36
- .providesBaseService(({ deps }) => ({
37
- createUser: async (data: { name: string; email: string }) => {
38
- const id = await deps.db.create("users", data);
39
- return { ...data, id: id.valueOf() };
40
- },
41
- getUsers: async () => {
42
- const users = await deps.db.find("users", (b) =>
43
- b.whereIndex("idx_users_all", (eb) => eb("id", "!=", "")),
44
- );
45
- return users.map((u) => ({ ...u, id: u.id.valueOf() }));
46
- },
47
- }))
39
+ .providesBaseService(({ defineService }) =>
40
+ defineService({
41
+ createUser: function (data: { name: string; email: string }) {
42
+ return this.serviceTx(userSchema)
43
+ .mutate(({ uow }) => uow.create("users", data))
44
+ .transform(({ mutateResult }) => ({ ...data, id: mutateResult.valueOf() }))
45
+ .build();
46
+ },
47
+ getUsers: function () {
48
+ return this.serviceTx(userSchema)
49
+ .retrieve((uow) =>
50
+ uow.find("users", (b) => b.whereIndex("idx_users_all", (eb) => eb("id", "!=", ""))),
51
+ )
52
+ .transformRetrieve(([users]) => users.map((u) => ({ ...u, id: u.id.valueOf() })))
53
+ .build();
54
+ },
55
+ }),
56
+ )
48
57
  .build();
49
58
 
50
59
  const postFragmentDef = defineFragment<{}>("post-fragment")
51
60
  .extend(withDatabase(postSchema))
52
- .providesBaseService(({ deps }) => ({
53
- createPost: async (data: { title: string; userId: string }) => {
54
- const id = await deps.db.create("posts", data);
55
- return { ...data, id: id.valueOf() };
56
- },
57
- getPosts: async () => {
58
- const posts = await deps.db.find("posts", (b) =>
59
- b.whereIndex("idx_posts_all", (eb) => eb("id", "!=", "")),
60
- );
61
- return posts.map((p) => ({ ...p, id: p.id.valueOf() }));
62
- },
63
- }))
61
+ .providesBaseService(({ defineService }) =>
62
+ defineService({
63
+ createPost: function (data: { title: string; userId: string }) {
64
+ return this.serviceTx(postSchema)
65
+ .mutate(({ uow }) => uow.create("posts", data))
66
+ .transform(({ mutateResult }) => ({ ...data, id: mutateResult.valueOf() }))
67
+ .build();
68
+ },
69
+ getPosts: function () {
70
+ return this.serviceTx(postSchema)
71
+ .retrieve((uow) =>
72
+ uow.find("posts", (b) => b.whereIndex("idx_posts_all", (eb) => eb("id", "!=", ""))),
73
+ )
74
+ .transformRetrieve(([posts]) => posts.map((p) => ({ ...p, id: p.id.valueOf() })))
75
+ .build();
76
+ },
77
+ }),
78
+ )
64
79
  .build();
65
80
 
66
81
  // Build test setup with new builder API
@@ -71,10 +86,12 @@ describe("buildDatabaseFragmentsTest", () => {
71
86
  .build();
72
87
 
73
88
  // Test user fragment
74
- const user = await fragments.user.services.createUser({
75
- name: "Test User",
76
- email: "test@example.com",
77
- });
89
+ const user = await fragments.user.fragment.callServices(() =>
90
+ fragments.user.services.createUser({
91
+ name: "Test User",
92
+ email: "test@example.com",
93
+ }),
94
+ );
78
95
 
79
96
  expect(user).toMatchObject({
80
97
  id: expect.any(String),
@@ -83,10 +100,12 @@ describe("buildDatabaseFragmentsTest", () => {
83
100
  });
84
101
 
85
102
  // Test post fragment
86
- const post = await fragments.post.services.createPost({
87
- title: "Test Post",
88
- userId: user.id,
89
- });
103
+ const post = await fragments.post.fragment.callServices(() =>
104
+ fragments.post.services.createPost({
105
+ title: "Test Post",
106
+ userId: user.id,
107
+ }),
108
+ );
90
109
 
91
110
  expect(post).toMatchObject({
92
111
  id: expect.any(String),
@@ -95,10 +114,14 @@ describe("buildDatabaseFragmentsTest", () => {
95
114
  });
96
115
 
97
116
  // Verify data exists
98
- const users = await fragments.user.services.getUsers();
117
+ const users = await fragments.user.fragment.callServices(() =>
118
+ fragments.user.services.getUsers(),
119
+ );
99
120
  expect(users).toHaveLength(1);
100
121
 
101
- const posts = await fragments.post.services.getPosts();
122
+ const posts = await fragments.post.fragment.callServices(() =>
123
+ fragments.post.services.getPosts(),
124
+ );
102
125
  expect(posts).toHaveLength(1);
103
126
  expect(posts[0]!.userId).toBe(user.id);
104
127
 
@@ -109,18 +132,24 @@ describe("buildDatabaseFragmentsTest", () => {
109
132
  it("should reset database and recreate fragments", async () => {
110
133
  const userFragmentDef = defineFragment<{}>("user-fragment")
111
134
  .extend(withDatabase(userSchema))
112
- .providesBaseService(({ deps }) => ({
113
- createUser: async (data: { name: string; email: string }) => {
114
- const id = await deps.db.create("users", data);
115
- return { ...data, id: id.valueOf() };
116
- },
117
- getUsers: async () => {
118
- const users = await deps.db.find("users", (b) =>
119
- b.whereIndex("idx_users_all", (eb) => eb("id", "!=", "")),
120
- );
121
- return users.map((u) => ({ ...u, id: u.id.valueOf() }));
122
- },
123
- }))
135
+ .providesBaseService(({ defineService }) =>
136
+ defineService({
137
+ createUser: function (data: { name: string; email: string }) {
138
+ return this.serviceTx(userSchema)
139
+ .mutate(({ uow }) => uow.create("users", data))
140
+ .transform(({ mutateResult }) => ({ ...data, id: mutateResult.valueOf() }))
141
+ .build();
142
+ },
143
+ getUsers: function () {
144
+ return this.serviceTx(userSchema)
145
+ .retrieve((uow) =>
146
+ uow.find("users", (b) => b.whereIndex("idx_users_all", (eb) => eb("id", "!=", ""))),
147
+ )
148
+ .transformRetrieve(([users]) => users.map((u) => ({ ...u, id: u.id.valueOf() })))
149
+ .build();
150
+ },
151
+ }),
152
+ )
124
153
  .build();
125
154
 
126
155
  const { fragments, test } = await buildDatabaseFragmentsTest()
@@ -129,27 +158,31 @@ describe("buildDatabaseFragmentsTest", () => {
129
158
  .build();
130
159
 
131
160
  // Create a user
132
- await fragments.user.services.createUser({
133
- name: "User 1",
134
- email: "user1@example.com",
135
- });
161
+ await fragments.user.fragment.callServices(() =>
162
+ fragments.user.services.createUser({
163
+ name: "User 1",
164
+ email: "user1@example.com",
165
+ }),
166
+ );
136
167
 
137
168
  // Verify user exists
138
- let users = await fragments.user.services.getUsers();
169
+ let users = await fragments.user.fragment.callServices(() =>
170
+ fragments.user.services.getUsers(),
171
+ );
139
172
  expect(users).toHaveLength(1);
140
173
 
141
174
  // Reset database
142
175
  await test.resetDatabase();
143
176
 
144
177
  // Verify database is empty
145
- users = await fragments.user.services.getUsers();
178
+ users = await fragments.user.fragment.callServices(() => fragments.user.services.getUsers());
146
179
  expect(users).toHaveLength(0);
147
180
 
148
181
  // Cleanup
149
182
  await test.cleanup();
150
183
  });
151
184
 
152
- it("should expose db for direct queries", async () => {
185
+ it("should allow handlerTx for direct queries", async () => {
153
186
  const userFragmentDef = defineFragment<{}>("user-fragment")
154
187
  .extend(withDatabase(userSchema))
155
188
  .providesBaseService(() => ({}))
@@ -161,18 +194,32 @@ describe("buildDatabaseFragmentsTest", () => {
161
194
  .build();
162
195
 
163
196
  // Use db directly
164
- const userId = await fragments.user.db.create("users", {
165
- name: "Direct DB User",
166
- email: "direct@example.com",
197
+ const userId = await fragments.user.fragment.inContext(async function () {
198
+ return await this.handlerTx()
199
+ .mutate(({ forSchema }) =>
200
+ forSchema(userSchema).create("users", {
201
+ name: "Direct DB User",
202
+ email: "direct@example.com",
203
+ }),
204
+ )
205
+ .transform(({ mutateResult }) => mutateResult)
206
+ .execute();
167
207
  });
168
208
 
169
209
  expect(userId).toBeDefined();
170
210
  expect(typeof userId.valueOf()).toBe("string");
171
211
 
172
212
  // Find using db
173
- const users = await fragments.user.db.find("users", (b) =>
174
- b.whereIndex("idx_users_all", (eb) => eb("id", "=", userId)),
175
- );
213
+ const users = await fragments.user.fragment.inContext(async function () {
214
+ return await this.handlerTx()
215
+ .retrieve(({ forSchema }) =>
216
+ forSchema(userSchema).find("users", (b) =>
217
+ b.whereIndex("idx_users_all", (eb) => eb("id", "=", userId)),
218
+ ),
219
+ )
220
+ .transformRetrieve(([result]) => result)
221
+ .execute();
222
+ });
176
223
 
177
224
  expect(users).toHaveLength(1);
178
225
  expect(users[0]).toMatchObject({
@@ -203,12 +250,12 @@ describe("buildDatabaseFragmentsTest", () => {
203
250
  // Test that deps are accessible
204
251
  expect(fragments.user.deps).toBeDefined();
205
252
  expect(fragments.user.deps.testValue).toBe("test-dependency");
206
- expect(fragments.user.deps.db).toBeDefined();
253
+ expect(fragments.user.deps.databaseAdapter).toBeDefined();
207
254
  expect(fragments.user.deps.schema).toBeDefined();
255
+ expect(fragments.user.deps.createUnitOfWork).toBeDefined();
208
256
 
209
257
  // Test that adapter is accessible
210
258
  expect(test.adapter).toBeDefined();
211
- expect(test.adapter.createQueryEngine).toBeDefined();
212
259
 
213
260
  await test.cleanup();
214
261
  });
@@ -280,13 +327,17 @@ describe("buildDatabaseFragmentsTest", () => {
280
327
  apiKey: config.apiKey,
281
328
  };
282
329
  })
283
- .providesBaseService(({ deps }) => ({
284
- getApiKey: () => deps.apiKey,
285
- createUser: async (data: { name: string; email: string }) => {
286
- const id = await deps.db.create("users", data);
287
- return { ...data, id: id.valueOf() };
288
- },
289
- }))
330
+ .providesBaseService(({ deps, defineService }) =>
331
+ defineService({
332
+ getApiKey: () => deps.apiKey,
333
+ createUser: function (data: { name: string; email: string }) {
334
+ return this.serviceTx(userSchema)
335
+ .mutate(({ uow }) => uow.create("users", data))
336
+ .transform(({ mutateResult }) => ({ ...data, id: mutateResult.valueOf() }))
337
+ .build();
338
+ },
339
+ }),
340
+ )
290
341
  .build();
291
342
 
292
343
  const { fragments, test } = await buildDatabaseFragmentsTest()
@@ -310,10 +361,12 @@ describe("buildDatabaseFragmentsTest", () => {
310
361
  });
311
362
 
312
363
  // Verify database operations work
313
- const user = await fragments.requiredConfig.services.createUser({
314
- name: "Config Test User",
315
- email: "config@example.com",
316
- });
364
+ const user = await fragments.requiredConfig.fragment.callServices(() =>
365
+ fragments.requiredConfig.services.createUser({
366
+ name: "Config Test User",
367
+ email: "config@example.com",
368
+ }),
369
+ );
317
370
 
318
371
  expect(user).toMatchObject({
319
372
  id: expect.any(String),
@@ -346,7 +399,8 @@ describe("buildDatabaseFragmentsTest", () => {
346
399
  .withFragment("bad", instantiate(badFragmentDef).withRoutes([]))
347
400
  .build();
348
401
 
349
- await expect(buildPromise).rejects.toThrow(/Failed to extract schema from fragment/);
350
- await expect(buildPromise).rejects.toThrow(/API key is required/);
402
+ await expect(buildPromise).rejects.toThrow(
403
+ /Failed to extract schema from fragment.*API key is required/s,
404
+ );
351
405
  });
352
406
  });