@chidchanun/bcp 0.2.13 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ Universal React application APIs.
10
10
 
11
11
  Common exports include `Form`, `Link`, `createIsland`, `navigate`, `notFound`, loader/action/guard hooks and router/navigation APIs.
12
12
 
13
- Related guides: [Routing](routing.md), [Server Data Loaders](server-data-loaders.md), [Route Guards](route-guards.md), [Form Actions](form-actions.md).
13
+ Related guides: [Routing](routing.md), [Server Data Loaders](server-data-loaders.md), [Route Guards](route-guards.md), [Form Actions](form-actions.md), [Testing Platform](testing-platform.md).
14
14
 
15
15
  ## `bcp/island`
16
16
 
@@ -54,7 +54,7 @@ Server-only Database Platform v2 APIs.
54
54
 
55
55
  Built-in SQL providers are MySQL, PostgreSQL and SQLite. `BcpDatabase` instances expose lazy query/execute/transaction operations plus explicit `connect()`, `disconnect()` and backward-compatible `close()` lifecycle methods.
56
56
 
57
- Related guides: [Database](database.md), [Database Migrations](database-migrations.md), [Transactional Outbox & Events](transactional-outbox-events.md).
57
+ Related guides: [Database](database.md), [Database Migrations](database-migrations.md), [Transactional Outbox & Events](transactional-outbox-events.md), [Testing Platform](testing-platform.md).
58
58
 
59
59
  ## `bcp/auth`
60
60
 
@@ -62,7 +62,7 @@ Server-only authentication and authorization APIs.
62
62
 
63
63
  The default authentication mode remains stateless signed JWT-cookie authentication. Applications may opt into server-side session stores, revocation, logout-all, idle timeout, role/permission guards and resource-aware authorization policies.
64
64
 
65
- Related guides: [Authentication](authentication.md), [Auth Session Stores](auth-session-store.md), [Auth Route Guards](auth-route-guards.md), [Authorization & Security v2](authorization-security.md), [JWT Sessions](session-auth.md).
65
+ Related guides: [Authentication](authentication.md), [Auth Session Stores](auth-session-store.md), [Auth Route Guards](auth-route-guards.md), [Authorization & Security v2](authorization-security.md), [JWT Sessions](session-auth.md), [Testing Platform](testing-platform.md).
66
66
 
67
67
  ## `bcp/jobs`
68
68
 
@@ -78,7 +78,7 @@ BCP does not install a Redis library and does not create the connection. Applica
78
78
 
79
79
  The processing model is at-least-once. Handlers that perform non-idempotent external side effects should use application-level idempotency protection.
80
80
 
81
- Related guides: [Background Jobs Platform](background-jobs.md), [Job Scheduling Platform](job-scheduling.md), [Durable Jobs Platform](durable-jobs.md), [Transactional Outbox & Events](transactional-outbox-events.md), [Realtime Platform](realtime-platform.md), [Observability Platform v2](observability.md).
81
+ Related guides: [Background Jobs Platform](background-jobs.md), [Job Scheduling Platform](job-scheduling.md), [Durable Jobs Platform](durable-jobs.md), [Transactional Outbox & Events](transactional-outbox-events.md), [Realtime Platform](realtime-platform.md), [Testing Platform](testing-platform.md), [Observability Platform v2](observability.md).
82
82
 
83
83
  ## `bcp/workflow`
84
84
 
@@ -111,7 +111,7 @@ When an existing `BackgroundJobQueue` is supplied, workflow execution and delay
111
111
 
112
112
  `WorkflowStore.claim()` / `release()` form the run-level lease boundary for multi-instance stores.
113
113
 
114
- Related guides: [Workflow Orchestration](workflow-orchestration.md), [Durable Jobs Platform](durable-jobs.md), [Realtime Platform](realtime-platform.md), [Observability Platform v2](observability.md).
114
+ Related guides: [Workflow Orchestration](workflow-orchestration.md), [Durable Jobs Platform](durable-jobs.md), [Realtime Platform](realtime-platform.md), [Testing Platform](testing-platform.md), [Observability Platform v2](observability.md).
115
115
 
