@stratal/testing 0.0.13 → 0.0.14

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 (155) hide show
  1. package/dist/index.d.mts +1070 -0
  2. package/dist/index.d.mts.map +1 -0
  3. package/dist/index.mjs +1689 -0
  4. package/dist/index.mjs.map +1 -0
  5. package/dist/mocks/index.d.mts +2 -0
  6. package/dist/mocks/index.mjs +2 -0
  7. package/dist/mocks/nodemailer.d.mts +12 -0
  8. package/dist/mocks/nodemailer.d.mts.map +1 -0
  9. package/dist/mocks/nodemailer.mjs +7 -0
  10. package/dist/mocks/nodemailer.mjs.map +1 -0
  11. package/dist/mocks/zenstack-language.d.mts +48 -0
  12. package/dist/mocks/zenstack-language.d.mts.map +1 -0
  13. package/dist/mocks/zenstack-language.mjs +48 -0
  14. package/dist/mocks/zenstack-language.mjs.map +1 -0
  15. package/dist/vitest-plugin/index.d.mts +50 -0
  16. package/dist/vitest-plugin/index.d.mts.map +1 -0
  17. package/dist/vitest-plugin/index.mjs +86 -0
  18. package/dist/vitest-plugin/index.mjs.map +1 -0
  19. package/package.json +21 -19
  20. package/dist/auth/acting-as.d.ts +0 -21
  21. package/dist/auth/acting-as.d.ts.map +0 -1
  22. package/dist/auth/acting-as.js +0 -68
  23. package/dist/auth/acting-as.js.map +0 -1
  24. package/dist/auth/index.d.ts +0 -2
  25. package/dist/auth/index.d.ts.map +0 -1
  26. package/dist/auth/index.js +0 -2
  27. package/dist/auth/index.js.map +0 -1
  28. package/dist/core/env/index.d.ts +0 -2
  29. package/dist/core/env/index.d.ts.map +0 -1
  30. package/dist/core/env/index.js +0 -2
  31. package/dist/core/env/index.js.map +0 -1
  32. package/dist/core/env/test-env.d.ts +0 -9
  33. package/dist/core/env/test-env.d.ts.map +0 -1
  34. package/dist/core/env/test-env.js +0 -14
  35. package/dist/core/env/test-env.js.map +0 -1
  36. package/dist/core/http/fetch-mock.types.d.ts +0 -48
  37. package/dist/core/http/fetch-mock.types.d.ts.map +0 -1
  38. package/dist/core/http/fetch-mock.types.js +0 -2
  39. package/dist/core/http/fetch-mock.types.js.map +0 -1
  40. package/dist/core/http/index.d.ts +0 -6
  41. package/dist/core/http/index.d.ts.map +0 -1
  42. package/dist/core/http/index.js +0 -5
  43. package/dist/core/http/index.js.map +0 -1
  44. package/dist/core/http/mock-fetch.d.ts +0 -88
  45. package/dist/core/http/mock-fetch.d.ts.map +0 -1
  46. package/dist/core/http/mock-fetch.js +0 -111
  47. package/dist/core/http/mock-fetch.js.map +0 -1
  48. package/dist/core/http/test-http-client.d.ts +0 -54
  49. package/dist/core/http/test-http-client.d.ts.map +0 -1
  50. package/dist/core/http/test-http-client.js +0 -75
  51. package/dist/core/http/test-http-client.js.map +0 -1
  52. package/dist/core/http/test-http-request.d.ts +0 -60
  53. package/dist/core/http/test-http-request.d.ts.map +0 -1
  54. package/dist/core/http/test-http-request.js +0 -106
  55. package/dist/core/http/test-http-request.js.map +0 -1
  56. package/dist/core/http/test-response.d.ts +0 -161
  57. package/dist/core/http/test-response.d.ts.map +0 -1
  58. package/dist/core/http/test-response.js +0 -309
  59. package/dist/core/http/test-response.js.map +0 -1
  60. package/dist/core/index.d.ts +0 -7
  61. package/dist/core/index.d.ts.map +0 -1
  62. package/dist/core/index.js +0 -7
  63. package/dist/core/index.js.map +0 -1
  64. package/dist/core/override/index.d.ts +0 -2
  65. package/dist/core/override/index.d.ts.map +0 -1
  66. package/dist/core/override/index.js +0 -2
  67. package/dist/core/override/index.js.map +0 -1
  68. package/dist/core/override/provider-override-builder.d.ts +0 -78
  69. package/dist/core/override/provider-override-builder.d.ts.map +0 -1
  70. package/dist/core/override/provider-override-builder.js +0 -94
  71. package/dist/core/override/provider-override-builder.js.map +0 -1
  72. package/dist/core/sse/index.d.ts +0 -3
  73. package/dist/core/sse/index.d.ts.map +0 -1
  74. package/dist/core/sse/index.js +0 -3
  75. package/dist/core/sse/index.js.map +0 -1
  76. package/dist/core/sse/test-sse-connection.d.ts +0 -61
  77. package/dist/core/sse/test-sse-connection.d.ts.map +0 -1
  78. package/dist/core/sse/test-sse-connection.js +0 -233
  79. package/dist/core/sse/test-sse-connection.js.map +0 -1
  80. package/dist/core/sse/test-sse-request.d.ts +0 -42
  81. package/dist/core/sse/test-sse-request.d.ts.map +0 -1
  82. package/dist/core/sse/test-sse-request.js +0 -76
  83. package/dist/core/sse/test-sse-request.js.map +0 -1
  84. package/dist/core/test.d.ts +0 -48
  85. package/dist/core/test.d.ts.map +0 -1
  86. package/dist/core/test.js +0 -53
  87. package/dist/core/test.js.map +0 -1
  88. package/dist/core/testing-module-builder.d.ts +0 -57
  89. package/dist/core/testing-module-builder.d.ts.map +0 -1
  90. package/dist/core/testing-module-builder.js +0 -109
  91. package/dist/core/testing-module-builder.js.map +0 -1
  92. package/dist/core/testing-module.d.ts +0 -113
  93. package/dist/core/testing-module.d.ts.map +0 -1
  94. package/dist/core/testing-module.js +0 -177
  95. package/dist/core/testing-module.js.map +0 -1
  96. package/dist/core/ws/index.d.ts +0 -3
  97. package/dist/core/ws/index.d.ts.map +0 -1
  98. package/dist/core/ws/index.js +0 -3
  99. package/dist/core/ws/index.js.map +0 -1
  100. package/dist/core/ws/test-ws-connection.d.ts +0 -54
  101. package/dist/core/ws/test-ws-connection.d.ts.map +0 -1
  102. package/dist/core/ws/test-ws-connection.js +0 -119
  103. package/dist/core/ws/test-ws-connection.js.map +0 -1
  104. package/dist/core/ws/test-ws-request.d.ts +0 -43
  105. package/dist/core/ws/test-ws-request.d.ts.map +0 -1
  106. package/dist/core/ws/test-ws-request.js +0 -83
  107. package/dist/core/ws/test-ws-request.js.map +0 -1
  108. package/dist/errors/index.d.ts +0 -3
  109. package/dist/errors/index.d.ts.map +0 -1
  110. package/dist/errors/index.js +0 -3
  111. package/dist/errors/index.js.map +0 -1
  112. package/dist/errors/setup-error.d.ts +0 -9
  113. package/dist/errors/setup-error.d.ts.map +0 -1
  114. package/dist/errors/setup-error.js +0 -11
  115. package/dist/errors/setup-error.js.map +0 -1
  116. package/dist/errors/test-error.d.ts +0 -9
  117. package/dist/errors/test-error.d.ts.map +0 -1
  118. package/dist/errors/test-error.js +0 -15
  119. package/dist/errors/test-error.js.map +0 -1
  120. package/dist/index.d.ts +0 -21
  121. package/dist/index.d.ts.map +0 -1
  122. package/dist/index.js +0 -29
  123. package/dist/index.js.map +0 -1
  124. package/dist/mocks/index.d.ts +0 -3
  125. package/dist/mocks/index.d.ts.map +0 -1
  126. package/dist/mocks/index.js +0 -3
  127. package/dist/mocks/index.js.map +0 -1
  128. package/dist/mocks/nodemailer.d.ts +0 -10
  129. package/dist/mocks/nodemailer.d.ts.map +0 -1
  130. package/dist/mocks/nodemailer.js +0 -9
  131. package/dist/mocks/nodemailer.js.map +0 -1
  132. package/dist/mocks/zenstack-language.d.ts +0 -46
  133. package/dist/mocks/zenstack-language.d.ts.map +0 -1
  134. package/dist/mocks/zenstack-language.js +0 -47
  135. package/dist/mocks/zenstack-language.js.map +0 -1
  136. package/dist/storage/fake-storage.service.d.ts +0 -114
  137. package/dist/storage/fake-storage.service.d.ts.map +0 -1
  138. package/dist/storage/fake-storage.service.js +0 -233
  139. package/dist/storage/fake-storage.service.js.map +0 -1
  140. package/dist/storage/index.d.ts +0 -2
  141. package/dist/storage/index.d.ts.map +0 -1
  142. package/dist/storage/index.js +0 -2
  143. package/dist/storage/index.js.map +0 -1
  144. package/dist/types.d.ts +0 -5
  145. package/dist/types.d.ts.map +0 -1
  146. package/dist/types.js +0 -3
  147. package/dist/types.js.map +0 -1
  148. package/dist/vitest-plugin/index.d.ts +0 -2
  149. package/dist/vitest-plugin/index.d.ts.map +0 -1
  150. package/dist/vitest-plugin/index.js +0 -2
  151. package/dist/vitest-plugin/index.js.map +0 -1
  152. package/dist/vitest-plugin/stratal-test.d.ts +0 -28
  153. package/dist/vitest-plugin/stratal-test.d.ts.map +0 -1
  154. package/dist/vitest-plugin/stratal-test.js +0 -47
  155. package/dist/vitest-plugin/stratal-test.js.map +0 -1
@@ -0,0 +1,1070 @@
1
+ import { Application, ApplicationConfig, StratalEnv } from "stratal";
2
+ import { DynamicModule, InjectionToken, ModuleClass, ModuleOptions } from "stratal/module";
3
+ import { DownloadResult, PresignedUrlResult, StorageConfig, StorageManagerService, StorageService, StreamingBlobPayloadInputTypes, UploadOptions, UploadResult } from "stratal/storage";
4
+ import { Container } from "stratal/di";
5
+ import { ConnectionName, DatabaseService, DefaultConnectionName } from "@stratal/framework/database";
6
+ import { AuthService } from "@stratal/framework/auth";
7
+ import { HttpResponse, RequestHandler, http } from "msw";
8
+
9
+ //#region src/storage/fake-storage.service.d.ts
10
+ /**
11
+ * Stored file representation in memory
12
+ */
13
+ interface StoredFile {
14
+ content: Uint8Array;
15
+ mimeType: string;
16
+ size: number;
17
+ metadata?: Record<string, string>;
18
+ uploadedAt: Date;
19
+ }
20
+ /**
21
+ * FakeStorageService
22
+ *
23
+ * In-memory storage implementation for testing.
24
+ * Registered by default in TestingModuleBuilder.
25
+ *
26
+ * Similar to Laravel's Storage::fake() - stores files in memory
27
+ * and provides assertion helpers for testing.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * // Access via TestingModule
32
+ * module.storage.assertExists('path/to/file.pdf')
33
+ * module.storage.assertMissing('deleted/file.pdf')
34
+ * module.storage.clear() // Reset between tests
35
+ * ```
36
+ */
37
+ declare class FakeStorageService extends StorageService {
38
+ protected readonly storageManager: StorageManagerService;
39
+ protected readonly options: StorageConfig;
40
+ private files;
41
+ constructor(storageManager: StorageManagerService, options: StorageConfig);
42
+ /**
43
+ * Upload content to fake storage
44
+ */
45
+ upload(body: StreamingBlobPayloadInputTypes, relativePath: string, options: UploadOptions, disk?: string): Promise<UploadResult>;
46
+ /**
47
+ * Download a file from fake storage
48
+ */
49
+ download(path: string): Promise<DownloadResult>;
50
+ /**
51
+ * Delete a file from fake storage
52
+ */
53
+ delete(path: string): Promise<void>;
54
+ /**
55
+ * Check if a file exists in fake storage
56
+ */
57
+ exists(path: string): Promise<boolean>;
58
+ /**
59
+ * Generate a fake presigned download URL
60
+ */
61
+ getPresignedDownloadUrl(path: string, expiresIn?: number): Promise<PresignedUrlResult>;
62
+ /**
63
+ * Generate a fake presigned upload URL
64
+ */
65
+ getPresignedUploadUrl(path: string, expiresIn?: number): Promise<PresignedUrlResult>;
66
+ /**
67
+ * Generate a fake presigned delete URL
68
+ */
69
+ getPresignedDeleteUrl(path: string, expiresIn?: number): Promise<PresignedUrlResult>;
70
+ /**
71
+ * Chunked upload (same as regular upload for fake)
72
+ */
73
+ chunkedUpload(body: StreamingBlobPayloadInputTypes, path: string, options: Omit<UploadOptions, 'size'> & {
74
+ size?: number;
75
+ }, disk?: string): Promise<UploadResult>;
76
+ /**
77
+ * Assert that a file exists at the given path
78
+ *
79
+ * @param path - Path to check
80
+ * @throws AssertionError if file does not exist
81
+ */
82
+ assertExists(path: string): void;
83
+ /**
84
+ * Assert that a file does NOT exist at the given path
85
+ *
86
+ * @param path - Path to check
87
+ * @throws AssertionError if file exists
88
+ */
89
+ assertMissing(path: string): void;
90
+ /**
91
+ * Assert storage is empty
92
+ *
93
+ * @throws AssertionError if any files exist
94
+ */
95
+ assertEmpty(): void;
96
+ /**
97
+ * Assert storage has exactly N files
98
+ *
99
+ * @param count - Expected number of files
100
+ * @throws AssertionError if count doesn't match
101
+ */
102
+ assertCount(count: number): void;
103
+ /**
104
+ * Get all stored files (for inspection)
105
+ */
106
+ getStoredFiles(): Map<string, StoredFile>;
107
+ /**
108
+ * Get all stored file paths
109
+ */
110
+ getStoredPaths(): string[];
111
+ /**
112
+ * Get a specific file by path
113
+ */
114
+ getFile(path: string): StoredFile | undefined;
115
+ /**
116
+ * Clear all stored files (call in beforeEach for test isolation)
117
+ */
118
+ clear(): void;
119
+ private createPresignedUrl;
120
+ private bodyToUint8Array;
121
+ }
122
+ //#endregion
123
+ //#region src/types.d.ts
124
+ declare abstract class Seeder<K extends ConnectionName = DefaultConnectionName> {
125
+ abstract run(db: DatabaseService<K>): Promise<void>;
126
+ }
127
+ //#endregion
128
+ //#region src/core/http/test-response.d.ts
129
+ /**
130
+ * TestResponse
131
+ *
132
+ * Wrapper around Response with assertion methods.
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * response
137
+ * .assertOk()
138
+ * .assertStatus(200)
139
+ * .assertJsonPath('data.id', userId)
140
+ * ```
141
+ */
142
+ declare class TestResponse {
143
+ private readonly response;
144
+ private jsonData;
145
+ private textData;
146
+ constructor(response: Response);
147
+ /**
148
+ * Get the raw Response object.
149
+ */
150
+ get raw(): Response;
151
+ /**
152
+ * Get the response status code.
153
+ */
154
+ get status(): number;
155
+ /**
156
+ * Get response headers.
157
+ */
158
+ get headers(): Headers;
159
+ /**
160
+ * Get the response body as JSON.
161
+ */
162
+ json<T = unknown>(): Promise<T>;
163
+ /**
164
+ * Get the response body as text.
165
+ */
166
+ text(): Promise<string>;
167
+ /**
168
+ * Assert response status is 200 OK.
169
+ */
170
+ assertOk(): this;
171
+ /**
172
+ * Assert response status is 201 Created.
173
+ */
174
+ assertCreated(): this;
175
+ /**
176
+ * Assert response status is 204 No Content.
177
+ */
178
+ assertNoContent(): this;
179
+ /**
180
+ * Assert response status is 400 Bad Request.
181
+ */
182
+ assertBadRequest(): this;
183
+ /**
184
+ * Assert response status is 401 Unauthorized.
185
+ */
186
+ assertUnauthorized(): this;
187
+ /**
188
+ * Assert response status is 403 Forbidden.
189
+ */
190
+ assertForbidden(): this;
191
+ /**
192
+ * Assert response status is 404 Not Found.
193
+ */
194
+ assertNotFound(): this;
195
+ /**
196
+ * Assert response status is 422 Unprocessable Entity.
197
+ */
198
+ assertUnprocessable(): this;
199
+ /**
200
+ * Assert response status is 500 Internal Server Error.
201
+ */
202
+ assertServerError(): this;
203
+ /**
204
+ * Assert response has the given status code.
205
+ */
206
+ assertStatus(expected: number): this;
207
+ /**
208
+ * Assert response status is in the 2xx success range.
209
+ */
210
+ assertSuccessful(): this;
211
+ /**
212
+ * Assert JSON response contains the given data.
213
+ */
214
+ assertJson(expected: Record<string, unknown>): Promise<this>;
215
+ /**
216
+ * Assert JSON response has value at the given path.
217
+ *
218
+ * @param path - Dot-notation path (e.g., 'data.user.id')
219
+ * @param expected - Expected value at path
220
+ */
221
+ assertJsonPath(path: string, expected: unknown): Promise<this>;
222
+ /**
223
+ * Assert JSON response structure matches the given schema.
224
+ */
225
+ assertJsonStructure(structure: string[]): Promise<this>;
226
+ /**
227
+ * Assert a JSON path exists (value can be anything, including null).
228
+ *
229
+ * @param path - Dot-notation path (e.g., 'data.user.id')
230
+ */
231
+ assertJsonPathExists(path: string): Promise<this>;
232
+ /**
233
+ * Assert a JSON path does not exist.
234
+ *
235
+ * @param path - Dot-notation path (e.g., 'data.user.deletedAt')
236
+ */
237
+ assertJsonPathMissing(path: string): Promise<this>;
238
+ /**
239
+ * Assert JSON value at path matches a custom predicate.
240
+ *
241
+ * @param path - Dot-notation path (e.g., 'data.items')
242
+ * @param matcher - Predicate function to validate the value
243
+ */
244
+ assertJsonPathMatches(path: string, matcher: (value: unknown) => boolean): Promise<this>;
245
+ /**
246
+ * Assert string value at path contains a substring.
247
+ *
248
+ * @param path - Dot-notation path (e.g., 'data.message')
249
+ * @param substring - Substring to search for
250
+ */
251
+ assertJsonPathContains(path: string, substring: string): Promise<this>;
252
+ /**
253
+ * Assert array value at path includes a specific item.
254
+ *
255
+ * @param path - Dot-notation path (e.g., 'data.tags')
256
+ * @param item - Item to search for in the array
257
+ */
258
+ assertJsonPathIncludes(path: string, item: unknown): Promise<this>;
259
+ /**
260
+ * Assert array length at path equals the expected count.
261
+ *
262
+ * @param path - Dot-notation path (e.g., 'data.items')
263
+ * @param count - Expected array length
264
+ */
265
+ assertJsonPathCount(path: string, count: number): Promise<this>;
266
+ /**
267
+ * Assert multiple JSON paths at once (batch assertion).
268
+ *
269
+ * @param expectations - Object mapping paths to expected values
270
+ */
271
+ assertJsonPaths(expectations: Record<string, unknown>): Promise<this>;
272
+ /**
273
+ * Assert response has the given header.
274
+ */
275
+ assertHeader(name: string, expected?: string): this;
276
+ /**
277
+ * Assert response does not have the given header.
278
+ */
279
+ assertHeaderMissing(name: string): this;
280
+ /**
281
+ * Get value at dot-notation path.
282
+ */
283
+ private getValueAtPath;
284
+ /**
285
+ * Check if a path exists in the object (even if value is null/undefined).
286
+ */
287
+ private hasValueAtPath;
288
+ }
289
+ //#endregion
290
+ //#region src/core/http/test-http-request.d.ts
291
+ /**
292
+ * TestHttpRequest
293
+ *
294
+ * Request builder with fluent API for configuring test HTTP requests.
295
+ *
296
+ * @example
297
+ * ```typescript
298
+ * const response = await module.http
299
+ * .post('/api/v1/register')
300
+ * .withBody({ name: 'Test School' })
301
+ * .withHeaders({ 'X-Custom': 'value' })
302
+ * .send()
303
+ * ```
304
+ *
305
+ * @example Authenticated request
306
+ * ```typescript
307
+ * const response = await module.http
308
+ * .get('/api/v1/profile')
309
+ * .actingAs({ id: user.id })
310
+ * .send()
311
+ * ```
312
+ */
313
+ declare class TestHttpRequest {
314
+ private readonly method;
315
+ private readonly path;
316
+ private readonly module;
317
+ private readonly host;
318
+ private body;
319
+ private requestHeaders;
320
+ private actingAsUser;
321
+ constructor(method: string, path: string, headers: Headers, module: TestingModule, host?: string | null);
322
+ /**
323
+ * Set the request body
324
+ */
325
+ withBody(data: unknown): this;
326
+ /**
327
+ * Add headers to the request
328
+ */
329
+ withHeaders(headers: Record<string, string>): this;
330
+ /**
331
+ * Set Content-Type to application/json
332
+ */
333
+ asJson(): this;
334
+ /**
335
+ * Authenticate the request as a specific user
336
+ */
337
+ actingAs(user: {
338
+ id: string;
339
+ }): this;
340
+ /**
341
+ * Send the request and return response
342
+ *
343
+ * Calls module.fetch() - NOT SELF.fetch()
344
+ */
345
+ send(): Promise<TestResponse>;
346
+ private applyAuthentication;
347
+ }
348
+ //#endregion
349
+ //#region src/core/http/test-http-client.d.ts
350
+ /**
351
+ * TestHttpClient
352
+ *
353
+ * Fluent HTTP client for making test requests.
354
+ *
355
+ * @example
356
+ * ```typescript
357
+ * const response = await module.http
358
+ * .forHost('example.com')
359
+ * .post('/api/v1/users')
360
+ * .withBody({ name: 'Test' })
361
+ * .send()
362
+ *
363
+ * response.assertCreated()
364
+ * ```
365
+ */
366
+ declare class TestHttpClient {
367
+ private readonly module;
368
+ private defaultHeaders;
369
+ private host;
370
+ constructor(module: TestingModule);
371
+ /**
372
+ * Set the host for the request
373
+ */
374
+ forHost(host: string): this;
375
+ /**
376
+ * Set default headers for all requests
377
+ */
378
+ withHeaders(headers: Record<string, string>): this;
379
+ /**
380
+ * Create a GET request
381
+ */
382
+ get(path: string): TestHttpRequest;
383
+ /**
384
+ * Create a POST request
385
+ */
386
+ post(path: string): TestHttpRequest;
387
+ /**
388
+ * Create a PUT request
389
+ */
390
+ put(path: string): TestHttpRequest;
391
+ /**
392
+ * Create a PATCH request
393
+ */
394
+ patch(path: string): TestHttpRequest;
395
+ /**
396
+ * Create a DELETE request
397
+ */
398
+ delete(path: string): TestHttpRequest;
399
+ private createRequest;
400
+ }
401
+ //#endregion
402
+ //#region src/core/sse/test-sse-connection.d.ts
403
+ /**
404
+ * Represents a parsed SSE event
405
+ */
406
+ interface TestSseEvent {
407
+ data: string;
408
+ event?: string;
409
+ id?: string;
410
+ retry?: number;
411
+ }
412
+ /**
413
+ * TestSseConnection
414
+ *
415
+ * Live SSE connection wrapper with assertion helpers for testing.
416
+ *
417
+ * @example
418
+ * ```typescript
419
+ * const sse = await module.sse('/streaming/sse').connect()
420
+ * await sse.assertEvent({ event: 'message', data: 'hello', id: '1' })
421
+ * await sse.waitForEnd()
422
+ * ```
423
+ */
424
+ declare class TestSseConnection {
425
+ private readonly response;
426
+ private readonly eventQueue;
427
+ private eventWaiters;
428
+ private streamEnded;
429
+ private endWaiters;
430
+ constructor(response: Response);
431
+ /**
432
+ * Wait for the next SSE event
433
+ */
434
+ waitForEvent(timeout?: number): Promise<TestSseEvent>;
435
+ /**
436
+ * Wait for the stream to end
437
+ */
438
+ waitForEnd(timeout?: number): Promise<void>;
439
+ /**
440
+ * Collect all remaining events until the stream ends
441
+ */
442
+ collectEvents(timeout?: number): Promise<TestSseEvent[]>;
443
+ /**
444
+ * Assert that the next event matches the expected partial shape
445
+ */
446
+ assertEvent(expected: Partial<TestSseEvent>, timeout?: number): Promise<void>;
447
+ /**
448
+ * Assert that the next event's data matches the expected string
449
+ */
450
+ assertEventData(expected: string, timeout?: number): Promise<void>;
451
+ /**
452
+ * Assert that the next event's data is JSON matching the expected value
453
+ */
454
+ assertJsonEventData<T>(expected: T, timeout?: number): Promise<void>;
455
+ /**
456
+ * Access the raw Response
457
+ */
458
+ get raw(): Response;
459
+ private startReading;
460
+ private parseEvent;
461
+ private dispatchEvent;
462
+ }
463
+ //#endregion
464
+ //#region src/core/sse/test-sse-request.d.ts
465
+ /**
466
+ * TestSseRequest
467
+ *
468
+ * Builder for SSE connection requests. Follows the TestWsRequest pattern.
469
+ *
470
+ * @example
471
+ * ```typescript
472
+ * const sse = await module.sse('/streaming/sse').connect()
473
+ * await sse.assertEvent({ event: 'message', data: 'hello' })
474
+ * await sse.waitForEnd()
475
+ * ```
476
+ *
477
+ * @example Authenticated SSE
478
+ * ```typescript
479
+ * const sse = await module.sse('/streaming/sse').actingAs({ id: user.id }).connect()
480
+ * ```
481
+ */
482
+ declare class TestSseRequest {
483
+ private readonly path;
484
+ private readonly module;
485
+ private requestHeaders;
486
+ private actingAsUser;
487
+ constructor(path: string, module: TestingModule);
488
+ /**
489
+ * Add custom headers to the request
490
+ */
491
+ withHeaders(headers: Record<string, string>): this;
492
+ /**
493
+ * Authenticate the SSE connection as a specific user
494
+ */
495
+ actingAs(user: {
496
+ id: string;
497
+ }): this;
498
+ /**
499
+ * Send the request and return a live SSE connection
500
+ */
501
+ connect(): Promise<TestSseConnection>;
502
+ private applyAuthentication;
503
+ }
504
+ //#endregion
505
+ //#region src/core/ws/test-ws-connection.d.ts
506
+ /**
507
+ * TestWsConnection
508
+ *
509
+ * Live WebSocket connection wrapper with assertion helpers for testing.
510
+ *
511
+ * @example
512
+ * ```typescript
513
+ * const ws = await module.ws('/ws/chat').connect()
514
+ * ws.send('hello')
515
+ * await ws.assertMessage('echo:hello')
516
+ * ws.close()
517
+ * await ws.waitForClose()
518
+ * ```
519
+ */
520
+ declare class TestWsConnection {
521
+ private readonly ws;
522
+ private readonly messageQueue;
523
+ private messageWaiters;
524
+ private closeEvent;
525
+ private closeWaiters;
526
+ constructor(ws: WebSocket);
527
+ /**
528
+ * Send a message through the WebSocket
529
+ */
530
+ send(data: string | ArrayBuffer | Uint8Array): void;
531
+ /**
532
+ * Close the WebSocket connection
533
+ */
534
+ close(code?: number, reason?: string): void;
535
+ /**
536
+ * Wait for the next message, returning its data
537
+ */
538
+ waitForMessage(timeout?: number): Promise<string | ArrayBuffer>;
539
+ /**
540
+ * Wait for the connection to close
541
+ */
542
+ waitForClose(timeout?: number): Promise<{
543
+ code?: number;
544
+ reason?: string;
545
+ }>;
546
+ /**
547
+ * Assert that the next message equals the expected value
548
+ */
549
+ assertMessage(expected: string, timeout?: number): Promise<void>;
550
+ /**
551
+ * Assert that the connection closes, optionally with an expected code
552
+ */
553
+ assertClosed(expectedCode?: number, timeout?: number): Promise<void>;
554
+ /**
555
+ * Access the raw Cloudflare WebSocket
556
+ */
557
+ get raw(): WebSocket;
558
+ }
559
+ //#endregion
560
+ //#region src/core/ws/test-ws-request.d.ts
561
+ /**
562
+ * TestWsRequest
563
+ *
564
+ * Builder for WebSocket upgrade requests. Follows the TestHttpRequest pattern.
565
+ *
566
+ * @example
567
+ * ```typescript
568
+ * const ws = await module.ws('/ws/chat').connect()
569
+ * ws.send('hello')
570
+ * await ws.assertMessage('echo:hello')
571
+ * ws.close()
572
+ * ```
573
+ *
574
+ * @example Authenticated WebSocket
575
+ * ```typescript
576
+ * const ws = await module.ws('/ws/chat').actingAs({ id: user.id }).connect()
577
+ * ```
578
+ */
579
+ declare class TestWsRequest {
580
+ private readonly path;
581
+ private readonly module;
582
+ private requestHeaders;
583
+ private actingAsUser;
584
+ constructor(path: string, module: TestingModule);
585
+ /**
586
+ * Add custom headers to the upgrade request
587
+ */
588
+ withHeaders(headers: Record<string, string>): this;
589
+ /**
590
+ * Authenticate the WebSocket connection as a specific user
591
+ */
592
+ actingAs(user: {
593
+ id: string;
594
+ }): this;
595
+ /**
596
+ * Send the upgrade request and return a live WebSocket connection
597
+ */
598
+ connect(): Promise<TestWsConnection>;
599
+ private applyAuthentication;
600
+ }
601
+ //#endregion
602
+ //#region src/core/testing-module.d.ts
603
+ /**
604
+ * TestingModule
605
+ *
606
+ * Provides access to the test application, container, HTTP client, and utilities.
607
+ *
608
+ * @example
609
+ * ```typescript
610
+ * const module = await Test.createTestingModule({
611
+ * modules: [RegistrationModule],
612
+ * }).compile()
613
+ *
614
+ * // Make HTTP requests
615
+ * const response = await module.http
616
+ * .post('/api/v1/register')
617
+ * .withBody({ ... })
618
+ * .send()
619
+ *
620
+ * // Access services
621
+ * const service = module.get(REGISTRATION_TOKENS.RegistrationService)
622
+ *
623
+ * // Database utilities
624
+ * await module.truncateDb()
625
+ * await module.seed(new UserSeeder())
626
+ * await module.assertDatabaseHas('user', { email: 'test@example.com' })
627
+ *
628
+ * // Cleanup
629
+ * await module.close()
630
+ * ```
631
+ */
632
+ declare class TestingModule {
633
+ private readonly app;
634
+ private readonly env;
635
+ private readonly ctx;
636
+ private _http;
637
+ private readonly _requestContainer;
638
+ constructor(app: Application, env: StratalEnv, ctx: ExecutionContext);
639
+ /**
640
+ * Resolve a service from the container
641
+ */
642
+ get<T>(token: InjectionToken<T>): T;
643
+ /**
644
+ * Get HTTP test client for making requests
645
+ */
646
+ get http(): TestHttpClient;
647
+ /**
648
+ * Get fake storage service for assertions
649
+ */
650
+ get storage(): FakeStorageService;
651
+ /**
652
+ * Create a WebSocket test request builder for the given path
653
+ */
654
+ ws(path: string): TestWsRequest;
655
+ /**
656
+ * Create an SSE test request builder for the given path
657
+ */
658
+ sse(path: string): TestSseRequest;
659
+ /**
660
+ * Get Application instance
661
+ */
662
+ get application(): Application;
663
+ /**
664
+ * Get DI Container (request-scoped)
665
+ */
666
+ get container(): Container;
667
+ /**
668
+ * Execute an HTTP request through HonoApp
669
+ */
670
+ fetch(request: Request): Promise<Response>;
671
+ /**
672
+ * Run callback in request scope (for DB operations, service access)
673
+ */
674
+ runInRequestScope<T>(callback: (container: Container) => T | Promise<T>): Promise<T>;
675
+ /**
676
+ * Get database service instance (resolved in request scope)
677
+ */
678
+ getDb(): DatabaseService;
679
+ getDb<K extends ConnectionName>(name: K): DatabaseService<K>;
680
+ /**
681
+ * Truncate all non-prisma tables in the database
682
+ */
683
+ truncateDb(name?: ConnectionName): Promise<void>;
684
+ /**
685
+ * Run seeders in a database transaction
686
+ */
687
+ seed(...seeders: Seeder[]): Promise<void>;
688
+ seed(name: ConnectionName, ...seeders: Seeder[]): Promise<void>;
689
+ /**
690
+ * Assert that a record exists in the database
691
+ */
692
+ assertDatabaseHas(table: string, data: Record<string, unknown>, name?: ConnectionName): Promise<void>;
693
+ /**
694
+ * Assert that a record does not exist in the database
695
+ */
696
+ assertDatabaseMissing(table: string, data: Record<string, unknown>, name?: ConnectionName): Promise<void>;
697
+ /**
698
+ * Assert the number of records in a table
699
+ */
700
+ assertDatabaseCount(table: string, expected: number, name?: ConnectionName): Promise<void>;
701
+ /**
702
+ * Cleanup - call in afterAll
703
+ */
704
+ close(): Promise<void>;
705
+ }
706
+ //#endregion
707
+ //#region src/core/testing-module-builder.d.ts
708
+ /**
709
+ * Configuration for creating a testing module
710
+ *
711
+ * Extends ModuleOptions to support all module properties like NestJS.
712
+ *
713
+ * @example
714
+ * ```typescript
715
+ * const module = await Test.createTestingModule({
716
+ * imports: [RegistrationModule, GeoModule],
717
+ * providers: [{ provide: MOCK_TOKEN, useValue: mockValue }],
718
+ * controllers: [TestController],
719
+ * }).compile()
720
+ * ```
721
+ */
722
+ interface TestingModuleConfig extends ModuleOptions {
723
+ /** Optional environment overrides */
724
+ env?: Partial<StratalEnv>;
725
+ /** Logging configuration. Defaults: level=ERROR, formatter='json' */
726
+ logging?: ApplicationConfig['logging'];
727
+ }
728
+ /**
729
+ * Builder for creating test modules with provider overrides
730
+ */
731
+ declare class TestingModuleBuilder {
732
+ private config;
733
+ private overrides;
734
+ constructor(config: TestingModuleConfig);
735
+ /**
736
+ * Override a provider with a custom implementation
737
+ */
738
+ overrideProvider<T>(token: InjectionToken<T>): ProviderOverrideBuilder<T>;
739
+ /**
740
+ * Add a provider override (internal use by ProviderOverrideBuilder)
741
+ *
742
+ * @internal
743
+ */
744
+ addProviderOverride<T>(override: ProviderOverrideConfig<T>): this;
745
+ /**
746
+ * Merge additional environment bindings
747
+ */
748
+ withEnv(env: Partial<StratalEnv>): this;
749
+ /**
750
+ * Compile the testing module
751
+ *
752
+ * Creates the Application, applies overrides, initializes, and returns TestingModule.
753
+ */
754
+ compile(): Promise<TestingModule>;
755
+ /**
756
+ * Create a test root module with the given options
757
+ */
758
+ private createTestRootModule;
759
+ }
760
+ //#endregion
761
+ //#region src/core/override/provider-override-builder.d.ts
762
+ /**
763
+ * Provider override configuration
764
+ */
765
+ interface ProviderOverrideConfig<T = unknown> {
766
+ token: InjectionToken<T>;
767
+ type: 'value' | 'class' | 'factory' | 'existing';
768
+ implementation: T | (new (...args: unknown[]) => T) | ((container: Container) => T) | InjectionToken<T>;
769
+ }
770
+ /**
771
+ * Fluent builder for provider overrides
772
+ *
773
+ * Provides a NestJS-style API for overriding providers in tests.
774
+ *
775
+ * @example
776
+ * ```typescript
777
+ * const module = await Test.createTestingModule({
778
+ * imports: [RegistrationModule],
779
+ * })
780
+ * .overrideProvider(EMAIL_TOKENS.EmailService)
781
+ * .useValue(mockEmailService)
782
+ * .compile()
783
+ * ```
784
+ */
785
+ declare class ProviderOverrideBuilder<T> {
786
+ private readonly parent;
787
+ private readonly token;
788
+ constructor(parent: TestingModuleBuilder, token: InjectionToken<T>);
789
+ /**
790
+ * Use a static value as the provider
791
+ *
792
+ * The value will be registered directly in the container.
793
+ *
794
+ * @param value - The value to use as the provider
795
+ * @returns The parent TestingModuleBuilder for chaining
796
+ */
797
+ useValue(value: T): TestingModuleBuilder;
798
+ /**
799
+ * Use a class as the provider
800
+ *
801
+ * The class will be registered as a singleton.
802
+ *
803
+ * @param cls - The class constructor to use as the provider
804
+ * @returns The parent TestingModuleBuilder for chaining
805
+ */
806
+ useClass(cls: new (...args: unknown[]) => T): TestingModuleBuilder;
807
+ /**
808
+ * Use a factory function as the provider
809
+ *
810
+ * The factory receives the container and should return the provider instance.
811
+ *
812
+ * @param factory - Factory function that creates the provider
813
+ * @returns The parent TestingModuleBuilder for chaining
814
+ */
815
+ useFactory(factory: (container: Container) => T): TestingModuleBuilder;
816
+ /**
817
+ * Use an existing token as the provider (alias)
818
+ *
819
+ * The override token will resolve to the same instance as the target token.
820
+ *
821
+ * @param existingToken - The token to alias
822
+ * @returns The parent TestingModuleBuilder for chaining
823
+ *
824
+ * @example
825
+ * ```typescript
826
+ * const module = await Test.createTestingModule({
827
+ * imports: [MyModule],
828
+ * })
829
+ * .overrideProvider(ABSTRACT_TOKEN)
830
+ * .useExisting(ConcreteService)
831
+ * .compile()
832
+ * ```
833
+ */
834
+ useExisting(existingToken: InjectionToken<T>): TestingModuleBuilder;
835
+ }
836
+ //#endregion
837
+ //#region src/core/test.d.ts
838
+ /**
839
+ * Test
840
+ *
841
+ * Static class for creating testing modules.
842
+ * Provides a NestJS-style API for configuring test modules.
843
+ *
844
+ * @example
845
+ * ```typescript
846
+ * // In vitest.setup.ts:
847
+ * Test.setBaseModules([CoreModule])
848
+ *
849
+ * // In test files:
850
+ * const module = await Test.createTestingModule({
851
+ * imports: [RegistrationModule, GeoModule],
852
+ * })
853
+ * .overrideProvider(EMAIL_TOKENS.EmailService)
854
+ * .useValue(mockEmailService)
855
+ * .compile()
856
+ * ```
857
+ */
858
+ declare class Test {
859
+ /**
860
+ * Base modules to include in all test modules
861
+ * Set once in vitest.setup.ts
862
+ */
863
+ private static baseModules;
864
+ /**
865
+ * Set base modules to include in all test modules
866
+ * Should be called once in vitest.setup.ts
867
+ *
868
+ * @param modules - Modules to include before test-specific modules (e.g., CoreModule)
869
+ */
870
+ static setBaseModules(modules: (ModuleClass | DynamicModule)[]): void;
871
+ /**
872
+ * Get base modules
873
+ */
874
+ static getBaseModules(): (ModuleClass | DynamicModule)[];
875
+ /**
876
+ * Create a testing module builder
877
+ *
878
+ * @param config - Configuration with modules and optional env overrides
879
+ * @returns TestingModuleBuilder for configuring and compiling the module
880
+ */
881
+ static createTestingModule(config: TestingModuleConfig): TestingModuleBuilder;
882
+ }
883
+ //#endregion
884
+ //#region src/core/http/fetch-mock.types.d.ts
885
+ /**
886
+ * Options for mocking JSON responses
887
+ */
888
+ interface MockJsonOptions {
889
+ /**
890
+ * HTTP status code for the response
891
+ * @default 200
892
+ */
893
+ status?: number;
894
+ /**
895
+ * Custom headers to include in the response
896
+ */
897
+ headers?: Record<string, string>;
898
+ /**
899
+ * Delay in milliseconds before responding
900
+ */
901
+ delay?: number;
902
+ /**
903
+ * HTTP method to match (GET, POST, PUT, PATCH, DELETE, etc.)
904
+ * @default 'GET'
905
+ */
906
+ method?: string;
907
+ /**
908
+ * Specific path to override URL pathname
909
+ * If not provided, the pathname from the URL will be used
910
+ */
911
+ path?: string;
912
+ }
913
+ /**
914
+ * Options for mocking error responses
915
+ */
916
+ interface MockErrorOptions {
917
+ /**
918
+ * Custom headers to include in the error response
919
+ */
920
+ headers?: Record<string, string>;
921
+ /**
922
+ * HTTP method to match (GET, POST, PUT, PATCH, DELETE, etc.)
923
+ * @default 'GET'
924
+ */
925
+ method?: string;
926
+ /**
927
+ * Specific path to override URL pathname
928
+ * If not provided, the pathname from the URL will be used
929
+ */
930
+ path?: string;
931
+ }
932
+ //#endregion
933
+ //#region src/core/http/mock-fetch.d.ts
934
+ /**
935
+ * MSW-based fetch mock for declarative HTTP mocking in tests.
936
+ *
937
+ * Replaces the old Cloudflare `fetchMock` (undici MockAgent) with MSW's `setupServer`.
938
+ * Works in both Node.js and workerd test environments.
939
+ *
940
+ * @example
941
+ * ```typescript
942
+ * import { createMockFetch } from '@stratal/testing'
943
+ *
944
+ * const mock = createMockFetch()
945
+ *
946
+ * beforeAll(() => mock.listen())
947
+ * afterEach(() => mock.reset())
948
+ * afterAll(() => mock.close())
949
+ *
950
+ * it('should mock external API', async () => {
951
+ * mock.mockJsonResponse('https://api.example.com/data', { success: true })
952
+ *
953
+ * const response = await fetch('https://api.example.com/data')
954
+ * const json = await response.json()
955
+ *
956
+ * expect(json.success).toBe(true)
957
+ * })
958
+ * ```
959
+ */
960
+ declare class MockFetch {
961
+ private server;
962
+ constructor(handlers?: RequestHandler[]);
963
+ /** Start intercepting. Call in beforeAll/beforeEach. */
964
+ listen(): void;
965
+ /** Reset runtime handlers. Call in afterEach. */
966
+ reset(): void;
967
+ /** Stop intercepting. Call in afterAll. */
968
+ close(): void;
969
+ /** Add runtime handler(s) for a single test. */
970
+ use(...handlers: RequestHandler[]): void;
971
+ /**
972
+ * Mock a JSON response.
973
+ *
974
+ * @param url - Full URL to mock (e.g., 'https://api.example.com/users')
975
+ * @param data - JSON data to return
976
+ * @param options - HTTP method, status code, headers
977
+ *
978
+ * @example
979
+ * ```typescript
980
+ * mock.mockJsonResponse('https://api.example.com/users', { users: [] })
981
+ * mock.mockJsonResponse('https://api.example.com/users', { created: true }, { method: 'POST', status: 201 })
982
+ * ```
983
+ */
984
+ mockJsonResponse(url: string, data: Record<string, unknown> | unknown[], options?: MockJsonOptions): void;
985
+ /**
986
+ * Mock an error response.
987
+ *
988
+ * @param url - Full URL to mock
989
+ * @param status - HTTP error status code
990
+ * @param message - Optional error message
991
+ * @param options - HTTP method, headers
992
+ *
993
+ * @example
994
+ * ```typescript
995
+ * mock.mockError('https://api.example.com/fail', 401, 'Unauthorized')
996
+ * mock.mockError('https://api.example.com/fail', 500, 'Server Error', { method: 'POST' })
997
+ * ```
998
+ */
999
+ mockError(url: string, status: number, message?: string, options?: MockErrorOptions): void;
1000
+ }
1001
+ /**
1002
+ * Factory function to create a new MockFetch instance
1003
+ *
1004
+ * @param handlers - Optional initial MSW request handlers
1005
+ * @returns A new MockFetch instance
1006
+ *
1007
+ * @example
1008
+ * ```typescript
1009
+ * import { createMockFetch } from '@stratal/testing'
1010
+ *
1011
+ * const mock = createMockFetch()
1012
+ *
1013
+ * beforeAll(() => mock.listen())
1014
+ * afterEach(() => mock.reset())
1015
+ * afterAll(() => mock.close())
1016
+ * ```
1017
+ */
1018
+ declare function createMockFetch(handlers?: RequestHandler[]): MockFetch;
1019
+ //#endregion
1020
+ //#region src/auth/acting-as.d.ts
1021
+ /**
1022
+ * ActingAs
1023
+ *
1024
+ * Creates authentication sessions for testing.
1025
+ * Uses Better Auth's internalAdapter to create real database sessions.
1026
+ *
1027
+ * @example
1028
+ * ```typescript
1029
+ * const actingAs = new ActingAs(authService)
1030
+ * const headers = await actingAs.createSessionForUser({ id: 'user-123' })
1031
+ * ```
1032
+ */
1033
+ declare class ActingAs {
1034
+ private readonly authService;
1035
+ constructor(authService: AuthService);
1036
+ createSessionForUser(user: {
1037
+ id: string;
1038
+ }): Promise<Headers>;
1039
+ }
1040
+ //#endregion
1041
+ //#region src/core/env/test-env.d.ts
1042
+ /**
1043
+ * Get test environment with optional overrides
1044
+ *
1045
+ * @param overrides - Optional partial env to merge with cloudflare:test env
1046
+ * @returns Complete Env object for testing
1047
+ */
1048
+ declare function getTestEnv(overrides?: Partial<StratalEnv>): StratalEnv;
1049
+ //#endregion
1050
+ //#region src/errors/test-error.d.ts
1051
+ /**
1052
+ * Base error class for all test framework errors.
1053
+ * Extends from Error and allows easy identification via `instanceof`.
1054
+ */
1055
+ declare class TestError extends Error {
1056
+ readonly cause?: Error | undefined;
1057
+ constructor(message: string, cause?: Error | undefined);
1058
+ }
1059
+ //#endregion
1060
+ //#region src/errors/setup-error.d.ts
1061
+ /**
1062
+ * Error thrown when test setup fails.
1063
+ * Examples: schema creation failure, migration failure, application bootstrap failure.
1064
+ */
1065
+ declare class TestSetupError extends TestError {
1066
+ constructor(message: string, cause?: Error);
1067
+ }
1068
+ //#endregion
1069
+ export { ActingAs, FakeStorageService, HttpResponse, type MockErrorOptions, MockFetch, type MockJsonOptions, ProviderOverrideBuilder, type ProviderOverrideConfig, Seeder, type StoredFile, Test, TestError, TestHttpClient, TestHttpRequest, TestResponse, TestSetupError, TestSseConnection, type TestSseEvent, TestSseRequest, TestWsConnection, TestWsRequest, TestingModule, TestingModuleBuilder, type TestingModuleConfig, createMockFetch, getTestEnv, http };
1070
+ //# sourceMappingURL=index.d.mts.map