link-assistant-router 1.11.1

Link.Assistant.Router — Claude MAX OAuth proxy and token gateway for Anthropic APIs
Documentation
use super::*;

fn messages_request(headers: &HeaderMap, body: &serde_json::Value) -> Request<Body> {
    let mut request = Request::builder()
        .method("POST")
        .uri("/api/services/anthropic/v1/messages")
        .body(Body::from(serde_json::to_vec(body).unwrap()))
        .unwrap();
    *request.headers_mut() = headers.clone();
    request
}

#[tokio::test]
async fn messages_ingress_rejects_required_fields_before_zai_catalog_or_inference() {
    let (base_url, requests, handle) = recording_upstream().await;
    let data = tempfile::tempdir().unwrap();
    let mut state = crate::model_routing::tests::auto_state(Vec::new(), data.path());
    install_provider(&mut state, &base_url, &[]);
    let headers = client_headers(&state, ClientKind::ClaudeCode, "owner-a");
    let complete = serde_json::json!({
        "model": "glm-5.3-flash",
        "max_tokens": 8,
        "messages": [{"role": "user", "content": "ping"}]
    });

    for field in ["model", "max_tokens", "messages"] {
        let mut body = complete.clone();
        body.as_object_mut().unwrap().remove(field);
        let response = crate::proxy::proxy_handler(
            axum::extract::State(state.clone()),
            messages_request(&headers, &body),
        )
        .await;
        assert_eq!(response.status(), StatusCode::BAD_REQUEST, "{field}");
        let body = response.into_body().collect().await.unwrap().to_bytes();
        let payload: serde_json::Value = serde_json::from_slice(&body).unwrap();
        assert_eq!(payload["type"], "error", "{field}: {payload}");
        assert_eq!(payload["error"]["type"], "invalid_request_error");
        assert!(
            payload["error"]["message"]
                .as_str()
                .is_some_and(|message| message.contains(field)),
            "{field}: {payload}"
        );
    }

    assert!(
        requests.lock().unwrap().is_empty(),
        "invalid Messages input contacted z.ai"
    );
    handle.abort();
}

#[tokio::test]
async fn valid_glm_flash_messages_request_still_reaches_zai() {
    let (base_url, requests, handle) = recording_upstream().await;
    let data = tempfile::tempdir().unwrap();
    let mut state = crate::model_routing::tests::auto_state(Vec::new(), data.path());
    install_provider(&mut state, &base_url, &[]);
    let headers = client_headers(&state, ClientKind::ClaudeCode, "owner-a");
    let body = serde_json::json!({
        "model": "glm-5.3-flash",
        "max_tokens": 8,
        "messages": [{"role": "user", "content": "ping"}]
    });
    let response = crate::proxy::proxy_handler(
        axum::extract::State(state),
        messages_request(&headers, &body),
    )
    .await;

    assert_eq!(response.status(), StatusCode::OK);
    let calls = requests.lock().unwrap();
    assert_eq!(calls.len(), 2, "one catalog call and one inference call");
    assert_eq!(calls[0].0, crate::zai_coding_plan::CATALOG_PATH);
    assert_eq!(calls[1].0, "/api/anthropic/v1/messages");
    assert_eq!(
        serde_json::from_str::<serde_json::Value>(&calls[1].2).unwrap(),
        body
    );
    drop(calls);
    handle.abort();
}