@grest-ts/testkit 0.0.5

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 (200) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +413 -0
  3. package/dist/src/GGBundleTest.d.ts +8 -0
  4. package/dist/src/GGBundleTest.d.ts.map +1 -0
  5. package/dist/src/GGBundleTest.js +75 -0
  6. package/dist/src/GGBundleTest.js.map +1 -0
  7. package/dist/src/GGTest.d.ts +131 -0
  8. package/dist/src/GGTest.d.ts.map +1 -0
  9. package/dist/src/GGTest.js +245 -0
  10. package/dist/src/GGTest.js.map +1 -0
  11. package/dist/src/GGTestContext.d.ts +36 -0
  12. package/dist/src/GGTestContext.d.ts.map +1 -0
  13. package/dist/src/GGTestContext.js +63 -0
  14. package/dist/src/GGTestContext.js.map +1 -0
  15. package/dist/src/GGTestRunner.d.ts +108 -0
  16. package/dist/src/GGTestRunner.d.ts.map +1 -0
  17. package/dist/src/GGTestRunner.js +242 -0
  18. package/dist/src/GGTestRunner.js.map +1 -0
  19. package/dist/src/GGTestRuntime.d.ts +103 -0
  20. package/dist/src/GGTestRuntime.d.ts.map +1 -0
  21. package/dist/src/GGTestRuntime.js +219 -0
  22. package/dist/src/GGTestRuntime.js.map +1 -0
  23. package/dist/src/GGTestRuntimeWorker.d.ts +41 -0
  24. package/dist/src/GGTestRuntimeWorker.d.ts.map +1 -0
  25. package/dist/src/GGTestRuntimeWorker.js +136 -0
  26. package/dist/src/GGTestRuntimeWorker.js.map +1 -0
  27. package/dist/src/GGTestSharedRef.d.ts +35 -0
  28. package/dist/src/GGTestSharedRef.d.ts.map +1 -0
  29. package/dist/src/GGTestSharedRef.js +126 -0
  30. package/dist/src/GGTestSharedRef.js.map +1 -0
  31. package/dist/src/GGTestkitExtensionsDiscovery.d.ts +21 -0
  32. package/dist/src/GGTestkitExtensionsDiscovery.d.ts.map +1 -0
  33. package/dist/src/GGTestkitExtensionsDiscovery.js +24 -0
  34. package/dist/src/GGTestkitExtensionsDiscovery.js.map +1 -0
  35. package/dist/src/IGGLocalDiscoveryServer.d.ts +16 -0
  36. package/dist/src/IGGLocalDiscoveryServer.d.ts.map +1 -0
  37. package/dist/src/IGGLocalDiscoveryServer.js +2 -0
  38. package/dist/src/IGGLocalDiscoveryServer.js.map +1 -0
  39. package/dist/src/callOn/GGCallOnSelector.d.ts +42 -0
  40. package/dist/src/callOn/GGCallOnSelector.d.ts.map +1 -0
  41. package/dist/src/callOn/GGCallOnSelector.js +35 -0
  42. package/dist/src/callOn/GGCallOnSelector.js.map +1 -0
  43. package/dist/src/callOn/GGContractClass.implement.d.ts +8 -0
  44. package/dist/src/callOn/GGContractClass.implement.d.ts.map +1 -0
  45. package/dist/src/callOn/GGContractClass.implement.js +31 -0
  46. package/dist/src/callOn/GGContractClass.implement.js.map +1 -0
  47. package/dist/src/callOn/GGTestActionForLocatorOnCall.d.ts +28 -0
  48. package/dist/src/callOn/GGTestActionForLocatorOnCall.d.ts.map +1 -0
  49. package/dist/src/callOn/GGTestActionForLocatorOnCall.js +118 -0
  50. package/dist/src/callOn/GGTestActionForLocatorOnCall.js.map +1 -0
  51. package/dist/src/callOn/TestableIPC.d.ts +72 -0
  52. package/dist/src/callOn/TestableIPC.d.ts.map +1 -0
  53. package/dist/src/callOn/TestableIPC.js +34 -0
  54. package/dist/src/callOn/TestableIPC.js.map +1 -0
  55. package/dist/src/callOn/callOn.d.ts +113 -0
  56. package/dist/src/callOn/callOn.d.ts.map +1 -0
  57. package/dist/src/callOn/callOn.js +122 -0
  58. package/dist/src/callOn/callOn.js.map +1 -0
  59. package/dist/src/callOn/registerOnCallHandler.d.ts +13 -0
  60. package/dist/src/callOn/registerOnCallHandler.d.ts.map +1 -0
  61. package/dist/src/callOn/registerOnCallHandler.js +111 -0
  62. package/dist/src/callOn/registerOnCallHandler.js.map +1 -0
  63. package/dist/src/index-node.d.ts +35 -0
  64. package/dist/src/index-node.d.ts.map +1 -0
  65. package/dist/src/index-node.js +50 -0
  66. package/dist/src/index-node.js.map +1 -0
  67. package/dist/src/mockable/GGMockable.d.ts +19 -0
  68. package/dist/src/mockable/GGMockable.d.ts.map +1 -0
  69. package/dist/src/mockable/GGMockable.js +2 -0
  70. package/dist/src/mockable/GGMockable.js.map +1 -0
  71. package/dist/src/mockable/GGMockableCall.d.ts +2 -0
  72. package/dist/src/mockable/GGMockableCall.d.ts.map +1 -0
  73. package/dist/src/mockable/GGMockableCall.js +41 -0
  74. package/dist/src/mockable/GGMockableCall.js.map +1 -0
  75. package/dist/src/mockable/GGMockableIPC.d.ts +17 -0
  76. package/dist/src/mockable/GGMockableIPC.d.ts.map +1 -0
  77. package/dist/src/mockable/GGMockableIPC.js +8 -0
  78. package/dist/src/mockable/GGMockableIPC.js.map +1 -0
  79. package/dist/src/mockable/GGMockableInterceptor.d.ts +24 -0
  80. package/dist/src/mockable/GGMockableInterceptor.d.ts.map +1 -0
  81. package/dist/src/mockable/GGMockableInterceptor.js +32 -0
  82. package/dist/src/mockable/GGMockableInterceptor.js.map +1 -0
  83. package/dist/src/mockable/GGMockableInterceptorsServer.d.ts +12 -0
  84. package/dist/src/mockable/GGMockableInterceptorsServer.d.ts.map +1 -0
  85. package/dist/src/mockable/GGMockableInterceptorsServer.js +55 -0
  86. package/dist/src/mockable/GGMockableInterceptorsServer.js.map +1 -0
  87. package/dist/src/mockable/mockable.d.ts +46 -0
  88. package/dist/src/mockable/mockable.d.ts.map +1 -0
  89. package/dist/src/mockable/mockable.js +47 -0
  90. package/dist/src/mockable/mockable.js.map +1 -0
  91. package/dist/src/runner/InlineRunner.d.ts +12 -0
  92. package/dist/src/runner/InlineRunner.d.ts.map +1 -0
  93. package/dist/src/runner/InlineRunner.js +42 -0
  94. package/dist/src/runner/InlineRunner.js.map +1 -0
  95. package/dist/src/runner/IsolatedRunner.d.ts +17 -0
  96. package/dist/src/runner/IsolatedRunner.d.ts.map +1 -0
  97. package/dist/src/runner/IsolatedRunner.js +155 -0
  98. package/dist/src/runner/IsolatedRunner.js.map +1 -0
  99. package/dist/src/runner/RuntimeRunner.d.ts +14 -0
  100. package/dist/src/runner/RuntimeRunner.d.ts.map +1 -0
  101. package/dist/src/runner/RuntimeRunner.js +2 -0
  102. package/dist/src/runner/RuntimeRunner.js.map +1 -0
  103. package/dist/src/runner/WorkerRunner.d.ts +17 -0
  104. package/dist/src/runner/WorkerRunner.d.ts.map +1 -0
  105. package/dist/src/runner/WorkerRunner.js +155 -0
  106. package/dist/src/runner/WorkerRunner.js.map +1 -0
  107. package/dist/src/runner/isolated-loader.mjs +91 -0
  108. package/dist/src/runner/worker-loader.mjs +49 -0
  109. package/dist/src/testers/GGCallInterceptor.d.ts +71 -0
  110. package/dist/src/testers/GGCallInterceptor.d.ts.map +1 -0
  111. package/dist/src/testers/GGCallInterceptor.js +170 -0
  112. package/dist/src/testers/GGCallInterceptor.js.map +1 -0
  113. package/dist/src/testers/GGMockWith.d.ts +30 -0
  114. package/dist/src/testers/GGMockWith.d.ts.map +1 -0
  115. package/dist/src/testers/GGMockWith.js +70 -0
  116. package/dist/src/testers/GGMockWith.js.map +1 -0
  117. package/dist/src/testers/GGSpyWith.d.ts +40 -0
  118. package/dist/src/testers/GGSpyWith.d.ts.map +1 -0
  119. package/dist/src/testers/GGSpyWith.js +90 -0
  120. package/dist/src/testers/GGSpyWith.js.map +1 -0
  121. package/dist/src/testers/GGTestAction.d.ts +126 -0
  122. package/dist/src/testers/GGTestAction.d.ts.map +1 -0
  123. package/dist/src/testers/GGTestAction.js +245 -0
  124. package/dist/src/testers/GGTestAction.js.map +1 -0
  125. package/dist/src/testers/GGTestComponent.d.ts +15 -0
  126. package/dist/src/testers/GGTestComponent.d.ts.map +1 -0
  127. package/dist/src/testers/GGTestComponent.js +2 -0
  128. package/dist/src/testers/GGTestComponent.js.map +1 -0
  129. package/dist/src/testers/GGTestSelector.d.ts +54 -0
  130. package/dist/src/testers/GGTestSelector.d.ts.map +1 -0
  131. package/dist/src/testers/GGTestSelector.js +179 -0
  132. package/dist/src/testers/GGTestSelector.js.map +1 -0
  133. package/dist/src/testers/IGGTestInterceptor.d.ts +8 -0
  134. package/dist/src/testers/IGGTestInterceptor.d.ts.map +1 -0
  135. package/dist/src/testers/IGGTestInterceptor.js +2 -0
  136. package/dist/src/testers/IGGTestInterceptor.js.map +1 -0
  137. package/dist/src/testers/IGGTestWith.d.ts +13 -0
  138. package/dist/src/testers/IGGTestWith.d.ts.map +1 -0
  139. package/dist/src/testers/IGGTestWith.js +2 -0
  140. package/dist/src/testers/IGGTestWith.js.map +1 -0
  141. package/dist/src/testers/RuntimeSelector.d.ts +117 -0
  142. package/dist/src/testers/RuntimeSelector.d.ts.map +1 -0
  143. package/dist/src/testers/RuntimeSelector.js +2 -0
  144. package/dist/src/testers/RuntimeSelector.js.map +1 -0
  145. package/dist/src/tsconfig.json +17 -0
  146. package/dist/src/utils/GGExpectations.d.ts +18 -0
  147. package/dist/src/utils/GGExpectations.d.ts.map +1 -0
  148. package/dist/src/utils/GGExpectations.js +59 -0
  149. package/dist/src/utils/GGExpectations.js.map +1 -0
  150. package/dist/src/utils/GGTestError.d.ts +13 -0
  151. package/dist/src/utils/GGTestError.d.ts.map +1 -0
  152. package/dist/src/utils/GGTestError.js +26 -0
  153. package/dist/src/utils/GGTestError.js.map +1 -0
  154. package/dist/src/utils/captureStack.d.ts +9 -0
  155. package/dist/src/utils/captureStack.d.ts.map +1 -0
  156. package/dist/src/utils/captureStack.js +51 -0
  157. package/dist/src/utils/captureStack.js.map +1 -0
  158. package/dist/tsconfig.publish.tsbuildinfo +1 -0
  159. package/package.json +66 -0
  160. package/src/GGBundleTest.ts +89 -0
  161. package/src/GGTest.ts +318 -0
  162. package/src/GGTestContext.ts +74 -0
  163. package/src/GGTestRunner.ts +308 -0
  164. package/src/GGTestRuntime.ts +265 -0
  165. package/src/GGTestRuntimeWorker.ts +159 -0
  166. package/src/GGTestSharedRef.ts +116 -0
  167. package/src/GGTestkitExtensionsDiscovery.ts +26 -0
  168. package/src/IGGLocalDiscoveryServer.ts +16 -0
  169. package/src/callOn/GGCallOnSelector.ts +61 -0
  170. package/src/callOn/GGContractClass.implement.ts +43 -0
  171. package/src/callOn/GGTestActionForLocatorOnCall.ts +134 -0
  172. package/src/callOn/TestableIPC.ts +81 -0
  173. package/src/callOn/callOn.ts +224 -0
  174. package/src/callOn/registerOnCallHandler.ts +123 -0
  175. package/src/index-node.ts +64 -0
  176. package/src/mockable/GGMockable.ts +22 -0
  177. package/src/mockable/GGMockableCall.ts +45 -0
  178. package/src/mockable/GGMockableIPC.ts +20 -0
  179. package/src/mockable/GGMockableInterceptor.ts +44 -0
  180. package/src/mockable/GGMockableInterceptorsServer.ts +69 -0
  181. package/src/mockable/mockable.ts +71 -0
  182. package/src/runner/InlineRunner.ts +47 -0
  183. package/src/runner/IsolatedRunner.ts +179 -0
  184. package/src/runner/RuntimeRunner.ts +15 -0
  185. package/src/runner/WorkerRunner.ts +179 -0
  186. package/src/runner/isolated-loader.mjs +91 -0
  187. package/src/runner/worker-loader.mjs +49 -0
  188. package/src/testers/GGCallInterceptor.ts +224 -0
  189. package/src/testers/GGMockWith.ts +92 -0
  190. package/src/testers/GGSpyWith.ts +115 -0
  191. package/src/testers/GGTestAction.ts +333 -0
  192. package/src/testers/GGTestComponent.ts +16 -0
  193. package/src/testers/GGTestSelector.ts +223 -0
  194. package/src/testers/IGGTestInterceptor.ts +11 -0
  195. package/src/testers/IGGTestWith.ts +15 -0
  196. package/src/testers/RuntimeSelector.ts +151 -0
  197. package/src/tsconfig.json +17 -0
  198. package/src/utils/GGExpectations.ts +78 -0
  199. package/src/utils/GGTestError.ts +37 -0
  200. package/src/utils/captureStack.ts +54 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Grest Games OÜ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,413 @@
