pi-workflows 0.11.2

Terminal viewer and live replay server for pi-workflows run bundles
Documentation
use piw::bundle::types::{SessionEntryRecord, SessionEventRecord};
use piw::format::parse_timestamp_ms;
use piw::session::{reduce_session_events, SessionReplayIndex};
use serde::Deserialize;
use serde_json::Value;
use std::path::PathBuf;

#[derive(Deserialize)]
struct FixturePosition {
    #[serde(rename = "throughSeq")]
    through_seq: u64,
    expected: Value,
}

#[derive(Deserialize)]
struct Fixture {
    schema: String,
    entries: Vec<SessionEntryRecord>,
    events: Vec<SessionEventRecord>,
    positions: Vec<FixturePosition>,
}

fn fixture_dir() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../fixtures/session-events")
}

#[test]
fn temporal_reducer_matches_typescript_fixtures() {
    let mut paths: Vec<PathBuf> = std::fs::read_dir(fixture_dir())
        .unwrap()
        .filter_map(|entry| entry.ok().map(|entry| entry.path()))
        .filter(|path| path.extension().and_then(|value| value.to_str()) == Some("json"))
        .collect();
    paths.sort();
    assert!(!paths.is_empty(), "no session event fixtures found");

    for path in paths {
        let fixture: Fixture = serde_json::from_str(&std::fs::read_to_string(&path).unwrap())
            .unwrap_or_else(|error| panic!("{}: {error}", path.display()));
        assert_eq!(fixture.schema, "pi-workflows.session-event-fixture.v1");
        let index = SessionReplayIndex::new(&fixture.entries, &fixture.events, 2);
        for position in fixture.positions {
            let actual =
                reduce_session_events(&fixture.entries, &fixture.events, position.through_seq);
            assert_eq!(
                serde_json::to_value(&actual).unwrap(),
                position.expected,
                "{} at seq {}",
                path.display(),
                position.through_seq
            );
            assert_eq!(index.state_at_seq(position.through_seq), actual);
        }
        if let Some(event) = fixture.events.get(4) {
            let at = parse_timestamp_ms(&event.at).unwrap();
            assert_eq!(index.seq_at_or_before(at), event.seq);
        }
    }
}