@velajs/testing 1.0.0 → 1.22.0
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/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/README.md +16 -2
- package/dist/eval/index.d.ts +9 -9
- package/dist/index.d.ts +38 -41
- package/dist/index.js +54 -79
- package/dist/index.js.map +1 -1
- package/dist/websocket-node/index.d.ts +1 -1
- package/package.json +25 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.22.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Continue the coordinated framework release on the 1.x line with the current checked provider, endpoint, identity, CRUD, live, and Studio APIs. Breaking API changes are accepted during this development phase; maintained applications use the current contracts.
|
|
8
|
+
- Publish from the pnpm packages workspace with TypeScript 7 and GitHub OIDC. Obsolete standalone examples have been removed; runnable applications live in apps/.
|
|
9
|
+
- Updated workspace dependencies
|
|
10
|
+
- @velajs/vela@1.22.0
|
|
11
|
+
|
|
12
|
+
## 2.0.1
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Publish from the unified packages workspace with corrected repository paths, shared native tooling, TypeScript 7 checks, and npm OIDC releases. Runnable examples now live in apps/.
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @velajs/vela@2.0.1
|
|
19
|
+
|
|
20
|
+
## 2.0.0
|
|
21
|
+
|
|
22
|
+
Typed override descriptors and production-equivalent request and environment scope.
|
|
23
|
+
|
|
24
|
+
Requires the coordinated Vela 2.0 package set. See the workspace migration guide.
|
|
25
|
+
|
|
3
26
|
## 1.0.0
|
|
4
27
|
|
|
5
28
|
### Major Changes
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ksh
|
|
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
CHANGED
|
@@ -54,11 +54,13 @@ const moduleRef = await Test.createTestingModule({ imports: [UsersModule] })
|
|
|
54
54
|
|
|
55
55
|
`useValue`, `useClass`, and `useFactory` are all supported. Same builder for `overrideGuard`, `overridePipe`, `overrideInterceptor`, `overrideFilter`.
|
|
56
56
|
|
|
57
|
+
`get(token)` and overrides infer their value contract from a class or `InjectionToken<Value>`. An incompatible mock fails typechecking. Raw string/symbol tokens resolve to `unknown`; use an injection token when the contract matters.
|
|
58
|
+
|
|
57
59
|
```ts
|
|
58
60
|
.overrideGuard(AuthGuard).useValue({ canActivate: () => true })
|
|
59
61
|
.overrideInterceptor(LogInterceptor).useClass(NoopInterceptor)
|
|
60
62
|
.overrideProvider(CONFIG).useFactory({
|
|
61
|
-
factory: (env
|
|
63
|
+
factory: (env) => ({ env: env.getEnv() }),
|
|
62
64
|
inject: [EnvService],
|
|
63
65
|
})
|
|
64
66
|
```
|
|
@@ -67,10 +69,12 @@ Inline providers (skip importing a module):
|
|
|
67
69
|
|
|
68
70
|
```ts
|
|
69
71
|
const moduleRef = await Test.createTestingModule({
|
|
70
|
-
providers: [InlineService],
|
|
72
|
+
providers: [InlineService, defineProvider(CONFIG, { useValue: { env: 'test' } })],
|
|
71
73
|
}).compile();
|
|
72
74
|
```
|
|
73
75
|
|
|
76
|
+
Import `defineProvider` from `@velajs/vela`. Factory dependency types come from the required `inject` tuple; use `inject: []` for factories without dependencies.
|
|
77
|
+
|
|
74
78
|
## HTTP testing
|
|
75
79
|
|
|
76
80
|
```ts
|
|
@@ -82,6 +86,16 @@ expect(res.status).toBe(200);
|
|
|
82
86
|
expect(await res.json()).toEqual([{ id: 1, name: 'Item 1' }]);
|
|
83
87
|
```
|
|
84
88
|
|
|
89
|
+
The fluent HTTP client returns `TestResponse`. Its `json()` method returns `unknown`; pass a schema or `defineDto` descriptor to validate the body and infer its output:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const response = await moduleRef.http.get('/items').send();
|
|
93
|
+
const items = await response.json(z.array(z.object({ id: z.number(), name: z.string() })));
|
|
94
|
+
expect(items[0]?.name).toBe('Item 1');
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`runInRequestScope(callback)` creates a real framework request context, including typed `RequestContextKey` storage, and disposes its child container when the callback finishes.
|
|
98
|
+
|
|
85
99
|
## Lifecycle
|
|
86
100
|
|
|
87
101
|
`compile()` calls `onModuleInit` and `onApplicationBootstrap` automatically. `moduleRef.close()` triggers `onModuleDestroy` and `onApplicationShutdown`.
|
package/dist/eval/index.d.ts
CHANGED
|
@@ -74,7 +74,7 @@ interface ExactMatchOptions {
|
|
|
74
74
|
* trimmed by default; a case with no `expected` scores 0 (there is nothing to
|
|
75
75
|
* match against, so it fails closed).
|
|
76
76
|
*/
|
|
77
|
-
declare function exactMatch(options?: ExactMatchOptions): Scorer;
|
|
77
|
+
export declare function exactMatch(options?: ExactMatchOptions): Scorer;
|
|
78
78
|
/** Options for {@link contains}. */
|
|
79
79
|
interface ContainsOptions {
|
|
80
80
|
/** `all` (default) requires every needle; `any` requires at least one. */
|
|
@@ -87,7 +87,7 @@ interface ContainsOptions {
|
|
|
87
87
|
* chooses whether every needle must be present (`all`, the default) or just one
|
|
88
88
|
* (`any`). Scores 1 when the condition holds, else 0.
|
|
89
89
|
*/
|
|
90
|
-
declare function contains(needles: string | readonly string[], options?: ContainsOptions): Scorer;
|
|
90
|
+
export declare function contains(needles: string | readonly string[], options?: ContainsOptions): Scorer;
|
|
91
91
|
/** Options for {@link keyword}. */
|
|
92
92
|
interface KeywordOptions {
|
|
93
93
|
/** Match with case sensitivity (default: case-insensitive). */
|
|
@@ -99,13 +99,13 @@ interface KeywordOptions {
|
|
|
99
99
|
* keyword list would silently score everything 1 — the opposite of a useful
|
|
100
100
|
* eval — so it is rejected at construction time.
|
|
101
101
|
*/
|
|
102
|
-
declare function keyword(keywords: readonly string[], options?: KeywordOptions): Scorer;
|
|
102
|
+
export declare function keyword(keywords: readonly string[], options?: KeywordOptions): Scorer;
|
|
103
103
|
/**
|
|
104
104
|
* Score 1 when `pattern` matches the output, else 0. The global/sticky flags are
|
|
105
105
|
* stripped from a copy of the pattern so a scorer reused across many samples
|
|
106
106
|
* never carries `lastIndex` state between calls.
|
|
107
107
|
*/
|
|
108
|
-
declare function regex(pattern: RegExp): Scorer;
|
|
108
|
+
export declare function regex(pattern: RegExp): Scorer;
|
|
109
109
|
/** Options for {@link llmScorer}. */
|
|
110
110
|
interface LlmScorerOptions {
|
|
111
111
|
/** The rubric the judge grades against, e.g. "answers the question accurately". */
|
|
@@ -119,20 +119,20 @@ interface LlmScorerOptions {
|
|
|
119
119
|
judge: (prompt: string) => Promise<string>;
|
|
120
120
|
}
|
|
121
121
|
/** Render the grading prompt for one sample under a rubric. */
|
|
122
|
-
declare function renderJudgePrompt(criteria: string, sample: ScorerInput): string;
|
|
122
|
+
export declare function renderJudgePrompt(criteria: string, sample: ScorerInput): string;
|
|
123
123
|
/**
|
|
124
124
|
* Read a judge reply into a verdict. The score is the leading number, clamped,
|
|
125
125
|
* and the full reply is kept as the reason. Requiring the number at the very
|
|
126
126
|
* start is deliberate: a reply with no leading number fails closed to 0, and a
|
|
127
127
|
* digit buried later in the justification can never be misread as the grade.
|
|
128
128
|
*/
|
|
129
|
-
declare function parseJudgeReply(reply: string): ScoreResult;
|
|
129
|
+
export declare function parseJudgeReply(reply: string): ScoreResult;
|
|
130
130
|
/**
|
|
131
131
|
* LLM-as-judge scorer. It builds a rubric prompt, hands it to the injected
|
|
132
132
|
* `judge`, and parses the reply into a `[0, 1]` grade. Unparseable replies fail
|
|
133
133
|
* soft (score 0 with the raw reply as the reason) rather than throwing.
|
|
134
134
|
*/
|
|
135
|
-
declare function llmScorer(options: LlmScorerOptions): Scorer;
|
|
135
|
+
export declare function llmScorer(options: LlmScorerOptions): Scorer;
|
|
136
136
|
//#endregion
|
|
137
137
|
//#region src/eval/evaluate.d.ts
|
|
138
138
|
/**
|
|
@@ -148,7 +148,7 @@ declare function llmScorer(options: LlmScorerOptions): Scorer;
|
|
|
148
148
|
* @param run Produces the output string for a given input.
|
|
149
149
|
* @param scorers A name → scorer map; the names key the per-scorer aggregate.
|
|
150
150
|
*/
|
|
151
|
-
declare function evaluate(dataset: readonly EvalCase[], run: (input: string) => Awaitable<string>, scorers: Record<string, Scorer>): Promise<EvalReport>;
|
|
151
|
+
export declare function evaluate(dataset: readonly EvalCase[], run: (input: string) => Awaitable<string>, scorers: Record<string, Scorer>): Promise<EvalReport>;
|
|
152
152
|
//#endregion
|
|
153
|
-
export {
|
|
153
|
+
export type { Awaitable, CaseReport, ContainsOptions, EvalAggregate, EvalCase, EvalReport, ExactMatchOptions, KeywordOptions, LlmScorerOptions, ScoreResult, Scorer, ScorerInput };
|
|
154
154
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModuleOptions, Token, Type, VelaApplication } from "@velajs/vela";
|
|
1
|
+
import { CanActivate, DependencyToken, ExceptionFilter, InferToken, InferTokens, ModuleOptions, NestInterceptor, PipeTransform, ProviderDefinition, SchemaParser, Token, Type, VelaApplication, defineProvider } from "@velajs/vela";
|
|
2
2
|
import { Container } from "@velajs/vela/internal";
|
|
3
3
|
import { ISeeder } from "@velajs/vela/seeder";
|
|
4
4
|
//#region src/db/test-database.d.ts
|
|
@@ -45,7 +45,7 @@ interface TestDatabase {
|
|
|
45
45
|
* await res.assertJsonPath('data.id', 1);
|
|
46
46
|
* ```
|
|
47
47
|
*/
|
|
48
|
-
declare class TestResponse {
|
|
48
|
+
export declare class TestResponse {
|
|
49
49
|
private readonly response;
|
|
50
50
|
private jsonData;
|
|
51
51
|
private textData;
|
|
@@ -56,8 +56,9 @@ declare class TestResponse {
|
|
|
56
56
|
get status(): number;
|
|
57
57
|
/** The response headers. */
|
|
58
58
|
get headers(): Headers;
|
|
59
|
-
/**
|
|
60
|
-
json
|
|
59
|
+
/** Read JSON as unknown, or infer validated output from a supplied parser. */
|
|
60
|
+
json(): Promise<unknown>;
|
|
61
|
+
json<Value>(parser: SchemaParser<Value>): Promise<Value>;
|
|
61
62
|
/** Read (and cache) the response body as text. */
|
|
62
63
|
text(): Promise<string>;
|
|
63
64
|
/** Assert status is 200 OK. */
|
|
@@ -125,7 +126,7 @@ declare class TestResponse {
|
|
|
125
126
|
* res.assertCreated();
|
|
126
127
|
* ```
|
|
127
128
|
*/
|
|
128
|
-
declare class TestHttpRequest {
|
|
129
|
+
export declare class TestHttpRequest {
|
|
129
130
|
private readonly method;
|
|
130
131
|
private readonly path;
|
|
131
132
|
private readonly module;
|
|
@@ -171,7 +172,7 @@ declare class TestHttpRequest {
|
|
|
171
172
|
* res.assertCreated();
|
|
172
173
|
* ```
|
|
173
174
|
*/
|
|
174
|
-
declare class TestHttpClient {
|
|
175
|
+
export declare class TestHttpClient {
|
|
175
176
|
private readonly module;
|
|
176
177
|
private readonly host;
|
|
177
178
|
private readonly defaultHeaders;
|
|
@@ -212,7 +213,7 @@ interface TestSseEvent {
|
|
|
212
213
|
* await sse.waitForEnd();
|
|
213
214
|
* ```
|
|
214
215
|
*/
|
|
215
|
-
declare class TestSseConnection {
|
|
216
|
+
export declare class TestSseConnection {
|
|
216
217
|
private readonly response;
|
|
217
218
|
private readonly eventQueue;
|
|
218
219
|
private eventWaiters;
|
|
@@ -253,7 +254,7 @@ declare class TestSseConnection {
|
|
|
253
254
|
* await sse.assertEvent({ event: 'message', data: 'hello' });
|
|
254
255
|
* ```
|
|
255
256
|
*/
|
|
256
|
-
declare class TestSseRequest {
|
|
257
|
+
export declare class TestSseRequest {
|
|
257
258
|
private readonly path;
|
|
258
259
|
private readonly module;
|
|
259
260
|
private readonly requestHeaders;
|
|
@@ -285,7 +286,7 @@ declare class TestSseRequest {
|
|
|
285
286
|
* ws.close();
|
|
286
287
|
* ```
|
|
287
288
|
*/
|
|
288
|
-
declare class TestWsConnection {
|
|
289
|
+
export declare class TestWsConnection {
|
|
289
290
|
private readonly ws;
|
|
290
291
|
private readonly cleanup?;
|
|
291
292
|
private readonly messageQueue;
|
|
@@ -322,9 +323,9 @@ type WsConnector = (module: TestingModule, path: string, headers: Headers) => Pr
|
|
|
322
323
|
* Register the transport that {@link TestWsRequest.connect} uses. Called by a
|
|
323
324
|
* platform adapter's side-effect import (e.g. `@velajs/testing/websocket-node`).
|
|
324
325
|
*/
|
|
325
|
-
declare function registerWsConnector(connector: WsConnector): void;
|
|
326
|
+
export declare function registerWsConnector(connector: WsConnector): void;
|
|
326
327
|
/** The currently registered WebSocket connector, if any. */
|
|
327
|
-
declare function getWsConnector(): WsConnector | null;
|
|
328
|
+
export declare function getWsConnector(): WsConnector | null;
|
|
328
329
|
/**
|
|
329
330
|
* TestWsRequest
|
|
330
331
|
*
|
|
@@ -339,7 +340,7 @@ declare function getWsConnector(): WsConnector | null;
|
|
|
339
340
|
* await ws.assertMessage('echo:hi');
|
|
340
341
|
* ```
|
|
341
342
|
*/
|
|
342
|
-
declare class TestWsRequest {
|
|
343
|
+
export declare class TestWsRequest {
|
|
343
344
|
private readonly path;
|
|
344
345
|
private readonly module;
|
|
345
346
|
private readonly requestHeaders;
|
|
@@ -365,6 +366,7 @@ type TestPrincipal = Record<string, unknown>;
|
|
|
365
366
|
* resolver against it. Kept generic so `@velajs/testing` needs no auth deps.
|
|
366
367
|
*/
|
|
367
368
|
type ActingAsResolver = (module: TestingModule, principal: TestPrincipal) => Promise<Headers>;
|
|
369
|
+
type HonoApp = ReturnType<VelaApplication['getHonoApp']>;
|
|
368
370
|
/**
|
|
369
371
|
* TestingModule
|
|
370
372
|
*
|
|
@@ -379,7 +381,7 @@ type ActingAsResolver = (module: TestingModule, principal: TestPrincipal) => Pro
|
|
|
379
381
|
* .then((r) => r.assertCreated());
|
|
380
382
|
* ```
|
|
381
383
|
*/
|
|
382
|
-
declare class TestingModule {
|
|
384
|
+
export declare class TestingModule {
|
|
383
385
|
private readonly app;
|
|
384
386
|
private readonly container;
|
|
385
387
|
private _http;
|
|
@@ -387,7 +389,7 @@ declare class TestingModule {
|
|
|
387
389
|
private authResolver;
|
|
388
390
|
constructor(app: VelaApplication, container: Container);
|
|
389
391
|
/** Resolve a provider from the root container. */
|
|
390
|
-
get<
|
|
392
|
+
get<const Key extends Token>(token: Key): InferToken<Key>;
|
|
391
393
|
/** Build (once) and return the underlying application. */
|
|
392
394
|
createApplication(): Promise<VelaApplication>;
|
|
393
395
|
/** Lazy fluent HTTP client bound to this module. */
|
|
@@ -400,7 +402,7 @@ declare class TestingModule {
|
|
|
400
402
|
* Drive a `Request` through the full Hono pipeline. The Hono app is built
|
|
401
403
|
* once and reused across requests.
|
|
402
404
|
*/
|
|
403
|
-
fetch(
|
|
405
|
+
fetch(...args: Parameters<HonoApp['fetch']>): Promise<Response>;
|
|
404
406
|
/**
|
|
405
407
|
* Register a default auth resolver used by `actingAs(principal)` when no
|
|
406
408
|
* resolver is passed explicitly.
|
|
@@ -409,8 +411,8 @@ declare class TestingModule {
|
|
|
409
411
|
/** The default auth resolver, if one was registered. */
|
|
410
412
|
getAuthResolver(): ActingAsResolver | null;
|
|
411
413
|
/**
|
|
412
|
-
* Run `callback` inside a request-scoped child container seeded with a
|
|
413
|
-
*
|
|
414
|
+
* Run `callback` inside a request-scoped child container seeded with a real
|
|
415
|
+
* RequestContext, so REQUEST-scoped providers (and anything injecting
|
|
414
416
|
* `REQUEST_CONTEXT`) resolve. The child is disposed afterwards.
|
|
415
417
|
*/
|
|
416
418
|
runInRequestScope<T>(callback: (container: Container) => T | Promise<T>): Promise<T>;
|
|
@@ -429,41 +431,36 @@ declare class TestingModule {
|
|
|
429
431
|
/** Dispose the application. */
|
|
430
432
|
close(signal?: string): Promise<void>;
|
|
431
433
|
private ensureHono;
|
|
432
|
-
/**
|
|
433
|
-
* Build a minimal, functional {@link RequestContext} for out-of-band request
|
|
434
|
-
* scopes. Vela has no `createMockRouterContext`; a real (empty) Hono `Context`
|
|
435
|
-
* backs the `hono` field so nothing dangles.
|
|
436
|
-
*/
|
|
437
|
-
private createMockRequestContext;
|
|
438
434
|
}
|
|
439
435
|
//#endregion
|
|
440
436
|
//#region src/testing-module.builder.d.ts
|
|
441
|
-
|
|
442
|
-
|
|
437
|
+
type OverrideToken<Key extends Token> = Parameters<typeof defineProvider<Key>>[0];
|
|
438
|
+
export declare class OverrideBy<Key extends Token> {
|
|
439
|
+
private readonly commit;
|
|
443
440
|
private readonly token;
|
|
444
|
-
constructor(
|
|
445
|
-
useValue(value:
|
|
446
|
-
useClass(cls: Type): TestingModuleBuilder;
|
|
447
|
-
useFactory(options: {
|
|
448
|
-
factory: (...args:
|
|
449
|
-
inject
|
|
441
|
+
constructor(commit: (provider: ProviderDefinition) => TestingModuleBuilder, token: OverrideToken<Key>);
|
|
442
|
+
useValue(value: NoInfer<InferToken<Key>>): TestingModuleBuilder;
|
|
443
|
+
useClass(cls: Type<NoInfer<InferToken<Key>>>): TestingModuleBuilder;
|
|
444
|
+
useFactory<const Inject extends readonly DependencyToken[] = readonly []>(options: {
|
|
445
|
+
factory: (...args: InferTokens<Inject>) => NoInfer<InferToken<Key>> | Promise<NoInfer<InferToken<Key>>>;
|
|
446
|
+
inject: Inject;
|
|
450
447
|
}): TestingModuleBuilder;
|
|
451
448
|
}
|
|
452
|
-
declare class TestingModuleBuilder {
|
|
449
|
+
export declare class TestingModuleBuilder {
|
|
453
450
|
private readonly metadata;
|
|
454
451
|
private overrides;
|
|
455
452
|
constructor(metadata: ModuleOptions);
|
|
456
|
-
overrideProvider(token:
|
|
457
|
-
overrideGuard(guard:
|
|
458
|
-
overridePipe(pipe:
|
|
459
|
-
overrideInterceptor(interceptor:
|
|
460
|
-
overrideFilter(filter:
|
|
453
|
+
overrideProvider<const Key extends Token>(token: OverrideToken<Key>): OverrideBy<Key>;
|
|
454
|
+
overrideGuard<const Guard extends Type<CanActivate>>(guard: OverrideToken<Guard>): OverrideBy<Guard>;
|
|
455
|
+
overridePipe<const Pipe extends Type<PipeTransform>>(pipe: OverrideToken<Pipe>): OverrideBy<Pipe>;
|
|
456
|
+
overrideInterceptor<const Interceptor extends Type<NestInterceptor>>(interceptor: OverrideToken<Interceptor>): OverrideBy<Interceptor>;
|
|
457
|
+
overrideFilter<const Filter extends Type<ExceptionFilter>>(filter: OverrideToken<Filter>): OverrideBy<Filter>;
|
|
461
458
|
private addOverride;
|
|
462
459
|
compile(): Promise<TestingModule>;
|
|
463
460
|
}
|
|
464
461
|
//#endregion
|
|
465
462
|
//#region src/test.d.ts
|
|
466
|
-
declare const Test: {
|
|
463
|
+
export declare const Test: {
|
|
467
464
|
createTestingModule(metadata: ModuleOptions): TestingModuleBuilder;
|
|
468
465
|
};
|
|
469
466
|
//#endregion
|
|
@@ -472,13 +469,13 @@ declare const Test: {
|
|
|
472
469
|
* Read the value at a dot-notation path (e.g. `data.user.id`).
|
|
473
470
|
* Returns `undefined` when any segment along the way is null/undefined.
|
|
474
471
|
*/
|
|
475
|
-
declare function getValueAtPath(obj: unknown, path: string): unknown;
|
|
472
|
+
export declare function getValueAtPath(obj: unknown, path: string): unknown;
|
|
476
473
|
/**
|
|
477
474
|
* Whether a dot-notation path exists on the object, even when the value at the
|
|
478
475
|
* path is `null`/`undefined`. Distinguishes "key present but null" from "key
|
|
479
476
|
* absent".
|
|
480
477
|
*/
|
|
481
|
-
declare function hasValueAtPath(obj: unknown, path: string): boolean;
|
|
478
|
+
export declare function hasValueAtPath(obj: unknown, path: string): boolean;
|
|
482
479
|
//#endregion
|
|
483
|
-
export {
|
|
480
|
+
export type { ActingAsResolver, TestDatabase, TestPrincipal, TestSseEvent, WsConnector };
|
|
484
481
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as registerWsConnector, n as TestWsRequest, r as getWsConnector, t as TestWsConnection } from "./test-ws-connection-BHlEKwQz.js";
|
|
2
|
-
import { REQUEST_CONTEXT } from "@velajs/vela";
|
|
3
|
-
import { MetadataRegistry, VelaApplication as VelaApplication$1, bootstrap } from "@velajs/vela/internal";
|
|
2
|
+
import { REQUEST_CONTEXT, defineProvider } from "@velajs/vela";
|
|
3
|
+
import { MetadataRegistry, VelaApplication as VelaApplication$1, bootstrap, createRequestContext, setRequestContainer } from "@velajs/vela/internal";
|
|
4
4
|
import { Context } from "hono";
|
|
5
5
|
import { SeederRegistry } from "@velajs/vela/seeder";
|
|
6
6
|
import { expect } from "vitest";
|
|
@@ -37,6 +37,9 @@ function hasValueAtPath(obj, path) {
|
|
|
37
37
|
}
|
|
38
38
|
//#endregion
|
|
39
39
|
//#region src/http/test-response.ts
|
|
40
|
+
function isJsonObject(value) {
|
|
41
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
42
|
+
}
|
|
40
43
|
/**
|
|
41
44
|
* TestResponse
|
|
42
45
|
*
|
|
@@ -53,7 +56,7 @@ function hasValueAtPath(obj, path) {
|
|
|
53
56
|
*/
|
|
54
57
|
var TestResponse = class {
|
|
55
58
|
response;
|
|
56
|
-
jsonData
|
|
59
|
+
jsonData;
|
|
57
60
|
textData = null;
|
|
58
61
|
constructor(response) {
|
|
59
62
|
this.response = response;
|
|
@@ -70,10 +73,10 @@ var TestResponse = class {
|
|
|
70
73
|
get headers() {
|
|
71
74
|
return this.response.headers;
|
|
72
75
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return
|
|
76
|
+
async json(parser) {
|
|
77
|
+
this.jsonData ??= this.response.clone().json();
|
|
78
|
+
const value = await this.jsonData;
|
|
79
|
+
return parser === void 0 ? value : parser.parse(value);
|
|
77
80
|
}
|
|
78
81
|
/** Read (and cache) the response body as text. */
|
|
79
82
|
async text() {
|
|
@@ -129,6 +132,7 @@ var TestResponse = class {
|
|
|
129
132
|
/** Assert each key in `expected` equals the corresponding top-level value. */
|
|
130
133
|
async assertJson(expected) {
|
|
131
134
|
const actual = await this.json();
|
|
135
|
+
if (!isJsonObject(actual)) expect.fail("Expected JSON body to be an object.");
|
|
132
136
|
for (const [key, value] of Object.entries(expected)) expect(actual[key], `Expected JSON key "${key}" to be ${JSON.stringify(value)}, got ${JSON.stringify(actual[key])}`).toStrictEqual(value);
|
|
133
137
|
return this;
|
|
134
138
|
}
|
|
@@ -150,17 +154,20 @@ var TestResponse = class {
|
|
|
150
154
|
/** Assert the top-level JSON object has every key in `structure`. */
|
|
151
155
|
async assertJsonStructure(structure) {
|
|
152
156
|
const json = await this.json();
|
|
157
|
+
if (!isJsonObject(json)) expect.fail("Expected JSON body to be an object.");
|
|
153
158
|
for (const key of structure) expect(key in json, `Expected JSON to have key "${key}", got keys: ${JSON.stringify(Object.keys(json))}`).toBe(true);
|
|
154
159
|
return this;
|
|
155
160
|
}
|
|
156
161
|
/** Assert a path exists (value may be anything, including `null`). */
|
|
157
162
|
async assertJsonPathExists(path) {
|
|
158
|
-
|
|
163
|
+
const json = await this.json();
|
|
164
|
+
expect(hasValueAtPath(json, path), `Expected JSON path "${path}" to exist`).toBe(true);
|
|
159
165
|
return this;
|
|
160
166
|
}
|
|
161
167
|
/** Assert a path does not exist. */
|
|
162
168
|
async assertJsonPathMissing(path) {
|
|
163
|
-
|
|
169
|
+
const json = await this.json();
|
|
170
|
+
expect(hasValueAtPath(json, path), `Expected JSON path "${path}" to not exist`).toBe(false);
|
|
164
171
|
return this;
|
|
165
172
|
}
|
|
166
173
|
/** Assert the value at a path satisfies a predicate. */
|
|
@@ -172,21 +179,21 @@ var TestResponse = class {
|
|
|
172
179
|
/** Assert the string value at a path contains `substring`. */
|
|
173
180
|
async assertJsonPathContains(path, substring) {
|
|
174
181
|
const value = getValueAtPath(await this.json(), path);
|
|
175
|
-
|
|
182
|
+
if (typeof value !== "string") expect.fail(`Expected JSON path "${path}" to be a string, got ${typeof value}`);
|
|
176
183
|
expect(value.includes(substring), `Expected JSON path "${path}" to contain "${substring}", got "${String(value)}"`).toBe(true);
|
|
177
184
|
return this;
|
|
178
185
|
}
|
|
179
186
|
/** Assert the array value at a path includes `item`. */
|
|
180
187
|
async assertJsonPathIncludes(path, item) {
|
|
181
188
|
const value = getValueAtPath(await this.json(), path);
|
|
182
|
-
|
|
189
|
+
if (!Array.isArray(value)) expect.fail(`Expected JSON path "${path}" to be an array, got ${typeof value}`);
|
|
183
190
|
expect(value.includes(item), `Expected JSON path "${path}" to include ${JSON.stringify(item)}`).toBe(true);
|
|
184
191
|
return this;
|
|
185
192
|
}
|
|
186
193
|
/** Assert the array value at a path has `count` items. */
|
|
187
194
|
async assertJsonPathCount(path, count) {
|
|
188
195
|
const value = getValueAtPath(await this.json(), path);
|
|
189
|
-
|
|
196
|
+
if (!Array.isArray(value)) expect.fail(`Expected JSON path "${path}" to be an array, got ${typeof value}`);
|
|
190
197
|
expect(value.length, `Expected JSON path "${path}" to have ${count} items, got ${value.length}`).toBe(count);
|
|
191
198
|
return this;
|
|
192
199
|
}
|
|
@@ -436,7 +443,8 @@ var TestSseConnection = class {
|
|
|
436
443
|
}
|
|
437
444
|
/** Assert the next event matches the expected partial shape. */
|
|
438
445
|
async assertEvent(expected, timeout = 5e3) {
|
|
439
|
-
|
|
446
|
+
const event = await this.waitForEvent(timeout);
|
|
447
|
+
expect(event).toMatchObject(expected);
|
|
440
448
|
}
|
|
441
449
|
/** Assert the next event's `data` equals `expected`. */
|
|
442
450
|
async assertEventData(expected, timeout = 5e3) {
|
|
@@ -446,7 +454,8 @@ var TestSseConnection = class {
|
|
|
446
454
|
/** Assert the next event's `data` is JSON equal to `expected`. */
|
|
447
455
|
async assertJsonEventData(expected, timeout = 5e3) {
|
|
448
456
|
const event = await this.waitForEvent(timeout);
|
|
449
|
-
|
|
457
|
+
const parsed = JSON.parse(event.data);
|
|
458
|
+
expect(parsed).toEqual(expected);
|
|
450
459
|
}
|
|
451
460
|
startReading() {
|
|
452
461
|
const body = this.response.body;
|
|
@@ -638,8 +647,8 @@ var TestingModule = class {
|
|
|
638
647
|
* Drive a `Request` through the full Hono pipeline. The Hono app is built
|
|
639
648
|
* once and reused across requests.
|
|
640
649
|
*/
|
|
641
|
-
async fetch(
|
|
642
|
-
return (await this.ensureHono()).fetch(
|
|
650
|
+
async fetch(...args) {
|
|
651
|
+
return (await this.ensureHono()).fetch(...args);
|
|
643
652
|
}
|
|
644
653
|
/**
|
|
645
654
|
* Register a default auth resolver used by `actingAs(principal)` when no
|
|
@@ -654,13 +663,15 @@ var TestingModule = class {
|
|
|
654
663
|
return this.authResolver;
|
|
655
664
|
}
|
|
656
665
|
/**
|
|
657
|
-
* Run `callback` inside a request-scoped child container seeded with a
|
|
658
|
-
*
|
|
666
|
+
* Run `callback` inside a request-scoped child container seeded with a real
|
|
667
|
+
* RequestContext, so REQUEST-scoped providers (and anything injecting
|
|
659
668
|
* `REQUEST_CONTEXT`) resolve. The child is disposed afterwards.
|
|
660
669
|
*/
|
|
661
670
|
async runInRequestScope(callback) {
|
|
662
671
|
const child = this.container.createChild();
|
|
663
|
-
|
|
672
|
+
const hono = new Context(new Request("http://localhost/"));
|
|
673
|
+
setRequestContainer(hono, child);
|
|
674
|
+
child.setRequestInstance(REQUEST_CONTEXT, createRequestContext(hono));
|
|
664
675
|
try {
|
|
665
676
|
return await callback(child);
|
|
666
677
|
} finally {
|
|
@@ -684,11 +695,13 @@ var TestingModule = class {
|
|
|
684
695
|
}
|
|
685
696
|
/** Assert a row matching `where` exists in `table` (via a {@link TestDatabase}). */
|
|
686
697
|
async assertDatabaseHas(db, table, where) {
|
|
687
|
-
|
|
698
|
+
const exists = await db.has(table, where);
|
|
699
|
+
expect(exists, `Expected ${table} to have a row matching ${JSON.stringify(where)}`).toBe(true);
|
|
688
700
|
}
|
|
689
701
|
/** Assert no row matching `where` exists in `table`. */
|
|
690
702
|
async assertDatabaseMissing(db, table, where) {
|
|
691
|
-
|
|
703
|
+
const exists = await db.has(table, where);
|
|
704
|
+
expect(exists, `Expected ${table} NOT to have a row matching ${JSON.stringify(where)}`).toBe(false);
|
|
692
705
|
}
|
|
693
706
|
/** Assert `table` has exactly `expected` rows. */
|
|
694
707
|
async assertDatabaseCount(db, table, expected) {
|
|
@@ -706,71 +719,27 @@ var TestingModule = class {
|
|
|
706
719
|
}
|
|
707
720
|
return this.honoApp;
|
|
708
721
|
}
|
|
709
|
-
/**
|
|
710
|
-
* Build a minimal, functional {@link RequestContext} for out-of-band request
|
|
711
|
-
* scopes. Vela has no `createMockRouterContext`; a real (empty) Hono `Context`
|
|
712
|
-
* backs the `hono` field so nothing dangles.
|
|
713
|
-
*/
|
|
714
|
-
createMockRequestContext() {
|
|
715
|
-
const bag = /* @__PURE__ */ new Map();
|
|
716
|
-
const request = new Request("http://localhost/");
|
|
717
|
-
const hono = new Context(request);
|
|
718
|
-
return {
|
|
719
|
-
id: crypto.randomUUID(),
|
|
720
|
-
receivedAt: /* @__PURE__ */ new Date(),
|
|
721
|
-
request,
|
|
722
|
-
hono,
|
|
723
|
-
set(key, value) {
|
|
724
|
-
bag.set(key, value);
|
|
725
|
-
},
|
|
726
|
-
get(key) {
|
|
727
|
-
return bag.get(key);
|
|
728
|
-
},
|
|
729
|
-
has(key) {
|
|
730
|
-
return bag.has(key);
|
|
731
|
-
}
|
|
732
|
-
};
|
|
733
|
-
}
|
|
734
722
|
};
|
|
735
723
|
//#endregion
|
|
736
724
|
//#region src/testing-module.builder.ts
|
|
737
725
|
var OverrideBy = class {
|
|
738
|
-
|
|
726
|
+
commit;
|
|
739
727
|
token;
|
|
740
|
-
constructor(
|
|
741
|
-
this.
|
|
728
|
+
constructor(commit, token) {
|
|
729
|
+
this.commit = commit;
|
|
742
730
|
this.token = token;
|
|
743
731
|
}
|
|
744
732
|
useValue(value) {
|
|
745
|
-
this.
|
|
746
|
-
token: this.token,
|
|
747
|
-
provider: {
|
|
748
|
-
provide: this.token,
|
|
749
|
-
useValue: value
|
|
750
|
-
}
|
|
751
|
-
});
|
|
752
|
-
return this.builder;
|
|
733
|
+
return this.commit(defineProvider(this.token, { useValue: value }));
|
|
753
734
|
}
|
|
754
735
|
useClass(cls) {
|
|
755
|
-
this.
|
|
756
|
-
token: this.token,
|
|
757
|
-
provider: {
|
|
758
|
-
provide: this.token,
|
|
759
|
-
useClass: cls
|
|
760
|
-
}
|
|
761
|
-
});
|
|
762
|
-
return this.builder;
|
|
736
|
+
return this.commit(defineProvider(this.token, { useClass: cls }));
|
|
763
737
|
}
|
|
764
738
|
useFactory(options) {
|
|
765
|
-
this.
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
useFactory: options.factory,
|
|
770
|
-
inject: options.inject
|
|
771
|
-
}
|
|
772
|
-
});
|
|
773
|
-
return this.builder;
|
|
739
|
+
return this.commit(defineProvider(this.token, {
|
|
740
|
+
useFactory: options.factory,
|
|
741
|
+
inject: options.inject
|
|
742
|
+
}));
|
|
774
743
|
}
|
|
775
744
|
};
|
|
776
745
|
var TestingModuleBuilder = class {
|
|
@@ -780,19 +749,25 @@ var TestingModuleBuilder = class {
|
|
|
780
749
|
this.metadata = metadata;
|
|
781
750
|
}
|
|
782
751
|
overrideProvider(token) {
|
|
783
|
-
return new OverrideBy(
|
|
752
|
+
return new OverrideBy((provider) => {
|
|
753
|
+
this.addOverride({
|
|
754
|
+
token,
|
|
755
|
+
provider
|
|
756
|
+
});
|
|
757
|
+
return this;
|
|
758
|
+
}, token);
|
|
784
759
|
}
|
|
785
760
|
overrideGuard(guard) {
|
|
786
|
-
return
|
|
761
|
+
return this.overrideProvider(guard);
|
|
787
762
|
}
|
|
788
763
|
overridePipe(pipe) {
|
|
789
|
-
return
|
|
764
|
+
return this.overrideProvider(pipe);
|
|
790
765
|
}
|
|
791
766
|
overrideInterceptor(interceptor) {
|
|
792
|
-
return
|
|
767
|
+
return this.overrideProvider(interceptor);
|
|
793
768
|
}
|
|
794
769
|
overrideFilter(filter) {
|
|
795
|
-
return
|
|
770
|
+
return this.overrideProvider(filter);
|
|
796
771
|
}
|
|
797
772
|
addOverride(entry) {
|
|
798
773
|
const idx = this.overrides.findIndex((o) => o.token === entry.token);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["VelaApplication"],"sources":["../src/http/path-utils.ts","../src/http/test-response.ts","../src/http/test-http-request.ts","../src/http/test-http-client.ts","../src/sse/test-sse-connection.ts","../src/sse/test-sse-request.ts","../src/testing-module.ts","../src/testing-module.builder.ts","../src/test.ts"],"sourcesContent":["// Ported from @stratal/testing (MIT, © Temitayo Fadojutimi).\n\n/**\n * Read the value at a dot-notation path (e.g. `data.user.id`).\n * Returns `undefined` when any segment along the way is null/undefined.\n */\nexport function getValueAtPath(obj: unknown, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n}\n\n/**\n * Whether a dot-notation path exists on the object, even when the value at the\n * path is `null`/`undefined`. Distinguishes \"key present but null\" from \"key\n * absent\".\n */\nexport function hasValueAtPath(obj: unknown, path: string): boolean {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined) {\n return false;\n }\n\n if (typeof current !== 'object') {\n return false;\n }\n\n const record = current as Record<string, unknown>;\n\n if (!(part in record)) {\n return false;\n }\n\n current = record[part];\n }\n\n return true;\n}\n","// Ported from @stratal/testing (MIT, © Temitayo Fadojutimi), minus Macroable —\n// vela has no Macroable, so TestResponse is a plain class.\nimport { expect } from 'vitest';\nimport { getValueAtPath, hasValueAtPath } from './path-utils.js';\n\n/**\n * TestResponse\n *\n * Wraps a `Response` with fluent, chainable assertions. Synchronous status /\n * header assertions return `this`; JSON assertions (which must read the body)\n * return `Promise<this>`.\n *\n * @example\n * ```ts\n * const res = await module.http.get('/users/1').send();\n * res.assertOk();\n * await res.assertJsonPath('data.id', 1);\n * ```\n */\nexport class TestResponse {\n private jsonData: unknown = null;\n private textData: string | null = null;\n\n constructor(private readonly response: Response) {}\n\n /** The raw `Response`. */\n get raw(): Response {\n return this.response;\n }\n\n /** The response status code. */\n get status(): number {\n return this.response.status;\n }\n\n /** The response headers. */\n get headers(): Headers {\n return this.response.headers;\n }\n\n /** Parse (and cache) the response body as JSON. */\n async json<T = unknown>(): Promise<T> {\n if (this.jsonData === null) {\n this.jsonData = await this.response.clone().json();\n }\n return this.jsonData as T;\n }\n\n /** Read (and cache) the response body as text. */\n async text(): Promise<string> {\n this.textData ??= await this.response.clone().text();\n return this.textData;\n }\n\n // ============================================================\n // Status assertions\n // ============================================================\n\n /** Assert status is 200 OK. */\n assertOk(): this {\n return this.assertStatus(200);\n }\n\n /** Assert status is 201 Created. */\n assertCreated(): this {\n return this.assertStatus(201);\n }\n\n /** Assert status is 204 No Content. */\n assertNoContent(): this {\n return this.assertStatus(204);\n }\n\n /** Assert status is 400 Bad Request. */\n assertBadRequest(): this {\n return this.assertStatus(400);\n }\n\n /** Assert status is 401 Unauthorized. */\n assertUnauthorized(): this {\n return this.assertStatus(401);\n }\n\n /** Assert status is 403 Forbidden. */\n assertForbidden(): this {\n return this.assertStatus(403);\n }\n\n /** Assert status is 404 Not Found. */\n assertNotFound(): this {\n return this.assertStatus(404);\n }\n\n /** Assert status is 422 Unprocessable Entity. */\n assertUnprocessable(): this {\n return this.assertStatus(422);\n }\n\n /** Assert status is 500 Internal Server Error. */\n assertServerError(): this {\n return this.assertStatus(500);\n }\n\n /** Assert the response has the given status code. */\n assertStatus(expected: number): this {\n expect(this.response.status, `Expected status ${expected}, got ${this.response.status}`).toBe(\n expected,\n );\n return this;\n }\n\n /** Assert the status is in the 2xx range. */\n assertSuccessful(): this {\n expect(\n this.response.status >= 200 && this.response.status < 300,\n `Expected successful status (2xx), got ${this.response.status}`,\n ).toBe(true);\n return this;\n }\n\n // ============================================================\n // JSON assertions\n // ============================================================\n\n /** Assert each key in `expected` equals the corresponding top-level value. */\n async assertJson(expected: Record<string, unknown>): Promise<this> {\n const actual = await this.json<Record<string, unknown>>();\n\n for (const [key, value] of Object.entries(expected)) {\n expect(\n actual[key],\n `Expected JSON key \"${key}\" to be ${JSON.stringify(value)}, got ${JSON.stringify(actual[key])}`,\n ).toStrictEqual(value);\n }\n\n return this;\n }\n\n /** Assert the value at a dot-notation path equals `expected`. */\n async assertJsonPath(path: string, expected: unknown): Promise<this> {\n const json = await this.json();\n const actual = getValueAtPath(json, path);\n\n expect(\n actual,\n `Expected JSON path \"${path}\" to be ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`,\n ).toStrictEqual(expected);\n\n return this;\n }\n\n /** Assert every path/value pair in `expectations` matches (batch assert). */\n async assertJsonPaths(expectations: Record<string, unknown>): Promise<this> {\n const json = await this.json();\n\n for (const [path, expected] of Object.entries(expectations)) {\n const actual = getValueAtPath(json, path);\n expect(\n actual,\n `Expected JSON path \"${path}\" to be ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`,\n ).toStrictEqual(expected);\n }\n\n return this;\n }\n\n /** Assert the top-level JSON object has every key in `structure`. */\n async assertJsonStructure(structure: string[]): Promise<this> {\n const json = await this.json<Record<string, unknown>>();\n\n for (const key of structure) {\n expect(\n key in json,\n `Expected JSON to have key \"${key}\", got keys: ${JSON.stringify(Object.keys(json))}`,\n ).toBe(true);\n }\n\n return this;\n }\n\n /** Assert a path exists (value may be anything, including `null`). */\n async assertJsonPathExists(path: string): Promise<this> {\n const json = await this.json();\n\n expect(hasValueAtPath(json, path), `Expected JSON path \"${path}\" to exist`).toBe(true);\n\n return this;\n }\n\n /** Assert a path does not exist. */\n async assertJsonPathMissing(path: string): Promise<this> {\n const json = await this.json();\n\n expect(hasValueAtPath(json, path), `Expected JSON path \"${path}\" to not exist`).toBe(false);\n\n return this;\n }\n\n /** Assert the value at a path satisfies a predicate. */\n async assertJsonPathMatches(path: string, matcher: (value: unknown) => boolean): Promise<this> {\n const json = await this.json();\n const value = getValueAtPath(json, path);\n\n expect(\n matcher(value),\n `Expected JSON path \"${path}\" to match predicate, got ${JSON.stringify(value)}`,\n ).toBe(true);\n\n return this;\n }\n\n /** Assert the string value at a path contains `substring`. */\n async assertJsonPathContains(path: string, substring: string): Promise<this> {\n const json = await this.json();\n const value = getValueAtPath(json, path);\n\n expect(\n typeof value === 'string',\n `Expected JSON path \"${path}\" to be a string, got ${typeof value}`,\n ).toBe(true);\n\n expect(\n (value as string).includes(substring),\n `Expected JSON path \"${path}\" to contain \"${substring}\", got \"${String(value)}\"`,\n ).toBe(true);\n\n return this;\n }\n\n /** Assert the array value at a path includes `item`. */\n async assertJsonPathIncludes(path: string, item: unknown): Promise<this> {\n const json = await this.json();\n const value = getValueAtPath(json, path);\n\n expect(\n Array.isArray(value),\n `Expected JSON path \"${path}\" to be an array, got ${typeof value}`,\n ).toBe(true);\n\n expect(\n (value as unknown[]).includes(item),\n `Expected JSON path \"${path}\" to include ${JSON.stringify(item)}`,\n ).toBe(true);\n\n return this;\n }\n\n /** Assert the array value at a path has `count` items. */\n async assertJsonPathCount(path: string, count: number): Promise<this> {\n const json = await this.json();\n const value = getValueAtPath(json, path);\n\n expect(\n Array.isArray(value),\n `Expected JSON path \"${path}\" to be an array, got ${typeof value}`,\n ).toBe(true);\n\n expect(\n (value as unknown[]).length,\n `Expected JSON path \"${path}\" to have ${count} items, got ${(value as unknown[]).length}`,\n ).toBe(count);\n\n return this;\n }\n\n // ============================================================\n // Header assertions\n // ============================================================\n\n /** Assert a header is present, optionally equal to `expected`. */\n assertHeader(name: string, expected?: string): this {\n const actual = this.response.headers.get(name);\n\n expect(actual !== null, `Expected header \"${name}\" to be present`).toBe(true);\n\n if (expected !== undefined) {\n expect(actual, `Expected header \"${name}\" to be \"${expected}\", got \"${actual}\"`).toBe(\n expected,\n );\n }\n\n return this;\n }\n\n /** Assert a header is absent. */\n assertHeaderMissing(name: string): this {\n const actual = this.response.headers.get(name);\n\n expect(actual, `Expected header \"${name}\" to be absent, but got \"${actual}\"`).toBeNull();\n\n return this;\n }\n}\n","// Adapted from @stratal/testing (MIT, © Temitayo Fadojutimi): stratal's hard\n// AuthService import is replaced by a generic auth-resolver seam so\n// @velajs/testing stays free of optional-package dependencies.\nimport type { ActingAsResolver, TestPrincipal, TestingModule } from '../testing-module.js';\nimport { TestResponse } from './test-response.js';\n\n/**\n * TestHttpRequest\n *\n * Fluent builder for a single test HTTP request. `send()` builds a `Request`\n * and drives it through `module.fetch()` (the full Hono pipeline).\n *\n * @example\n * ```ts\n * const res = await module.http\n * .post('/users')\n * .withBody({ name: 'A' })\n * .withHeaders({ 'X-Trace': '1' })\n * .send();\n * res.assertCreated();\n * ```\n */\nexport class TestHttpRequest {\n private body: unknown = undefined;\n private readonly requestHeaders: Headers;\n private principal: TestPrincipal | null = null;\n private resolver: ActingAsResolver | null = null;\n\n constructor(\n private readonly method: string,\n private readonly path: string,\n headers: Headers,\n private readonly module: TestingModule,\n private readonly host: string | null = null,\n ) {\n this.requestHeaders = new Headers(headers);\n }\n\n /** Set the request body (JSON-serialized on send). */\n withBody(data: unknown): this {\n this.body = data;\n return this;\n }\n\n /** Merge additional headers. */\n withHeaders(headers: Record<string, string>): this {\n for (const [key, value] of Object.entries(headers)) {\n this.requestHeaders.set(key, value);\n }\n return this;\n }\n\n /** Set `Content-Type: application/json`. */\n asJson(): this {\n this.requestHeaders.set('Content-Type', 'application/json');\n return this;\n }\n\n /**\n * Authenticate the request as `principal`. The `resolver` (or a default one\n * registered via `module.setAuthResolver`) turns the principal into request\n * headers. The resolver signature `(module, principal) => Promise<Headers>`\n * is the cross-package contract sibling packages (e.g. `@velajs/better-auth`)\n * build against.\n */\n actingAs(principal: TestPrincipal, resolver?: ActingAsResolver): this {\n this.principal = principal;\n this.resolver = resolver ?? null;\n return this;\n }\n\n /** Build the `Request` and send it through `module.fetch()`. */\n async send(): Promise<TestResponse> {\n await this.applyAuthentication();\n\n const hasBody = this.body !== undefined && this.body !== null;\n if (hasBody && !this.requestHeaders.has('Content-Type')) {\n this.requestHeaders.set('Content-Type', 'application/json');\n }\n\n const url = new URL(this.path, `http://${this.host ?? 'localhost'}`);\n const request = new Request(url.toString(), {\n method: this.method,\n headers: this.requestHeaders,\n body: hasBody ? JSON.stringify(this.body) : null,\n });\n\n const response = await this.module.fetch(request);\n return new TestResponse(response);\n }\n\n private async applyAuthentication(): Promise<void> {\n if (!this.principal) return;\n\n const resolver = this.resolver ?? this.module.getAuthResolver();\n if (!resolver) {\n throw new Error(\n 'actingAs() requires an auth resolver. Pass one explicitly — ' +\n 'actingAs(principal, resolver) — or register a default with ' +\n 'module.setAuthResolver(resolver). For better-auth: ' +\n 'import { actingAs } from \"@velajs/better-auth/testing\".',\n );\n }\n\n const headers = await resolver(this.module, this.principal);\n for (const [key, value] of headers.entries()) {\n this.requestHeaders.set(key, value);\n }\n }\n}\n","// Adapted from @stratal/testing (MIT, © Temitayo Fadojutimi). Stratal's i18n\n// `withLocale` is dropped (vela i18n differs — optional follow-up).\nimport type { TestingModule } from '../testing-module.js';\nimport { TestHttpRequest } from './test-http-request.js';\n\n/**\n * TestHttpClient\n *\n * Fluent entry point for test HTTP requests. `forHost`/`withHeaders` return a\n * new immutable client; the verb methods start a {@link TestHttpRequest}.\n *\n * @example\n * ```ts\n * const res = await module.http\n * .forHost('example.com')\n * .post('/users')\n * .withBody({ name: 'A' })\n * .send();\n * res.assertCreated();\n * ```\n */\nexport class TestHttpClient {\n constructor(\n private readonly module: TestingModule,\n private readonly host: string | null = null,\n private readonly defaultHeaders: Headers = new Headers(),\n ) {}\n\n /**\n * Return a new client bound to `host`. Also sets the `Host` header so domain\n * routing works even when the runtime reads the header rather than the URL.\n */\n forHost(host: string): TestHttpClient {\n const headers = new Headers(this.defaultHeaders);\n headers.set('Host', host);\n return new TestHttpClient(this.module, host, headers);\n }\n\n /** Return a new client with additional default headers on every request. */\n withHeaders(headers: Record<string, string>): TestHttpClient {\n const next = new Headers(this.defaultHeaders);\n for (const [key, value] of Object.entries(headers)) {\n next.set(key, value);\n }\n return new TestHttpClient(this.module, this.host, next);\n }\n\n get(path: string): TestHttpRequest {\n return this.createRequest('GET', path);\n }\n\n post(path: string): TestHttpRequest {\n return this.createRequest('POST', path);\n }\n\n put(path: string): TestHttpRequest {\n return this.createRequest('PUT', path);\n }\n\n patch(path: string): TestHttpRequest {\n return this.createRequest('PATCH', path);\n }\n\n delete(path: string): TestHttpRequest {\n return this.createRequest('DELETE', path);\n }\n\n private createRequest(method: string, path: string): TestHttpRequest {\n return new TestHttpRequest(method, path, this.defaultHeaders, this.module, this.host);\n }\n}\n","// Ported near-verbatim from @stratal/testing (MIT, © Temitayo Fadojutimi).\n// Web-standard only (ReadableStream + TextDecoder), so it is edge-pure.\nimport { expect } from 'vitest';\n\n/** A parsed Server-Sent Event. */\nexport interface TestSseEvent {\n data: string;\n event?: string;\n id?: string;\n retry?: number;\n}\n\n/**\n * TestSseConnection\n *\n * Reads a streaming `text/event-stream` response body and exposes queue-based\n * wait/assert helpers over the parsed events.\n *\n * @example\n * ```ts\n * const sse = await module.sse('/stream/events').connect();\n * await sse.assertEventData('ping');\n * await sse.waitForEnd();\n * ```\n */\nexport class TestSseConnection {\n private readonly eventQueue: TestSseEvent[] = [];\n private eventWaiters: ((event: TestSseEvent) => void)[] = [];\n private streamEnded = false;\n private endWaiters: (() => void)[] = [];\n\n constructor(private readonly response: Response) {\n this.startReading();\n }\n\n /** The raw `Response`. */\n get raw(): Response {\n return this.response;\n }\n\n /** Wait for the next event (rejects after `timeout` ms). */\n async waitForEvent(timeout = 5000): Promise<TestSseEvent> {\n if (this.eventQueue.length > 0) {\n return this.eventQueue.shift()!;\n }\n\n if (this.streamEnded) {\n throw new Error('SSE: stream has ended, no more events');\n }\n\n return new Promise<TestSseEvent>((resolve, reject) => {\n const waiter = (event: TestSseEvent): void => {\n clearTimeout(timer);\n resolve(event);\n };\n\n const timer = setTimeout(() => {\n const index = this.eventWaiters.indexOf(waiter);\n if (index !== -1) this.eventWaiters.splice(index, 1);\n reject(new Error(`SSE: no event received within ${timeout}ms`));\n }, timeout);\n\n this.eventWaiters.push(waiter);\n });\n }\n\n /** Wait for the stream to end (rejects after `timeout` ms). */\n async waitForEnd(timeout = 5000): Promise<void> {\n if (this.streamEnded) return;\n\n return new Promise<void>((resolve, reject) => {\n const waiter = (): void => {\n clearTimeout(timer);\n resolve();\n };\n\n const timer = setTimeout(() => {\n const index = this.endWaiters.indexOf(waiter);\n if (index !== -1) this.endWaiters.splice(index, 1);\n reject(new Error(`SSE: stream did not end within ${timeout}ms`));\n }, timeout);\n\n this.endWaiters.push(waiter);\n });\n }\n\n /** Collect all remaining events until the stream ends. */\n async collectEvents(timeout = 5000): Promise<TestSseEvent[]> {\n const events: TestSseEvent[] = [];\n\n if (this.streamEnded) {\n return [...this.eventQueue.splice(0)];\n }\n\n return new Promise<TestSseEvent[]>((resolve, reject) => {\n const originalDispatch = this.dispatchEvent.bind(this);\n this.dispatchEvent = (event: TestSseEvent): void => {\n events.push(event);\n originalDispatch(event);\n };\n\n const endWaiter = (): void => {\n clearTimeout(timer);\n this.dispatchEvent = originalDispatch;\n resolve(events);\n };\n\n const timer = setTimeout(() => {\n this.dispatchEvent = originalDispatch;\n const index = this.endWaiters.indexOf(endWaiter);\n if (index !== -1) this.endWaiters.splice(index, 1);\n reject(new Error(`SSE: stream did not end within ${timeout}ms`));\n }, timeout);\n\n events.push(...this.eventQueue.splice(0));\n\n this.endWaiters.push(endWaiter);\n });\n }\n\n /** Assert the next event matches the expected partial shape. */\n async assertEvent(expected: Partial<TestSseEvent>, timeout = 5000): Promise<void> {\n const event = await this.waitForEvent(timeout);\n expect(event).toMatchObject(expected);\n }\n\n /** Assert the next event's `data` equals `expected`. */\n async assertEventData(expected: string, timeout = 5000): Promise<void> {\n const event = await this.waitForEvent(timeout);\n expect(event.data, `Expected SSE data \"${expected}\", got \"${event.data}\"`).toBe(expected);\n }\n\n /** Assert the next event's `data` is JSON equal to `expected`. */\n async assertJsonEventData<T>(expected: T, timeout = 5000): Promise<void> {\n const event = await this.waitForEvent(timeout);\n const parsed = JSON.parse(event.data) as unknown;\n expect(parsed).toEqual(expected);\n }\n\n private startReading(): void {\n const body = this.response.body;\n if (!body) {\n this.streamEnded = true;\n return;\n }\n\n const reader = body.getReader() as ReadableStreamDefaultReader<Uint8Array>;\n const decoder = new TextDecoder();\n let buffer = '';\n\n const read = async (): Promise<void> => {\n try {\n for (;;) {\n const { done, value } = await reader.read();\n\n if (done) {\n if (buffer.trim()) {\n const event = this.parseEvent(buffer);\n if (event) this.dispatchEvent(event);\n }\n this.endStream();\n return;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const parts = buffer.split('\\n\\n');\n buffer = parts.pop()!;\n\n for (const part of parts) {\n if (!part.trim()) continue;\n const event = this.parseEvent(part);\n if (event) this.dispatchEvent(event);\n }\n }\n } catch {\n this.endStream();\n }\n };\n\n void read();\n }\n\n private endStream(): void {\n this.streamEnded = true;\n for (const waiter of this.endWaiters) {\n waiter();\n }\n this.endWaiters = [];\n }\n\n private parseEvent(raw: string): TestSseEvent | null {\n const lines = raw.split('\\n');\n const dataLines: string[] = [];\n let event: string | undefined;\n let id: string | undefined;\n let retry: number | undefined;\n\n for (const line of lines) {\n if (line.startsWith(':')) continue; // comment line\n\n const colonIndex = line.indexOf(':');\n if (colonIndex === -1) continue;\n\n const field = line.slice(0, colonIndex);\n const value =\n line[colonIndex + 1] === ' ' ? line.slice(colonIndex + 2) : line.slice(colonIndex + 1);\n\n switch (field) {\n case 'data':\n dataLines.push(value);\n break;\n case 'event':\n event = value;\n break;\n case 'id':\n id = value;\n break;\n case 'retry': {\n const parsed = parseInt(value, 10);\n if (!Number.isNaN(parsed)) retry = parsed;\n break;\n }\n }\n }\n\n if (dataLines.length === 0) return null;\n\n const result: TestSseEvent = { data: dataLines.join('\\n') };\n if (event !== undefined) result.event = event;\n if (id !== undefined) result.id = id;\n if (retry !== undefined) result.retry = retry;\n\n return result;\n }\n\n private dispatchEvent(event: TestSseEvent): void {\n if (this.eventWaiters.length > 0) {\n this.eventWaiters.shift()!(event);\n } else {\n this.eventQueue.push(event);\n }\n }\n}\n","// Adapted from @stratal/testing (MIT, © Temitayo Fadojutimi). Auth uses the\n// generic resolver seam instead of a hard AuthService import.\nimport { expect } from 'vitest';\nimport type { ActingAsResolver, TestPrincipal, TestingModule } from '../testing-module.js';\nimport { TestSseConnection } from './test-sse-connection.js';\n\n/**\n * TestSseRequest\n *\n * Builder for a Server-Sent Events connection. `connect()` issues a GET through\n * `module.fetch()`, asserts a `text/event-stream` 200, and wraps the streaming\n * body in a {@link TestSseConnection}.\n *\n * @example\n * ```ts\n * const sse = await module.sse('/stream/events').connect();\n * await sse.assertEvent({ event: 'message', data: 'hello' });\n * ```\n */\nexport class TestSseRequest {\n private readonly requestHeaders = new Headers();\n private principal: TestPrincipal | null = null;\n private resolver: ActingAsResolver | null = null;\n\n constructor(\n private readonly path: string,\n private readonly module: TestingModule,\n ) {}\n\n /** Merge additional headers onto the SSE request. */\n withHeaders(headers: Record<string, string>): this {\n for (const [key, value] of Object.entries(headers)) {\n this.requestHeaders.set(key, value);\n }\n return this;\n }\n\n /** Authenticate the connection (see {@link TestHttpRequest.actingAs}). */\n actingAs(principal: TestPrincipal, resolver?: ActingAsResolver): this {\n this.principal = principal;\n this.resolver = resolver ?? null;\n return this;\n }\n\n /** Open the stream and return a live {@link TestSseConnection}. */\n async connect(): Promise<TestSseConnection> {\n await this.applyAuthentication();\n\n this.requestHeaders.set('Accept', 'text/event-stream');\n\n const url = new URL(this.path, 'http://localhost');\n const request = new Request(url.toString(), { headers: this.requestHeaders });\n\n const response = await this.module.fetch(request);\n\n expect(response.status, `Expected status 200, got ${response.status}`).toBe(200);\n\n const contentType = response.headers.get('content-type') ?? '';\n expect(\n contentType.includes('text/event-stream'),\n `Expected content-type \"text/event-stream\", got \"${contentType}\"`,\n ).toBe(true);\n\n return new TestSseConnection(response);\n }\n\n private async applyAuthentication(): Promise<void> {\n if (!this.principal) return;\n\n const resolver = this.resolver ?? this.module.getAuthResolver();\n if (!resolver) {\n throw new Error(\n 'actingAs() requires an auth resolver. Pass one explicitly or register ' +\n 'a default with module.setAuthResolver(resolver).',\n );\n }\n\n const headers = await resolver(this.module, this.principal);\n for (const [key, value] of headers.entries()) {\n this.requestHeaders.set(key, value);\n }\n }\n}\n","import { Context } from 'hono';\nimport {\n REQUEST_CONTEXT,\n type RequestContext,\n type Token,\n type Type,\n type VelaApplication,\n} from '@velajs/vela';\nimport type { Container } from '@velajs/vela/internal';\nimport { SeederRegistry, type ISeeder } from '@velajs/vela/seeder';\nimport { expect } from 'vitest';\nimport type { TestDatabase } from './db/test-database.js';\nimport { TestHttpClient } from './http/test-http-client.js';\nimport { TestSseRequest } from './sse/test-sse-request.js';\nimport { TestWsRequest } from './ws/test-ws-request.js';\n\n/** A test principal — an opaque object the auth resolver turns into headers. */\nexport type TestPrincipal = Record<string, unknown>;\n\n/**\n * Turns a principal into request headers (session cookie, bearer token, …).\n * The signature `(module, principal) => Promise<Headers>` is a cross-package\n * contract: sibling packages (e.g. `@velajs/better-auth/testing`) build a\n * resolver against it. Kept generic so `@velajs/testing` needs no auth deps.\n */\nexport type ActingAsResolver = (\n module: TestingModule,\n principal: TestPrincipal,\n) => Promise<Headers>;\n\ntype HonoApp = ReturnType<VelaApplication['getHonoApp']>;\n\n/**\n * TestingModule\n *\n * The compiled test harness. Beyond `get`/`createApplication`/`close`, it adds\n * Laravel-flavored ergonomics: a fluent HTTP client, SSE/WS builders, request-\n * scope execution, seeding, and database assertion wrappers.\n *\n * @example\n * ```ts\n * const module = await Test.createTestingModule({ imports: [AppModule] }).compile();\n * await module.http.post('/users').withBody({ name: 'A' }).send()\n * .then((r) => r.assertCreated());\n * ```\n */\nexport class TestingModule {\n private _http: TestHttpClient | null = null;\n private honoApp: HonoApp | null = null;\n private authResolver: ActingAsResolver | null = null;\n\n constructor(\n private readonly app: VelaApplication,\n private readonly container: Container,\n ) {}\n\n /** Resolve a provider from the root container. */\n get<T>(token: Token<T>): T {\n return this.container.resolve(token);\n }\n\n /** Build (once) and return the underlying application. */\n async createApplication(): Promise<VelaApplication> {\n await this.app.initRoutes();\n return this.app;\n }\n\n /** Lazy fluent HTTP client bound to this module. */\n get http(): TestHttpClient {\n this._http ??= new TestHttpClient(this);\n return this._http;\n }\n\n /** Start an SSE connection builder for `path`. */\n sse(path: string): TestSseRequest {\n return new TestSseRequest(path, this);\n }\n\n /** Start a WebSocket connection builder for `path` (needs a transport adapter). */\n ws(path: string): TestWsRequest {\n return new TestWsRequest(path, this);\n }\n\n /**\n * Drive a `Request` through the full Hono pipeline. The Hono app is built\n * once and reused across requests.\n */\n async fetch(request: Request, env?: unknown, ctx?: unknown): Promise<Response> {\n const hono = await this.ensureHono();\n return hono.fetch(request, env as never, ctx as never);\n }\n\n /**\n * Register a default auth resolver used by `actingAs(principal)` when no\n * resolver is passed explicitly.\n */\n setAuthResolver(resolver: ActingAsResolver): this {\n this.authResolver = resolver;\n return this;\n }\n\n /** The default auth resolver, if one was registered. */\n getAuthResolver(): ActingAsResolver | null {\n return this.authResolver;\n }\n\n /**\n * Run `callback` inside a request-scoped child container seeded with a mock\n * {@link RequestContext}, so REQUEST-scoped providers (and anything injecting\n * `REQUEST_CONTEXT`) resolve. The child is disposed afterwards.\n */\n async runInRequestScope<T>(callback: (container: Container) => T | Promise<T>): Promise<T> {\n const child = this.container.createChild();\n child.setRequestInstance(REQUEST_CONTEXT, this.createMockRequestContext());\n try {\n return await callback(child);\n } finally {\n await child.dispose();\n }\n }\n\n /**\n * Run the given `@Seeder()` classes, each in its own request scope. Throws if\n * a class is not a registered seeder. Requires `SeederModule` (or the seeders\n * themselves) to be present in the module graph.\n */\n async seed(...SeederClasses: Type<ISeeder>[]): Promise<void> {\n const registry = this.container.resolve(SeederRegistry);\n const known = new Set<unknown>(registry.list().map((s) => s.target));\n\n for (const SeederClass of SeederClasses) {\n if (!known.has(SeederClass)) {\n throw new Error(\n `Seeder \"${SeederClass.name}\" is not registered. Add it to a module's ` +\n 'providers or SeederModule.forRoot({ seeders: [...] }).',\n );\n }\n await this.runInRequestScope(async (child) => {\n const instance = child.resolve<ISeeder>(SeederClass);\n await instance.run();\n });\n }\n }\n\n /** Assert a row matching `where` exists in `table` (via a {@link TestDatabase}). */\n async assertDatabaseHas(\n db: TestDatabase,\n table: string,\n where: Record<string, unknown>,\n ): Promise<void> {\n const exists = await db.has(table, where);\n expect(exists, `Expected ${table} to have a row matching ${JSON.stringify(where)}`).toBe(true);\n }\n\n /** Assert no row matching `where` exists in `table`. */\n async assertDatabaseMissing(\n db: TestDatabase,\n table: string,\n where: Record<string, unknown>,\n ): Promise<void> {\n const exists = await db.has(table, where);\n expect(exists, `Expected ${table} NOT to have a row matching ${JSON.stringify(where)}`).toBe(\n false,\n );\n }\n\n /** Assert `table` has exactly `expected` rows. */\n async assertDatabaseCount(db: TestDatabase, table: string, expected: number): Promise<void> {\n const actual = await db.count(table);\n expect(actual, `Expected ${table} count ${expected}, got ${actual}`).toBe(expected);\n }\n\n /** Dispose the application. */\n async close(signal?: string): Promise<void> {\n await this.app.close(signal);\n }\n\n private async ensureHono(): Promise<HonoApp> {\n if (!this.honoApp) {\n const app = await this.createApplication();\n this.honoApp = app.getHonoApp();\n }\n return this.honoApp;\n }\n\n /**\n * Build a minimal, functional {@link RequestContext} for out-of-band request\n * scopes. Vela has no `createMockRouterContext`; a real (empty) Hono `Context`\n * backs the `hono` field so nothing dangles.\n */\n private createMockRequestContext(): RequestContext {\n const bag = new Map<string | symbol, unknown>();\n const request = new Request('http://localhost/');\n const hono = new Context(request);\n return {\n id: crypto.randomUUID(),\n receivedAt: new Date(),\n request,\n hono,\n set(key, value) {\n bag.set(key, value);\n },\n get<V>(key: string | symbol): V | undefined {\n return bag.get(key) as V | undefined;\n },\n has(key) {\n return bag.has(key);\n },\n };\n }\n}\n","import { type ModuleOptions, type ProviderOptions, type Token, type Type } from '@velajs/vela';\nimport { MetadataRegistry, VelaApplication, bootstrap } from '@velajs/vela/internal';\nimport { TestingModule } from './testing-module.js';\n\ninterface OverrideEntry {\n token: Token;\n provider: ProviderOptions;\n}\n\nexport class OverrideBy {\n constructor(\n private readonly builder: TestingModuleBuilder,\n private readonly token: Token,\n ) {}\n\n useValue(value: unknown): TestingModuleBuilder {\n this.builder['addOverride']({\n token: this.token,\n provider: { provide: this.token, useValue: value },\n });\n return this.builder;\n }\n\n useClass(cls: Type): TestingModuleBuilder {\n this.builder['addOverride']({\n token: this.token,\n provider: { provide: this.token, useClass: cls },\n });\n return this.builder;\n }\n\n useFactory(options: {\n factory: (...args: unknown[]) => unknown;\n inject?: Token[];\n }): TestingModuleBuilder {\n this.builder['addOverride']({\n token: this.token,\n provider: {\n provide: this.token,\n useFactory: options.factory,\n inject: options.inject,\n },\n });\n return this.builder;\n }\n}\n\nexport class TestingModuleBuilder {\n private overrides: OverrideEntry[] = [];\n\n constructor(private readonly metadata: ModuleOptions) {}\n\n overrideProvider(token: Token): OverrideBy {\n return new OverrideBy(this, token);\n }\n\n overrideGuard(guard: Type): OverrideBy {\n return new OverrideBy(this, guard);\n }\n\n overridePipe(pipe: Type): OverrideBy {\n return new OverrideBy(this, pipe);\n }\n\n overrideInterceptor(interceptor: Type): OverrideBy {\n return new OverrideBy(this, interceptor);\n }\n\n overrideFilter(filter: Type): OverrideBy {\n return new OverrideBy(this, filter);\n }\n\n private addOverride(entry: OverrideEntry): void {\n const idx = this.overrides.findIndex((o) => o.token === entry.token);\n if (idx !== -1) {\n this.overrides[idx] = entry;\n } else {\n this.overrides.push(entry);\n }\n }\n\n async compile(): Promise<TestingModule> {\n class TestRootModule {}\n MetadataRegistry.setModuleOptions(TestRootModule, {\n imports: this.metadata.imports,\n providers: this.metadata.providers,\n controllers: this.metadata.controllers,\n exports: this.metadata.exports,\n });\n\n // Use the framework's single bootstrap primitive. Hand-copying its\n // registrations caused test applications to drift from production (most\n // critically REQUEST_CONTEXT token/request-child behavior).\n const { container, routeManager, loader } = await bootstrap(TestRootModule);\n\n // Force-apply overrides into every module bucket that already holds the\n // token (plus root). Without this, controller constructor-injection (which\n // passes requestingModuleId to findRegistration) finds the module's own\n // registration first and never consults the root override. The default\n // 'all-existing' buckets replace every non-root bucket holding the token\n // and re-register at root — the supported form of the old private loop.\n for (const override of this.overrides) {\n container.replaceProvider(override.provider);\n }\n\n const app = new VelaApplication(container, routeManager);\n const instances = await loader.resolveAllInstances();\n app.setInstances(instances);\n\n await app.callOnModuleInit();\n await app.callOnApplicationBootstrap();\n await app.initRoutes();\n\n return new TestingModule(app, container);\n }\n}\n","import type { ModuleOptions } from '@velajs/vela';\nimport { TestingModuleBuilder } from './testing-module.builder.js';\n\nexport const Test = {\n createTestingModule(metadata: ModuleOptions): TestingModuleBuilder {\n return new TestingModuleBuilder(metadata);\n },\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["VelaApplication"],"sources":["../src/http/path-utils.ts","../src/http/test-response.ts","../src/http/test-http-request.ts","../src/http/test-http-client.ts","../src/sse/test-sse-connection.ts","../src/sse/test-sse-request.ts","../src/testing-module.ts","../src/testing-module.builder.ts","../src/test.ts"],"sourcesContent":["// Ported from @stratal/testing (MIT, © Temitayo Fadojutimi).\n\n/**\n * Read the value at a dot-notation path (e.g. `data.user.id`).\n * Returns `undefined` when any segment along the way is null/undefined.\n */\nexport function getValueAtPath(obj: unknown, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n}\n\n/**\n * Whether a dot-notation path exists on the object, even when the value at the\n * path is `null`/`undefined`. Distinguishes \"key present but null\" from \"key\n * absent\".\n */\nexport function hasValueAtPath(obj: unknown, path: string): boolean {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined) {\n return false;\n }\n\n if (typeof current !== 'object') {\n return false;\n }\n\n const record = current as Record<string, unknown>;\n\n if (!(part in record)) {\n return false;\n }\n\n current = record[part];\n }\n\n return true;\n}\n","// Ported from @stratal/testing (MIT, © Temitayo Fadojutimi), minus Macroable —\n// vela has no Macroable, so TestResponse is a plain class.\nimport { expect } from 'vitest';\nimport type { SchemaParser } from '@velajs/vela';\nimport { getValueAtPath, hasValueAtPath } from './path-utils.js';\n\nfunction isJsonObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * TestResponse\n *\n * Wraps a `Response` with fluent, chainable assertions. Synchronous status /\n * header assertions return `this`; JSON assertions (which must read the body)\n * return `Promise<this>`.\n *\n * @example\n * ```ts\n * const res = await module.http.get('/users/1').send();\n * res.assertOk();\n * await res.assertJsonPath('data.id', 1);\n * ```\n */\nexport class TestResponse {\n private jsonData: Promise<unknown> | undefined;\n private textData: string | null = null;\n\n constructor(private readonly response: Response) {}\n\n /** The raw `Response`. */\n get raw(): Response {\n return this.response;\n }\n\n /** The response status code. */\n get status(): number {\n return this.response.status;\n }\n\n /** The response headers. */\n get headers(): Headers {\n return this.response.headers;\n }\n\n /** Read JSON as unknown, or infer validated output from a supplied parser. */\n json(): Promise<unknown>;\n json<Value>(parser: SchemaParser<Value>): Promise<Value>;\n async json<Value>(parser?: SchemaParser<Value>): Promise<unknown> {\n this.jsonData ??= this.response.clone().json();\n const value = await this.jsonData;\n return parser === undefined ? value : parser.parse(value);\n }\n\n /** Read (and cache) the response body as text. */\n async text(): Promise<string> {\n this.textData ??= await this.response.clone().text();\n return this.textData;\n }\n\n // ============================================================\n // Status assertions\n // ============================================================\n\n /** Assert status is 200 OK. */\n assertOk(): this {\n return this.assertStatus(200);\n }\n\n /** Assert status is 201 Created. */\n assertCreated(): this {\n return this.assertStatus(201);\n }\n\n /** Assert status is 204 No Content. */\n assertNoContent(): this {\n return this.assertStatus(204);\n }\n\n /** Assert status is 400 Bad Request. */\n assertBadRequest(): this {\n return this.assertStatus(400);\n }\n\n /** Assert status is 401 Unauthorized. */\n assertUnauthorized(): this {\n return this.assertStatus(401);\n }\n\n /** Assert status is 403 Forbidden. */\n assertForbidden(): this {\n return this.assertStatus(403);\n }\n\n /** Assert status is 404 Not Found. */\n assertNotFound(): this {\n return this.assertStatus(404);\n }\n\n /** Assert status is 422 Unprocessable Entity. */\n assertUnprocessable(): this {\n return this.assertStatus(422);\n }\n\n /** Assert status is 500 Internal Server Error. */\n assertServerError(): this {\n return this.assertStatus(500);\n }\n\n /** Assert the response has the given status code. */\n assertStatus(expected: number): this {\n expect(this.response.status, `Expected status ${expected}, got ${this.response.status}`).toBe(\n expected,\n );\n return this;\n }\n\n /** Assert the status is in the 2xx range. */\n assertSuccessful(): this {\n expect(\n this.response.status >= 200 && this.response.status < 300,\n `Expected successful status (2xx), got ${this.response.status}`,\n ).toBe(true);\n return this;\n }\n\n // ============================================================\n // JSON assertions\n // ============================================================\n\n /** Assert each key in `expected` equals the corresponding top-level value. */\n async assertJson(expected: Record<string, unknown>): Promise<this> {\n const actual = await this.json();\n if (!isJsonObject(actual)) {\n expect.fail('Expected JSON body to be an object.');\n }\n\n for (const [key, value] of Object.entries(expected)) {\n expect(\n actual[key],\n `Expected JSON key \"${key}\" to be ${JSON.stringify(value)}, got ${JSON.stringify(actual[key])}`,\n ).toStrictEqual(value);\n }\n\n return this;\n }\n\n /** Assert the value at a dot-notation path equals `expected`. */\n async assertJsonPath(path: string, expected: unknown): Promise<this> {\n const json = await this.json();\n const actual = getValueAtPath(json, path);\n\n expect(\n actual,\n `Expected JSON path \"${path}\" to be ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`,\n ).toStrictEqual(expected);\n\n return this;\n }\n\n /** Assert every path/value pair in `expectations` matches (batch assert). */\n async assertJsonPaths(expectations: Record<string, unknown>): Promise<this> {\n const json = await this.json();\n\n for (const [path, expected] of Object.entries(expectations)) {\n const actual = getValueAtPath(json, path);\n expect(\n actual,\n `Expected JSON path \"${path}\" to be ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`,\n ).toStrictEqual(expected);\n }\n\n return this;\n }\n\n /** Assert the top-level JSON object has every key in `structure`. */\n async assertJsonStructure(structure: string[]): Promise<this> {\n const json = await this.json();\n if (!isJsonObject(json)) {\n expect.fail('Expected JSON body to be an object.');\n }\n\n for (const key of structure) {\n expect(\n key in json,\n `Expected JSON to have key \"${key}\", got keys: ${JSON.stringify(Object.keys(json))}`,\n ).toBe(true);\n }\n\n return this;\n }\n\n /** Assert a path exists (value may be anything, including `null`). */\n async assertJsonPathExists(path: string): Promise<this> {\n const json = await this.json();\n\n expect(hasValueAtPath(json, path), `Expected JSON path \"${path}\" to exist`).toBe(true);\n\n return this;\n }\n\n /** Assert a path does not exist. */\n async assertJsonPathMissing(path: string): Promise<this> {\n const json = await this.json();\n\n expect(hasValueAtPath(json, path), `Expected JSON path \"${path}\" to not exist`).toBe(false);\n\n return this;\n }\n\n /** Assert the value at a path satisfies a predicate. */\n async assertJsonPathMatches(path: string, matcher: (value: unknown) => boolean): Promise<this> {\n const json = await this.json();\n const value = getValueAtPath(json, path);\n\n expect(\n matcher(value),\n `Expected JSON path \"${path}\" to match predicate, got ${JSON.stringify(value)}`,\n ).toBe(true);\n\n return this;\n }\n\n /** Assert the string value at a path contains `substring`. */\n async assertJsonPathContains(path: string, substring: string): Promise<this> {\n const json = await this.json();\n const value = getValueAtPath(json, path);\n\n if (typeof value !== 'string') {\n expect.fail(`Expected JSON path \"${path}\" to be a string, got ${typeof value}`);\n }\n\n expect(\n value.includes(substring),\n `Expected JSON path \"${path}\" to contain \"${substring}\", got \"${String(value)}\"`,\n ).toBe(true);\n\n return this;\n }\n\n /** Assert the array value at a path includes `item`. */\n async assertJsonPathIncludes(path: string, item: unknown): Promise<this> {\n const json = await this.json();\n const value = getValueAtPath(json, path);\n\n if (!Array.isArray(value)) {\n expect.fail(`Expected JSON path \"${path}\" to be an array, got ${typeof value}`);\n }\n\n expect(\n value.includes(item),\n `Expected JSON path \"${path}\" to include ${JSON.stringify(item)}`,\n ).toBe(true);\n\n return this;\n }\n\n /** Assert the array value at a path has `count` items. */\n async assertJsonPathCount(path: string, count: number): Promise<this> {\n const json = await this.json();\n const value = getValueAtPath(json, path);\n\n if (!Array.isArray(value)) {\n expect.fail(`Expected JSON path \"${path}\" to be an array, got ${typeof value}`);\n }\n\n expect(\n value.length,\n `Expected JSON path \"${path}\" to have ${count} items, got ${value.length}`,\n ).toBe(count);\n\n return this;\n }\n\n // ============================================================\n // Header assertions\n // ============================================================\n\n /** Assert a header is present, optionally equal to `expected`. */\n assertHeader(name: string, expected?: string): this {\n const actual = this.response.headers.get(name);\n\n expect(actual !== null, `Expected header \"${name}\" to be present`).toBe(true);\n\n if (expected !== undefined) {\n expect(actual, `Expected header \"${name}\" to be \"${expected}\", got \"${actual}\"`).toBe(\n expected,\n );\n }\n\n return this;\n }\n\n /** Assert a header is absent. */\n assertHeaderMissing(name: string): this {\n const actual = this.response.headers.get(name);\n\n expect(actual, `Expected header \"${name}\" to be absent, but got \"${actual}\"`).toBeNull();\n\n return this;\n }\n}\n","// Adapted from @stratal/testing (MIT, © Temitayo Fadojutimi): stratal's hard\n// AuthService import is replaced by a generic auth-resolver seam so\n// @velajs/testing stays free of optional-package dependencies.\nimport type { ActingAsResolver, TestPrincipal, TestingModule } from '../testing-module.js';\nimport { TestResponse } from './test-response.js';\n\n/**\n * TestHttpRequest\n *\n * Fluent builder for a single test HTTP request. `send()` builds a `Request`\n * and drives it through `module.fetch()` (the full Hono pipeline).\n *\n * @example\n * ```ts\n * const res = await module.http\n * .post('/users')\n * .withBody({ name: 'A' })\n * .withHeaders({ 'X-Trace': '1' })\n * .send();\n * res.assertCreated();\n * ```\n */\nexport class TestHttpRequest {\n private body: unknown = undefined;\n private readonly requestHeaders: Headers;\n private principal: TestPrincipal | null = null;\n private resolver: ActingAsResolver | null = null;\n\n constructor(\n private readonly method: string,\n private readonly path: string,\n headers: Headers,\n private readonly module: TestingModule,\n private readonly host: string | null = null,\n ) {\n this.requestHeaders = new Headers(headers);\n }\n\n /** Set the request body (JSON-serialized on send). */\n withBody(data: unknown): this {\n this.body = data;\n return this;\n }\n\n /** Merge additional headers. */\n withHeaders(headers: Record<string, string>): this {\n for (const [key, value] of Object.entries(headers)) {\n this.requestHeaders.set(key, value);\n }\n return this;\n }\n\n /** Set `Content-Type: application/json`. */\n asJson(): this {\n this.requestHeaders.set('Content-Type', 'application/json');\n return this;\n }\n\n /**\n * Authenticate the request as `principal`. The `resolver` (or a default one\n * registered via `module.setAuthResolver`) turns the principal into request\n * headers. The resolver signature `(module, principal) => Promise<Headers>`\n * is the cross-package contract sibling packages (e.g. `@velajs/better-auth`)\n * build against.\n */\n actingAs(principal: TestPrincipal, resolver?: ActingAsResolver): this {\n this.principal = principal;\n this.resolver = resolver ?? null;\n return this;\n }\n\n /** Build the `Request` and send it through `module.fetch()`. */\n async send(): Promise<TestResponse> {\n await this.applyAuthentication();\n\n const hasBody = this.body !== undefined && this.body !== null;\n if (hasBody && !this.requestHeaders.has('Content-Type')) {\n this.requestHeaders.set('Content-Type', 'application/json');\n }\n\n const url = new URL(this.path, `http://${this.host ?? 'localhost'}`);\n const request = new Request(url.toString(), {\n method: this.method,\n headers: this.requestHeaders,\n body: hasBody ? JSON.stringify(this.body) : null,\n });\n\n const response = await this.module.fetch(request);\n return new TestResponse(response);\n }\n\n private async applyAuthentication(): Promise<void> {\n if (!this.principal) return;\n\n const resolver = this.resolver ?? this.module.getAuthResolver();\n if (!resolver) {\n throw new Error(\n 'actingAs() requires an auth resolver. Pass one explicitly — ' +\n 'actingAs(principal, resolver) — or register a default with ' +\n 'module.setAuthResolver(resolver). For better-auth: ' +\n 'import { actingAs } from \"@velajs/better-auth/testing\".',\n );\n }\n\n const headers = await resolver(this.module, this.principal);\n for (const [key, value] of headers.entries()) {\n this.requestHeaders.set(key, value);\n }\n }\n}\n","// Adapted from @stratal/testing (MIT, © Temitayo Fadojutimi). Stratal's i18n\n// `withLocale` is dropped (vela i18n differs — optional follow-up).\nimport type { TestingModule } from '../testing-module.js';\nimport { TestHttpRequest } from './test-http-request.js';\n\n/**\n * TestHttpClient\n *\n * Fluent entry point for test HTTP requests. `forHost`/`withHeaders` return a\n * new immutable client; the verb methods start a {@link TestHttpRequest}.\n *\n * @example\n * ```ts\n * const res = await module.http\n * .forHost('example.com')\n * .post('/users')\n * .withBody({ name: 'A' })\n * .send();\n * res.assertCreated();\n * ```\n */\nexport class TestHttpClient {\n constructor(\n private readonly module: TestingModule,\n private readonly host: string | null = null,\n private readonly defaultHeaders: Headers = new Headers(),\n ) {}\n\n /**\n * Return a new client bound to `host`. Also sets the `Host` header so domain\n * routing works even when the runtime reads the header rather than the URL.\n */\n forHost(host: string): TestHttpClient {\n const headers = new Headers(this.defaultHeaders);\n headers.set('Host', host);\n return new TestHttpClient(this.module, host, headers);\n }\n\n /** Return a new client with additional default headers on every request. */\n withHeaders(headers: Record<string, string>): TestHttpClient {\n const next = new Headers(this.defaultHeaders);\n for (const [key, value] of Object.entries(headers)) {\n next.set(key, value);\n }\n return new TestHttpClient(this.module, this.host, next);\n }\n\n get(path: string): TestHttpRequest {\n return this.createRequest('GET', path);\n }\n\n post(path: string): TestHttpRequest {\n return this.createRequest('POST', path);\n }\n\n put(path: string): TestHttpRequest {\n return this.createRequest('PUT', path);\n }\n\n patch(path: string): TestHttpRequest {\n return this.createRequest('PATCH', path);\n }\n\n delete(path: string): TestHttpRequest {\n return this.createRequest('DELETE', path);\n }\n\n private createRequest(method: string, path: string): TestHttpRequest {\n return new TestHttpRequest(method, path, this.defaultHeaders, this.module, this.host);\n }\n}\n","// Ported near-verbatim from @stratal/testing (MIT, © Temitayo Fadojutimi).\n// Web-standard only (ReadableStream + TextDecoder), so it is edge-pure.\nimport { expect } from 'vitest';\n\n/** A parsed Server-Sent Event. */\nexport interface TestSseEvent {\n data: string;\n event?: string;\n id?: string;\n retry?: number;\n}\n\n/**\n * TestSseConnection\n *\n * Reads a streaming `text/event-stream` response body and exposes queue-based\n * wait/assert helpers over the parsed events.\n *\n * @example\n * ```ts\n * const sse = await module.sse('/stream/events').connect();\n * await sse.assertEventData('ping');\n * await sse.waitForEnd();\n * ```\n */\nexport class TestSseConnection {\n private readonly eventQueue: TestSseEvent[] = [];\n private eventWaiters: ((event: TestSseEvent) => void)[] = [];\n private streamEnded = false;\n private endWaiters: (() => void)[] = [];\n\n constructor(private readonly response: Response) {\n this.startReading();\n }\n\n /** The raw `Response`. */\n get raw(): Response {\n return this.response;\n }\n\n /** Wait for the next event (rejects after `timeout` ms). */\n async waitForEvent(timeout = 5000): Promise<TestSseEvent> {\n if (this.eventQueue.length > 0) {\n return this.eventQueue.shift()!;\n }\n\n if (this.streamEnded) {\n throw new Error('SSE: stream has ended, no more events');\n }\n\n return new Promise<TestSseEvent>((resolve, reject) => {\n const waiter = (event: TestSseEvent): void => {\n clearTimeout(timer);\n resolve(event);\n };\n\n const timer = setTimeout(() => {\n const index = this.eventWaiters.indexOf(waiter);\n if (index !== -1) this.eventWaiters.splice(index, 1);\n reject(new Error(`SSE: no event received within ${timeout}ms`));\n }, timeout);\n\n this.eventWaiters.push(waiter);\n });\n }\n\n /** Wait for the stream to end (rejects after `timeout` ms). */\n async waitForEnd(timeout = 5000): Promise<void> {\n if (this.streamEnded) return;\n\n return new Promise<void>((resolve, reject) => {\n const waiter = (): void => {\n clearTimeout(timer);\n resolve();\n };\n\n const timer = setTimeout(() => {\n const index = this.endWaiters.indexOf(waiter);\n if (index !== -1) this.endWaiters.splice(index, 1);\n reject(new Error(`SSE: stream did not end within ${timeout}ms`));\n }, timeout);\n\n this.endWaiters.push(waiter);\n });\n }\n\n /** Collect all remaining events until the stream ends. */\n async collectEvents(timeout = 5000): Promise<TestSseEvent[]> {\n const events: TestSseEvent[] = [];\n\n if (this.streamEnded) {\n return [...this.eventQueue.splice(0)];\n }\n\n return new Promise<TestSseEvent[]>((resolve, reject) => {\n const originalDispatch = this.dispatchEvent.bind(this);\n this.dispatchEvent = (event: TestSseEvent): void => {\n events.push(event);\n originalDispatch(event);\n };\n\n const endWaiter = (): void => {\n clearTimeout(timer);\n this.dispatchEvent = originalDispatch;\n resolve(events);\n };\n\n const timer = setTimeout(() => {\n this.dispatchEvent = originalDispatch;\n const index = this.endWaiters.indexOf(endWaiter);\n if (index !== -1) this.endWaiters.splice(index, 1);\n reject(new Error(`SSE: stream did not end within ${timeout}ms`));\n }, timeout);\n\n events.push(...this.eventQueue.splice(0));\n\n this.endWaiters.push(endWaiter);\n });\n }\n\n /** Assert the next event matches the expected partial shape. */\n async assertEvent(expected: Partial<TestSseEvent>, timeout = 5000): Promise<void> {\n const event = await this.waitForEvent(timeout);\n expect(event).toMatchObject(expected);\n }\n\n /** Assert the next event's `data` equals `expected`. */\n async assertEventData(expected: string, timeout = 5000): Promise<void> {\n const event = await this.waitForEvent(timeout);\n expect(event.data, `Expected SSE data \"${expected}\", got \"${event.data}\"`).toBe(expected);\n }\n\n /** Assert the next event's `data` is JSON equal to `expected`. */\n async assertJsonEventData<T>(expected: T, timeout = 5000): Promise<void> {\n const event = await this.waitForEvent(timeout);\n const parsed = JSON.parse(event.data) as unknown;\n expect(parsed).toEqual(expected);\n }\n\n private startReading(): void {\n const body = this.response.body;\n if (!body) {\n this.streamEnded = true;\n return;\n }\n\n const reader = body.getReader() as ReadableStreamDefaultReader<Uint8Array>;\n const decoder = new TextDecoder();\n let buffer = '';\n\n const read = async (): Promise<void> => {\n try {\n for (;;) {\n const { done, value } = await reader.read();\n\n if (done) {\n if (buffer.trim()) {\n const event = this.parseEvent(buffer);\n if (event) this.dispatchEvent(event);\n }\n this.endStream();\n return;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const parts = buffer.split('\\n\\n');\n buffer = parts.pop()!;\n\n for (const part of parts) {\n if (!part.trim()) continue;\n const event = this.parseEvent(part);\n if (event) this.dispatchEvent(event);\n }\n }\n } catch {\n this.endStream();\n }\n };\n\n void read();\n }\n\n private endStream(): void {\n this.streamEnded = true;\n for (const waiter of this.endWaiters) {\n waiter();\n }\n this.endWaiters = [];\n }\n\n private parseEvent(raw: string): TestSseEvent | null {\n const lines = raw.split('\\n');\n const dataLines: string[] = [];\n let event: string | undefined;\n let id: string | undefined;\n let retry: number | undefined;\n\n for (const line of lines) {\n if (line.startsWith(':')) continue; // comment line\n\n const colonIndex = line.indexOf(':');\n if (colonIndex === -1) continue;\n\n const field = line.slice(0, colonIndex);\n const value =\n line[colonIndex + 1] === ' ' ? line.slice(colonIndex + 2) : line.slice(colonIndex + 1);\n\n switch (field) {\n case 'data':\n dataLines.push(value);\n break;\n case 'event':\n event = value;\n break;\n case 'id':\n id = value;\n break;\n case 'retry': {\n const parsed = parseInt(value, 10);\n if (!Number.isNaN(parsed)) retry = parsed;\n break;\n }\n }\n }\n\n if (dataLines.length === 0) return null;\n\n const result: TestSseEvent = { data: dataLines.join('\\n') };\n if (event !== undefined) result.event = event;\n if (id !== undefined) result.id = id;\n if (retry !== undefined) result.retry = retry;\n\n return result;\n }\n\n private dispatchEvent(event: TestSseEvent): void {\n if (this.eventWaiters.length > 0) {\n this.eventWaiters.shift()!(event);\n } else {\n this.eventQueue.push(event);\n }\n }\n}\n","// Adapted from @stratal/testing (MIT, © Temitayo Fadojutimi). Auth uses the\n// generic resolver seam instead of a hard AuthService import.\nimport { expect } from 'vitest';\nimport type { ActingAsResolver, TestPrincipal, TestingModule } from '../testing-module.js';\nimport { TestSseConnection } from './test-sse-connection.js';\n\n/**\n * TestSseRequest\n *\n * Builder for a Server-Sent Events connection. `connect()` issues a GET through\n * `module.fetch()`, asserts a `text/event-stream` 200, and wraps the streaming\n * body in a {@link TestSseConnection}.\n *\n * @example\n * ```ts\n * const sse = await module.sse('/stream/events').connect();\n * await sse.assertEvent({ event: 'message', data: 'hello' });\n * ```\n */\nexport class TestSseRequest {\n private readonly requestHeaders = new Headers();\n private principal: TestPrincipal | null = null;\n private resolver: ActingAsResolver | null = null;\n\n constructor(\n private readonly path: string,\n private readonly module: TestingModule,\n ) {}\n\n /** Merge additional headers onto the SSE request. */\n withHeaders(headers: Record<string, string>): this {\n for (const [key, value] of Object.entries(headers)) {\n this.requestHeaders.set(key, value);\n }\n return this;\n }\n\n /** Authenticate the connection (see {@link TestHttpRequest.actingAs}). */\n actingAs(principal: TestPrincipal, resolver?: ActingAsResolver): this {\n this.principal = principal;\n this.resolver = resolver ?? null;\n return this;\n }\n\n /** Open the stream and return a live {@link TestSseConnection}. */\n async connect(): Promise<TestSseConnection> {\n await this.applyAuthentication();\n\n this.requestHeaders.set('Accept', 'text/event-stream');\n\n const url = new URL(this.path, 'http://localhost');\n const request = new Request(url.toString(), { headers: this.requestHeaders });\n\n const response = await this.module.fetch(request);\n\n expect(response.status, `Expected status 200, got ${response.status}`).toBe(200);\n\n const contentType = response.headers.get('content-type') ?? '';\n expect(\n contentType.includes('text/event-stream'),\n `Expected content-type \"text/event-stream\", got \"${contentType}\"`,\n ).toBe(true);\n\n return new TestSseConnection(response);\n }\n\n private async applyAuthentication(): Promise<void> {\n if (!this.principal) return;\n\n const resolver = this.resolver ?? this.module.getAuthResolver();\n if (!resolver) {\n throw new Error(\n 'actingAs() requires an auth resolver. Pass one explicitly or register ' +\n 'a default with module.setAuthResolver(resolver).',\n );\n }\n\n const headers = await resolver(this.module, this.principal);\n for (const [key, value] of headers.entries()) {\n this.requestHeaders.set(key, value);\n }\n }\n}\n","import { Context } from 'hono';\nimport {\n REQUEST_CONTEXT,\n type InferToken,\n type Token,\n type Type,\n type VelaApplication,\n} from '@velajs/vela';\nimport { createRequestContext, setRequestContainer, type Container } from '@velajs/vela/internal';\nimport { SeederRegistry, type ISeeder } from '@velajs/vela/seeder';\nimport { expect } from 'vitest';\nimport type { TestDatabase } from './db/test-database.js';\nimport { TestHttpClient } from './http/test-http-client.js';\nimport { TestSseRequest } from './sse/test-sse-request.js';\nimport { TestWsRequest } from './ws/test-ws-request.js';\n\n/** A test principal — an opaque object the auth resolver turns into headers. */\nexport type TestPrincipal = Record<string, unknown>;\n\n/**\n * Turns a principal into request headers (session cookie, bearer token, …).\n * The signature `(module, principal) => Promise<Headers>` is a cross-package\n * contract: sibling packages (e.g. `@velajs/better-auth/testing`) build a\n * resolver against it. Kept generic so `@velajs/testing` needs no auth deps.\n */\nexport type ActingAsResolver = (\n module: TestingModule,\n principal: TestPrincipal,\n) => Promise<Headers>;\n\ntype HonoApp = ReturnType<VelaApplication['getHonoApp']>;\n\n/**\n * TestingModule\n *\n * The compiled test harness. Beyond `get`/`createApplication`/`close`, it adds\n * Laravel-flavored ergonomics: a fluent HTTP client, SSE/WS builders, request-\n * scope execution, seeding, and database assertion wrappers.\n *\n * @example\n * ```ts\n * const module = await Test.createTestingModule({ imports: [AppModule] }).compile();\n * await module.http.post('/users').withBody({ name: 'A' }).send()\n * .then((r) => r.assertCreated());\n * ```\n */\nexport class TestingModule {\n private _http: TestHttpClient | null = null;\n private honoApp: HonoApp | null = null;\n private authResolver: ActingAsResolver | null = null;\n\n constructor(\n private readonly app: VelaApplication,\n private readonly container: Container,\n ) {}\n\n /** Resolve a provider from the root container. */\n get<const Key extends Token>(token: Key): InferToken<Key> {\n return this.container.resolve(token);\n }\n\n /** Build (once) and return the underlying application. */\n async createApplication(): Promise<VelaApplication> {\n await this.app.initRoutes();\n return this.app;\n }\n\n /** Lazy fluent HTTP client bound to this module. */\n get http(): TestHttpClient {\n this._http ??= new TestHttpClient(this);\n return this._http;\n }\n\n /** Start an SSE connection builder for `path`. */\n sse(path: string): TestSseRequest {\n return new TestSseRequest(path, this);\n }\n\n /** Start a WebSocket connection builder for `path` (needs a transport adapter). */\n ws(path: string): TestWsRequest {\n return new TestWsRequest(path, this);\n }\n\n /**\n * Drive a `Request` through the full Hono pipeline. The Hono app is built\n * once and reused across requests.\n */\n async fetch(...args: Parameters<HonoApp['fetch']>): Promise<Response> {\n const hono = await this.ensureHono();\n return hono.fetch(...args);\n }\n\n /**\n * Register a default auth resolver used by `actingAs(principal)` when no\n * resolver is passed explicitly.\n */\n setAuthResolver(resolver: ActingAsResolver): this {\n this.authResolver = resolver;\n return this;\n }\n\n /** The default auth resolver, if one was registered. */\n getAuthResolver(): ActingAsResolver | null {\n return this.authResolver;\n }\n\n /**\n * Run `callback` inside a request-scoped child container seeded with a real\n * RequestContext, so REQUEST-scoped providers (and anything injecting\n * `REQUEST_CONTEXT`) resolve. The child is disposed afterwards.\n */\n async runInRequestScope<T>(callback: (container: Container) => T | Promise<T>): Promise<T> {\n const child = this.container.createChild();\n const hono = new Context(new Request('http://localhost/'));\n setRequestContainer(hono, child);\n child.setRequestInstance(REQUEST_CONTEXT, createRequestContext(hono));\n try {\n return await callback(child);\n } finally {\n await child.dispose();\n }\n }\n\n /**\n * Run the given `@Seeder()` classes, each in its own request scope. Throws if\n * a class is not a registered seeder. Requires `SeederModule` (or the seeders\n * themselves) to be present in the module graph.\n */\n async seed(...SeederClasses: Type<ISeeder>[]): Promise<void> {\n const registry = this.container.resolve(SeederRegistry);\n const known = new Set<unknown>(registry.list().map((s) => s.target));\n\n for (const SeederClass of SeederClasses) {\n if (!known.has(SeederClass)) {\n throw new Error(\n `Seeder \"${SeederClass.name}\" is not registered. Add it to a module's ` +\n 'providers or SeederModule.forRoot({ seeders: [...] }).',\n );\n }\n await this.runInRequestScope(async (child) => {\n const instance = child.resolve(SeederClass);\n await instance.run();\n });\n }\n }\n\n /** Assert a row matching `where` exists in `table` (via a {@link TestDatabase}). */\n async assertDatabaseHas(\n db: TestDatabase,\n table: string,\n where: Record<string, unknown>,\n ): Promise<void> {\n const exists = await db.has(table, where);\n expect(exists, `Expected ${table} to have a row matching ${JSON.stringify(where)}`).toBe(true);\n }\n\n /** Assert no row matching `where` exists in `table`. */\n async assertDatabaseMissing(\n db: TestDatabase,\n table: string,\n where: Record<string, unknown>,\n ): Promise<void> {\n const exists = await db.has(table, where);\n expect(exists, `Expected ${table} NOT to have a row matching ${JSON.stringify(where)}`).toBe(\n false,\n );\n }\n\n /** Assert `table` has exactly `expected` rows. */\n async assertDatabaseCount(db: TestDatabase, table: string, expected: number): Promise<void> {\n const actual = await db.count(table);\n expect(actual, `Expected ${table} count ${expected}, got ${actual}`).toBe(expected);\n }\n\n /** Dispose the application. */\n async close(signal?: string): Promise<void> {\n await this.app.close(signal);\n }\n\n private async ensureHono(): Promise<HonoApp> {\n if (!this.honoApp) {\n const app = await this.createApplication();\n this.honoApp = app.getHonoApp();\n }\n return this.honoApp;\n }\n}\n","import {\n defineProvider,\n type CanActivate,\n type DependencyToken,\n type ExceptionFilter,\n type InferToken,\n type InferTokens,\n type ModuleOptions,\n type NestInterceptor,\n type PipeTransform,\n type ProviderDefinition,\n type Token,\n type Type,\n} from '@velajs/vela';\nimport { MetadataRegistry, VelaApplication, bootstrap } from '@velajs/vela/internal';\nimport { TestingModule } from './testing-module.js';\n\ninterface OverrideEntry {\n token: Token;\n provider: ProviderDefinition;\n}\n\n// Keep the same token proof required by core provider authoring. An erased\n// registry identity can be resolved, but cannot authorize a typed replacement.\ntype OverrideToken<Key extends Token> = Parameters<typeof defineProvider<Key>>[0];\n\nexport class OverrideBy<Key extends Token> {\n constructor(\n private readonly commit: (provider: ProviderDefinition) => TestingModuleBuilder,\n private readonly token: OverrideToken<Key>,\n ) {}\n\n useValue(value: NoInfer<InferToken<Key>>): TestingModuleBuilder {\n return this.commit(defineProvider<Key>(this.token, { useValue: value }));\n }\n\n useClass(cls: Type<NoInfer<InferToken<Key>>>): TestingModuleBuilder {\n return this.commit(defineProvider<Key>(this.token, { useClass: cls }));\n }\n\n useFactory<const Inject extends readonly DependencyToken[] = readonly []>(options: {\n factory: (\n ...args: InferTokens<Inject>\n ) => NoInfer<InferToken<Key>> | Promise<NoInfer<InferToken<Key>>>;\n inject: Inject;\n }): TestingModuleBuilder {\n return this.commit(\n defineProvider<Key, Inject>(this.token, {\n useFactory: options.factory,\n inject: options.inject,\n }),\n );\n }\n}\n\nexport class TestingModuleBuilder {\n private overrides: OverrideEntry[] = [];\n\n constructor(private readonly metadata: ModuleOptions) {}\n\n overrideProvider<const Key extends Token>(token: OverrideToken<Key>): OverrideBy<Key> {\n return new OverrideBy<Key>((provider) => {\n this.addOverride({ token, provider });\n return this;\n }, token);\n }\n\n overrideGuard<const Guard extends Type<CanActivate>>(\n guard: OverrideToken<Guard>,\n ): OverrideBy<Guard> {\n return this.overrideProvider<Guard>(guard);\n }\n\n overridePipe<const Pipe extends Type<PipeTransform>>(\n pipe: OverrideToken<Pipe>,\n ): OverrideBy<Pipe> {\n return this.overrideProvider<Pipe>(pipe);\n }\n\n overrideInterceptor<const Interceptor extends Type<NestInterceptor>>(\n interceptor: OverrideToken<Interceptor>,\n ): OverrideBy<Interceptor> {\n return this.overrideProvider<Interceptor>(interceptor);\n }\n\n overrideFilter<const Filter extends Type<ExceptionFilter>>(\n filter: OverrideToken<Filter>,\n ): OverrideBy<Filter> {\n return this.overrideProvider<Filter>(filter);\n }\n\n private addOverride(entry: OverrideEntry): void {\n const idx = this.overrides.findIndex((o) => o.token === entry.token);\n if (idx !== -1) {\n this.overrides[idx] = entry;\n } else {\n this.overrides.push(entry);\n }\n }\n\n async compile(): Promise<TestingModule> {\n class TestRootModule {}\n MetadataRegistry.setModuleOptions(TestRootModule, {\n imports: this.metadata.imports,\n providers: this.metadata.providers,\n controllers: this.metadata.controllers,\n exports: this.metadata.exports,\n });\n\n // Use the framework's single bootstrap primitive. Hand-copying its\n // registrations caused test applications to drift from production (most\n // critically REQUEST_CONTEXT token/request-child behavior).\n const { container, routeManager, loader } = await bootstrap(TestRootModule);\n\n // Force-apply overrides into every module bucket that already holds the\n // token (plus root). Without this, controller constructor-injection (which\n // passes requestingModuleId to findRegistration) finds the module's own\n // registration first and never consults the root override. The default\n // 'all-existing' buckets replace every non-root bucket holding the token\n // and re-register at root — the supported form of the old private loop.\n for (const override of this.overrides) {\n container.replaceProvider(override.provider);\n }\n\n const app = new VelaApplication(container, routeManager);\n const instances = await loader.resolveAllInstances();\n app.setInstances(instances);\n\n await app.callOnModuleInit();\n await app.callOnApplicationBootstrap();\n await app.initRoutes();\n\n return new TestingModule(app, container);\n }\n}\n","import type { ModuleOptions } from '@velajs/vela';\nimport { TestingModuleBuilder } from './testing-module.builder.js';\n\nexport const Test = {\n createTestingModule(metadata: ModuleOptions): TestingModuleBuilder {\n return new TestingModuleBuilder(metadata);\n },\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velajs/testing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"description": "Testing utilities for Vela framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"edge",
|
|
@@ -11,15 +11,16 @@
|
|
|
11
11
|
"testing",
|
|
12
12
|
"vela"
|
|
13
13
|
],
|
|
14
|
-
"homepage": "https://github.com/velajs/testing#readme",
|
|
14
|
+
"homepage": "https://github.com/velajs/vela/tree/main/packages/testing#readme",
|
|
15
15
|
"bugs": {
|
|
16
|
-
"url": "https://github.com/velajs/
|
|
16
|
+
"url": "https://github.com/velajs/vela/issues"
|
|
17
17
|
},
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"author": "ksh",
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
22
|
-
"url": "git+https://github.com/velajs/
|
|
22
|
+
"url": "git+https://github.com/velajs/vela.git",
|
|
23
|
+
"directory": "packages/testing"
|
|
23
24
|
},
|
|
24
25
|
"files": [
|
|
25
26
|
"dist",
|
|
@@ -48,26 +49,28 @@
|
|
|
48
49
|
}
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
|
-
"@arethetypeswrong/cli": "
|
|
52
|
-
"@changesets/cli": "
|
|
53
|
-
"@
|
|
54
|
-
"@
|
|
55
|
-
"@
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
52
|
+
"@arethetypeswrong/cli": "0.18.5",
|
|
53
|
+
"@changesets/cli": "3.0.1",
|
|
54
|
+
"@hono/node-server": "^1.19.9",
|
|
55
|
+
"@hono/node-ws": "^1.3.1",
|
|
56
|
+
"@swc/core": "1.15.43",
|
|
57
|
+
"@types/node": "24.13.3",
|
|
58
|
+
"hono": "4.13.8",
|
|
59
|
+
"oxfmt": "0.58.0",
|
|
60
|
+
"oxlint": "1.73.0",
|
|
61
|
+
"publint": "0.3.21",
|
|
62
|
+
"tsdown": "0.23.0",
|
|
63
|
+
"typescript": "7.0.2",
|
|
64
|
+
"unplugin-swc": "1.5.9",
|
|
65
|
+
"vitest": "4.1.10",
|
|
66
|
+
"@velajs/vela": "1.22.0"
|
|
64
67
|
},
|
|
65
68
|
"peerDependencies": {
|
|
66
69
|
"@hono/node-server": ">=1",
|
|
67
70
|
"@hono/node-ws": ">=1",
|
|
68
|
-
"@velajs/vela": ">=1.21.0 <2",
|
|
69
71
|
"hono": ">=4",
|
|
70
|
-
"vitest": ">=3"
|
|
72
|
+
"vitest": ">=3",
|
|
73
|
+
"@velajs/vela": "^1.22.0"
|
|
71
74
|
},
|
|
72
75
|
"peerDependenciesMeta": {
|
|
73
76
|
"@hono/node-ws": {
|
|
@@ -80,6 +83,9 @@
|
|
|
80
83
|
"engines": {
|
|
81
84
|
"node": ">=24"
|
|
82
85
|
},
|
|
86
|
+
"publishConfig": {
|
|
87
|
+
"access": "public"
|
|
88
|
+
},
|
|
83
89
|
"scripts": {
|
|
84
90
|
"build": "tsdown",
|
|
85
91
|
"test": "vitest run",
|
|
@@ -90,11 +96,6 @@
|
|
|
90
96
|
"format:check": "oxfmt --check .",
|
|
91
97
|
"publint": "publint",
|
|
92
98
|
"attw": "attw --pack . --profile esm-only",
|
|
93
|
-
"changeset": "changeset",
|
|
94
|
-
"version-packages": "changeset version",
|
|
95
|
-
"release:preflight": "npm view @velajs/vela@1.21.0 version",
|
|
96
|
-
"release:check": "node scripts/check-release-lock.mjs && pnpm verify && pnpm audit --audit-level=high",
|
|
97
|
-
"release": "pnpm release:preflight && pnpm release:check && changeset publish",
|
|
98
99
|
"verify": "pnpm lint && pnpm format:check && pnpm build && pnpm typecheck && pnpm typecheck:tests && pnpm test && pnpm publint && pnpm attw"
|
|
99
100
|
}
|
|
100
101
|
}
|