kcode-k1-chat-thread-actions 0.5.0

In-memory Kmap actions and session state for K1 chat threads
Documentation
use super::*;

fn id(byte: u8) -> AccessId {
    AccessId::new(TxId::from_bytes([byte; 12]))
}

#[test]
fn current_time_accepts_only_empty_objects() {
    for arguments in ["{}", " \n {\t} "] {
        assert!(launch_current_time(arguments).is_ok_and(|time| !time.is_empty()));
    }
    for arguments in ["", "{", "null", "[]", "{\"zone\":\"UTC\"}"] {
        assert_eq!(
            launch_current_time(arguments),
            Err("invalid CurrentTime arguments".to_owned())
        );
    }
}

#[test]
fn schemas_and_ids_are_strict() {
    let valid = r#"{"title":"t","navigation_hint":"h","narrative":"n","connections":["000000000000000000000000"]}"#;
    assert!(parse::<CreateArguments>("KmapCreateNode", valid).is_ok());
    for invalid_json in [
        r#"{"title":"t","navigation_hint":"h","narrative":"n"}"#,
        r#"{"title":"t","navigation_hint":"h","narrative":"n","connections":[],"extra":1}"#,
        "[]",
        "{",
    ] {
        assert_eq!(
            parse::<CreateArguments>("KmapCreateNode", invalid_json).err(),
            Some("invalid KmapCreateNode arguments".to_owned())
        );
    }
    for invalid_id in [
        "00000000000000000000000",
        "0000000000000000000000000",
        "00000000000000000000000A",
        "00000000000000000000000g",
    ] {
        assert!(parse_id(invalid_id, "KmapOpenNode").is_err());
    }
}

#[test]
fn open_formatting_tracks_components_order_and_first_provenance() {
    let mut state = SessionState::default();
    let first = state.record_open(vec![
        LoadedNode {
            access_id: id(1),
            source: None,
            title: "Root".to_owned(),
            navigation_hint: "Start".to_owned(),
            narrative: Some("Root text".to_owned()),
        },
        LoadedNode {
            access_id: id(2),
            source: Some(id(1)),
            title: "Child".to_owned(),
            navigation_hint: "Next".to_owned(),
            narrative: None,
        },
    ]);
    assert_eq!(
        first,
        format!(
            "Node ID: {}\nTitle: Root\nNavigation Hint: Start\nNarrative: Root text\n\nNode ID: {}\nTitle: Child\nNavigation Hint: Next",
            render_id(id(1)),
            render_id(id(2))
        )
    );
    let second = state.record_open(vec![LoadedNode {
        access_id: id(2),
        source: Some(id(9)),
        title: "Child".to_owned(),
        navigation_hint: "Next".to_owned(),
        narrative: Some("Later".to_owned()),
    }]);
    assert_eq!(
        second,
        format!("Node ID: {}\nNarrative: Later", render_id(id(2)))
    );
    assert_eq!(state.provenance.get(&id(2)), Some(&id(1)));
    assert_eq!(
        state.record_open(Vec::new()),
        "No new Kmap node components."
    );
}

#[test]
fn session_penalties_deduplicate_and_preserve_loaded_order() {
    let mut state = SessionState::default();
    for node in [id(1), id(2), id(3)] {
        state.add_loaded(node);
    }
    state.provenance.insert(id(2), id(1));
    let nodes = state.new_penalties(vec![id(2), id(2), id(9), id(1)]);
    assert_eq!(nodes, vec![id(2), id(1)]);
    let measurements = state.measurements(&nodes);
    assert_eq!(measurements.len(), 1);
    assert_eq!(measurements[0].source, id(1));
    assert_eq!(measurements[0].target, id(2));
    state.commit_penalties(nodes);
    assert_eq!(state.eligible(), vec![id(3)]);
}

#[test]
fn turn_binding_requires_identical_context_profile_and_policy() {
    let active = (1, 2, 3);
    let mut slot = None;
    assert_eq!(bind_once(&mut slot, active), Ok(()));
    assert_eq!(bind_once(&mut slot, active), Ok(()));
    for different in [(9, 2, 3), (1, 9, 3), (1, 2, 9)] {
        assert_eq!(
            bind_once(&mut slot, different),
            Err(
                "different Kmap access context, profile ID, or policy is already active".to_owned()
            )
        );
    }
}