116
116
  ## `bcp/events`
117
117
 
@@ -136,7 +136,7 @@ import {
136
136
 
137
137
  Delivery is at-least-once; consumers should use idempotency controls for non-repeatable side effects.
138
138
 
139
- Related guides: [Transactional Outbox & Events](transactional-outbox-events.md), [Database](database.md), [Durable Jobs Platform](durable-jobs.md), [Realtime Platform](realtime-platform.md), [Observability Platform v2](observability.md).
139
+ Related guides: [Transactional Outbox & Events](transactional-outbox-events.md), [Database](database.md), [Durable Jobs Platform](durable-jobs.md), [Realtime Platform](realtime-platform.md), [Testing Platform](testing-platform.md), [Observability Platform v2](observability.md).
140
140
 
141
141
  ## `bcp/realtime`
142
142
 
@@ -168,47 +168,145 @@ import {
168
168
  } from "bcp/realtime";
169
169
  ```
170
170
 
171
- ### Hub and channels
172
-
173
171
  `createRealtime()` creates a server-side hub. Connections can `join()`, `leave()`, `emit()`, `send()`, `touch()` and `disconnect()`.
174
172
 
175
- `hub.broadcast(channel, event, payload)` delivers through the configured broker and reaches connections that joined the channel.
173
+ `RealtimeBroker` is the cross-hub pub/sub boundary and `RealtimePresenceStore` is the channel-presence boundary. Built-in memory implementations are intended for tests/single-process use; shared deployments can supply provider adapters.
176
174
 
177
- ### Broker
175
+ `RealtimeOptions.authenticate` can resolve identity from a Request/connection payload, while `authorizeChannel` runs before channel membership is accepted.
178
176
 
179
- `RealtimeBroker` is the cross-hub pub/sub boundary. The memory implementation is process-local; production multi-instance applications can provide a shared broker such as Redis Pub/Sub or NATS without changing the hub surface.
177
+ BCP does not install a WebSocket provider. `RealtimeSocket` defines the minimal transport surface consumed by `hub.attachSocket()`.
180
178
 
181
- ### Presence
179
+ `hub.sse()` and `createRealtimeSseResponse()` expose Web-standard Server-Sent Events responses with abort, filtering, retry hint and keep-alive options.
182
180
 
183
- `RealtimePresenceStore` tracks per-channel connection presence. `hub.members(channel)` returns current presence records.
181
+ Realtime delivery is transient. Use database/outbox/jobs/workflows for durable state and refetch durable state/history after reconnect when catch-up is required.
184
182
 
185
- The memory store is intended for tests/single-process use. Shared deployments should use a shared presence adapter.
183
+ Related guides: [Realtime Platform](realtime-platform.md), [Authentication](authentication.md), [Testing Platform](testing-platform.md), [Observability Platform v2](observability.md).
186
184
 
187
- ### Authentication and channel authorization
185
+ ## `bcp/testing`
188
186
 
189
- `RealtimeOptions.authenticate` can resolve application user identity from a `Request`/connection data. `getUserId` maps that identity into presence records.
187
+ Server-only Testing Platform APIs added in `0.2.14`.
190
188
 
191
- `RealtimeOptions.authorizeChannel` runs before `join()` completes and can enforce private-room access.
189
+ ```ts
190
+ import {
191
+ createFakeClock,
192
+ createJobTestHarness,
193
+ createOutboxTestHarness,
194
+ createRealtimeTestHarness,
195
+ createRealtimeTestSocket,
196
+ createRouteTestHandler,
197
+ createSequenceIdFactory,
198
+ createTestApp,
199
+ createTestAuthSession,
200
+ createTestFormData,
201
+ createWorkflowTestHarness,
202
+ expectResponse,
203
+ readSseEvents,
204
+ runTestMiddleware,
205
+ runTestPageAction,
206
+ runTestPageGuards,
207
+ runTestPageLoader,
208
+ withTestTransaction,
209
+ type CreateTestAuthSessionOptions,
210
+ type FakeClock,
211
+ type JobTestHarness,
212
+ type OutboxTestHarness,
213
+ type ParsedSseEvent,
214
+ type RealtimeTestConnection,
215
+ type RealtimeTestHarness,
216
+ type RealtimeTestSocket,
217
+ type TestApp,
218
+ type TestAppOptions,
219
+ type TestAuthSession,
220
+ type TestPageActionOptions,
221
+ type TestPageContextOptions,
222
+ type TestPageGuardOptions,
223
+ type TestRequestHandler,
224
+ type TestRequestOptions,
225
+ type TestResponseExpectation,
226
+ type TestRouteFunction,
227
+ type TestRouteHandlerOptions,
228
+ type TestRouteMethod,
229
+ type TestRouteModule,
230
+ type TestTransactionDatabase,
231
+ type WorkflowTestHarness,
232
+ } from "bcp/testing";
233
+ ```
192
234
 
193
- ### Socket adapter
235
+ ### Request application harness
194
236
 
195
- BCP does not install a WebSocket provider. `RealtimeSocket` defines the minimal `send`, `close`, `onMessage`, `onClose` and optional `onError` surface consumed by `hub.attachSocket()`.
237
+ `createTestApp()` runs a Web-standard `Request -> Response` handler with convenience methods for GET/POST/PUT/PATCH/DELETE, JSON bodies, mutable default headers and an in-memory cookie jar that consumes `Set-Cookie` responses.
196
238
 
197
- The built-in JSON protocol accepts `join`, `leave`, `event` and `ping` messages. `ping` receives a `realtime.pong` event.
239
+ ### API route module testing
198
240
 
199
- ### Server-Sent Events
241
+ `createRouteTestHandler()` adapts BCP-style method exports (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`) to a Request handler. Missing methods return 405 with an `Allow` header.
200
242
 
