import { describe, expect, it } from 'vitest'; import { getLaunchJoinMilestonesFromMembers } from '@renderer/components/team/provisioningSteps'; const members = [{ name: 'alice' }, { name: 'bob' }, { name: 'tom' }, { name: 'jane' }]; describe('getLaunchJoinMilestonesFromMembers', () => { it('does not count shell-only liveness as process alive', () => { const milestones = getLaunchJoinMilestonesFromMembers({ members, memberSpawnStatuses: { alice: { status: 'online', launchState: 'runtime_pending_bootstrap', runtimeAlive: true, livenessSource: 'process', livenessKind: 'shell_only', updatedAt: '2026-04-24T12:00:00.000Z', }, bob: { status: 'online', launchState: 'runtime_pending_bootstrap', runtimeAlive: true, livenessSource: 'process', livenessKind: 'runtime_process', updatedAt: '2026-04-24T12:00:00.000Z', }, tom: { status: 'online', launchState: 'runtime_pending_bootstrap', runtimeAlive: true, livenessSource: 'process', livenessKind: 'runtime_process_candidate', updatedAt: '2026-04-24T12:00:00.000Z', }, jane: { status: 'online', launchState: 'runtime_pending_bootstrap', runtimeAlive: true, livenessSource: 'process', updatedAt: '2026-04-24T12:00:00.000Z', }, }, }); expect(milestones.processOnlyAliveCount).toBe(1); expect(milestones.pendingSpawnCount).toBe(3); }); it('does not count missing liveness kind as process alive', () => { const milestones = getLaunchJoinMilestonesFromMembers({ members, memberSpawnStatuses: { alice: { status: 'online', launchState: 'runtime_pending_bootstrap', runtimeAlive: true, livenessSource: 'process', updatedAt: '2026-04-24T12:00:00.000Z', }, }, }); expect(milestones.processOnlyAliveCount).toBe(0); expect(milestones.pendingSpawnCount).toBe(4); }); it('keeps bootstrap-stalled runtime processes out of process-alive progress', () => { const milestones = getLaunchJoinMilestonesFromMembers({ members, memberSpawnStatuses: { alice: { status: 'waiting', launchState: 'runtime_pending_bootstrap', runtimeAlive: true, livenessKind: 'runtime_process', bootstrapStalled: true, updatedAt: '2026-04-24T12:05:00.000Z', }, }, }); expect(milestones.processOnlyAliveCount).toBe(0); expect(milestones.pendingSpawnCount).toBe(4); }); it('uses runtimeProcessPendingCount instead of legacy runtimeAlivePendingCount for snapshot pending math', () => { const milestones = getLaunchJoinMilestonesFromMembers({ members, memberSpawnSnapshot: { expectedMembers: ['alice', 'bob', 'tom', 'jane'], summary: { confirmedCount: 0, pendingCount: 4, failedCount: 0, runtimeAlivePendingCount: 3, runtimeProcessPendingCount: 1, shellOnlyPendingCount: 1, runtimeCandidatePendingCount: 1, permissionPendingCount: 1, }, }, }); expect(milestones.processOnlyAliveCount).toBe(1); expect(milestones.pendingSpawnCount).toBe(3); }); it('does not trust legacy runtimeAlivePendingCount without runtime process count', () => { const milestones = getLaunchJoinMilestonesFromMembers({ members, memberSpawnSnapshot: { expectedMembers: ['alice', 'bob', 'tom', 'jane'], summary: { confirmedCount: 0, pendingCount: 4, failedCount: 0, runtimeAlivePendingCount: 3, }, }, }); expect(milestones.processOnlyAliveCount).toBe(0); expect(milestones.pendingSpawnCount).toBe(4); }); it('counts skipped teammates separately from pending and failed launch members', () => { const milestones = getLaunchJoinMilestonesFromMembers({ members, memberSpawnStatuses: { alice: { status: 'skipped', launchState: 'skipped_for_launch', skippedForLaunch: true, runtimeAlive: false, bootstrapConfirmed: false, hardFailure: false, updatedAt: '2026-04-24T12:00:00.000Z', }, }, }); expect(milestones.skippedSpawnCount).toBe(1); expect(milestones.failedSpawnCount).toBe(0); expect(milestones.pendingSpawnCount).toBe(3); }); it('does not let a stale clean snapshot hide live registered-only members', () => { const milestones = getLaunchJoinMilestonesFromMembers({ members, memberSpawnStatuses: { alice: { status: 'online', launchState: 'confirmed_alive', runtimeAlive: true, bootstrapConfirmed: true, updatedAt: '2026-04-24T12:00:00.000Z', }, bob: { status: 'waiting', launchState: 'runtime_pending_bootstrap', runtimeAlive: false, bootstrapConfirmed: false, livenessKind: 'registered_only', updatedAt: '2026-04-24T12:00:01.000Z', }, tom: { status: 'online', launchState: 'confirmed_alive', runtimeAlive: true, bootstrapConfirmed: true, updatedAt: '2026-04-24T12:00:00.000Z', }, jane: { status: 'online', launchState: 'confirmed_alive', runtimeAlive: true, bootstrapConfirmed: true, updatedAt: '2026-04-24T12:00:00.000Z', }, }, memberSpawnSnapshot: { expectedMembers: ['alice', 'bob', 'tom', 'jane'], summary: { confirmedCount: 4, pendingCount: 0, failedCount: 0, runtimeAlivePendingCount: 0, }, }, }); expect(milestones.heartbeatConfirmedCount).toBe(3); expect(milestones.pendingSpawnCount).toBe(1); expect(milestones.expectedTeammateCount).toBe(4); }); });