1
+ # Testkit
2
+
3
+ ## Overview
4
+
5
+ The testkit provides comprehensive testing capabilities to the framework, mostly focusing on backend integration & component level testing.
6
+
7
+ ### Main features:
8
+
9
+ * Runs on top of vitest - all normal vitest features are available!
10
+ * Component & integration testing
11
+ * Parallel execution of tests.
12
+ * Test a single or multiple components.
13
+ * Mock or spy communication between runtimes.
14
+ * Mock external dependencies (like third party API-s) using @mockable decorator.
15
+ * Manage databases (clone, seed, etc...).
16
+ * Unit testing
17
+ * Standard vitest and its capabilities. Nothing too special here.
18
+
19
+ Overall idea is that you get to run your services as if they were running in production, but get access to where you need - like:
20
+
21
+ * accessing logs
22
+ * accessing metrics
23
+ * accessing & updating config
24
+ * mock or spy on API calls / external requests / events etc...
25
+ * easy setup for isolated databases.
26
+
27
+ To fully understand the fun, you have to try it out on how easy and fast testing microservice environments can become.
28
+ Start from a single service and easily scale up to multi service environments with ease.
29
+ This is a capability easy to underestimate when starting a project, but you absolutely love it when things start scaling up!
30
+
31
+ ## Capabilities example
32
+
33
+ ```typescript
34
+ import {GGTest} from "@grest-ts/testkit";
35
+ import {MyRuntime} from "./MyRuntime";
36
+ import {MyApi} from "./MyApi.api";
37
+
38
+ describe("my test", () => {
39
+ const t = GGTest.startInline(MyRuntime);
40
+ const client = MyApi.createTestClient();
41
+
42
+ test("selector extensions", async () => {
43
+ // t.myRuntime.logs - from @grest-ts/logger/testkit
44
+ const cursor = await t.myRuntime.logs.cursor();
45
+ await client.doSomething();
46
+ const logs = await cursor.retrieve();
47
+
48
+ // t.myRuntime.config - from @grest-ts/config/testkit
49
+ await t.myRuntime.config.update(MY_CONFIG_KEY, newValue);
50
+
51
+ // t.all() selects all runtimes
52
+ await t.all().config.update(SHARED_KEY, value);
53
+ });
54
+
55
+ test("schema extensions - mock/spy", async () => {
56
+ // MyApi.mock.methodName - from @grest-ts/http/testkit
57
+ await client.getUser({id: 1})
58
+ .with(MyApi.mock.getUser.andReturn({name: "Alice"}));
59
+
60
+ // MyApi.spy.methodName - passthrough with validation
61
+ await client.createUser({name: "Bob"})
62
+ .with(MyApi.spy.createUser.toMatchObject({name: "Bob"}));
63
+ });
64
+
65
+ test("log expectations with .with()", async () => {
66
+ await client.doSomething()
67
+ .with(t.myRuntime.logs.expect("expected message"));
68
+ });
69
+
70
+ test("waitFor - wait for async side effects", async () => {
71
+ await client.triggerAsyncProcess()
72
+ .waitFor(t.myRuntime.logs.expect("process completed"));
73
+ });
74
+ });
75
+ ```
76
+
77
+ ---
78
+
79
+ ## GGTestContext
80
+
81
+ `GGTestContext` is the recommended way to make API calls in tests. It extends `GGContext` and provides lifecycle hooks, API registration, and context management (e.g. auth headers).
82
+
83
+ ### Basic usage
84
+
85
+ ```typescript
86
+ import {GGTest, GGTestContext} from "@grest-ts/testkit";
87
+ import {MyRuntime} from "../src/main";
88
+ import {UserApi} from "../src/api/UserApi.api";
89
+ import {ChecklistApi} from "../src/api/ChecklistApi.api";
90
+
91
+ describe("my tests", () => {
92
+ GGTest.startWorker(MyRuntime);
93
+
94
+ const alice = new GGTestContext("Alice")
95
+ .apis({
96
+ user: UserApi,
97
+ checklist: ChecklistApi
98
+ })
99
+ .beforeAll(async () => {
100
+ const result = await alice.user.register({
101
+ username: "alice", email: "alice@example.com", password: "secret123"
102
+ });
103
+ alice.set(AUTH_TOKEN, result.token);
104
+ });
105
+
106
+ test('list items', async () => {
107
+ await alice.checklist.list().toMatchObject([]);
108
+ });
109
+ });
110
+ ```
111
+
112
+ ### `.apis()`
113
+
114
+ Registers APIs and returns `this` merged with test clients for all registered items.
115
+ After calling `.apis()`, you can access each API directly as a property.
116
+
117
+ ```typescript
118
+ const admin = new GGTestContext("Admin")
119
+ .apis({
120
+ building: BuildingApi,
121
+ apartment: ApartmentApi,
122
+ client: ClientApi
123
+ });
124
+
125
+ // Now call API methods directly:
126
+ await admin.building.sync({...});
127
+ await admin.apartment.list({...});
128
+ ```
129
+
130
+ ### `.callOn()`
131
+
132
+ Call methods on APIs or services not registered via `.apis()`.
133
+
134
+ ```typescript
135
+ // Call an API you didn't register upfront
136
+ await alice.callOn(UserPublicApi).login({username: "alice", password: "secret123"});
137
+ ```
138
+
139
+ ### Lifecycle hooks
140
+
141
+ ```typescript
142
+ const alice = new GGTestContext("Alice")
143
+ .apis({user: UserApi})
144
+ .resetAfterEach() // Reset context state (headers etc) after each test
145
+ .beforeAll(async () => { /* once */ }) // Runs once before all tests
146
+ .beforeEach(async () => { /* each */ }) // Runs before each test
147
+ .afterEach(async () => { /* each */ }) // Runs after each test
148
+ .afterAll(async () => { /* cleanup */ }); // Runs once after all tests
149
+ ```
150
+
151
+ ### Context state management
152
+
153
+ `GGTestContext` extends `GGContext`, so you can set/get/delete context values (typically auth headers):
154
+
155
+ ```typescript
156
+ alice.set(AUTH_TOKEN, token); // Set a context value (sent as header)
157
+ alice.get(AUTH_TOKEN); // Read a context value
158
+ alice.delete(AUTH_TOKEN); // Remove a context value
159
+ ```
160
+
161
+ ### Extending GGTestContext
162
+
163
+ For domain-specific helpers, extend `GGTestContext`:
164
+
165
+ ```typescript
166
+ import {GGTestContext} from "@grest-ts/testkit";
167
+
168
+ export class MyUserContext extends GGTestContext {
169
+
170
+ public user: User;
171
+
172
+ public async login(data: {username: string, password: string}) {
173
+ const result = await this.callOn(UserPublicApi).login(data);
174
+ this.set(AUTH_TOKEN, result.token);
175
+ this.user = result.user;
176
+ }
177
+
178
+ public async loginAndSetup() {
179
+ await this.login({username: "admin", password: "admin"});
180
+ }
181
+ }
182
+
183
+ // Usage in tests:
184
+ const admin = new MyUserContext("Admin")
185
+ .apis({checklist: ChecklistApi})
186
+ .beforeAll(async () => {
187
+ await admin.loginAndSetup();
188
+ });
189
+ ```
190
+
191
+ ---
192
+
193
+ ## Response assertions
194
+
195
+ All API calls through `GGTestContext` return a `GGTestAction` - a PromiseLike with chainable assertion methods. Assertions are checked when the action is awaited.
196
+
197
+ ### Data assertions
198
+
199
+ ```typescript
200
+ // Exact match
201
+ await alice.user.get({id}).toEqual({id, username: "alice", email: "alice@example.com"});
202
+
203
+ // Partial match (like Jest toMatchObject)
204
+ await alice.user.get({id}).toMatchObject({username: "alice"});
205
+
206
+ // Undefined (for void endpoints)
207
+ await alice.user.delete({id}).toBeUndefined();
208
+
209
+ // Array length
210
+ await alice.checklist.list().toHaveLength(3);
211
+
212
+ // Array containment
213
+ await alice.checklist.list()
214
+ .arrayToContain({title: "Buy groceries"});
215
+ ```
216
+
217
+ ### Error assertions
218
+
219
+ When testing error responses, use `.toBeError()` with an error class. After `.toBeError()`, further `.toMatchObject()` calls check the error data.
220
+
221
+ ```typescript
222
+ import {NOT_AUTHORIZED, VALIDATION_ERROR, NOT_FOUND, FORBIDDEN, EXISTS} from "@grest-ts/schema";
223
+
224
+ // Authorization errors
225
+ await john.building.sync({...}).toBeError(NOT_AUTHORIZED);
226
+
227
+ // Validation errors - check individual field errors
228
+ await alice.user.register({username: "ab", password: "", email: "invalid"})
229
+ .toBeError(VALIDATION_ERROR)
230
+ .toMatchObject({
231
+ username: {__issue: {message: "Value must be between 3 and 10 characters"}},
232
+ email: {__issue: {message: "Invalid email format"}},
233
+ });
234
+
235
+ // Not found
236
+ await alice.user.get({id: 999}).toBeError(NOT_FOUND);
237
+
238
+ // Custom error types
239
+ await alice.user.login({username: "alice", password: "wrong"})
240
+ .toBeError(InvalidCredentialsError);
241
+ ```
242
+
243
+ ### Chaining with interceptors
244
+
245
+ Assertions chain naturally with `.with()` and `.waitFor()`:
246
+
247
+ ```typescript
248
+ await alice.user.login(loginData)
249
+ .with(BlockerApi.spy.checkBlock.toMatchObject({username: "alice"}))
250
+ .toMatchObject({user: {username: "alice"}, token: expect.any(String)});
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Database cloning
256
+
257
+ The testkit provides database cloning for test isolation. Each test suite gets its own database clone, ensuring parallel test execution without conflicts.
258
+
259
+ ### MySQL
260
+
261
+ ```typescript
262
+ import {GGTest} from "@grest-ts/testkit";
263
+ import {MyConfig} from "../src/MyConfig";
264
+ import {mysqlLocal} from "../config/local";
265
+
266
+ describe("my tests", () => {
267
+ GGTest.startWorker(MyRuntime);
268
+
269
+ // Basic clone with explicit source config
270
+ GGTest.with(MyConfig.mysql).clone({from: mysqlLocal});
271
+
272
+ // With seed files
273
+ GGTest.with(MyConfig.mysql).clone({
274
+ from: mysqlLocal,
275
+ seedFiles: ["./test/seed/admin-users.sql"]
276
+ });
277
+
278
+ // Shared clone across parallel workers (same DB for all workers in this group)
279
+ GGTest.with(MyConfig.mysql).clone({
280
+ from: mysqlLocal,
281
+ group: "shared",
282
+ seedFiles: ["./test/seed/admin-users.sql"]
283
+ });
284
+ });
285
+ ```
286
+
287
+ ### PostgreSQL
288
+
289
+ ```typescript
290
+ // Same API, just with postgres config
291
+ GGTest.with(MyConfig.postgres).clone({from: postgresLocal});
292
+ GGTest.with(MyConfig.postgres).clone({from: postgresLocal, group: "shared"});
293
+ ```
294
+
295
+ ### Shorthand forms
296
+
297
+ ```typescript
298
+ // If GGResource has a default value, no `from` needed:
299
+ GGTest.with(MyConfig.mysql).clone();
300
+
301
+ // Seed files as string or array:
302
+ GGTest.with(MyConfig.mysql).clone("seed.sql");
303
+ GGTest.with(MyConfig.mysql).clone(["seed1.sql", "seed2.sql"]);
304
+ ```
305
+
306
+ ### Clone options
307
+
308
+ | Option | Type | Description |
309
+ |--------|------|-------------|
310
+ | `from` | `{host, user}` | Source database config and credentials. Required when GGResource has no default value. |
311
+ | `seedFiles` | `string[]` | SQL files to run after cloning the schema. |
312
+ | `group` | `string` | Group name for shared DB cloning across workers. Tests with the same group share one clone. |
313
+
314
+ ### How it works
315
+
316
+ 1. The source database schema is cloned into a unique test schema (named `{db}_{runId}_{groupId}`).
317
+ 2. If the source database doesn't exist and a `schemaFile` is configured, it is created automatically.
318
+ 3. Seed files are executed after cloning.
319
+ 4. The cloned schema is automatically cleaned up after tests complete.
320
+ 5. When `group` is specified, multiple test suites share the same clone via reference counting.
321
+
322
+ ---
323
+
324
+ ## Mocking & spying on @mockable services
325
+
326
+ Services decorated with `@mockable` can be mocked or spied on in tests using `mockOf()` and `spyOn()`.
327
+
328
+ This is different from schema-level mock/spy (like `MyApi.mock.method`) which intercepts HTTP calls between runtimes.
329
+ `mockOf`/`spyOn` intercept calls to internal services within a runtime.
330
+
331
+ ### mockOf() - replace with fake data
332
+
333
+ ```typescript
334
+ import {GGTest, mockOf} from "@grest-ts/testkit";
335
+
336
+ test('mock external service', async () => {
337
+ await alice.checklist.add({title: "Visit Times Square", address: "123 Main St"})
338
+ .with(mockOf(AddressResolverService).resolveAddress
339
+ .toEqual({address: "123 Main St"}) // Validate input
340
+ .andReturn({lat: 40.7589, lng: -73.9851}) // Return fake data
341
+ )
342
+ .toMatchObject({
343
+ title: "Visit Times Square",
344
+ lat: 40.7589,
345
+ lng: -73.9851
346
+ });
347
+ });
348
+ ```
349
+
350
+ ### spyOn() - call through and validate
351
+
352
+ ```typescript
353
+ import {GGTest, spyOn} from "@grest-ts/testkit";
354
+
355
+ test('spy on external service', async () => {
356
+ await alice.checklist.add({title: "Visit Times Square", address: "123 Main St"})
357
+ .with(spyOn(AddressResolverService).resolveAddress
358
+ .toEqual({address: "123 Main St"}) // Validate input
359
+ .responseToMatchObject({lat: 40.7128, lng: -74.0060}) // Validate real response
360
+ );
361
+ });
362
+ ```
363
+
364
+ ### Mock/spy options
365
+
366
+ ```typescript
367
+ // Mock with expected call count
368
+ .with(mockOf(Service).method
369
+ .andReturn(result)
370
+ .times(2)) // Expect exactly 2 calls
371
+
372
+ // Mock with delay
373
+ .with(mockOf(Service).method
374
+ .andReturn(result)
375
+ .sleep(100)) // Wait 100ms before returning
376
+
377
+ // Mock returning an error
378
+ .with(mockOf(Service).method
379
+ .andReturn(new NOT_AUTHORIZED()))
380
+
381
+ // Spy switching to response validation
382
+ .with(spyOn(Service).method
383
+ .toMatchObject({input: "data"}) // Validate input
384
+ .response.toMatchObject({out: 1}) // Switch to response, then validate
385
+ )
386
+
387
+ // Spy expecting error response
388
+ .with(spyOn(Service).method
389
+ .toBeError(NOT_FOUND)
390
+ )
391
+ ```
392
+
393
+ ### Schema mock/spy vs @mockable mock/spy
394
+
395
+ | | Schema mock/spy (`MyApi.mock.method`) | @mockable mock/spy (`mockOf(Service).method`) |
396
+ |---|---|---|
397
+ | **What it intercepts** | HTTP calls between runtimes | Internal method calls within a runtime |
398
+ | **Use case** | Mock/spy on service-to-service communication | Mock/spy on external dependencies (3rd party APIs, etc.) |
399
+ | **How to use** | `MyApi.mock.method.andReturn(...)` | `mockOf(Service).method.andReturn(...)` |
400
+ | **Import** | `import "@grest-ts/http/testkit"` | `import {mockOf, spyOn} from "@grest-ts/testkit"` |
401
+
402
+ ---
403
+
404
+ ## Usage
405
+
406
+ Check README-testkit.md files within packages you are interested in. Common ones being:
407
+
408
+ - [Logger](../../packages/logger/logger/README-testkit.md) - Accessing logs during the test flow.
409
+ - [Metrics](../../packages/metrics/README-testkit.md) - Accessing metrics during the test flow.
410
+
411
+ ## Extending framework with custom packages and adding testkit capabilities.
412
+
413
+ - [Extending Guide](./README-extending.md) - How to create testkit extensions - adding capabilities to the testing framework for custom packages.
@@ -0,0 +1,8 @@
1
+ interface GGBundleTestOptions {
2
+ entryPoint: string;
3
+ }
4
+ export declare class GGBundleTest {
5
+ static verify(options: GGBundleTestOptions): void;
6
+ }
7
+ export {};
8
+ //# sourceMappingURL=GGBundleTest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GGBundleTest.d.ts","sourceRoot":"","sources":["../../src/GGBundleTest.ts"],"names":[],"mappings":"AAoBA,UAAU,mBAAmB;IACzB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,YAAY;IAErB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,mBAAmB;CA8D7C"}
@@ -0,0 +1,75 @@
1
+ import { describe, test, expect, beforeAll } from "vitest";
2
+ import { fileURLToPath } from "url";
3
+ const FORBIDDEN_SYMBOLS = {
4
+ IPC: ['IPCServer', 'IPCClient', 'IPCSocket'],
5
+ 'discovery-local': [
6
+ 'GGLocalDiscoveryClient',
7
+ 'GGLocalDiscoveryResilientClient',
8
+ 'GGLocalDiscoveryServer',
9
+ ],
10
+ testkit: [
11
+ 'GGTestRunner',
12
+ 'GGTestRuntime',
13
+ 'startWorker',
14
+ 'startInline',
15
+ 'startIsolated',
16
+ ],
17
+ };
18
+ export class GGBundleTest {
19
+ static verify(options) {
20
+ const entryPoint = options.entryPoint.startsWith('file://')
21
+ ? fileURLToPath(options.entryPoint)
22
+ : options.entryPoint;
23
+ describe("production bundle", () => {
24
+ let result;
25
+ let bundleText;
26
+ beforeAll(async () => {
27
+ const esbuild = await import("esbuild");
28
+ const externalizeNonGG = {
29
+ name: 'externalize-non-gg',
30
+ setup(build) {
31
+ build.onResolve({ filter: /./ }, args => {
32
+ // Only externalize import statements, not entry points
33
+ if (args.kind !== 'import-statement' && args.kind !== 'dynamic-import')
34
+ return null;
35
+ // Keep relative/absolute paths bundled
36
+ if (args.path.startsWith('.') || args.path.startsWith('/'))
37
+ return null;
38
+ // Keep @grest-ts/* packages bundled
39
+ if (args.path.startsWith('@grest-ts/'))
40
+ return null;
41
+ return { path: args.path, external: true };
42
+ });
43
+ }
44
+ };
45
+ result = await esbuild.build({
46
+ entryPoints: [entryPoint],
47
+ bundle: true,
48
+ write: false,
49
+ platform: 'node',
50
+ format: 'esm',
51
+ define: { 'process.env.NODE_ENV': '"production"' },
52
+ plugins: [externalizeNonGG],
53
+ });
54
+ bundleText = result.outputFiles.map(f => f.text).join('\n');
55
+ });
56
+ test("bundle has no errors", () => {
57
+ expect(result.errors).toHaveLength(0);
58
+ });
59
+ test("bundle is non-trivially sized", () => {
60
+ expect(bundleText.length).toBeGreaterThan(100);
61
+ });
62
+ for (const [group, symbols] of Object.entries(FORBIDDEN_SYMBOLS)) {
63
+ for (const symbol of symbols) {
64
+ test(`does not contain dev-only symbol: ${symbol} (${group})`, () => {
65
+ // Match actual definitions (var X = class, class X, function X)
66
+ // but not dead-code references like: const { X } = await null
67
+ const definitionPattern = new RegExp(`\\b(var|let|const|class|function)\\s+${symbol}\\b`);
68
+ expect(bundleText).not.toMatch(definitionPattern);
69
+ });
70
+ }
71
+ }
72
+ });
73
+ }
74
+ }
75
+ //# sourceMappingURL=GGBundleTest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GGBundleTest.js","sourceRoot":"","sources":["../../src/GGBundleTest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ import { GG_TEST_RESOURCE, TestResource } from "./GGTestRunner";
2
+ import { RuntimeConstructor, RuntimeInput, StartResult, Selector } from "./testers/RuntimeSelector";
3
+ import { IGGTestWith } from "./testers/IGGTestWith";
4
+ /**
5
+ * Make a result awaitable by adding a then() method.
6
+ */
7
+ type Awaitable<T> = T & PromiseLike<T>;
8
+ /**
9
+ * Static API for test setup operations.
10
+ * Use GGTest.startWorker(), GGTest.with(), etc. in test files.
11
+ *
12
+ * @example
13
+ * // Single runtime
14
+ * const t = GGTest.startWorker(MainRuntime);
15
+ * t.logs.cursor();
16
+ *
17
+ * // Multiple instances
18
+ * const t = GGTest.startWorker([MainRuntime, MainRuntime]);
19
+ * t[0].logs.cursor();
20
+ *
21
+ * // Named runtimes
22
+ * const t = GGTest.startWorker({main: MainRuntime, sub: SubRuntime});
23
+ * t.main.logs.cursor();
24
+ * t.sub.config.update();
25
+ */
26
+ export declare class GGTest {
27
+ private static readonly forceCoverageInline;
28
+ /**
29
+ * Get a resource wrapper for calling test operations.
30
+ * Returns an object with methods defined by the resource's [GG_TEST_RESOURCE].
31
+ *
32
+ * @example
33
+ * GGTest.with(MyConfig.mysql).clone();
34
+ * GGTest.with(MyConfig.mysql).clone("seed.sql");
35
+ */
36
+ static with<T extends TestResource>(resource: T): T[typeof GG_TEST_RESOURCE];
37
+ /**
38
+ * Start runtimes in inline mode.
39
+ * Inline mode runs the service code in the same process as the test.
40
+ *
41
+ * @example
42
+ * // Single runtime
43
+ * const t = GGTest.startInline(MainRuntime);
44
+ *
45
+ * // Array of runtimes (multiple instances)
46
+ * const t = GGTest.startInline([MainRuntime, MainRuntime]);
47
+ *
48
+ * // Object with named runtimes
49
+ * const t = GGTest.startInline({main: MainRuntime, sub: SubRuntime});
50
+ */
51
+ static startInline<const T extends RuntimeInput>(input: T): Awaitable<StartResult<T>>;
52
+ /**
53
+ * Start runtimes in worker mode.
54
+ * Worker mode runs the service code in a worker thread.
55
+ *
56
+ * @example
57
+ * // Single runtime
58
+ * const t = GGTest.startWorker(MainRuntime);
59
+ *
60
+ * // Array of runtimes (multiple instances)
61
+ * const t = GGTest.startWorker([MainRuntime, MainRuntime]);
62
+ *
63
+ * // Object with named runtimes
64
+ * const t = GGTest.startWorker({main: MainRuntime, sub: SubRuntime});
65
+ */
66
+ static startWorker<T extends RuntimeInput>(input: T): Awaitable<StartResult<T>>;
67
+ /**
68
+ * Start runtimes in isolated mode.
69
+ * Isolated mode runs the service code in a separate process.
70
+ *
71
+ * @example
72
+ * // Single runtime
73
+ * const t = GGTest.startIsolated(MainRuntime);
74
+ *
75
+ * // Array of runtimes (multiple instances)
76
+ * const t = GGTest.startIsolated([MainRuntime, MainRuntime]);
77
+ *
78
+ * // Object with named runtimes
79
+ * const t = GGTest.startIsolated({main: MainRuntime, sub: SubRuntime});
80
+ */
81
+ static startIsolated<T extends RuntimeInput>(input: T): Awaitable<StartResult<T>>;
82
+ /**
83
+ * Wait for an async expectation to be satisfied.
84
+ * Use this for standalone async expectations outside of action chains.
85
+ *
86
+ * Polls the expectation every 20ms until it's satisfied or timeout is reached.
87
+ *
88
+ * @param expectation - The expectation to wait for
89
+ * @param timeout - Max time to wait in ms (default: 5000)
90
+ *
91
+ * @example
92
+ * // Wait for a log after injecting an event
93
+ * await UserEventsPublisher.inject.registered({...});
94
+ * await GGTest.waitFor(t.logs.expect(/validation failed/i));
95
+ *
96
+ * @example
97
+ * // Wait for a metric to be incremented
98
+ * await GGTest.waitFor(t.main.metrics.expect(SomeMetric).inc({label: 'value'}));
99
+ */
100
+ static waitFor(expectation: IGGTestWith, timeout?: number): Promise<void>;
101
+ private static startWithMode;
102
+ /**
103
+ * Extract runtime constructors from the input in the order they should be created.
104
+ */
105
+ private static extractConstructors;
106
+ /**
107
+ * Start runtimes immediately (for in-test usage).
108
+ * Runtimes are automatically tracked by the runner for afterEach cleanup.
109
+ */
110
+ private static startRuntimesInTestBlock;
111
+ /**
112
+ * Makes a result thenable by adding a then() method.
113
+ * This allows both sync and async usage patterns.
114
+ *
115
+ * IMPORTANT: The then() method removes itself after being called to prevent
116
+ * infinite thenable resolution. When onfulfilled returns the result,
117
+ * JavaScript would try to resolve it again if then() still existed.
118
+ */
119
+ private static makeAwaitable;
120
+ private static getCoverageMode;
121
+ private static createRuntimes;
122
+ }
123
+ /**
124
+ * @deprecated Use StartResult instead
125
+ */
126
+ export type { RuntimeResult } from "./testers/RuntimeSelector";
127
+ /**
128
+ * @deprecated Use Awaitable<StartResult<T>> instead
129
+ */
130
+ export type AwaitableRuntimeResult<R extends RuntimeConstructor[]> = Awaitable<Selector<R>>;
131
+ //# sourceMappingURL=GGTest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GGTest.d.ts","sourceRoot":"","sources":["../../src/GGTest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,gBAAgB,EAAgC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5F,OAAO,EAAC,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAC,MAAM,2BAA2B,CAAC;AAGlG,OAAO,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAC;AAMlD;;GAEG;AACH,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;AAEvC;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,MAAM;IAQf,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAwC;IAMnF;;;;;;;OAOG;WACW,IAAI,CAAC,CAAC,SAAS,YAAY,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,gBAAgB,CAAC;IAInF;;;;;;;;;;;;;OAaG;WACW,WAAW,CAAC,KAAK,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAI5F;;;;;;;;;;;;;OAaG;WACW,WAAW,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAItF;;;;;;;;;;;;;OAaG;WACW,aAAa,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAIxF;;;;;;;;;;;;;;;;;OAiBG;WACiB,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA4C5F,OAAO,CAAC,MAAM,CAAC,aAAa;IAqB5B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAuBlC;;;OAGG;mBACkB,wBAAwB;IAM7C;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAkB5B,OAAO,CAAC,MAAM,CAAC,eAAe;IAO9B,OAAO,CAAC,MAAM,CAAC,cAAc;CAsChC;AAMD;;GAEG;AACH,YAAY,EAAC,aAAa,EAAC,MAAM,2BAA2B,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,kBAAkB,EAAE,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC"}