ReviewOS

also looking at this

stacks/drone

test: retain execution lifetime failure observations

#62
Draft chris wants to merge codex/execution-lifetime-observations into main
10 files +174 -64

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

tests/fixtures/public-safety-access/execution-lifetime-setup.tsmodified+3-2
Changes to tests/fixtures/public-safety-access/execution-lifetime-setup.ts
@@ -1,4 +1,5 @@
11import assert from 'node:assert/strict'
2import type { FixturePhaseObserver } from './handover-setup'
23import { setupHandoverFixture } from './handover-setup'
34import { examplePublicSafetyConfig } from '../../../resources/functions/drone/public-safety/fixtures'
45
@@ -8,8 +9,8 @@ export interface LifetimeSlot {
89 recording: string | null, completion: unknown, latestEvent: unknown,
910 candidate: unknown, pendingEvent: unknown, worker: Worker | null,
1011}
11export async function setupExecutionLifetime(mode: string) {
12 const f = await setupHandoverFixture()
12export async function setupExecutionLifetime(mode: string, observe?: FixturePhaseObserver) {
13 const f = await setupHandoverFixture(observe)
1314 const core = await import('../../../resources/functions/drone/public-safety/execution/service')
1415 const { executionOwner } = await import('../../../resources/functions/drone/public-safety/execution/state')
1516 const { executionWords } = await import('../../../resources/functions/drone/public-safety/execution/protocol')
tests/fixtures/public-safety-access/execution-lifetime.tsmodified+3-3
Changes to tests/fixtures/public-safety-access/execution-lifetime.ts
@@ -15,7 +15,7 @@ async function observe<T>(phase: string, work: () => Promise<T>): Promise<T> {
1515 try { const result = await work(); emit('settled'); return result }
1616 catch (error) { emit('rejected'); throw error }
1717}
18const h = await observe('setup', () => setupExecutionLifetime(mode))
18const h = await observe('setup', () => setupExecutionLifetime(mode, observe))
1919let attachment: Awaited<ReturnType<typeof h.core.attachIncidentExecution>> | undefined
2020let expiredClose: string | null = null
2121try {
@@ -29,7 +29,7 @@ try {
2929 assert.equal(value.latestEvent.sequence, ++last)
3030 })))
3131 await observe('activate', () => attachment!.activate())
32 await h.wait(() => run.slot.phase === 'completed', 'The real eight-second producer must complete', 20000)
32 await observe('producer-completed', () => h.wait(() => run.slot.phase === 'completed', 'The real eight-second producer must complete', 20000))
3333 assert(ack.event)
3434 assert.equal(ack.event.data.outcome, 'completed')
3535 assert.equal(ack.event.data.transferred, true)
@@ -39,7 +39,7 @@ try {
3939 attachment.release()
4040 }
4141 else {
42 await h.wait(() => Atomics.load(run.slot.control, h.executionWords.emitted) === 1, 'The real producer must reach its first blocked callback')
42 await observe('producer-first-callback', () => h.wait(() => Atomics.load(run.slot.control, h.executionWords.emitted) === 1, 'The real producer must reach its first blocked callback'))
4343 assert.equal(Atomics.load(run.slot.control, h.executionWords.credit), 0)
4444 assert.equal(Atomics.load(run.slot.control, h.executionWords.returned), 0)
4545 const stoppingAt = performance.now()
tests/fixtures/public-safety-access/handover-setup.tsmodified+18-9
Changes to tests/fixtures/public-safety-access/handover-setup.ts
@@ -2,8 +2,13 @@ import { randomUUID } from 'node:crypto'
22import { setupIncidentDatabase } from './setup'
33import { examplePublicSafetyConfig } from '../../../resources/functions/drone/public-safety/fixtures'
44import { simulatePublicSafety } from '../../../resources/functions/drone/public-safety/runtime'
5export async function setupHandoverFixture() {
6 const { database, User } = await setupIncidentDatabase()
5export type FixturePhaseObserver = <T>(phase: string, work: () => Promise<T>) => Promise<T>
6
7export async function setupHandoverFixture(observe: FixturePhaseObserver = (phase, work) => {
8 void phase
9 return work()
10}) {
11 const { database, User } = await observe('fixture-database', () => setupIncidentDatabase())
712 const access = await import('../../../resources/functions/drone/public-safety/access/service')
813 const records = await import('../../../resources/functions/drone/public-safety/access/records')
914 const handover = await import('../../../resources/functions/drone/public-safety/access/handover')
@@ -20,14 +25,18 @@ export async function setupHandoverFixture() {
2025 for (const ability of incidentAbilities) Gate.define(`public-safety:${ability}`, (user, key) => publicSafetyIncidentGate(user, key, ability))
2126 for (const ability of ['read', 'write'] as const) Gate.define(`public-safety:handover-${ability}`, (user, key) => policy.publicSafetyHandoverGate(user, key, ability))
2227 const as = <T>(token: string, work: () => Promise<T>) => runWithRequest(enhanceRequest(new Request('https://incident.invalid/api/public-safety/incidents', { headers: { authorization: `Bearer ${token}` } }) as Parameters<typeof enhanceRequest>[0]), work)
23 const sender = await User.create({ name: 'Outgoing Coordinator', email: 'handover-sender@example.invalid', password: 'temporary-fixture-password' })
24 const recipient = await User.create({ name: 'Incoming Coordinator', email: 'handover-recipient@example.invalid', password: 'temporary-fixture-password' })
25 const unrelated = await User.create({ name: 'Unrelated Observer', email: 'handover-unrelated@example.invalid', password: 'temporary-fixture-password' })
26 for (const permission of ['public-safety:access', 'public-safety:create']) await Rbac.createPermission(permission, 'web')
27 for (const user of [sender, recipient, unrelated]) await Rbac.givePermission(user.id, 'public-safety:access', 'web')
28 for (const user of [sender, recipient]) await Rbac.givePermission(user.id, 'public-safety:create', 'web')
28 const sender = await observe('fixture-sender-account', async () => User.create({ name: 'Outgoing Coordinator', email: 'handover-sender@example.invalid', password: 'temporary-fixture-password' }))
29 const recipient = await observe('fixture-recipient-account', async () => User.create({ name: 'Incoming Coordinator', email: 'handover-recipient@example.invalid', password: 'temporary-fixture-password' }))
30 const unrelated = await observe('fixture-unrelated-account', async () => User.create({ name: 'Unrelated Observer', email: 'handover-unrelated@example.invalid', password: 'temporary-fixture-password' }))
31 await observe('fixture-rbac', async () => {
32 for (const permission of ['public-safety:access', 'public-safety:create']) await Rbac.createPermission(permission, 'web')
33 for (const user of [sender, recipient, unrelated]) await Rbac.givePermission(user.id, 'public-safety:access', 'web')
34 for (const user of [sender, recipient]) await Rbac.givePermission(user.id, 'public-safety:create', 'web')
35 })
2936 const credential = async (userId: number, scopes = ['public-safety:read', 'public-safety:coordinate']) => (await tokens.createToken(userId, 'handover fixture', scopes, { withRefreshToken: false })).plainTextToken
30 const senderToken = await credential(sender.id), recipientToken = await credential(recipient.id), unrelatedToken = await credential(unrelated.id)
37 const senderToken = await observe('fixture-sender-token', () => credential(sender.id))
38 const recipientToken = await observe('fixture-recipient-token', () => credential(recipient.id))
39 const unrelatedToken = await observe('fixture-unrelated-token', () => credential(unrelated.id))
3140 const seed = async (key: string, lifetime = 86400000) => as(senderToken, async () => {
3241 const config = examplePublicSafetyConfig()
3342 config.id = key