use std::net::TcpListener;
use lys_pass::Target;
use super::LysLive;
use crate::serve::pass::LiveCheck;
type TestResult = Result<(), Box<dyn std::error::Error>>;
fn closed_origin() -> Result<String, std::io::Error> {
let listener = TcpListener::bind("127.0.0.1:0")?;
let port = listener.local_addr()?.port();
drop(listener);
Ok(format!("http://127.0.0.1:{port}/"))
}
#[test]
fn an_unreachable_lys_is_named_lys_could_not_be_asked() -> TestResult {
let live = LysLive::new(&closed_origin()?)?;
let target = Target::new("haematite.database", "wards", "branch")?;
let error = live
.ask("a-pass", "holder", &target)
.err()
.ok_or("an unreachable Lys answered")?;
assert_eq!(error.name(), "lys_could_not_be_asked");
Ok(())
}
#[test]
fn an_issuer_that_is_not_an_http_origin_is_refused() {
for issuer in [
"not a url",
"ftp://lys.example/",
"https://user@lys.example/",
] {
assert!(
LysLive::new(issuer).is_err(),
"{issuer} was taken as the issuer"
);
}
}
#[test]
fn the_check_is_asked_under_api_as_the_app() -> TestResult {
use std::io::{Read, Write};
let listener = TcpListener::bind("127.0.0.1:0")?;
let origin = format!("http://{}", listener.local_addr()?);
let seen = std::thread::spawn(move || -> Result<String, std::io::Error> {
let (mut stream, _) = listener.accept()?;
stream.set_read_timeout(Some(std::time::Duration::from_secs(5)))?;
let mut request = Vec::new();
let mut chunk = [0_u8; 4096];
loop {
let read = stream.read(&mut chunk)?;
if read == 0 {
break;
}
request.extend_from_slice(&chunk[..read]);
let text = String::from_utf8_lossy(&request).to_lowercase();
if let Some(end) = text.find("\r\n\r\n") {
let length = text
.lines()
.find_map(|line| line.strip_prefix("content-length: "))
.and_then(|n| n.trim().parse::<usize>().ok())
.unwrap_or(0);
if request.len() >= end + 4 + length {
break;
}
}
}
let body =
r#"{"revision":1,"results":[{"allowed":true,"mode":"outright","grant":"grant-1"}]}"#;
write!(
stream,
"HTTP/1.1 200 OK\r\ncontent-type: application/json\r\ncontent-length: {}\r\nconnection: close\r\n\r\n{body}",
body.len()
)?;
Ok(String::from_utf8_lossy(&request).to_lowercase())
});
let live = LysLive::new(&origin)?.asking_as("lys-app.haematite.test".to_owned());
let target = Target::new("haematite.database", "wards", "branch")?;
live.ask("the-seats-pass", "agent-1", &target)?;
let request = seen.join().map_err(|_| "the fake Lys panicked")??;
assert!(
request.starts_with("post /api/grants/check/batch "),
"{request}"
);
assert!(
request.contains("authorization: bearer lys-app.haematite.test"),
"{request}"
);
assert!(!request.contains("the-seats-pass"), "{request}");
Ok(())
}