@velajs/testing 0.6.0 → 1.0.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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 02bb42e: Target the published Vela 1.21 security release and use Vela's production bootstrap primitive so request context, global providers, and request-scoped test execution cannot drift from the real runtime. Standalone CI no longer relies on a sibling `link:../vela` checkout.
8
+
3
9
  ## 0.6.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -3,13 +3,13 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@velajs/testing)](https://www.npmjs.com/package/@velajs/testing)
4
4
  [![License: MIT](https://img.shields.io/npm/l/@velajs/testing)](https://github.com/velajs/testing/blob/main/LICENSE)
5
5
 
6
- Test-module builder for [Vela](https://github.com/velajs/vela). Compose modules in isolation, override providers/guards/pipes/interceptors/filters, and exercise controllers via Hono's `app.request()` without bootstrapping the real factory.
6
+ Test-module builder for [Vela](https://github.com/velajs/vela). Compose modules in isolation, override providers/guards/pipes/interceptors/filters, and exercise controllers via Hono's `app.request()`. Testing uses the same bootstrap primitive as production so request scope and framework-global providers cannot drift.
7
7
 
8
8
  ## Install
9
9
 
10
10
  ```bash
11
11
  pnpm add -D @velajs/testing
12
- # Peer (already in your project): @velajs/vela ^1.1.0, hono ^4
12
+ # Peer (already in your project): @velajs/vela >=1.21 <2, hono >=4
13
13
  ```
14
14
 
15
15
  No `reflect-metadata` needed — Vela ships its own polyfill.
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 { DiscoveryService, REQUEST_CONTEXT, Scope } from "@velajs/vela";
3
- import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_MIDDLEWARE, APP_PIPE, Container, MetadataRegistry, ModuleLoader, ModuleRef, RouteManager, VelaApplication as VelaApplication$1, bindAppProviders } from "@velajs/vela/internal";
2
+ import { REQUEST_CONTEXT } from "@velajs/vela";
3
+ import { MetadataRegistry, VelaApplication as VelaApplication$1, bootstrap } from "@velajs/vela/internal";
4
4
  import { Context } from "hono";
5
5
  import { SeederRegistry } from "@velajs/vela/seeder";
6
6
  import { expect } from "vitest";
@@ -807,45 +807,8 @@ var TestingModuleBuilder = class {
807
807
  controllers: this.metadata.controllers,
808
808
  exports: this.metadata.exports
809
809
  });
810
- const container = new Container();
811
- container.register({
812
- provide: Container,
813
- useValue: container
814
- });
815
- container.markGlobalToken(Container);
816
- container.register({
817
- provide: ModuleRef,
818
- useFactory: (c) => new ModuleRef(c),
819
- inject: [Container]
820
- });
821
- container.markGlobalToken(ModuleRef);
822
- container.register({
823
- provide: DiscoveryService,
824
- useFactory: (c) => new DiscoveryService(c),
825
- inject: [Container]
826
- });
827
- container.markGlobalToken(DiscoveryService);
828
- for (const t of [
829
- APP_GUARD,
830
- APP_PIPE,
831
- APP_INTERCEPTOR,
832
- APP_FILTER,
833
- APP_MIDDLEWARE
834
- ]) container.markGlobalToken(t);
835
- container.register({
836
- provide: REQUEST_CONTEXT,
837
- scope: Scope.REQUEST,
838
- useFactory: () => {
839
- throw new Error("REQUEST_CONTEXT can only be resolved inside a request — it is seeded by RouteManager when the request enters the pipeline.");
840
- }
841
- });
842
- container.markGlobalToken(REQUEST_CONTEXT);
843
- for (const override of this.overrides) container.register(override.provider);
844
- const routeManager = new RouteManager(container);
845
- const loader = new ModuleLoader(container, routeManager);
846
- loader.load(TestRootModule);
810
+ const { container, routeManager, loader } = await bootstrap(TestRootModule);
847
811
  for (const override of this.overrides) container.replaceProvider(override.provider);
848
- bindAppProviders(routeManager, container, loader);
849
812
  const app = new VelaApplication$1(container, routeManager);
850
813
  const instances = await loader.resolveAllInstances();
851
814
  app.setInstances(instances);
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 {\n DiscoveryService,\n REQUEST_CONTEXT,\n Scope,\n type ModuleOptions,\n type ProviderOptions,\n type Token,\n type Type,\n} from '@velajs/vela';\nimport {\n APP_FILTER,\n APP_GUARD,\n APP_INTERCEPTOR,\n APP_MIDDLEWARE,\n APP_PIPE,\n Container,\n MetadataRegistry,\n ModuleLoader,\n ModuleRef,\n RouteManager,\n VelaApplication,\n bindAppProviders,\n} 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 const container = new Container();\n\n // Replicate bootstrap()'s global token setup so request-pipeline tests\n // can resolve REQUEST_CONTEXT, ModuleRef, and the APP_* sentinel tokens\n // from any module scope. Without this, guards / decorators that inject\n // REQUEST_CONTEXT through ExecutionContext fail with \"no provider\"\n // when the request enters the pipeline. See vela/src/factory/bootstrap.ts.\n container.register({ provide: Container, useValue: container });\n container.markGlobalToken(Container);\n\n container.register({\n provide: ModuleRef,\n useFactory: (c: Container) => new ModuleRef(c),\n inject: [Container],\n });\n container.markGlobalToken(ModuleRef);\n\n // Decorator-driven discovery — global so any provider can inject it, and so\n // callOnApplicationBootstrap() reuses this instance instead of self-building\n // one. Mirrors vela/src/factory/bootstrap.ts.\n container.register({\n provide: DiscoveryService,\n useFactory: (c: Container) => new DiscoveryService(c),\n inject: [Container],\n });\n container.markGlobalToken(DiscoveryService);\n\n for (const t of [APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE]) {\n container.markGlobalToken(t);\n }\n\n // REQUEST_CONTEXT is seeded into each per-request child container by\n // RouteManager via setRequestInstance. Registering with a throw-factory\n // here ensures findRegistration succeeds (so the child's cached request\n // instance is returned) while surfacing a clear error if the token is\n // ever resolved outside the request path.\n container.register({\n provide: REQUEST_CONTEXT,\n scope: Scope.REQUEST,\n useFactory: () => {\n throw new Error(\n 'REQUEST_CONTEXT can only be resolved inside a request — ' +\n 'it is seeded by RouteManager when the request enters the pipeline.',\n );\n },\n });\n container.markGlobalToken(REQUEST_CONTEXT);\n\n // Pre-register at root so `moduleRef.get(token)` and framework-internal\n // resolves (instantiate(guard, container) with no requestingModuleId) see\n // the override immediately.\n for (const override of this.overrides) {\n container.register(override.provider);\n }\n\n const routeManager = new RouteManager(container);\n\n const loader = new ModuleLoader(container, routeManager);\n loader.load(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 bindAppProviders(routeManager, container, loader);\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 { 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velajs/testing",
3
- "version": "0.6.0",
3
+ "version": "1.0.0",
4
4
  "description": "Testing utilities for Vela framework",
5
5
  "keywords": [
6
6
  "edge",
@@ -52,7 +52,7 @@
52
52
  "@changesets/cli": "^2.31.0",
53
53
  "@swc/core": "^1.15.43",
54
54
  "@types/node": "^24.13.3",
55
- "@velajs/vela": "^1.12.0",
55
+ "@velajs/vela": "^1.21.0",
56
56
  "hono": "^4.12.26",
57
57
  "oxfmt": "^0.58.0",
58
58
  "oxlint": "^1.73.0",
@@ -65,7 +65,7 @@
65
65
  "peerDependencies": {
66
66
  "@hono/node-server": ">=1",
67
67
  "@hono/node-ws": ">=1",
68
- "@velajs/vela": ">=1.11.0",
68
+ "@velajs/vela": ">=1.21.0 <2",
69
69
  "hono": ">=4",
70
70
  "vitest": ">=3"
71
71
  },
@@ -92,7 +92,9 @@
92
92
  "attw": "attw --pack . --profile esm-only",
93
93
  "changeset": "changeset",
94
94
  "version-packages": "changeset version",
95
- "release": "pnpm build && changeset publish",
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",
96
98
  "verify": "pnpm lint && pnpm format:check && pnpm build && pnpm typecheck && pnpm typecheck:tests && pnpm test && pnpm publint && pnpm attw"
97
99
  }
98
100
  }