use serde_json::{Value, json};
use super::fixtures::{
PASS, Reply, Server, TestResult, config, refusal, request, request_with_headers,
};
use super::native_call_fixture::{Asked, Store};
const CAP: usize = 131_072;
const METADATA: &str = "https://identity.example/resource-metadata";
const DESCRIBE: &str = r#"{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"describe_store","arguments":{"selection":{"branch":"main","version":null},"page":{"rows":64,"bytes":65536},"after":{}}}}"#;
fn challenged() -> Result<super::Config, serde_json::Error> {
let mut config = config()?;
config.resource_metadata_url = Some(METADATA.to_owned());
Ok(config)
}
fn challenge(response: &Reply) -> Option<&str> {
response
.headers
.get("www-authenticate")
.and_then(|value| value.to_str().ok())
}
fn methods(asked: &[Asked], connection: usize) -> Vec<&str> {
asked
.iter()
.filter(|asked| asked.connection == connection)
.map(|asked| asked.method.as_str())
.collect()
}
#[test]
fn the_bearer_pass_is_presented_unchanged_before_the_verb_on_every_store_connection() -> TestResult
{
let store = Store::start()?;
let mcp = Server::mcp(config()?, store.path.clone(), CAP, 2)?;
let response = Reply::read(request(&mcp, "POST", DESCRIBE, true, &[])?)?;
assert_eq!(response.status, 200);
let body: Value = serde_json::from_slice(&response.body)?;
assert_eq!(body.pointer("/result/isError"), Some(&json!(true)));
let asked = store.asked()?;
assert_eq!(
methods(&asked, 0),
["hello", "session.pass", "collections.list"]
);
assert_eq!(
methods(&asked, 1),
["hello", "session.pass", "branches.list"]
);
assert_eq!(asked.len(), 6, "{asked:?}");
for presented in asked.iter().filter(|asked| asked.method == "session.pass") {
assert_eq!(presented.params, json!({ "pass": PASS }));
}
mcp.finish()?;
store.finish()
}
#[test]
fn a_request_without_a_bearer_is_challenged_and_never_reaches_the_store() -> TestResult {
let store = Store::start()?;
for (config, expected) in [
(config()?, "Bearer".to_owned()),
(
challenged()?,
format!("Bearer resource_metadata=\"{METADATA}\""),
),
] {
let mcp = Server::mcp(config, store.path.clone(), CAP, 2)?;
let response = Reply::read(request(&mcp, "POST", DESCRIBE, false, &[])?)?;
refusal(&response, 401, "mcp_sign_in_required")?;
assert_eq!(challenge(&response), Some(expected.as_str()));
let headers = [
("Content-Type", "application/json"),
("Accept", "application/json, text/event-stream"),
("Authorization", "Basic fixture-token"),
];
let response = Reply::read(request_with_headers(&mcp, "POST", DESCRIBE, &headers)?)?;
refusal(&response, 401, "mcp_sign_in_required")?;
assert_eq!(challenge(&response), Some(expected.as_str()));
mcp.finish()?;
}
assert!(store.asked()?.is_empty(), "{:?}", store.asked()?);
store.finish()
}
#[test]
fn a_pass_the_store_refuses_by_a_verification_name_is_answered_401_with_the_challenge() -> TestResult
{
for code in [
"pass_expired",
"pass_not_yet_valid",
"wrong_audience",
"signature_refused",
"pass_missing",
] {
let store = Store::refusing(code)?;
let mcp = Server::mcp(challenged()?, store.path.clone(), CAP, 2)?;
let response = Reply::read(request(&mcp, "POST", DESCRIBE, true, &[])?)?;
refusal(&response, 401, "mcp_pass_refused")?;
assert_eq!(
challenge(&response),
Some(format!("Bearer resource_metadata=\"{METADATA}\"").as_str()),
"{code}"
);
let asked = store.asked()?;
assert_eq!(methods(&asked, 0), ["hello", "session.pass"], "{code}");
assert_eq!(asked.len(), 2, "{code}: {asked:?}");
mcp.finish()?;
store.finish()?;
}
Ok(())
}
#[test]
fn a_store_that_refuses_for_another_reason_or_cannot_be_reached_is_unavailable_never_401()
-> TestResult {
for code in [
"lys_could_not_be_asked",
"lys_keys_pending",
"store_closing",
] {
let store = Store::refusing(code)?;
let mcp = Server::mcp(challenged()?, store.path.clone(), CAP, 2)?;
let response = Reply::read(request(&mcp, "POST", DESCRIBE, true, &[])?)?;
refusal(&response, 503, "mcp_store_unavailable")?;
assert_eq!(challenge(&response), None, "{code}");
let asked = store.asked()?;
assert_eq!(methods(&asked, 0), ["hello", "session.pass"], "{code}");
assert_eq!(asked.len(), 2, "{code}: {asked:?}");
mcp.finish()?;
store.finish()?;
}
let directory = tempfile::tempdir()?;
let mcp = Server::mcp(challenged()?, directory.path().join("absent.sock"), CAP, 2)?;
let response = Reply::read(request(&mcp, "POST", DESCRIBE, true, &[])?)?;
refusal(&response, 503, "mcp_store_unavailable")?;
assert_eq!(challenge(&response), None);
mcp.finish()
}