rig-core 0.44.0

An opinionated library for building LLM powered applications.
Documentation
use super::*;

#[test]
fn test_model_new() {
    let model = ModelInfo::new("gpt-4", "GPT-4");
    assert_eq!(model.id, "gpt-4");
    assert_eq!(model.name, Some("GPT-4".to_string()));
}

#[test]
fn test_model_display() {
    let model = ModelInfo::new("gpt-4", "GPT-4");
    assert_eq!(format!("{model}"), "GPT-4");
}

#[test]
fn a_rejected_listing_names_its_provider_and_path() {
    let error = with_route(
        ProviderError::from_http_response(http::StatusCode::NOT_FOUND, "Not found")
            .with_provider_request_id(Some("req_1".into())),
        "openai",
        "/v1/models",
    );

    let ProviderError::ProviderResponse(response) = &error else {
        panic!("a rejected listing keeps the provider's reply: {error:?}");
    };
    assert_eq!(response.status, Some(http::StatusCode::NOT_FOUND));
    assert_eq!(response.body, "Not found");
    assert_eq!(
        error.to_string(),
        "ProviderResponseError: status 404 Not Found: Not found (request id: req_1) \
         [provider=openai path=/v1/models]"
    );
}

#[test]
fn a_listing_that_did_not_parse_names_its_provider_and_path() {
    let json = serde_json::from_str::<serde_json::Value>("{").expect_err("malformed");
    for error in [
        ProviderError::Json(json.into()),
        ProviderError::Response("Invalid JSON".to_owned()),
    ] {
        let ProviderError::Response(message) = with_route(error, "openai", "/v1/models") else {
            panic!("a listing decode failure reports as a response error");
        };
        assert!(message.starts_with("provider=openai\npath=/v1/models\nparse_error\n"));
    }
}

#[test]
fn a_listing_transport_failure_is_unchanged_by_its_route() {
    let error = with_route(
        ProviderError::Http(crate::http_client::Error::StreamEnded.into()),
        "openai",
        "/v1/models",
    );
    assert!(matches!(
        error,
        ProviderError::Http(ref error) if matches!(**error, crate::http_client::Error::StreamEnded)
    ));
    assert!(error.is_retryable());
}

#[test]
fn a_listing_route_is_a_diagnostic_and_is_not_serialized() {
    let error = with_route(
        ProviderError::from_http_response(http::StatusCode::UNAUTHORIZED, "bad key"),
        "openai",
        "/v1/models",
    );
    let report = error.report();
    let restored: crate::error::ErrorReport =
        serde_json::from_str(&serde_json::to_string(&report).expect("serialize"))
            .expect("deserialize");
    let response = restored.provider_response.expect("reply is preserved");
    assert_eq!(response.route, None);
    assert_eq!(response.status, Some(http::StatusCode::UNAUTHORIZED));
    assert!(report.message.contains("[provider=openai path=/v1/models]"));
}

#[test]
fn test_model_serde() {
    let model = ModelInfo {
        id: "gpt-4".to_string(),
        name: Some("GPT-4".to_string()),
        description: None,
        r#type: Some("chat".to_string()),
        created_at: Some(1677610600),
        owned_by: Some("openai".to_string()),
        context_length: Some(8192),
        max_output_tokens: Some(4096),
    };

    let json = serde_json::to_string(&model).unwrap();
    assert!(json.contains("gpt-4"));
    assert!(json.contains("GPT-4"));

    let deserialized: ModelInfo = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.id, "gpt-4");
    assert_eq!(deserialized.name, Some("GPT-4".to_string()));
}

#[test]
fn test_model_list_serde() {
    let list = ModelList {
        data: vec![ModelInfo::from_id("gpt-4")],
    };

    let json = serde_json::to_string(&list).unwrap();
    assert!(json.contains("gpt-4"));

    let deserialized: ModelList = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.len(), 1);
}

#[test]
fn test_format_response_body_preview_with_truncation() {
    let body = vec![b'a'; RESPONSE_BODY_PREVIEW_LIMIT + 3];
    let preview = format_response_body_preview(&body);

    assert!(preview.starts_with(&"a".repeat(RESPONSE_BODY_PREVIEW_LIMIT)));
    assert!(preview.ends_with("\n...<truncated 3 bytes>"));
}

#[test]
fn test_parse_error_with_context_includes_parse_error_and_preview() {
    let body = br#"{"models":[{"displayName":"broken"}]}"#;
    let json_error = serde_json::from_slice::<serde_json::Value>(b"{")
        .expect_err("expected malformed JSON to fail");
    let error = parse_error(
        "Gemini",
        "/v1beta/models?pageSize=1000",
        format_args!("parse_error={json_error}"),
        body,
    );

    match error {
        ProviderError::Response(message) => {
            assert!(message.contains("provider=Gemini"));
            assert!(message.contains("path=/v1beta/models?pageSize=1000"));
            assert!(message.contains("parse_error=EOF while parsing an object"));
            assert!(message.contains(r#"{"models":[{"displayName":"broken"}]}"#));
        }
        _ => panic!("Expected a response error"),
    }
}