use serde_json::json;
use super::fake::{FakeServer, says};
use super::{Delta, Endpoint, Key, ProviderError, Stop};
const MADE_UP: &str = "not-a-real-key-0000-wxyz";
fn endpoint(address: &str, header: &str) -> Endpoint {
Endpoint {
address: format!("{address}/"),
model: "test-model".into(),
key_header: header.into(),
key: Some(Key::new(MADE_UP).expect("a key")),
silence: Endpoint::SILENCE,
}
}
#[test]
fn the_request_carries_the_model_the_stream_flag_and_the_key_in_its_header() {
let server = FakeServer::start(vec![(200, says("Hello there"))]);
let mut pieces = String::new();
let reply = endpoint(&server.address, "api-key")
.stream(
&json!({"max_tokens": 64, "messages": [{"role": "user", "content": "hi"}]}),
&mut |delta| {
if let Delta::Text(text) = delta {
pieces.push_str(&text);
}
},
&|| false,
)
.expect("a reply");
assert_eq!(pieces, "Hello there");
assert_eq!(reply.content, [json!({"type": "text", "text": "Hello there"})]);
assert_eq!(reply.stop, Stop::EndTurn);
let received = server.received();
assert_eq!(received.len(), 1);
let request = &received[0];
assert_eq!(request.line, "POST /v1/messages HTTP/1.1", "the trailing slash of the address is not doubled");
assert_eq!(request.header("api-key"), Some(MADE_UP));
assert_eq!(request.header("x-api-key"), None, "the key goes only in the header that was named");
assert_eq!(request.header("anthropic-version"), Some("2023-06-01"));
assert_eq!(request.body["model"], "test-model");
assert_eq!(request.body["stream"], true);
assert_eq!(request.body["messages"][0]["content"], "hi");
}
#[test]
fn an_authorization_header_carries_the_key_as_a_bearer_token() {
let server = FakeServer::start(vec![(200, says("ok"))]);
endpoint(&server.address, "Authorization")
.stream(&json!({"messages": []}), &mut |_| {}, &|| false)
.expect("a reply");
assert_eq!(server.received()[0].header("authorization"), Some(format!("Bearer {MADE_UP}").as_str()));
}
#[test]
fn a_refusal_says_what_the_server_said_and_never_the_key() {
let body = json!({"type": "error", "error": {"type": "authentication_error", "message": "invalid api key"}});
let server = FakeServer::start(vec![(401, body.to_string())]);
let error = endpoint(&server.address, "x-api-key")
.stream(&json!({"messages": []}), &mut |_| {}, &|| false)
.expect_err("refused");
let ProviderError::Refused { status, said, .. } = &error else { panic!("a refusal, got {error:?}") };
assert_eq!((*status, said.as_str()), (401, "invalid api key"));
assert!(!error.to_string().contains(MADE_UP));
assert!(!format!("{error:?}").contains(MADE_UP));
}
#[test]
fn an_address_nobody_answers_is_unreachable_and_names_only_the_address() {
let port = std::net::TcpListener::bind("127.0.0.1:0").expect("a port").local_addr().expect("addr").port();
let error = endpoint(&format!("http://127.0.0.1:{port}"), "x-api-key")
.stream(&json!({"messages": []}), &mut |_| {}, &|| false)
.expect_err("nobody there");
assert!(matches!(error, ProviderError::Unreachable { .. }), "{error:?}");
assert!(!error.to_string().contains(MADE_UP));
}
#[test]
fn a_tool_call_comes_through_the_endpoint_whole() {
let server = FakeServer::start(vec![(200, super::fake::calls("t1", "list_dir", &json!({"path": "."})))]);
let mut announced = Vec::new();
let reply = endpoint(&server.address, "x-api-key")
.stream(&json!({"messages": []}), &mut |delta| announced.push(delta), &|| false)
.expect("a reply");
assert_eq!(announced, [Delta::ToolCall("list_dir".into())]);
assert_eq!(reply.stop, Stop::ToolUse);
assert_eq!(reply.content, [json!({"type": "tool_use", "id": "t1", "name": "list_dir", "input": {"path": "."}})]);
}
#[test]
fn a_refusal_that_repeats_the_key_shows_it_never() {
let body = json!({"error": {"message": format!("Incorrect API key provided: {MADE_UP}. Check it.")}});
let server = FakeServer::start(vec![(401, body.to_string())]);
let error = endpoint(&server.address, "authorization")
.stream(&json!({"messages": []}), &mut |_| {}, &|| false)
.expect_err("refused");
let shown = error.to_string();
assert!(!shown.contains(MADE_UP), "{shown}");
assert!(shown.contains("Incorrect API key provided"), "the rest of the sentence stays: {shown}");
assert!(!format!("{error:?}").contains(MADE_UP));
let event =
json!({"type": "error", "error": {"type": "authentication_error", "message": format!("bad key {MADE_UP}")}});
let server = FakeServer::start(vec![(200, super::fake::sse(&[event]))]);
let error = endpoint(&server.address, "x-api-key")
.stream(&json!({"messages": []}), &mut |_| {}, &|| false)
.expect_err("stopped");
assert!(!error.to_string().contains(MADE_UP), "{error}");
}
#[test]
fn a_server_that_goes_quiet_in_the_middle_of_a_reply_is_given_up_on() {
let half = super::fake::sse(&[
json!({"type": "message_start", "message": {"usage": {"input_tokens": 3}}}),
json!({"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}),
json!({"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Half"}}),
]);
let server = FakeServer::start_holding(vec![(200, half)], std::time::Duration::from_secs(600));
let mut quiet = endpoint(&server.address, "x-api-key");
quiet.silence = std::time::Duration::from_secs(2);
let started = std::time::Instant::now();
let error = quiet.stream(&json!({"messages": []}), &mut |_| {}, &|| false).expect_err("given up on");
assert!(started.elapsed() < std::time::Duration::from_secs(120), "it did not wait for ever");
assert!(error.to_string().contains("sent nothing for 2 seconds"), "the person is told: {error}");
}
#[test]
fn a_stop_ends_a_reply_that_is_waiting_for_the_server() {
let half = super::fake::sse(&[
json!({"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}),
json!({"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Half"}}),
]);
let server = FakeServer::start_holding(vec![(200, half)], std::time::Duration::from_secs(600));
let started = std::time::Instant::now();
let pressed = || started.elapsed() > std::time::Duration::from_secs(1);
let _ = endpoint(&server.address, "x-api-key").stream(&json!({"messages": []}), &mut |_| {}, &pressed);
assert!(
started.elapsed() < std::time::Duration::from_secs(60),
"the stop was heard while the server was quiet, long before the silence limit"
);
}
fn keyless(address: &str, header: &str) -> Endpoint {
Endpoint {
address: format!("{address}/"),
model: "relay-model".into(),
key_header: header.into(),
key: None,
silence: Endpoint::SILENCE,
}
}
#[test]
fn an_endpoint_without_a_key_sends_no_key_header() {
let server = FakeServer::start(vec![(200, says("hello"))]);
let reply = keyless(&server.address, "x-api-key")
.stream(&json!({"messages": []}), &mut |_| {}, &|| false)
.expect("a reply");
assert_eq!(reply.content, [json!({"type": "text", "text": "hello"})]);
let received = server.received();
assert_eq!(received.len(), 1);
assert_eq!(received[0].header("x-api-key"), None, "no key header at all");
assert_eq!(received[0].header("authorization"), None);
assert_eq!(received[0].body["model"], "relay-model", "the endpoint's model goes out");
}
#[test]
fn an_endpoint_without_a_key_sends_no_authorization_header_either() {
let server = FakeServer::start(vec![(200, says("hello"))]);
keyless(&server.address, "authorization")
.stream(&json!({"messages": []}), &mut |_| {}, &|| false)
.expect("a reply");
let received = server.received();
assert_eq!(received.len(), 1);
assert_eq!(received[0].header("authorization"), None, "not even a bearer with an empty key");
assert_eq!(received[0].header("x-api-key"), None);
}