tea-session 0.1.0

Append-only session log, replay, and storage contracts for tea-rs
Documentation
use crate::common;

use common::{creation, envelope, user_message};
use serde_json::json;
use std::str::FromStr;
use tea_protocol::{ModelId, ProfileId, ReasoningEffort, SessionSequence};
use tea_session::SessionReducer;

#[test]
fn replay_reconstructs_creation_messages_and_configuration() {
    let records = vec![
        creation(),
        user_message(1),
        envelope(
            2,
            "configuration_changed",
            json!({
                "model":{"providerId":"openai","modelId":"openai/gpt-5"},
                "profileId":"coding-agent",
                "reasoningEffort":"high"
            }),
        ),
    ];
    let state = SessionReducer::replay(records).unwrap();
    assert_eq!(state.tail_sequence(), SessionSequence::new(2));
    assert_eq!(state.messages().len(), 1);
    assert_eq!(
        state.configuration().model_id(),
        Some(&ModelId::from_str("openai/gpt-5").unwrap())
    );
    assert_eq!(
        state.configuration().profile_id(),
        &ProfileId::from_str("coding-agent").unwrap()
    );
    assert_eq!(
        state.configuration().reasoning_effort(),
        Some(ReasoningEffort::High)
    );
}

#[test]
fn incremental_reduction_equals_full_replay() {
    let records = vec![
        creation(),
        user_message(1),
        envelope(
            2,
            "configuration_changed",
            json!({"model":{"providerId":"openai","modelId":"openai/gpt-5"}}),
        ),
    ];
    let expected = SessionReducer::replay(records.clone()).unwrap();
    let mut reducer = SessionReducer::new();
    for record in &records {
        reducer.apply(record).unwrap();
    }
    assert_eq!(reducer.state(), Some(&expected));
}

#[test]
fn failed_incremental_apply_does_not_mutate_state() {
    let mut reducer = SessionReducer::new();
    reducer.apply(&creation()).unwrap();
    let before = reducer.state().cloned();
    let gap = envelope(
        2,
        "configuration_changed",
        json!({"model":{"providerId":"openai","modelId":"openai/gpt-5"}}),
    );
    assert!(reducer.apply(&gap).is_err());
    assert_eq!(reducer.state(), before.as_ref());
}