Skip to content

Mock-driven supervisor state machine tests #43

@ImpulseB23

Description

@ImpulseB23

Follow-up to PRI-13. Current supervisor test coverage is only the pure functions (next_backoff, SidecarStatus serialization, default config constants). The state machine itself — spawn → run → terminate → backoff → respawn, healthy-uptime backoff reset — is only validated by manual E2E.

Scope

Introduce a SidecarLauncher trait inside sidecar_supervisor.rs:

trait SidecarLauncher: Send + Sync + 'static {
    async fn launch(&self) -> Result<LaunchedChild, io::Error>;
}

struct LaunchedChild {
    events: tokio::sync::mpsc::Receiver<CommandEvent>,
    writer: Box<dyn StdinWrite + Send>,
}

Production impl wraps app.shell().sidecar(SIDECAR_BINARY)?.spawn() as today. Test impl is a scripted launcher that:

  • Emits a configurable sequence of CommandEvents into the rx channel
  • Terminates on cue to drive the respawn branch
  • Records bootstrap + twitch_connect writes so tests can assert them

Tests to add:

  • Healthy run resets backoff: seed a launcher that stays alive past the healthy threshold, then terminates → next run_once starts at initial_backoff
  • Unhealthy run grows backoff: seed a launcher that terminates immediately twice → second backoff is 2× first
  • Bootstrap + twitch_connect replay on every respawn
  • sidecar_status events are emitted in the expected order per iteration

Out of scope

  • Real cross-process respawn (this lives in manual E2E and the release smoke test)
  • Mocking the ring buffer — drain loop stays out of these tests; focus is the supervisor lifecycle

Depends on

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions