@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.
- package/.turbo/turbo-build.log +41 -15
- package/CHANGELOG.md +112 -0
- package/dist/adapters.d.ts +20 -5
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +20 -210
- package/dist/adapters.js.map +1 -1
- package/dist/db-test.d.ts +120 -18
- package/dist/db-test.d.ts.map +1 -1
- package/dist/db-test.js +236 -57
- package/dist/db-test.js.map +1 -1
- package/dist/durable-hooks.d.ts +11 -0
- package/dist/durable-hooks.d.ts.map +1 -0
- package/dist/durable-hooks.js +17 -0
- package/dist/durable-hooks.js.map +1 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/model-checker-actors.d.ts +41 -0
- package/dist/model-checker-actors.d.ts.map +1 -0
- package/dist/model-checker-actors.js +406 -0
- package/dist/model-checker-actors.js.map +1 -0
- package/dist/model-checker-adapter.d.ts +32 -0
- package/dist/model-checker-adapter.d.ts.map +1 -0
- package/dist/model-checker-adapter.js +109 -0
- package/dist/model-checker-adapter.js.map +1 -0
- package/dist/model-checker.d.ts +128 -0
- package/dist/model-checker.d.ts.map +1 -0
- package/dist/model-checker.js +443 -0
- package/dist/model-checker.js.map +1 -0
- package/dist/test-adapters/drizzle-pglite.js +116 -0
- package/dist/test-adapters/drizzle-pglite.js.map +1 -0
- package/dist/test-adapters/in-memory.js +39 -0
- package/dist/test-adapters/in-memory.js.map +1 -0
- package/dist/test-adapters/kysely-pglite.js +105 -0
- package/dist/test-adapters/kysely-pglite.js.map +1 -0
- package/dist/test-adapters/kysely-sqlite.js +87 -0
- package/dist/test-adapters/kysely-sqlite.js.map +1 -0
- package/dist/test-adapters/model-checker.js +41 -0
- package/dist/test-adapters/model-checker.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +34 -34
- package/src/adapter-conformance.test.ts +324 -0
- package/src/adapters.ts +52 -320
- package/src/db-roundtrip-guard.test.ts +206 -0
- package/src/db-test.test.ts +133 -79
- package/src/db-test.ts +583 -99
- package/src/durable-hooks.test.ts +58 -0
- package/src/durable-hooks.ts +28 -0
- package/src/index.test.ts +250 -89
- package/src/index.ts +45 -6
- package/src/model-checker-actors.test.ts +81 -0
- package/src/model-checker-actors.ts +643 -0
- package/src/model-checker-adapter.ts +201 -0
- package/src/model-checker.test.ts +402 -0
- package/src/model-checker.ts +800 -0
- package/src/test-adapters/drizzle-pglite.ts +162 -0
- package/src/test-adapters/in-memory.ts +56 -0
- package/src/test-adapters/kysely-pglite.ts +151 -0
- package/src/test-adapters/kysely-sqlite.ts +119 -0
- package/src/test-adapters/model-checker.ts +58 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +1 -0
package/src/db-test.test.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { assert, describe, expect, it } from "vitest";
|
|
2
|
-
|
|
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(({
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
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(({
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
)
|
|
61
|
-
|
|
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.
|
|
75
|
-
|
|
76
|
-
|
|
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.
|
|
87
|
-
|
|
88
|
-
|
|
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.
|
|
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.
|
|
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(({
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
)
|
|
121
|
-
|
|
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.
|
|
133
|
-
|
|
134
|
-
|
|
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.
|
|
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
|
|
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.
|
|
165
|
-
|
|
166
|
-
|
|
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.
|
|
174
|
-
|
|
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.
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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.
|
|
314
|
-
|
|
315
|
-
|
|
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(
|
|
350
|
-
|
|
402
|
+
await expect(buildPromise).rejects.toThrow(
|
|
403
|
+
/Failed to extract schema from fragment.*API key is required/s,
|
|
404
|
+
);
|
|
351
405
|
});
|
|
352
406
|
});
|