11import { describeDriverMatrix } from "./shared-matrix" ;
22import { describe , expect , test , vi } from "vitest" ;
3- import { setupDriverTest } from "./shared-utils" ;
3+ import { setupDriverTest , waitFor } from "./shared-utils" ;
44
55const STRESS_TEST_TIMEOUT_MS = 60_000 ;
6+ const KITCHEN_SINK_TEST_TIMEOUT_MS = 120_000 ;
67const ACTOR_READY_TIMEOUT_MS = 15_000 ;
8+ const RUNTIME_LOG_TAIL_CHARS = 20_000 ;
9+
10+ async function withRuntimeLogTail < T > (
11+ getRuntimeOutput : ( ) => string ,
12+ fn : ( ) => Promise < T > ,
13+ ) : Promise < T > {
14+ try {
15+ return await fn ( ) ;
16+ } catch ( error ) {
17+ const runtimeOutput = getRuntimeOutput ( ) ;
18+ const runtimeTail = runtimeOutput . slice ( - RUNTIME_LOG_TAIL_CHARS ) ;
19+ if ( error instanceof Error && runtimeTail ) {
20+ error . message = `${ error . message } \n\nRuntime log tail:\n${ runtimeTail } ` ;
21+ }
22+ throw error ;
23+ }
24+ }
725
826/**
927 * Stress and resilience tests for the SQLite database subsystem.
@@ -64,7 +82,10 @@ describeDriverMatrix("Actor Db Stress", (driverTestConfig) => {
6482 test (
6583 "rapid create-insert-destroy cycles handle DB lifecycle correctly" ,
6684 async ( c ) => {
67- const { client } = await setupDriverTest ( c , driverTestConfig ) ;
85+ const { client, getRuntimeOutput } = await setupDriverTest (
86+ c ,
87+ driverTestConfig ,
88+ ) ;
6889
6990 // Perform rapid cycles of create -> insert -> destroy.
7091 // This exercises the close_database path racing with
@@ -78,7 +99,10 @@ describeDriverMatrix("Actor Db Stress", (driverTestConfig) => {
7899 // Poll the first insert because the actor can still be starting when the initial DB action is sent.
79100 await vi . waitFor (
80101 async ( ) => {
81- await getActor ( ) . insertBatch ( 10 ) ;
102+ await withRuntimeLogTail (
103+ getRuntimeOutput ,
104+ ( ) => getActor ( ) . insertBatch ( 10 ) ,
105+ ) ;
82106 } ,
83107 { timeout : ACTOR_READY_TIMEOUT_MS , interval : 100 } ,
84108 ) ;
@@ -88,9 +112,13 @@ describeDriverMatrix("Actor Db Stress", (driverTestConfig) => {
88112 // through sleep teardown under the task model.
89113 await vi . waitFor (
90114 async ( ) => {
91- const count = await client . dbStressActor
92- . getOrCreate ( actorKey )
93- . getCount ( ) ;
115+ const count = await withRuntimeLogTail (
116+ getRuntimeOutput ,
117+ ( ) =>
118+ client . dbStressActor
119+ . getOrCreate ( actorKey )
120+ . getCount ( ) ,
121+ ) ;
94122 expect ( count ) . toBeGreaterThanOrEqual ( 10 ) ;
95123 } ,
96124 { timeout : ACTOR_READY_TIMEOUT_MS , interval : 100 } ,
@@ -106,7 +134,10 @@ describeDriverMatrix("Actor Db Stress", (driverTestConfig) => {
106134 test (
107135 "DB operations complete without excessive blocking" ,
108136 async ( c ) => {
109- const { client } = await setupDriverTest ( c , driverTestConfig ) ;
137+ const { client, getRuntimeOutput } = await setupDriverTest (
138+ c ,
139+ driverTestConfig ,
140+ ) ;
110141
111142 const actorKey = [ `stress-health-${ crypto . randomUUID ( ) } ` ] ;
112143
@@ -117,9 +148,11 @@ describeDriverMatrix("Actor Db Stress", (driverTestConfig) => {
117148 // expected because the action itself runs on that loop.
118149 const health = await vi . waitFor (
119150 async ( ) =>
120- client . dbStressActor
121- . getOrCreate ( actorKey )
122- . measureEventLoopHealth ( 100 ) ,
151+ withRuntimeLogTail ( getRuntimeOutput , ( ) =>
152+ client . dbStressActor
153+ . getOrCreate ( actorKey )
154+ . measureEventLoopHealth ( 100 ) ,
155+ ) ,
123156 { timeout : ACTOR_READY_TIMEOUT_MS , interval : 100 } ,
124157 ) ;
125158
@@ -132,14 +165,101 @@ describeDriverMatrix("Actor Db Stress", (driverTestConfig) => {
132165 // Poll the integrity check because the actor may still be finishing the prior async insert loop.
133166 const integrity = await vi . waitFor (
134167 async ( ) =>
135- client . dbStressActor
136- . getOrCreate ( actorKey )
137- . integrityCheck ( ) ,
168+ withRuntimeLogTail ( getRuntimeOutput , ( ) =>
169+ client . dbStressActor
170+ . getOrCreate ( actorKey )
171+ . integrityCheck ( ) ,
172+ ) ,
138173 { timeout : ACTOR_READY_TIMEOUT_MS , interval : 100 } ,
139174 ) ;
140175 expect ( integrity . toLowerCase ( ) ) . toBe ( "ok" ) ;
141176 } ,
142177 STRESS_TEST_TIMEOUT_MS ,
143178 ) ;
179+
180+ test (
181+ "repeated autocommit upserts keep sqlite head txid consistent" ,
182+ async ( c ) => {
183+ const { client, getRuntimeOutput } = await setupDriverTest (
184+ c ,
185+ driverTestConfig ,
186+ ) ;
187+ const actor = client . dbStressActor . getOrCreate ( [
188+ `stress-autocommit-upsert-${ crypto . randomUUID ( ) } ` ,
189+ ] ) ;
190+
191+ await actor . reset ( ) ;
192+
193+ const count = await withRuntimeLogTail (
194+ getRuntimeOutput ,
195+ ( ) => actor . upsertMetaRows ( 240 ) ,
196+ ) ;
197+ expect ( count ) . toBe ( 32 ) ;
198+
199+ const integrity = await withRuntimeLogTail (
200+ getRuntimeOutput ,
201+ ( ) => actor . integrityCheck ( ) ,
202+ ) ;
203+ expect ( integrity . toLowerCase ( ) ) . toBe ( "ok" ) ;
204+ } ,
205+ STRESS_TEST_TIMEOUT_MS ,
206+ ) ;
207+
208+ test (
209+ "kitchen sink sqlite smoke survives write churn and wake" ,
210+ async ( c ) => {
211+ const { client, getRuntimeOutput } = await setupDriverTest (
212+ c ,
213+ driverTestConfig ,
214+ ) ;
215+ const actor = client . dbStressActor . getOrCreate ( [
216+ `stress-kitchen-sink-${ crypto . randomUUID ( ) } ` ,
217+ ] ) ;
218+
219+ await actor . reset ( ) ;
220+
221+ const first = await withRuntimeLogTail (
222+ getRuntimeOutput ,
223+ ( ) => actor . kitchenSinkSmoke ( 320 ) ,
224+ ) ;
225+ expect ( first . metaCount ) . toBeGreaterThanOrEqual ( 19 ) ;
226+ expect ( first . dataCount ) . toBeGreaterThan ( 0 ) ;
227+ expect ( first . payloadCount ) . toBeGreaterThan ( 0 ) ;
228+ expect ( first . pageCount ) . toBeGreaterThan ( 0 ) ;
229+ expect ( first . integrity . toLowerCase ( ) ) . toBe ( "ok" ) ;
230+
231+ const burst = await withRuntimeLogTail ( getRuntimeOutput , ( ) =>
232+ Promise . all ( [
233+ actor . upsertMetaRows ( 320 ) ,
234+ actor . kitchenSinkSmoke ( 96 ) ,
235+ actor . upsertMetaRows ( 320 ) ,
236+ ] ) ,
237+ ) ;
238+ expect ( burst [ 0 ] ) . toBeGreaterThanOrEqual ( 32 ) ;
239+ expect ( burst [ 1 ] . integrity . toLowerCase ( ) ) . toBe ( "ok" ) ;
240+ expect ( burst [ 2 ] ) . toBeGreaterThanOrEqual ( 32 ) ;
241+
242+ await actor . triggerSleep ( ) ;
243+ await waitFor ( driverTestConfig , 250 ) ;
244+
245+ // Poll because the actor can still be in the stopping window after triggerSleep.
246+ const afterWake = await vi . waitFor (
247+ async ( ) =>
248+ await withRuntimeLogTail (
249+ getRuntimeOutput ,
250+ ( ) => actor . kitchenSinkSmoke ( 96 ) ,
251+ ) ,
252+ { timeout : ACTOR_READY_TIMEOUT_MS , interval : 100 } ,
253+ ) ;
254+ expect ( afterWake . metaCount ) . toBeGreaterThanOrEqual (
255+ first . metaCount ,
256+ ) ;
257+ expect ( afterWake . dataCount ) . toBeGreaterThan ( first . dataCount ) ;
258+ expect ( afterWake . payloadCount ) . toBeGreaterThan ( 0 ) ;
259+ expect ( afterWake . pageCount ) . toBeGreaterThan ( 0 ) ;
260+ expect ( afterWake . integrity . toLowerCase ( ) ) . toBe ( "ok" ) ;
261+ } ,
262+ KITCHEN_SINK_TEST_TIMEOUT_MS ,
263+ ) ;
144264 } ) ;
145265} , { encodings : [ "bare" ] } ) ;
0 commit comments