201
- `hub.sse(channel, options)` and `createRealtimeSseResponse()` return Web-standard streaming `Response` objects using `text/event-stream`.
243
+ Return values are normalized for tests: Response is preserved, strings become text Responses, null/undefined become 204, and other values become JSON.
202
244
 
203
- Options include abort signal integration, event filtering, retry hints, keep-alive timing and response headers.
245
+ ### Page loader, guard and form action testing
204
246
 
205
- ### Heartbeat
247
+ `runTestPageGuards()` executes one or more guards through the production `executePageGuardFunctions()` path. Guard data is chained in order and redirect/other Response short-circuits are preserved.
206
248
 
207
- `hub.startHeartbeat()` sends `realtime.ping` messages and invokes stale connection cleanup. `hub.sweepStale()` is also public for deterministic infrastructure loops/tests.
249
+ `runTestPageLoader()` invokes the production `executePageLoaderFunction()` path with test params, URL/search params and optional guard data. The returned value is the normal `PageLoaderExecution` contract.
208
250
 
209
- Realtime delivery is transient. Use database/outbox/jobs/workflows for durable state and refetch durable state/history after reconnect when catch-up is required.
251
+ `runTestPageAction()` invokes the production `executePageActionFunction()` path, including action-name/method validation, guard data, FormData and Response/data handling.
252
+
253
+ `createTestFormData()` creates a FormData object from string/Blob fields and supports repeated values.
254
+
255
+ ### Response assertions
256
+
257
+ `expectResponse()` exposes lightweight runner-neutral assertions:
258
+
259
+ ```text
260
+ status()
261
+ header()
262
+ text()
263
+ json()
264
+ jsonMatches()
265
+ ```
266
+
267
+ BCP does not require Jest/Vitest assertion APIs.
268
+
269
+ ### Auth testing
270
+
271
+ `createTestAuthSession()` uses the production `createSessionToken()` implementation, including HS256 signature, issuer/audience and expiration semantics. It can also register the matching server-side record in an `AuthSessionStore`.
272
+
273
+ This allows route/auth tests to use a real BCP session cookie instead of introducing a test-only identity header.
274
+
275
+ ### Database rollback testing
276
+
277
+ `withTestTransaction()` executes a callback in a real BCP database transaction and deliberately rejects the transaction with an internal rollback sentinel after the test callback succeeds. The callback result is returned to the test while the transaction is rolled back.
278
+
279
+ Application callback errors remain unchanged and are not swallowed.
280
+
281
+ ### Middleware testing
282
+
283
+ `runTestMiddleware()` invokes the actual Middleware System v2 onion pipeline through `executeMiddlewarePipeline()` and supports a custom downstream Request handler.
284
+
285
+ ### Deterministic utilities
286
+
287
+ `createFakeClock()` exposes `now()`, `set()`, `advance()` and `reset()` for time-sensitive queues/workflows/schedulers.
288
+
289
+ `createSequenceIdFactory()` creates deterministic IDs for stable assertions.
290
+
291
+ ### Infrastructure harnesses
292
+
293
+ `createJobTestHarness()` drains currently eligible work through the real `BackgroundJobQueue.processNext()` API and exposes record/count assertions.
294
+
295
+ `createWorkflowTestHarness()` can start/run workflows until terminal or waiting state and optionally force persisted delays to resume.
296
+
297
+ `createOutboxTestHarness()` dispatches through the actual `OutboxDispatcher` and exposes event/state/statistics helpers.
298
+
299
+ `createRealtimeTestSocket()` implements the `RealtimeSocket` adapter contract entirely in memory. `createRealtimeTestHarness()` connects that socket to a real `RealtimeHub` and can assert server-sent channel events.
300
+
301
+ `readSseEvents()` reads event-stream Responses, ignores comment/retry frames and JSON-decodes `data:` payloads when possible.
302
+
303
+ ### Runner neutrality and boundary
304
+
305
+ `bcp/testing` has no dependency on Jest or Vitest. BCP's own suite uses Node `node:test`, but the helpers can be called from another runner.
306
+
307
+ The package is server-only; the client boundary validator rejects `bcp/testing` from pages/client islands.
210
308
 
