rollbridge 0.1.54 → 0.1.55

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.
@@ -1,16 +1,17 @@
1
1
  // @ts-check
2
2
 
3
- import assert from "node:assert/strict"
4
3
  import {spawn} from "node:child_process"
5
4
  import {once} from "node:events"
6
5
  import fs from "node:fs/promises"
7
6
  import os from "node:os"
8
7
  import path from "node:path"
9
- import test from "node:test"
8
+ import {describe, expect, test} from "@velocious/testing"
10
9
  import {fileURLToPath} from "node:url"
11
10
  import GuardianClient from "../src/guardian-client.js"
12
11
  import {waitForProcessExit} from "./support/process.js"
13
12
 
13
+ describe("guardian-client", () => {
14
+
14
15
  const legacyGuardianPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "fixtures", "pre-split3-process-guardian.js")
15
16
  const recoveryOwnerPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "fixtures", "guardian-recovery-owner.js")
16
17
 
@@ -18,17 +19,21 @@ test("guardian bootstrap capability is absent from process argv", async () => {
18
19
  const fixture = await createGuardian()
19
20
 
20
21
  try {
21
- assert.deepEqual(await fixture.client.capabilities(), {daemonRecovery: 1, generationReactivation: 1})
22
+ expect(await fixture.client.capabilities()).toEqual({daemonRecovery: 1, generationReactivation: 1})
22
23
  const commandLine = await fs.readFile(`/proc/${fixture.client.pid}/cmdline`, "utf8")
23
24
  const environment = await fs.readFile(`/proc/${fixture.client.pid}/environ`, "utf8")
24
25
  const status = await fs.readFile(`/proc/${fixture.client.pid}/status`, "utf8")
25
26
  const inventory = JSON.stringify(await fixture.client.inventory())
26
27
 
27
- assert.ok(commandLine.includes("process-guardian.js"))
28
- assert.ok(!commandLine.includes(fixture.token), "guardian capability must not be exposed through argv")
29
- assert.ok(!environment.includes(fixture.token), "guardian capability must not be exposed through env")
30
- assert.ok(!status.includes(fixture.token), "guardian capability must not be exposed through process status/title")
31
- assert.ok(!inventory.includes(fixture.token), "guardian capability must not be exposed through guardian status")
28
+ expect(commandLine.includes("process-guardian.js")).toBeTruthy()
29
+ // Guardian capability must not be exposed through argv.
30
+ expect(commandLine.includes(fixture.token)).toBe(false)
31
+ // Guardian capability must not be exposed through env.
32
+ expect(environment.includes(fixture.token)).toBe(false)
33
+ // Guardian capability must not be exposed through process status/title.
34
+ expect(status.includes(fixture.token)).toBe(false)
35
+ // Guardian capability must not be exposed through guardian status.
36
+ expect(inventory.includes(fixture.token)).toBe(false)
32
37
  } finally {
33
38
  await cleanupGuardian(fixture)
34
39
  }
@@ -43,11 +48,11 @@ test("guardian inventory removes only an exact owned provenance", async () => {
43
48
  const inventory = await fixture.client.inventory()
44
49
  const candidate = inventory.find((entry) => entry.key === "candidate")
45
50
 
46
- assert.ok(candidate)
47
- await assert.rejects(() => fixture.client.remove("candidate", `${candidate.provenance}-wrong`), /provenance mismatch/)
48
- assert.equal((await fixture.client.inventory()).length, 1)
51
+ if (!candidate) throw new Error("Missing required fixture: candidate")
52
+ await expect(fixture.client.remove("candidate", `${candidate.provenance}-wrong`)).rejects.toThrow(/provenance mismatch/)
53
+ expect((await fixture.client.inventory()).length).toBe(1)
49
54
  await fixture.client.remove("candidate", candidate.provenance)
50
- assert.deepEqual(await fixture.client.inventory(), [])
55
+ expect(await fixture.client.inventory()).toEqual([])
51
56
  } finally {
52
57
  await cleanupGuardian(fixture)
53
58
  }
@@ -64,7 +69,7 @@ test("guardian runs a strict activation lifecycle command for the exact register
64
69
  try {
65
70
  await processInstance.start()
66
71
  await processInstance.activateStrict()
67
- assert.equal(await fs.readFile(activationPath, "utf8"), "activated")
72
+ expect(await fs.readFile(activationPath, "utf8")).toBe("activated")
68
73
  } finally {
69
74
  await cleanupGuardian(fixture)
70
75
  }
@@ -79,7 +84,7 @@ test("guardian preserves a custom activation timeout in the registered process d
79
84
 
80
85
  try {
81
86
  await processInstance.start()
82
- await assert.rejects(() => processInstance.activateStrict(), /activate command timed out after 10ms/i)
87
+ await expect(processInstance.activateStrict()).rejects.toThrow(/activate command timed out after 10ms/i)
83
88
  } finally {
84
89
  await cleanupGuardian(fixture)
85
90
  }
@@ -112,20 +117,20 @@ test("client reactivates a retained process through a guardian without the react
112
117
  await processInstance.quiesceStrict()
113
118
  await processInstance.reactivateStrict()
114
119
 
115
- assert.equal(processInstance.status().pid, pid)
116
- assert.equal(processInstance.status().state, "running")
117
- assert.equal(processInstance.status().lifecycleRole, "active")
118
- assert.equal(await fs.readFile(lifecyclePath, "utf8"), "activate\nretire\nactivate\n")
120
+ expect(processInstance.status().pid).toBe(pid)
121
+ expect(processInstance.status().state).toBe("running")
122
+ expect(processInstance.status().lifecycleRole).toBe("active")
123
+ expect(await fs.readFile(lifecyclePath, "utf8")).toBe("activate\nretire\nactivate\n")
119
124
 
120
125
  const restarted = once(processInstance, "started")
121
126
 
122
- assert.ok(pid)
127
+ if (!pid) throw new Error("Missing required fixture: pid")
123
128
  process.kill(-pid, "SIGKILL")
124
129
  await restarted
125
- assert.notEqual(processInstance.status().pid, pid)
126
- assert.equal(processInstance.status().state, "running")
127
- assert.equal(processInstance.status().lifecycleRole, "active")
128
- assert.equal(await fs.readFile(lifecyclePath, "utf8"), "activate\nretire\nactivate\nactivate\n")
130
+ expect(processInstance.status().pid).not.toBe(pid)
131
+ expect(processInstance.status().state).toBe("running")
132
+ expect(processInstance.status().lifecycleRole).toBe("active")
133
+ expect(await fs.readFile(lifecyclePath, "utf8")).toBe("activate\nretire\nactivate\nactivate\n")
129
134
  } finally {
130
135
  await cleanupGuardian(fixture)
131
136
  }
@@ -157,10 +162,10 @@ test("client reverses a worker quiet hook through a pre-reactivation guardian",
157
162
  await processInstance.quiesceStrict()
158
163
  await processInstance.reactivateStrict()
159
164
 
160
- assert.equal(processInstance.status().pid, pid)
161
- assert.equal(processInstance.status().state, "running")
162
- assert.equal(processInstance.status().lifecycleRole, "active")
163
- assert.equal(await fs.readFile(lifecyclePath, "utf8"), "quiet\nresume\n")
165
+ expect(processInstance.status().pid).toBe(pid)
166
+ expect(processInstance.status().state).toBe("running")
167
+ expect(processInstance.status().lifecycleRole).toBe("active")
168
+ expect(await fs.readFile(lifecyclePath, "utf8")).toBe("quiet\nresume\n")
164
169
  } finally {
165
170
  await cleanupGuardian(fixture)
166
171
  }
@@ -178,8 +183,8 @@ test("guardian atomically updates process provenance with private owner state",
178
183
  const previousProvenance = (await fixture.client.inventory())[0]?.provenance
179
184
 
180
185
  await processInstance.updateDefinition({...definition("service"), env: {RELEASE: "v2"}}, nextOwnerState)
181
- assert.deepEqual(await fixture.client.ownerState(), nextOwnerState)
182
- assert.notEqual((await fixture.client.inventory())[0]?.provenance, previousProvenance)
186
+ expect(await fixture.client.ownerState()).toEqual(nextOwnerState)
187
+ expect((await fixture.client.inventory())[0]?.provenance).not.toBe(previousProvenance)
183
188
  } finally {
184
189
  await cleanupGuardian(fixture)
185
190
  }
@@ -201,9 +206,10 @@ test("guardian forwards each retained output line to its exact process proxy", a
201
206
  const [entry] = await Promise.race([logged, exitedFirst])
202
207
  const event = await forwarded
203
208
 
204
- assert.equal(entry.line, marker)
205
- assert.equal(event.status, undefined, "log events must not resend the complete retained process status")
206
- assert.ok(processInstance.status().logs.some((candidate) => candidate.line === marker))
209
+ expect(entry.line).toBe(marker)
210
+ // Log events must not resend the complete retained process status.
211
+ expect(event.status).toBe(undefined)
212
+ expect(processInstance.status().logs.some((candidate) => candidate.line === marker)).toBeTruthy()
207
213
  } finally {
208
214
  await cleanupGuardian(fixture)
209
215
  }
@@ -219,12 +225,12 @@ test("guardian delivers the final process status after dropping logs for a backp
219
225
  })
220
226
  const socket = fixture.client.socket
221
227
 
222
- assert.ok(socket)
228
+ if (!socket) throw new Error("Missing required fixture: socket")
223
229
  try {
224
230
  await processInstance.start()
225
231
  const pid = processInstance.status().pid
226
232
 
227
- assert.ok(pid)
233
+ if (!pid) throw new Error("Missing required fixture: pid")
228
234
  const finalStatus = fixture.client.waitForEvent("process")
229
235
 
230
236
  socket.pause()
@@ -242,7 +248,7 @@ test("guardian delivers the final process status after dropping logs for a backp
242
248
  } finally {
243
249
  clearTimeout(timeout)
244
250
  }
245
- assert.equal(processInstance.status().state, "failed")
251
+ expect(processInstance.status().state).toBe("failed")
246
252
  } finally {
247
253
  socket.resume()
248
254
  await cleanupGuardian(fixture)
@@ -262,7 +268,7 @@ test("guardian resynchronizes retained logs after dropping output for a backpres
262
268
  })
263
269
  const socket = fixture.client.socket
264
270
 
265
- assert.ok(socket)
271
+ if (!socket) throw new Error("Missing required fixture: socket")
266
272
  try {
267
273
  await processInstance.start()
268
274
  socket.pause()
@@ -282,7 +288,7 @@ test("guardian resynchronizes retained logs after dropping output for a backpres
282
288
  } finally {
283
289
  clearTimeout(timeout)
284
290
  }
285
- assert.equal(processInstance.status().logs.at(-1)?.line, finalLine)
291
+ expect(processInstance.status().logs.at(-1)?.line).toBe(finalLine)
286
292
  } finally {
287
293
  socket.resume()
288
294
  await cleanupGuardian(fixture)
@@ -298,13 +304,14 @@ test("guardian shutdown reports an exact owned process stop failure", async () =
298
304
  await processInstance.start()
299
305
  const [entry] = await fixture.client.inventory()
300
306
 
301
- assert.ok(entry)
307
+ if (!entry) throw new Error("Missing required fixture: entry")
302
308
  fixture.client.disconnect()
303
309
  await replacement.connect()
304
310
  await replacement.claimOwner(0, null)
305
- await assert.rejects(() => replacement.reconcileInventory(), /NOT_A_SIGNAL|Unknown signal/)
306
- assert.deepEqual((await replacement.inventory()).map(({key, provenance}) => ({key, provenance})), [{key: entry.key, provenance: entry.provenance}], "failed reconciliation must retain the exact registration")
307
- await assert.rejects(() => replacement.shutdown(), /NOT_A_SIGNAL|Unknown signal/)
311
+ await expect(replacement.reconcileInventory()).rejects.toThrow(/NOT_A_SIGNAL|Unknown signal/)
312
+ // Failed reconciliation must retain the exact registration.
313
+ expect((await replacement.inventory()).map(({key, provenance}) => ({key, provenance}))).toEqual([{key: entry.key, provenance: entry.provenance}])
314
+ await expect(replacement.shutdown()).rejects.toThrow(/NOT_A_SIGNAL|Unknown signal/)
308
315
  } finally {
309
316
  const inventory = await replacement.inventory().catch(() => [])
310
317
  const entry = inventory[0]
@@ -327,8 +334,8 @@ test("successful guardian shutdown closes an authenticated waiting contender bef
327
334
 
328
335
  try {
329
336
  await contender.connect()
330
- assert.ok(fixture.client.socket)
331
- assert.ok(contender.socket)
337
+ if (!fixture.client.socket) throw new Error("Missing required fixture: fixture.client.socket")
338
+ if (!contender.socket) throw new Error("Missing required fixture: contender.socket")
332
339
  const shutdownOrder = /** @type {string[]} */ ([])
333
340
  const onData = fixture.client.onData.bind(fixture.client)
334
341
 
@@ -339,14 +346,17 @@ test("successful guardian shutdown closes an authenticated waiting contender bef
339
346
  fixture.client.socket.once("close", () => shutdownOrder.push("caller-close"))
340
347
  const contenderClosed = new Promise((resolve) => contender.socket?.once("close", () => resolve(undefined)))
341
348
  const waitingClaim = contender.claimOwner(250, null)
342
- const rejectedClaim = assert.rejects(waitingClaim, /connection closed/)
349
+ const rejectedClaim = (async () => {
350
+ await expect(waitingClaim).rejects.toThrow(/connection closed/)
351
+ })()
343
352
 
344
353
  await fixture.client.shutdown()
345
354
  await rejectedClaim
346
355
  await contenderClosed
347
- assert.deepEqual(shutdownOrder, ["response", "caller-close"], "shutdown success must be received before the caller connection closes")
348
- await assert.rejects(() => contender.inventory(), /not connected/)
349
- await assert.rejects(fs.access(fixture.client.socketPath), {code: "ENOENT"})
356
+ // Shutdown success must be received before the caller connection closes.
357
+ expect(shutdownOrder).toEqual(["response", "caller-close"])
358
+ await expect(contender.inventory()).rejects.toThrow(/not connected/)
359
+ await expect(fs.access(fixture.client.socketPath)).rejects.toMatchObject({code: "ENOENT"})
350
360
  await fixture.client.guardianExit()
351
361
  } finally {
352
362
  contender.disconnect()
@@ -370,7 +380,7 @@ test("guardian restart uses the latest accepted command and exact environment",
370
380
 
371
381
  const [restart] = await waitForRestartRecords(markerPath, 1)
372
382
 
373
- assert.deepEqual({home: restart.home, marker: restart.marker}, {home: null, marker: "new"})
383
+ expect({home: restart.home, marker: restart.marker}).toEqual({home: null, marker: "new"})
374
384
  } finally {
375
385
  await reconnectAndShutdownGuardian(fixture, authority)
376
386
  }
@@ -390,7 +400,7 @@ test("guardian rearms recovery after an ownerless replacement aborts", async ()
390
400
 
391
401
  await new Promise((resolve) => setTimeout(resolve, 80))
392
402
  await replacement.abortOwnerReplacement(prepared.replacementId)
393
- assert.deepEqual((await waitForRestartRecords(markerPath, 1)).map(({home, marker}) => ({home, marker})), [{home: null, marker: "accepted"}])
403
+ expect((await waitForRestartRecords(markerPath, 1)).map(({home, marker}) => ({home, marker}))).toEqual([{home: null, marker: "accepted"}])
394
404
  } finally {
395
405
  replacement.disconnect()
396
406
  await reconnectAndShutdownGuardian(fixture, authority)
@@ -407,9 +417,9 @@ test("guardian retry backoff remains nonzero when reconnect grace is zero", asyn
407
417
  fixture.client.disconnect()
408
418
  const records = await waitForRestartRecords(markerPath, 2)
409
419
 
410
- assert.equal(typeof records[0]?.at, "number")
411
- assert.equal(typeof records[1]?.at, "number")
412
- assert.ok(Number(records[1].at) - Number(records[0].at) >= 900, `failed owner recovery retried after ${Number(records[1].at) - Number(records[0].at)}ms`)
420
+ expect(typeof records[0]?.at).toBe("number")
421
+ expect(typeof records[1]?.at).toBe("number")
422
+ expect({value: Boolean(Number(records[1].at) - Number(records[0].at) >= 900), context: `failed owner recovery retried after ${Number(records[1].at) - Number(records[0].at)}ms`}).toMatchObject({value: true})
413
423
  } finally {
414
424
  await reconnectAndShutdownGuardian(fixture, authority)
415
425
  }
@@ -436,7 +446,7 @@ test("a retired guardian-started owner does not block recovery of its replacemen
436
446
  await committed
437
447
  replacement.disconnect()
438
448
 
439
- assert.deepEqual((await waitForRestartRecords(secondMarkerPath, 1)).map(({home, marker}) => ({home, marker})), [{home: null, marker: "replacement"}])
449
+ expect((await waitForRestartRecords(secondMarkerPath, 1)).map(({home, marker}) => ({home, marker}))).toEqual([{home: null, marker: "replacement"}])
440
450
  } finally {
441
451
  replacement.disconnect()
442
452
  if (firstOwnerPid) {
@@ -559,7 +569,7 @@ test("guardian backs off when a restarted owner exits after claiming but before
559
569
  const records = await waitForRestartRecords(startedLogPath, 2)
560
570
 
561
571
  await waitForProcessExit(descendantPid)
562
- assert.ok(Number(records[1].at) - Number(records[0].at) >= 900, `post-claim failure retried after ${Number(records[1].at) - Number(records[0].at)}ms`)
572
+ expect({value: Boolean(Number(records[1].at) - Number(records[0].at) >= 900), context: `post-claim failure retried after ${Number(records[1].at) - Number(records[0].at)}ms`}).toMatchObject({value: true})
563
573
  } finally {
564
574
  if (descendantPid) {
565
575
  try { process.kill(descendantPid, "SIGKILL") } catch (_error) { /* Exact descendant already exited. */ }
@@ -590,7 +600,7 @@ test("guardian preserves restart backoff when an unready owner disconnect aborts
590
600
  await aborted
591
601
  const records = await waitForRestartRecords(startedLogPath, 2)
592
602
 
593
- assert.ok(Number(records[1].at) - Number(records[0].at) >= 900, `replacement abort retried after ${Number(records[1].at) - Number(records[0].at)}ms`)
603
+ expect({value: Boolean(Number(records[1].at) - Number(records[0].at) >= 900), context: `replacement abort retried after ${Number(records[1].at) - Number(records[0].at)}ms`}).toMatchObject({value: true})
594
604
  } finally {
595
605
  replacement.disconnect()
596
606
  await reconnectAndShutdownGuardian(fixture, authority)
@@ -629,7 +639,8 @@ test("replacement commit preserves a claimed guardian restart child through list
629
639
  process.kill(incumbentPid, "SIGUSR2")
630
640
  await Promise.race([waitForFileText(committedPath, new RegExp(prepared.replacementId)), published])
631
641
  await new Promise((resolve) => setTimeout(resolve, 50))
632
- assert.doesNotThrow(() => process.kill(incumbentPid, 0), "claimed incumbent must survive replacement commit until its listeners retire")
642
+ // The claimed incumbent must survive replacement commit until its listeners retire.
643
+ await expect(() => process.kill(incumbentPid, 0)).not.toThrow()
633
644
 
634
645
  process.kill(incumbentPid, "SIGUSR1")
635
646
  await published
@@ -670,7 +681,7 @@ test("ownerless replacement commit kills a superseded restart candidate", async
670
681
  await committed
671
682
  replacement.disconnect()
672
683
 
673
- assert.deepEqual((await waitForRestartRecords(secondMarkerPath, 1)).map(({home, marker}) => ({home, marker})), [{home: null, marker: "committed"}])
684
+ expect((await waitForRestartRecords(secondMarkerPath, 1)).map(({home, marker}) => ({home, marker}))).toEqual([{home: null, marker: "committed"}])
674
685
  } finally {
675
686
  replacement.disconnect()
676
687
  if (delayedOwnerPid) {
@@ -708,9 +719,10 @@ test("guardian logs an asynchronous daemon spawn failure before retrying", async
708
719
  const diagnosticPattern = /"code":"ENOENT".*"message":"guardian failed to restart daemon"/
709
720
  const diagnostic = await waitForFileText(logPath, diagnosticPattern)
710
721
 
711
- assert.match(diagnostic, diagnosticPattern)
722
+ expect(diagnostic).toMatch(diagnosticPattern)
712
723
  for (const privateValue of [privateArgs, privateEnvironment, privateExecutable, fixture.root, logPath]) {
713
- assert.ok(!diagnostic.includes(privateValue), `guardian diagnostic exposed ${privateValue}`)
724
+ // Guardian diagnostics must not expose private values, including in assertion failures.
725
+ expect(diagnostic.includes(privateValue)).toBe(false)
714
726
  }
715
727
  } finally {
716
728
  await reconnectAndShutdownGuardian(fixture, authority)
@@ -744,8 +756,8 @@ test("guardian publishes the authenticated ready owner's PID file", async () =>
744
756
  await fs.symlink(victimPath, pidPath)
745
757
  await fixture.client.ownerReady()
746
758
 
747
- assert.equal(await fs.readFile(pidPath, "utf8"), `${process.pid}\n`)
748
- assert.equal(await fs.readFile(victimPath, "utf8"), "unchanged\n")
759
+ expect(await fs.readFile(pidPath, "utf8")).toBe(`${process.pid}\n`)
760
+ expect(await fs.readFile(victimPath, "utf8")).toBe("unchanged\n")
749
761
  } finally {
750
762
  await cleanupGuardian(fixture)
751
763
  }
@@ -767,14 +779,15 @@ test("replacement commit notification waits for incumbent listener retirement",
767
779
 
768
780
  await fixture.client.commitOwnerReplacement(prepared.replacementId)
769
781
  await new Promise((resolve) => setImmediate(resolve))
770
- assert.equal(committed, false, "candidate publication must remain fenced while incumbent listener retirement is delayed")
782
+ // Candidate publication must remain fenced while incumbent listener retirement is delayed.
783
+ expect(committed).toBe(false)
771
784
  const listenersRetired = candidate.waitForEvent("replacement-listeners-retired")
772
785
 
773
786
  await fixture.client.completeOwnerListenerRetirement(prepared.replacementId)
774
787
  await listenersRetired
775
788
  await fixture.client.request({command: "finalize-owner-replacement", replacementId: prepared.replacementId})
776
789
  await notification
777
- assert.equal(committed, true)
790
+ expect(committed).toBe(true)
778
791
  await candidate.shutdown()
779
792
  await fixture.client.guardianExit()
780
793
  } finally {
@@ -783,7 +796,7 @@ test("replacement commit notification waits for incumbent listener retirement",
783
796
  }
784
797
  })
785
798
 
786
- test("completed direct listener retirement finalizes when the incumbent disconnects", {timeout: 3000}, async () => {
799
+ test("completed direct listener retirement finalizes when the incumbent disconnects", {timeoutMs: 3000}, async () => {
787
800
  const fixture = await createGuardian()
788
801
  const candidate = new GuardianClient({socketPath: fixture.client.socketPath, token: fixture.token})
789
802
  const authority = {configDigest: "incumbent", runtime: null}
@@ -802,7 +815,7 @@ test("completed direct listener retirement finalizes when the incumbent disconne
802
815
  fixture.client.disconnect()
803
816
  await committed
804
817
  await candidate.finalizeOwnerReplacement(prepared.replacementId)
805
- assert.deepEqual(await candidate.replacementStatus(), {
818
+ expect(await candidate.replacementStatus()).toEqual({
806
819
  committedReplacementId: prepared.replacementId,
807
820
  ownerClaimed: true,
808
821
  retirementFailed: false,
@@ -829,14 +842,11 @@ test("replacement staging rejects owner state published after prepare", async ()
829
842
  const prepared = await candidate.prepareOwnerReplacement(authority, nextAuthority)
830
843
 
831
844
  await fixture.client.publishOwnerState({authority, snapshot: {activeReleaseId: "v2"}})
832
- await assert.rejects(
833
- () => candidate.stageOwnerReplacement(prepared.replacementId, {authority: nextAuthority, snapshot: {activeReleaseId: "v1"}}),
834
- /owner state changed after prepare/i
835
- )
845
+ await expect(candidate.stageOwnerReplacement(prepared.replacementId, {authority: nextAuthority, snapshot: {activeReleaseId: "v1"}})).rejects.toThrow(/owner state changed after prepare/i)
836
846
  await candidate.abortOwnerReplacement(prepared.replacementId)
837
847
  const fresh = await candidate.prepareOwnerReplacement(authority, nextAuthority)
838
848
 
839
- assert.deepEqual(fresh.ownerState, {authority, snapshot: {activeReleaseId: "v2"}})
849
+ expect(fresh.ownerState).toEqual({authority, snapshot: {activeReleaseId: "v2"}})
840
850
  await candidate.abortOwnerReplacement(fresh.replacementId)
841
851
  await fixture.client.shutdown()
842
852
  await fixture.client.guardianExit()
@@ -846,7 +856,7 @@ test("replacement staging rejects owner state published after prepare", async ()
846
856
  }
847
857
  })
848
858
 
849
- test("staged replacement receives cleared local sources when the incumbent disconnects", {timeout: 3000}, async () => {
859
+ test("staged replacement receives cleared local sources when the incumbent disconnects", {timeoutMs: 3000}, async () => {
850
860
  const fixture = await createGuardian()
851
861
  const candidate = new GuardianClient({socketPath: fixture.client.socketPath, token: fixture.token})
852
862
  const authority = {configDigest: "incumbent", runtime: null}
@@ -873,7 +883,7 @@ test("staged replacement receives cleared local sources when the incumbent disco
873
883
  const committed = candidate.waitForEvent("replacement-committed")
874
884
 
875
885
  fixture.client.disconnect()
876
- assert.deepEqual(await cleared, {
886
+ expect(await cleared).toEqual({
877
887
  connections: {http: 0, websocket: 0},
878
888
  event: "owner-connection-state",
879
889
  releaseId: "v1",
@@ -882,7 +892,7 @@ test("staged replacement receives cleared local sources when the incumbent disco
882
892
  await committed
883
893
  const state = /** @type {{listenerConnectionSources?: Record<string, Record<string, {http: number, websocket: number}>>}} */ (await candidate.ownerState())
884
894
 
885
- assert.deepEqual(state.listenerConnectionSources, {})
895
+ expect(state.listenerConnectionSources).toEqual({})
886
896
  await candidate.shutdown()
887
897
  await fixture.client.guardianExit()
888
898
  } finally {
@@ -891,7 +901,7 @@ test("staged replacement receives cleared local sources when the incumbent disco
891
901
  }
892
902
  })
893
903
 
894
- test("staged successor state receives tombstones when an older completed listener disconnects", {timeout: 3000}, async () => {
904
+ test("staged successor state receives tombstones when an older completed listener disconnects", {timeoutMs: 3000}, async () => {
895
905
  const fixture = await createGuardian()
896
906
  const candidate = new GuardianClient({socketPath: fixture.client.socketPath, token: fixture.token})
897
907
  const successor = new GuardianClient({socketPath: fixture.client.socketPath, token: fixture.token})
@@ -933,12 +943,12 @@ test("staged successor state receives tombstones when an older completed listene
933
943
  fixture.client.disconnect()
934
944
  const tombstone = {connections: {http: 0, websocket: 0}, event: "owner-connection-state", releaseId: "v1", sourceId: "retired-local"}
935
945
 
936
- assert.deepEqual(await Promise.all([sourceCleared, stagedSourceCleared]), [tombstone, tombstone])
946
+ expect(await Promise.all([sourceCleared, stagedSourceCleared])).toEqual([tombstone, tombstone])
937
947
 
938
948
  await candidate.commitOwnerReplacement(second.replacementId)
939
949
  const successorState = /** @type {{listenerConnectionSources?: Record<string, Record<string, {http: number, websocket: number}>>}} */ (await successor.ownerState())
940
950
 
941
- assert.deepEqual(successorState.listenerConnectionSources, {})
951
+ expect(successorState.listenerConnectionSources).toEqual({})
942
952
  const secondCommitted = successor.waitForEvent("replacement-committed")
943
953
  const secondListenersRetired = successor.waitForEvent("replacement-listeners-retired")
944
954
 
@@ -969,7 +979,7 @@ test("replacement abort notifies both the candidate and committed owner", async
969
979
  const candidateAborted = candidate.waitForEvent("replacement-aborted")
970
980
 
971
981
  await candidate.abortOwnerReplacement(prepared.replacementId)
972
- assert.deepEqual(await Promise.all([incumbentAborted, candidateAborted]), [
982
+ expect(await Promise.all([incumbentAborted, candidateAborted])).toEqual([
973
983
  {event: "replacement-aborted", reason: "Replacement candidate aborted the prepared transaction"},
974
984
  {event: "replacement-aborted", reason: "Replacement candidate aborted the prepared transaction"}
975
985
  ])
@@ -988,7 +998,7 @@ test("retired owner replacement commit carries its exact recovered process key",
988
998
 
989
999
  client.request = async (request) => {
990
1000
  if (!request.key) throw new Error(`Guardian ${request.command} requires a process key`)
991
- assert.deepEqual(request, {command: "commit-retired-owner-replacement", key: processKey, replacementId})
1001
+ expect(request).toEqual({command: "commit-retired-owner-replacement", key: processKey, replacementId})
992
1002
  return {committed: true}
993
1003
  }
994
1004
 
@@ -999,7 +1009,7 @@ test("guardian owner-replacement capability classification is explicit and fail
999
1009
  const current = new GuardianClient({socketPath: "/unused", token: "authenticated-capability"})
1000
1010
 
1001
1011
  current.request = async (request) => {
1002
- assert.deepEqual(request, {command: "owner-replacement-capabilities"})
1012
+ expect(request).toEqual({command: "owner-replacement-capabilities"})
1003
1013
  return {
1004
1014
  commands: ["commit-retired-owner-replacement", "future-command"],
1005
1015
  futureField: {supported: true},
@@ -1007,7 +1017,7 @@ test("guardian owner-replacement capability classification is explicit and fail
1007
1017
  version: 2
1008
1018
  }
1009
1019
  }
1010
- assert.equal(await current.ownerReplacementProtocol(), "atomic")
1020
+ expect(await current.ownerReplacementProtocol()).toBe("atomic")
1011
1021
 
1012
1022
  for (const [commitDiagnostic, expected] of [
1013
1023
  ["Owner replacement transaction is not the prepared candidate", "atomic"],
@@ -1022,8 +1032,8 @@ test("guardian owner-replacement capability classification is explicit and fail
1022
1032
  if (request.command === "owner-replacement-capabilities") throw new Error("Guardian owner-replacement-capabilities requires a process key")
1023
1033
  throw new Error(commitDiagnostic)
1024
1034
  }
1025
- assert.equal(await older.ownerReplacementProtocol(), expected)
1026
- assert.deepEqual(requests, [
1035
+ expect(await older.ownerReplacementProtocol()).toBe(expected)
1036
+ expect(requests).toEqual([
1027
1037
  {command: "owner-replacement-capabilities"},
1028
1038
  {command: "commit-retired-owner-replacement", replacementId: "owner-replacement-capability-probe"}
1029
1039
  ])
@@ -1039,7 +1049,7 @@ test("guardian owner-replacement capability classification is explicit and fail
1039
1049
  const ambiguous = new GuardianClient({socketPath: "/unused", token: "authenticated-capability"})
1040
1050
 
1041
1051
  ambiguous.request = fixture
1042
- await assert.rejects(() => ambiguous.ownerReplacementProtocol(), /invalid owner-replacement capability response|ambiguous retired-owner capability response/)
1052
+ await expect(ambiguous.ownerReplacementProtocol()).rejects.toThrow(/invalid owner-replacement capability response|ambiguous retired-owner capability response/)
1043
1053
  }
1044
1054
  })
1045
1055
 
@@ -1052,14 +1062,11 @@ test("reserved process recovery rejects a reconstructed definition with differen
1052
1062
  await fixture.client.process(processKey, definition("worker")).recover()
1053
1063
  const [registration] = await fixture.client.inventory()
1054
1064
 
1055
- assert.ok(registration)
1065
+ if (!registration) throw new Error("Missing required fixture: registration")
1056
1066
  await candidate.connect()
1057
1067
  candidate.reserveProcessRecovery(processKey, registration.provenance)
1058
- await assert.rejects(
1059
- () => candidate.process(processKey, definition("different-worker")).recover(),
1060
- /provenance mismatch for reserved process/
1061
- )
1062
- assert.deepEqual((await fixture.client.inventory()).map(({key}) => key), [processKey])
1068
+ await expect(candidate.process(processKey, definition("different-worker")).recover()).rejects.toThrow(/provenance mismatch for reserved process/)
1069
+ expect((await fixture.client.inventory()).map(({key}) => key)).toEqual([processKey])
1063
1070
  } finally {
1064
1071
  candidate.disconnect()
1065
1072
  await cleanupGuardian(fixture)
@@ -1092,10 +1099,7 @@ test("retired owner replacement rejects a registered process absent from committ
1092
1099
  const prepared = await candidate.prepareOwnerReplacement(authority, authority)
1093
1100
 
1094
1101
  await candidate.stageOwnerReplacement(prepared.replacementId, {authority, snapshot: candidateSnapshot})
1095
- await assert.rejects(
1096
- () => candidate.commitRetiredOwnerReplacement(prepared.replacementId, candidateProcessKey),
1097
- /process .* does not belong to the committed owner/
1098
- )
1102
+ await expect(candidate.commitRetiredOwnerReplacement(prepared.replacementId, candidateProcessKey)).rejects.toThrow(/process .* does not belong to the committed owner/)
1099
1103
  } finally {
1100
1104
  candidate.disconnect()
1101
1105
  await cleanupGuardian(fixture)
@@ -1125,14 +1129,14 @@ test("retired owner replacement requires unchanged authority and the exact contr
1125
1129
  const changed = await candidate.prepareOwnerReplacement(authority, nextAuthority)
1126
1130
 
1127
1131
  await candidate.stageOwnerReplacement(changed.replacementId, {authority: nextAuthority, snapshot})
1128
- await assert.rejects(() => candidate.commitRetiredOwnerReplacement(changed.replacementId, processKey), /unchanged owner authority/)
1132
+ await expect(candidate.commitRetiredOwnerReplacement(changed.replacementId, processKey)).rejects.toThrow(/unchanged owner authority/)
1129
1133
  await candidate.abortOwnerReplacement(changed.replacementId)
1130
1134
 
1131
1135
  const occupied = await candidate.prepareOwnerReplacement(authority, authority)
1132
1136
 
1133
1137
  await candidate.stageOwnerReplacement(occupied.replacementId, {authority, snapshot})
1134
1138
  await fs.writeFile(controlPath, "occupied\n")
1135
- await assert.rejects(() => candidate.commitRetiredOwnerReplacement(occupied.replacementId, processKey), /control socket .* still exists/)
1139
+ await expect(candidate.commitRetiredOwnerReplacement(occupied.replacementId, processKey)).rejects.toThrow(/control socket .* still exists/)
1136
1140
  await candidate.abortOwnerReplacement(occupied.replacementId)
1137
1141
 
1138
1142
  await fs.rm(controlPath)
@@ -1140,34 +1144,22 @@ test("retired owner replacement requires unchanged authority and the exact contr
1140
1144
 
1141
1145
  await candidate.stageOwnerReplacement(ready.replacementId, {authority, snapshot})
1142
1146
  await contender.connect()
1143
- await assert.rejects(
1144
- () => contender.request({command: "commit-retired-owner-replacement", key: processKey, replacementId: ready.replacementId}),
1145
- /not the prepared candidate/
1146
- )
1147
- await assert.rejects(
1148
- () => candidate.commitRetiredOwnerReplacement("stale-replacement", processKey),
1149
- /not the prepared candidate/
1150
- )
1151
- await assert.rejects(
1152
- () => candidate.commitRetiredOwnerReplacement(ready.replacementId, "release:v1:wrong"),
1153
- /process .* is not registered/
1154
- )
1147
+ await expect(contender.request({command: "commit-retired-owner-replacement", key: processKey, replacementId: ready.replacementId})).rejects.toThrow(/not the prepared candidate/)
1148
+ await expect(candidate.commitRetiredOwnerReplacement("stale-replacement", processKey)).rejects.toThrow(/not the prepared candidate/)
1149
+ await expect(candidate.commitRetiredOwnerReplacement(ready.replacementId, "release:v1:wrong")).rejects.toThrow(/process .* is not registered/)
1155
1150
  const handoffRequested = fixture.client.waitForEvent("replacement-listener-handoff-requested")
1156
1151
 
1157
1152
  void handoffRequested.catch(() => undefined)
1158
1153
 
1159
1154
  await candidate.prepareRetiredOwnerListenerHandoff(ready.replacementId, processKey)
1160
1155
  await handoffRequested
1161
- await assert.rejects(
1162
- () => contender.prepareOwnerReplacement(authority, authority),
1163
- /listener retirement is pending/
1164
- )
1156
+ await expect(contender.prepareOwnerReplacement(authority, authority)).rejects.toThrow(/listener retirement is pending/)
1165
1157
  const committed = candidate.waitForEvent("replacement-committed")
1166
1158
  const listenersRetired = candidate.waitForEvent("replacement-listeners-retired")
1167
1159
  const connectionState = candidate.waitForEvent("owner-connection-state")
1168
1160
 
1169
1161
  await fixture.client.publishOwnerConnectionState(ready.replacementId, "listener-a", "v1", {http: 1, websocket: 2}, true)
1170
- assert.deepEqual(await connectionState, {connections: {http: 1, websocket: 2}, event: "owner-connection-state", releaseId: "v1", sourceId: "listener-a"})
1162
+ expect(await connectionState).toEqual({connections: {http: 1, websocket: 2}, event: "owner-connection-state", releaseId: "v1", sourceId: "listener-a"})
1171
1163
  await fixture.client.completeOwnerListenerRetirement(ready.replacementId)
1172
1164
  await listenersRetired
1173
1165
  const retirementRequested = fixture.client.waitForEvent("replacement-retirement-requested")
@@ -1221,7 +1213,7 @@ test("completed listener retirement survives owner recovery and clears a crashed
1221
1213
  const initial = candidate.waitForEvent("owner-connection-state")
1222
1214
 
1223
1215
  await fixture.client.publishOwnerConnectionState(prepared.replacementId, "listener-a", "v1", {http: 0, websocket: 1}, true)
1224
- assert.deepEqual(await initial, {connections: {http: 0, websocket: 1}, event: "owner-connection-state", releaseId: "v1", sourceId: "listener-a"})
1216
+ expect(await initial).toEqual({connections: {http: 0, websocket: 1}, event: "owner-connection-state", releaseId: "v1", sourceId: "listener-a"})
1225
1217
  const listenersRetired = candidate.waitForEvent("replacement-listeners-retired")
1226
1218
 
1227
1219
  await fixture.client.completeOwnerListenerRetirement(prepared.replacementId)
@@ -1232,9 +1224,9 @@ test("completed listener retirement survives owner recovery and clears a crashed
1232
1224
  await recovered.claimOwner(1000, authority)
1233
1225
  const stateAfterCandidateCrash = /** @type {{listenerConnectionSources?: Record<string, Record<string, {http: number, websocket: number}>>}} */ (await recovered.ownerState())
1234
1226
 
1235
- assert.equal(stateAfterCandidateCrash.listenerConnectionSources?.["candidate-local"], undefined)
1236
- assert.deepEqual(stateAfterCandidateCrash.listenerConnectionSources?.["listener-a"], {v1: {http: 0, websocket: 1}})
1237
- assert.deepEqual(await recovered.replacementStatus(), {
1227
+ expect(stateAfterCandidateCrash.listenerConnectionSources?.["candidate-local"]).toBe(undefined)
1228
+ expect(stateAfterCandidateCrash.listenerConnectionSources?.["listener-a"]).toEqual({v1: {http: 0, websocket: 1}})
1229
+ expect(await recovered.replacementStatus()).toEqual({
1238
1230
  committedReplacementId: prepared.replacementId,
1239
1231
  ownerClaimed: true,
1240
1232
  retirementFailed: false,
@@ -1245,17 +1237,17 @@ test("completed listener retirement survives owner recovery and clears a crashed
1245
1237
  const cleared = recovered.waitForEvent("owner-connection-state")
1246
1238
 
1247
1239
  fixture.client.disconnect()
1248
- assert.deepEqual(await Promise.race([
1240
+ expect(await Promise.race([
1249
1241
  cleared,
1250
1242
  new Promise((_, reject) => {
1251
1243
  const timer = setTimeout(() => reject(new Error("Recovered owner did not receive the retired source tombstone")), 500)
1252
1244
 
1253
1245
  timer.unref()
1254
1246
  })
1255
- ]), {connections: {http: 0, websocket: 0}, event: "owner-connection-state", releaseId: "v1", sourceId: "listener-a"})
1247
+ ])).toEqual({connections: {http: 0, websocket: 0}, event: "owner-connection-state", releaseId: "v1", sourceId: "listener-a"})
1256
1248
  const recoveredOwnerState = /** @type {{listenerConnectionSources?: Record<string, Record<string, {http: number, websocket: number}>>}} */ (await recovered.ownerState())
1257
1249
 
1258
- assert.deepEqual(recoveredOwnerState.listenerConnectionSources, {})
1250
+ expect(recoveredOwnerState.listenerConnectionSources).toEqual({})
1259
1251
  await recovered.publishOwnerState({
1260
1252
  authority,
1261
1253
  listenerConnectionSources: {"listener-a": {v1: {http: 0, websocket: 1}}},
@@ -1264,7 +1256,7 @@ test("completed listener retirement survives owner recovery and clears a crashed
1264
1256
  })
1265
1257
  const afterStalePublication = /** @type {{listenerConnectionSources?: Record<string, Record<string, {http: number, websocket: number}>>}} */ (await recovered.ownerState())
1266
1258
 
1267
- assert.deepEqual(afterStalePublication.listenerConnectionSources, {})
1259
+ expect(afterStalePublication.listenerConnectionSources).toEqual({})
1268
1260
  const recoveredProcess = recovered.process(processKey, definition("worker"))
1269
1261
 
1270
1262
  await recoveredProcess.recover()
@@ -1276,7 +1268,7 @@ test("completed listener retirement survives owner recovery and clears a crashed
1276
1268
  })
1277
1269
  const afterStaleProcessUpdate = /** @type {{listenerConnectionSources?: Record<string, Record<string, {http: number, websocket: number}>>}} */ (await recovered.ownerState())
1278
1270
 
1279
- assert.deepEqual(afterStaleProcessUpdate.listenerConnectionSources, {})
1271
+ expect(afterStaleProcessUpdate.listenerConnectionSources).toEqual({})
1280
1272
  await recovered.shutdown()
1281
1273
  await fixture.client.guardianExit()
1282
1274
  } finally {
@@ -1303,7 +1295,7 @@ test("direct retired-listener source relay survives committed owner recovery", a
1303
1295
  const initial = candidate.waitForEvent("owner-connection-state")
1304
1296
 
1305
1297
  await fixture.client.publishOwnerConnectionState(prepared.replacementId, "direct-listener", "v1", {http: 0, websocket: 1}, true)
1306
- assert.deepEqual(await initial, {connections: {http: 0, websocket: 1}, event: "owner-connection-state", releaseId: "v1", sourceId: "direct-listener"})
1298
+ expect(await initial).toEqual({connections: {http: 0, websocket: 1}, event: "owner-connection-state", releaseId: "v1", sourceId: "direct-listener"})
1307
1299
  const listenersRetired = candidate.waitForEvent("replacement-listeners-retired")
1308
1300
 
1309
1301
  await fixture.client.completeOwnerListenerRetirement(prepared.replacementId)
@@ -1315,7 +1307,7 @@ test("direct retired-listener source relay survives committed owner recovery", a
1315
1307
  const cleared = recovered.waitForEvent("owner-connection-state")
1316
1308
 
1317
1309
  await fixture.client.publishOwnerConnectionState(prepared.replacementId, "direct-listener", "v1", {http: 0, websocket: 0}, true)
1318
- assert.deepEqual(await cleared, {connections: {http: 0, websocket: 0}, event: "owner-connection-state", releaseId: "v1", sourceId: "direct-listener"})
1310
+ expect(await cleared).toEqual({connections: {http: 0, websocket: 0}, event: "owner-connection-state", releaseId: "v1", sourceId: "direct-listener"})
1319
1311
  await recovered.shutdown()
1320
1312
  await fixture.client.guardianExit()
1321
1313
  } finally {
@@ -1365,17 +1357,17 @@ test("incumbent listener disconnect before state completion aborts without commi
1365
1357
  await fixture.client.publishOwnerConnectionState(prepared.replacementId, "incumbent-local", "v1", {http: 0, websocket: 1}, true)
1366
1358
  await sourcePublished
1367
1359
  fixture.client.disconnect()
1368
- assert.deepEqual(await failed, {
1360
+ expect(await failed).toEqual({
1369
1361
  event: "replacement-retirement-failed",
1370
1362
  reason: "Incumbent listener disconnected during the prepared handoff",
1371
1363
  replacementId: prepared.replacementId
1372
1364
  })
1373
- assert.deepEqual(await aborted, {
1365
+ expect(await aborted).toEqual({
1374
1366
  event: "replacement-aborted",
1375
1367
  reason: "Incumbent listener disconnected during the prepared handoff"
1376
1368
  })
1377
- assert.equal(tombstones, 1)
1378
- assert.deepEqual(await candidate.replacementStatus(), {
1369
+ expect(tombstones).toBe(1)
1370
+ expect(await candidate.replacementStatus()).toEqual({
1379
1371
  committedReplacementId: null,
1380
1372
  ownerClaimed: false,
1381
1373
  retirementFailed: false,
@@ -1417,7 +1409,7 @@ test("replacement abort during listener handoff validation leaves the incumbent
1417
1409
  const abandonedHandoff = candidate.prepareRetiredOwnerListenerHandoff(prepared.replacementId, processKey)
1418
1410
 
1419
1411
  await candidate.abortOwnerReplacement(prepared.replacementId)
1420
- await assert.rejects(() => abandonedHandoff, /prepared candidate/)
1412
+ await expect(abandonedHandoff).rejects.toThrow(/prepared candidate/)
1421
1413
  await contender.connect()
1422
1414
  const fresh = await contender.prepareOwnerReplacement(authority, authority)
1423
1415
 
@@ -1467,7 +1459,7 @@ test("queued owner claim is revalidated against the latest committed authority",
1467
1459
  await new Promise((resolve) => setTimeout(resolve, 20))
1468
1460
  await fixture.client.publishOwnerState({authority: nextAuthority, snapshot: {activeReleaseId: null}})
1469
1461
  fixture.client.disconnect()
1470
- await assert.rejects(claim, /authority changed while the claim was queued/)
1462
+ await expect(claim).rejects.toThrow(/authority changed while the claim was queued/)
1471
1463
  await contender.claimOwner(500, nextAuthority)
1472
1464
  await contender.shutdown()
1473
1465
  await fixture.client.guardianExit()
@@ -1486,7 +1478,7 @@ test("first upgrade migrates a real pre-split guardian without replacing its own
1486
1478
 
1487
1479
  try {
1488
1480
  await legacyProcess.start()
1489
- await assert.rejects(() => fixture.client.capabilities(), /Guardian capabilities requires a process key/)
1481
+ await expect(fixture.client.capabilities()).rejects.toThrow(/Guardian capabilities requires a process key/)
1490
1482
  legacyPid = legacyProcess.status().pid
1491
1483
  const authority = {configDigest: "legacy-config", runtime: {digest: "legacy-runtime", format: 1, path: "/legacy", version: "0.1.28"}}
1492
1484
  const nextAuthority = {...authority, runtime: {...authority.runtime, digest: "candidate-runtime", path: "/candidate"}}
@@ -1500,7 +1492,7 @@ test("first upgrade migrates a real pre-split guardian without replacing its own
1500
1492
  }
1501
1493
  }
1502
1494
 
1503
- assert.ok(legacyPid)
1495
+ if (!legacyPid) throw new Error("Missing required fixture: legacyPid")
1504
1496
  upgraded = await fixture.client.upgradeLegacyGuardian({
1505
1497
  ownerState,
1506
1498
  socketPath: path.join(fixture.root, "guardian-v2.sock"),
@@ -1511,19 +1503,19 @@ test("first upgrade migrates a real pre-split guardian without replacing its own
1511
1503
  const restored = upgraded.process("release:v1:legacy-worker", processDefinition)
1512
1504
 
1513
1505
  await restored.recover()
1514
- assert.equal(restored.status().pid, legacyPid)
1515
- assert.deepEqual(prepared.ownerState, ownerState)
1516
- assert.deepEqual(await upgraded.stageOwnerReplacement(prepared.replacementId, {authority: nextAuthority, snapshot: ownerState.snapshot}), {committed: true})
1506
+ expect(restored.status().pid).toBe(legacyPid)
1507
+ expect(prepared.ownerState).toEqual(ownerState)
1508
+ expect(await upgraded.stageOwnerReplacement(prepared.replacementId, {authority: nextAuthority, snapshot: ownerState.snapshot})).toEqual({committed: true})
1517
1509
  await committed
1518
- assert.equal(restored.status().pid, legacyPid)
1510
+ expect(restored.status().pid).toBe(legacyPid)
1519
1511
 
1520
1512
  const currentProcess = upgraded.process("release:v2:current-worker", {
1521
1513
  ...definition("current-worker"),
1522
1514
  lifecycle: {activateCommand: "sleep 0.05", activateTimeoutMs: 10, drainTimeoutMs: 0}
1523
1515
  })
1524
1516
  await currentProcess.start()
1525
- await assert.rejects(() => currentProcess.activateStrict(), /activate command timed out after 10ms/i)
1526
- assert.equal(restored.status().pid, legacyPid)
1517
+ await expect(currentProcess.activateStrict()).rejects.toThrow(/activate command timed out after 10ms/i)
1518
+ expect(restored.status().pid).toBe(legacyPid)
1527
1519
  await upgraded.shutdown()
1528
1520
  await upgraded.guardianExit()
1529
1521
  } finally {
@@ -1562,11 +1554,8 @@ test("split guardian rejects an owner-state update when its nested legacy defini
1562
1554
 
1563
1555
  await restored.recover()
1564
1556
  await legacyProcess.updateDefinition({...processDefinition, env: {REVISION: "external"}})
1565
- await assert.rejects(
1566
- () => restored.updateDefinition({...processDefinition, env: {REVISION: "candidate"}}, {authority: nextAuthority, snapshot: {...ownerState.snapshot, serviceReleaseIds: {service: "v2"}}}),
1567
- /provenance mismatch/
1568
- )
1569
- assert.deepEqual(await upgraded.ownerState(), committedOwnerState)
1557
+ await expect(restored.updateDefinition({...processDefinition, env: {REVISION: "candidate"}}, {authority: nextAuthority, snapshot: {...ownerState.snapshot, serviceReleaseIds: {service: "v2"}}})).rejects.toThrow(/provenance mismatch/)
1558
+ expect(await upgraded.ownerState()).toEqual(committedOwnerState)
1570
1559
  } finally {
1571
1560
  await legacyProcess.stop().catch(() => {})
1572
1561
  upgraded?.disconnect()
@@ -1619,14 +1608,15 @@ test("split guardian defers owner handoff until a nested legacy definition updat
1619
1608
  void claim.finally(() => { claimSettled = true })
1620
1609
  upgraded.disconnect()
1621
1610
  await new Promise((resolve) => setTimeout(resolve, 50))
1622
- assert.equal(claimSettled, false, "owner handoff must wait for the nested definition update")
1611
+ // Owner handoff must wait for the nested definition update.
1612
+ expect(claimSettled).toBe(false)
1623
1613
 
1624
1614
  await fs.writeFile(gatePath, "allow\n")
1625
1615
  await claim
1626
1616
  const updateError = await updateResult
1627
1617
 
1628
- assert.match(String(updateError), /connection closed while awaiting update/)
1629
- assert.deepEqual(await contender.ownerState(), {...committedOwnerState, listenerConnectionSources: {}})
1618
+ expect(String(updateError)).toMatch(/connection closed while awaiting update/)
1619
+ expect(await contender.ownerState()).toEqual({...committedOwnerState, listenerConnectionSources: {}})
1630
1620
  } finally {
1631
1621
  await fs.writeFile(gatePath, "allow\n").catch(() => {})
1632
1622
  await contender.shutdown().catch(() => {})
@@ -1665,7 +1655,8 @@ test("a disconnected legacy upgrade candidate does not strand its bridge guardia
1665
1655
  new Promise((_, reject) => { timeout = setTimeout(() => reject(new Error("Legacy upgrade bridge guardian did not exit after candidate disconnect")), 1000) })
1666
1656
  ]).finally(() => { if (timeout) clearTimeout(timeout) })
1667
1657
  bridgeExited = true
1668
- assert.equal(fixture.child.exitCode, null, "the pre-split guardian must remain available after bridge abandonment")
1658
+ // The pre-split guardian must remain available after bridge abandonment.
1659
+ expect(fixture.child.exitCode).toBe(null)
1669
1660
  } finally {
1670
1661
  upgraded?.disconnect()
1671
1662
  if (!bridgeExited && upgraded?.pid) killExactProcessGroup(upgraded.pid)
@@ -1700,7 +1691,7 @@ test("a legacy bridge remains discoverable when its candidate disconnects after
1700
1691
  await upgraded.beginLegacyOwnerClaim(prepared.replacementId, 1000, statePath, recoverySnapshot)
1701
1692
  fixture.client.disconnect()
1702
1693
  await upgraded.completeLegacyOwnerClaim(prepared.replacementId)
1703
- assert.deepEqual(JSON.parse(await fs.readFile(statePath, "utf8")), recoverySnapshot)
1694
+ expect(JSON.parse(await fs.readFile(statePath, "utf8"))).toEqual(recoverySnapshot)
1704
1695
  upgraded.disconnect()
1705
1696
 
1706
1697
  replacement = new GuardianClient({...identity, pid: upgraded.pid})
@@ -1708,7 +1699,7 @@ test("a legacy bridge remains discoverable when its candidate disconnects after
1708
1699
  const resumed = await replacement.prepareOwnerReplacement(authority, nextAuthority)
1709
1700
  const committed = replacement.waitForEvent("replacement-committed")
1710
1701
 
1711
- assert.deepEqual(await replacement.stageOwnerReplacement(resumed.replacementId, {authority: nextAuthority, config: {statePath}, snapshot: ownerState.snapshot}), {committed: true})
1702
+ expect(await replacement.stageOwnerReplacement(resumed.replacementId, {authority: nextAuthority, config: {statePath}, snapshot: ownerState.snapshot})).toEqual({committed: true})
1712
1703
  await committed
1713
1704
  await replacement.shutdown()
1714
1705
  await upgraded.guardianExit()
@@ -1932,3 +1923,4 @@ function killExactProcessGroup(pid) {
1932
1923
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ESRCH") throw error
1933
1924
  }
1934
1925
  }
1926
+ })