#![allow(clippy::unwrap_used)]
use axum::http::HeaderMap;
use fraiseql_core::{security::SecurityContext, types::UserId};
use super::{ThreadKey, thread_key};
fn ctx(user_id: &str) -> SecurityContext {
let mut c = SecurityContext::system_job("t", "r", vec![], vec![], None);
c.user_id = UserId::new(user_id);
c
}
fn headers(session: Option<&str>) -> HeaderMap {
let mut h = HeaderMap::new();
if let Some(s) = session {
h.insert("mcp-session-id", s.parse().unwrap());
}
h
}
#[test]
fn one_principal_partitions_its_own_threads_by_the_header() {
let a = thread_key(Some(&ctx("alice")), &headers(Some("thread-1"))).unwrap();
let b = thread_key(Some(&ctx("alice")), &headers(Some("thread-2"))).unwrap();
assert_eq!(a.session_id, b.session_id, "same principal, same session");
assert_ne!(a.thread_id, b.thread_id, "different header, different thread");
}
#[test]
fn two_principals_sending_the_same_header_do_not_share_a_thread() {
let session = headers(Some("collide"));
let alice = thread_key(Some(&ctx("alice")), &session).unwrap();
let mallory = thread_key(Some(&ctx("mallory")), &session).unwrap();
assert_eq!(alice.thread_id, mallory.thread_id, "they did send the same header");
assert_ne!(
alice.session_id, mallory.session_id,
"…and must still address different stores — the session is derived from the \
authenticated principal, never from anything the client sends"
);
}
#[test]
fn the_session_id_is_a_pure_function_of_the_principal() {
let first = thread_key(Some(&ctx("alice")), &headers(Some("t"))).unwrap();
let second = thread_key(Some(&ctx("alice")), &headers(Some("t"))).unwrap();
assert_eq!(first, second);
assert_eq!(
first,
ThreadKey {
session_id: uuid::Uuid::new_v5(&super::PRINCIPAL_NAMESPACE, b"alice"),
thread_id: "t".to_string(),
}
);
}
#[test]
fn an_anonymous_caller_gets_no_thread() {
assert!(thread_key(None, &headers(Some("thread-1"))).is_none());
}
#[test]
fn a_caller_without_the_header_gets_no_thread() {
assert!(thread_key(Some(&ctx("alice")), &headers(None)).is_none());
assert!(
thread_key(Some(&ctx("alice")), &headers(Some(" "))).is_none(),
"a blank header is no header"
);
}