use crate::door::assistant_test_support::*;
use serde_json::json;
use std::sync::Arc;
#[test]
fn r3_09_an_unrelated_agent_cannot_squat_on_the_store_seat() -> Result {
let f = Fixture::new(json!({}))?;
let mut real = f.seat("agent-pass", "wards")?;
let error = f
.seat("squatter", "wards")
.err()
.ok_or_else(|| anyhow::anyhow!("unrelated agent admitted"))?;
assert!(error.to_string().contains("seat_not_yours"), "{error}");
let request = f.request("POST", "", &json!({"text":"real seat"}));
let (door, site) = (Arc::clone(&f.door), Arc::clone(&f.site));
let post = std::thread::spawn(move || {
written(crate::door::route::answer(
&door,
&site,
&request,
crate::serve::pass::now(),
))
});
let op = real.read()?;
assert_eq!(op["op"]["text"], "real seat");
real.send(&json!({"ack":{"id":op["op"]["id"]}}))?;
assert!(
post.join()
.map_err(|_| anyhow::anyhow!("post"))??
.starts_with("HTTP/1.1 202 ")
);
Ok(())
}
#[test]
fn r3_10_context_is_bound_to_the_url_store_and_a_typed_local_branch() -> Result {
let f = Fixture::new(json!({}))?;
let read = ack(f.seat("agent-pass", "wards")?);
for context in [
json!({"store":"pharmacy","branch":"main"}),
json!({"store":"wards","branch":7}),
json!({"store":"wards","branch":""}),
json!({"store":"wards","branch":"main\nother"}),
json!({"branch":"main"}),
] {
let response = written(f.answer("POST", "", &json!({"text":"context","context":context})))?;
assert!(
response.starts_with("HTTP/1.1 400 ") && response.contains("assistant_context_"),
"{response}"
);
}
check(
f.answer(
"POST",
"",
&json!({"text":"correct","context":{"store":"wards","branch":"main"}}),
),
202,
"id",
)?;
assert_eq!(read.recv()?["op"]["context"]["store"], "wards");
Ok(())
}
#[test]
fn r3_16_invalid_session_person_never_becomes_a_record_prefix() -> Result {
for person in [
"bad/person".to_owned(),
"bad\\person".into(),
"bad\nperson".into(),
"bad person".into(),
"person:other".into(),
"person.else".into(),
"person-é".into(),
String::new(),
"x".repeat(257),
] {
let f = Fixture::new(json!({"session_person":person}))?;
let response = written(f.answer("POST", "", &json!({"text":"words"})))?;
assert!(
response.starts_with("HTTP/1.1 400 ") && response.contains("assistant_person_invalid"),
"unsafe person admitted: {response}"
);
}
Ok(())
}
#[test]
fn r3_gap_per_store_pending_bound_and_ack_release_admit_a_third_op() -> Result {
let f = Fixture::new(json!({"max_per_person":2,"max_per_store":1}))?;
let mut seat = f.seat("agent-pass", "wards")?;
let request = f.request("POST", "", &json!({"text":"first"}));
let (door, site) = (Arc::clone(&f.door), Arc::clone(&f.site));
let first = std::thread::spawn(move || {
written(crate::door::route::answer(
&door,
&site,
&request,
crate::serve::pass::now(),
))
});
let op = seat.read()?;
let second = written(f.answer("POST", "", &json!({"text":"second"})))?;
assert!(second.contains("max_per_store=1; count=1"), "{second}");
seat.send(&json!({"ack":{"id":op["op"]["id"]}}))?;
assert!(
first
.join()
.map_err(|_| anyhow::anyhow!("post"))??
.starts_with("HTTP/1.1 202 ")
);
let read = ack(seat);
check(f.answer("POST", "", &json!({"text":"third"})), 202, "id")?;
assert_eq!(read.recv()?["op"]["text"], "third");
Ok(())
}
#[test]
fn r3_gap_outgoing_door_frame_names_the_byte_bound() -> Result {
let f = Fixture::new(json!({"frame_bytes":1024}))?;
let read = ack(f.seat("agent-pass", "wards")?);
let text = written(f.answer("POST", "", &json!({"text":"x".repeat(1024)})))?;
assert!(
text.starts_with("HTTP/1.1 429 ") && text.contains("frame_bytes=1024; count="),
"{text}"
);
check(f.answer("POST", "", &json!({"text":"small"})), 202, "id")?;
assert_eq!(read.recv()?["op"]["text"], "small");
Ok(())
}