211
- Related guides: [Realtime Platform](realtime-platform.md), [Authentication](authentication.md), [Observability Platform v2](observability.md).
309
+ Related guides: [Testing Platform](testing-platform.md), [Authentication](authentication.md), [Database](database.md), [Durable Jobs](durable-jobs.md), [Workflow Orchestration](workflow-orchestration.md), [Transactional Outbox & Events](transactional-outbox-events.md), [Realtime Platform](realtime-platform.md).
212
310
 
213
311
  ## `bcp/observability`
214
312
 
@@ -220,7 +318,7 @@ Related guides: [Observability Platform v2](observability.md), [Logging](develop
220
318
 
221
319
  Server request/runtime APIs including request context, cookies, CSRF and same-origin protection, logging, graceful shutdown hooks, multipart upload helpers, storage adapters, file delivery, response helpers and low-level session primitives.
222
320
 
223
- Related guides: [Server Request APIs](server-request-apis.md), [Authorization & Security v2](authorization-security.md), [File Upload](file-upload.md), [Storage](storage.md), [Storage Ecosystem](storage-ecosystem.md), [Production Hardening](production-hardening.md).
321
+ Related guides: [Server Request APIs](server-request-apis.md), [Authorization & Security v2](authorization-security.md), [File Upload](file-upload.md), [Storage](storage.md), [Storage Ecosystem](storage-ecosystem.md), [Production Hardening](production-hardening.md), [Testing Platform](testing-platform.md).
224
322
 
225
323
  ## `bcp/server-only`
226
324
 
@@ -236,7 +334,7 @@ Related guide: [Application Modules](application-modules.md).
236
334
 
237
335
  Middleware System v2 APIs and types.
238
336
 
239
- Related guide: [Middleware](middleware.md).
337
+ Related guides: [Middleware](middleware.md), [Testing Platform](testing-platform.md).
240
338
 
241
339
  ## Stability
242
340
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "framework": "bcp",
4
- "versionTarget": "0.2.13",
4
+ "versionTarget": "0.2.14",
5
5
  "releaseState": "unreleased",
6
6
  "sections": [
7
7
  {
@@ -88,10 +88,11 @@
88
88
  {
89
89
  "id": "developer-experience",
90
90
  "title": "Developer Experience",
91
- "description": "Project generators, diagnostics, project metadata and framework maintenance tooling.",
91
+ "description": "Project generators, diagnostics, testing, project metadata and framework maintenance tooling.",
92
92
  "pages": [
93
93
  { "route": "/docs/generators", "source": "generators.md", "title": "Project Generators" },
94
- { "route": "/docs/developer-tools", "source": "developer-tools.md", "title": "Doctor & Inspect" }
94
+ { "route": "/docs/developer-tools", "source": "developer-tools.md", "title": "Doctor & Inspect" },
95
+ { "route": "/docs/testing-platform", "source": "testing-platform.md", "title": "Testing Platform" }
95
96
  ]
96
97
  },
97
98
  {
@@ -114,7 +115,8 @@
114
115
  }
115
116
  ],
116
117
  "releases": [
117
- { "route": "/releases/0.2.13", "source": "releases/0.2.13.md", "version": "0.2.13", "state": "unreleased" },
118
+ { "route": "/releases/0.2.14", "source": "releases/0.2.14.md", "version": "0.2.14", "state": "unreleased" },
119
+ { "route": "/releases/0.2.13", "source": "releases/0.2.13.md", "version": "0.2.13" },
118
120
  { "route": "/releases/0.2.12", "source": "releases/0.2.12.md", "version": "0.2.12" },
119
121
  { "route": "/releases/0.2.11", "source": "releases/0.2.11.md", "version": "0.2.11" },
120
122
  { "route": "/releases/0.2.10", "source": "releases/0.2.10.md", "version": "0.2.10" },
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "framework": "bcp",
4
- "version": "0.2.13",
4
+ "version": "0.2.14",
5
5
  "releaseState": "unreleased",
6
- "baseline": "realtime-platform",
6
+ "baseline": "testing-platform",
7
7
  "runtime": {
8
8
  "node": ">=24.11.0",
9
9
  "react": "19",
@@ -23,6 +23,7 @@
23
23
  "bcp/workflow",
24
24
  "bcp/events",
25
25
  "bcp/realtime",
26
+ "bcp/testing",
26
27
  "bcp/observability",
27
28
  "bcp/server",
28
29
  "bcp/server-only",
@@ -121,6 +122,22 @@
121
122
  "realtimeServerSentEvents": true,
122
123
  "realtimeHeartbeats": true,
123
124
  "realtimeCrossHubBroadcast": true,
125
+ "testingPlatform": true,
126
+ "testRequestHarness": true,
127
+ "testRouteHandlers": true,
128
+ "testPageLoaders": true,
129
+ "testPageGuards": true,
130
+ "testFormActions": true,
131
+ "testResponseAssertions": true,
132
+ "testAuthSessions": true,
133
+ "testTransactionRollback": true,
134
+ "testMiddlewarePipeline": true,
135
+ "testJobHarness": true,
136
+ "testWorkflowHarness": true,
137
+ "testOutboxHarness": true,
138
+ "testRealtimeSocket": true,
139
+ "testSseReader": true,
140
+ "deterministicTestClock": true,
124
141
  "databaseMigrations": true,
125
142
  "databaseAdapterContract": true,
126
143
  "databasePostgresql": true,
@@ -160,7 +177,7 @@
160
177
  "s3-compatible"
161
178
  ],
162
179
  "compatibility": {
163
- "previousBaseline": "0.2.12",
180
+ "previousBaseline": "0.2.13",
164
181
  "intentionalBreakingChangesFromPreviousBaseline": false,
165
182
  "migrationGuide": "migration-0.2.md"
166
183
  },
@@ -183,7 +200,8 @@
183
200
  "workflowOrchestration": "workflow-orchestration.md",
184
201
  "transactionalOutboxEvents": "transactional-outbox-events.md",
185
202
  "realtimePlatform": "realtime-platform.md",
203
+ "testingPlatform": "testing-platform.md",
186
204
  "migrationGuide": "migration-0.2.md",
187
- "releaseNotes": "releases/0.2.13.md"
205
+ "releaseNotes": "releases/0.2.14.md"
188
206
  }
189
207
  }
@@ -0,0 +1,241 @@
1
+ # BCP Framework 0.2.14 — Testing Platform
2
+
3
+ **Release state:** unreleased
4
+
5
+ BCP `0.2.14` adds framework-native server-side testing utilities through the new `bcp/testing` entrypoint.
6
+
7
+ The milestone is intentionally test-runner neutral: BCP does not add Jest or Vitest as runtime dependencies. The framework's own suite continues to use Node's built-in `node:test`.
8
+
9
+ ## Highlights
10
+
11
+ - new server-only `bcp/testing` entrypoint,
12
+ - Request -> Response application test harness,
13
+ - persistent test cookie jar and default headers,
14
+ - API route module testing for GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS,
15
+ - page guard testing through the production guard executor,
16
+ - page loader testing through the production loader executor,
17
+ - form action testing through the production action executor,
18
+ - `FormData` construction helper,
19
+ - lightweight response assertions,
20
+ - real signed BCP auth session generation,
21
+ - optional `AuthSessionStore` registration,
22
+ - rollback-only database test transactions,
23
+ - real Middleware System v2 pipeline execution,
24
+ - deterministic fake clock,
25
+ - deterministic sequence ID factory,
26
+ - background job queue drain/count helpers,
27
+ - workflow execution/state helpers,
28
+ - transactional outbox dispatch/state helpers,
29
+ - in-memory `RealtimeSocket` test adapter,
30
+ - realtime connection/event harness,
31
+ - Server-Sent Events reader,
32
+ - prepared package compilation to `testing.mjs`,
33
+ - package smoke and client-boundary validation.
34
+
35
+ ## Public API
36
+
37
+ ```ts
38
+ import {
39
+ createFakeClock,
40
+ createJobTestHarness,
41
+ createOutboxTestHarness,
42
+ createRealtimeTestHarness,
43
+ createRealtimeTestSocket,
44
+ createRouteTestHandler,
45
+ createSequenceIdFactory,
46
+ createTestApp,
47
+ createTestAuthSession,
48
+ createTestFormData,
49
+ createWorkflowTestHarness,
50
+ expectResponse,
51
+ readSseEvents,
52
+ runTestMiddleware,
53
+ runTestPageAction,
54
+ runTestPageGuards,
55
+ runTestPageLoader,
56
+ withTestTransaction,
57
+ } from "bcp/testing";
58
+ ```
59
+
60
+ ## Request and route testing
61
+
62
+ ```ts
63
+ const app =
64
+ createTestApp({
65
+ handler:
66
+ createRouteTestHandler({
67
+ GET() {
68
+ return {
69
+ ok: true,
70
+ };
71
+ },
72
+ }),
73
+ });
74
+
75
+ const response =
76
+ await app.get(
77
+ "/api/health"
78
+ );
79
+
80
+ await expectResponse(response)
81
+ .status(200)
82
+ .json({
83
+ ok: true,
84
+ });
85
+ ```
86
+
87
+ The harness maintains cookies returned by `Set-Cookie` and sends them on future requests.
88
+
89
+ ## Page server runtime testing
90
+
91
+ Page loader, guard and form action helpers invoke the same execution functions used by BCP runtime:
92
+
93
+ ```ts
94
+ await runTestPageGuards(
95
+ guard,
96
+ {
97
+ params: {
98
+ id: "42",
99
+ },
100
+ }
101
+ );
102
+
103
+ await runTestPageLoader(
104
+ loader,
105
+ {
106
+ url:
107
+ "/users/42?tab=profile",
108
+ }
109
+ );
110
+
111
+ await runTestPageAction(
112
+ updateUser,
113
+ {
114
+ actionName:
115
+ "updateUser",
116
+ method: "PATCH",
117
+ fields: {
118
+ name: "BCP",
119
+ },
120
+ }
121
+ );
122
+ ```
123
+
124
+ This preserves guard-data chaining, loader/action Response short-circuits, action method validation and JSON-safe result validation.
125
+
126
+ ## Authentication testing
127
+
128
+ `createTestAuthSession()` uses the same BCP session token implementation as production auth.
129
+
130
+ ```ts
131
+ const session =
132
+ await createTestAuthSession(
133
+ {
134
+ id: 42,
135
+ },
136
+ {
137
+ secret,
138
+ store:
139
+ authSessionStore,
140
+ }
141
+ );
142
+
143
+ app.setCookie(
144
+ session.cookieName,
145
+ session.token
146
+ );
147
+ ```
148
+
149
+ This avoids introducing a framework-only fake user header.
150
+
151
+ ## Database rollback tests
152
+
153
+ ```ts
154
+ await withTestTransaction(
155
+ db,
156
+ async tx => {
157
+ await tx.execute(
158
+ "INSERT INTO users ..."
159
+ );
160
+
161
+ // assertions
162
+ }
163
+ );
164
+ ```
165
+
166
+ After the callback succeeds, BCP deliberately rejects the database transaction with an internal rollback signal, then returns the callback result to the test.
167
+
168
+ ## Infrastructure harnesses
169
+
170
+ `0.2.14` adds thin harnesses over the real platform contracts instead of separate mock implementations:
171
+
172
+ ```text
173
+ BackgroundJobQueue -> createJobTestHarness()
174
+ Workflow -> createWorkflowTestHarness()
175
+ OutboxStore + Dispatcher -> createOutboxTestHarness()
176
+ RealtimeHub -> createRealtimeTestHarness()
177
+ RealtimeSocket -> createRealtimeTestSocket()
178
+ ```
179
+
180
+ ## SSE
181
+
182
+ ```ts
183
+ const events =
184
+ await readSseEvents(
185
+ response,
186
+ {
187
+ limit: 1,
188
+ timeoutMs: 1_000,
189
+ }
190
+ );
191
+ ```
192
+
193
+ SSE comments and retry frames are ignored and JSON `data:` payloads are decoded when possible.
194
+
195
+ ## Package/runtime contract
196
+
197
+ The prepared npm package now exposes:
198
+
199
+ ```text
200
+ bcp/testing
201
+ types -> packages/client/src/testing.ts
202
+ browser -> packages/client/src/server-only.browser.mjs
203
+ default -> packages/client/src/testing.mjs
204
+ ```
205
+
206
+ `testing.mjs` is compiled during `package:prepare` so Node does not need a TypeScript loader to use the testing entrypoint from an installed package.
207
+
208
+ ## Compatibility
209
+
210
+ `0.2.14` has no intentional breaking changes from `0.2.13`.
211
+
212
+ All existing runtime entrypoints remain supported.
213
+
214
+ ## Validation
215
+
216
+ Before publishing:
217
+
218
+ ```bash
219
+ npm run typecheck
220
+ npm run test:unit
221
+ npm run test:integration
222
+ npm run test:e2e
223
+ npm run test:package
224
+ npm run rc:check
225
+ ```
226
+
227
+ The release must not be tagged or published until the final release commit passes the complete RC sequence.
228
+
229
+ ## Non-goals
230
+
231
+ `0.2.14` does not add:
232
+
233
+ - a DOM/browser renderer,
234
+ - Playwright integration,
235
+ - component snapshots,
236
+ - a coverage runner,
237
+ - a Jest dependency,
238
+ - a Vitest dependency,
239
+ - automatic Docker/database test provisioning.
240
+
241
+ These can be layered on top of the base testing contracts in later milestones.