relay-knowledge 1.1.17

Graph-database-based knowledge graph project.
Documentation
//! Direct contracts for bounded session and cancellation state.

use axum::http::HeaderMap;

use super::*;

#[test]
fn session_ids_are_unpredictable_header_safe_values() {
    let sessions = SessionRegistry::default();
    let first = create_session(&sessions);
    let second = create_session(&sessions);

    assert_ne!(first, second);
    assert!(is_session_id(&first));
    assert!(is_session_id(&second));
}

#[test]
fn issued_sessions_resolve_stable_namespaces_and_track_initialization() {
    let sessions = SessionRegistry::default();
    let session_id = create_session(&sessions);
    let headers = session_headers(&session_id);

    let before_initialized = sessions
        .require_session(&headers)
        .expect("issued session should resolve");
    assert_eq!(
        before_initialized.namespace(),
        format!("session:{session_id}")
    );
    assert!(!before_initialized.initialized);

    sessions
        .mark_initialized(before_initialized.session_id())
        .expect("session should initialize");
    let after_initialized = sessions
        .require_session(&headers)
        .expect("initialized session should resolve");
    assert!(after_initialized.initialized);
}

#[test]
fn missing_or_unknown_session_headers_are_rejected() {
    let sessions = SessionRegistry::default();
    let unknown_headers = session_headers("rk-unissued");

    assert_eq!(
        sessions
            .require_session(&HeaderMap::new())
            .expect_err("missing header should fail"),
        SessionLookupError::Missing
    );
    assert_eq!(
        sessions
            .require_session(&unknown_headers)
            .expect_err("unknown header should fail"),
        SessionLookupError::Unknown
    );
}

#[test]
fn session_eviction_preserves_recently_used_sessions() {
    let sessions = SessionRegistry::default();
    let active = create_session(&sessions);
    let stale = create_session(&sessions);
    for _ in 0..(MAX_TRACKED_SESSIONS - 2) {
        create_session(&sessions);
    }
    sessions
        .require_session(&session_headers(&active))
        .expect("active session should touch recency");

    let newest = create_session(&sessions);

    assert!(sessions.contains_session(&active));
    assert!(sessions.contains_session(&newest));
    assert!(!sessions.contains_session(&stale));
    assert_eq!(sessions.tracked_len(), MAX_TRACKED_SESSIONS);
}

#[test]
fn session_usage_history_is_bounded_for_stable_sessions() {
    let sessions = SessionRegistry::default();
    let session_id = create_session(&sessions);
    let headers = session_headers(&session_id);

    for _ in 0..(MAX_TRACKED_SESSIONS * 3) {
        sessions
            .require_session(&headers)
            .expect("stable session should resolve");
    }

    assert!(sessions.usage_history_len() <= MAX_TRACKED_SESSIONS * 2);
}

#[test]
fn cancellation_requests_are_idempotent_while_active() {
    let registry = CancellationRegistry::default();
    let (_receiver, _registration) = registry.register("string:call".to_owned());

    assert!(registry.cancel("string:call"));
    assert!(!registry.cancel("string:call"));
}

fn create_session(sessions: &SessionRegistry) -> String {
    sessions
        .create_session()
        .expect("OS entropy should create session")
}

fn session_headers(session_id: &str) -> HeaderMap {
    let mut headers = HeaderMap::new();
    headers.insert(MCP_SESSION_ID_HEADER, session_id.parse().unwrap());
    headers
}

fn is_session_id(value: &str) -> bool {
    value.len() == 67
        && value.starts_with("rk-")
        && value[3..].bytes().all(|byte| byte.is_ascii_hexdigit())
}