@luckystack/test-runner 0.14.0 → 0.16.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
@@ -5,6 +5,23 @@ All notable changes to `@luckystack/test-runner` are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.15.0] - 2026-10-19
9
+
10
+ ### Added
11
+
12
+ - `runAllTests({ apiTransport, expectServerError })`, `runContractTests({ apiTransport, expectServerError, resetToken })`, `runFuzzTests({ apiTransport, resetToken })`, `apiTransport` on the auth and rate-limit layer inputs, `ctx.callApi(input, { transport })` and the `CallApiOptions` type.
13
+
14
+ ### Changed
15
+
16
+ - **Breaking:** in socket-mode projects (the default `transport.invocation: 'socket'`) the contract, auth, rate-limit and fuzz layers and `ctx.callApi` test API routes over Socket.io, the path real clients use, instead of `/api/*` (ADR 0084). `createSocketProbeTransport` sends API routes over the socket too; the CSRF layer still probes over HTTP. Upgrade: expect results to change where the two pipelines differ; pass `apiTransport: 'http'` to `runAllTests`, or `{ transport: 'http' }` to `ctx.callApi`, to keep the old path.
17
+ - **Breaking:** the contract layer fails on routing errors (`notFound`, `methodNotAllowed`, `invalidName`) and on server errors (HTTP 5xx, `serverExecutionFailed`, `internalServerError`) instead of passing any well-formed error. Upgrade: list routes that throw on purpose in the new `expectServerError` option of `runAllTests` / `runContractTests`.
18
+
19
+ ### Fixed
20
+
21
+ - **Token-mode projects:** `runAllTests({ authToken })` sends the session as `Authorization: Bearer` instead of a Cookie and skips the CSRF layer with a note. Before, the CSRF layer failed every state-changing route by mistake and ran real mutations on the sweep's session.
22
+ - **Contract and fuzz probes that hit the rate limiter are reported as skipped instead of passed.** With a reset token (`resetToken` / `TEST_RESET_TOKEN`) both layers reset rate limits before each route.
23
+ - **The session `Cookie:` header uses the effective cookie name** when `http.sessionCookiePrefix` is set (`__Host-…` / `__Secure-…`), in the sweep, the test sockets and `ctx` calls. Before, the runner sent the unprefixed name, which the server no longer reads.
24
+
8
25
  ## [0.12.0] - 2026-10-15
9
26
 
10
27
  ### Changed
@@ -20,5 +20,11 @@ export interface ContractCheckInput {
20
20
  requestTimeoutMs?: number;
21
21
  /** How the probe reaches the server. Defaults to HTTP. */
22
22
  transport?: ProbeTransport;
23
+ /**
24
+ * The route is designed to fail on the server (e.g. a playground route that
25
+ * throws on purpose): a shaped 5xx / execution-failed reply then passes.
26
+ * Routing errors (unknown route, wrong method) still fail.
27
+ */
28
+ allowServerError?: boolean;
23
29
  }
24
30
  export declare const runContractCheck: (input: ContractCheckInput) => Promise<ContractCheckResult>;
@@ -1,5 +1,6 @@
1
1
  import { getPrismaClient } from '@luckystack/core';
2
2
  import { type ResolveTestEnvironmentInput } from './resolveTestEnvironment.js';
3
+ import type { Socket } from 'socket.io-client';
3
4
  import { type StreamChunkFrame, type StreamWatcher } from './streamWatcher.js';
4
5
  import type { ApiMethodMap } from './types.js';
5
6
  export interface CustomTestCase {
@@ -77,17 +78,23 @@ export interface CallSyncOptions {
77
78
  /** Socket transport: join `receiver` before sending. Default `true`. */
78
79
  joinRoom?: boolean;
79
80
  }
81
+ export interface CallApiOptions {
82
+ /** Force a transport. Defaults to the project's `transport.invocation`. */
83
+ transport?: 'socket' | 'http';
84
+ }
80
85
  export interface TestContext {
81
86
  /**
82
87
  * Invoke the route under test. Page / name / version are baked in.
83
88
  *
84
- * `input` travels exactly like the real client sends it: as the JSON body
85
- * for POST / PUT / DELETE routes, and as the `__luckystack_data` query-string
86
- * value for GET routes (a GET cannot carry a body). Either way the server
87
- * validates it as the route's `data`, so a GET route with required fields
88
- * receives them.
89
+ * The call takes the SAME transport the project's real client uses
90
+ * (`transport.invocation`): Socket.io (`apiRequest`) for `'socket'` (the
91
+ * default), the `/api/*` HTTP route for `'routed-http'`. Override per call
92
+ * with `opts.transport`. Over HTTP, `input` travels exactly like the real
93
+ * client sends it: as the JSON body for POST / PUT / DELETE routes, and as the
94
+ * `__luckystack_data` query-string value for GET routes (a GET cannot carry a
95
+ * body). Either way the server validates it as the route's `data`.
89
96
  */
90
- callApi: <TInput = unknown, TOutput = unknown>(input: TInput) => Promise<TOutput>;
97
+ callApi: <TInput = unknown, TOutput = unknown>(input: TInput, opts?: CallApiOptions) => Promise<TOutput>;
91
98
  /**
92
99
  * Invoke the sync route under test over the SAME transport the project's
93
100
  * real client uses (`transport.invocation`): Socket.io for `'socket'` (the
@@ -138,7 +145,15 @@ interface TestSessionState {
138
145
  lastResponse: unknown;
139
146
  }
140
147
  /** @internal Exported for the unit test only; consumers use `TestContext.callApi`. */
141
- export declare const buildCallApi: (baseUrl: string, routePath: string, state: TestSessionState, cookieName: string, method: string) => TestContext['callApi'];
148
+ export declare const buildCallApi: (baseUrl: string, routePath: string, state: TestSessionState, cookieName: string, method: string, caseSocket: CaseSyncSocket) => TestContext['callApi'];
149
+ /** @internal Exported for the unit test only. */
150
+ export interface CaseSyncSocket {
151
+ get: (token: string | null) => Promise<{
152
+ socket: Socket;
153
+ joinedRooms: Set<string>;
154
+ }>;
155
+ close: () => void;
156
+ }
142
157
  /** @internal Environment is already prepared by `runAllTests`. */
143
158
  export declare const runCustomTestsAfterEnvironmentPrepared: (input: PreparedRunCustomTestsInput) => Promise<RunCustomTestsSummary>;
144
159
  export declare const runCustomTests: (input: RunCustomTestsInput) => Promise<RunCustomTestsSummary>;
package/dist/index.d.ts CHANGED
@@ -37,7 +37,7 @@ export type { FuzzCheckInput } from './fuzzCheck.js';
37
37
  export type { RunFuzzTestsInput } from './runFuzzTests.js';
38
38
  export type { ResetServerStateInput } from './resetServerState.js';
39
39
  export { runCustomTests, discoverCustomTestFiles, } from './customTests.js';
40
- export type { CustomTestCase, CallSyncOptions, TestContext, TestExpect, CustomTestResult, RunCustomTestsInput, RunCustomTestsSummary, } from './customTests.js';
40
+ export type { CustomTestCase, CallApiOptions, CallSyncOptions, TestContext, TestExpect, CustomTestResult, RunCustomTestsInput, RunCustomTestsSummary, } from './customTests.js';
41
41
  export { openStreamWatcher } from './streamWatcher.js';
42
42
  export type { StreamWatcher, StreamChunkFrame, OpenStreamWatcherInput, } from './streamWatcher.js';
43
43
  export { runAllTests, logRunAllSummary } from './runAllTests.js';