nils-gemini-cli 0.7.3

CLI crate for nils-gemini-cli in the nils-cli workspace.
Documentation
use gemini_cli::rate_limits::client::{UsageRequest, fetch_usage, read_tokens};
use nils_test_support::http::{HttpResponse, LoopbackServer};
use nils_test_support::{EnvGuard, GlobalStateLock};
use pretty_assertions::assert_eq;
use std::fs;

fn write_target(dir: &tempfile::TempDir, contents: &str) -> std::path::PathBuf {
    let path = dir.path().join("target.json");
    fs::write(&path, contents).expect("write target");
    path
}

#[test]
fn rate_limits_client_read_tokens_supports_nested_and_root_tokens() {
    let dir = tempfile::TempDir::new().expect("tempdir");
    let target = write_target(
        &dir,
        r#"{"tokens":{"access_token":"tok_nested"},"access_token":"tok_root"}"#,
    );
    let token = read_tokens(&target).expect("tokens");
    assert_eq!(token, "tok_nested");
}

#[test]
fn rate_limits_client_fetch_usage_errors_include_body_preview() {
    let server = LoopbackServer::new().expect("server");
    server.add_route(
        "POST",
        "/v1internal:retrieveUserQuota",
        HttpResponse::new(500, "hello\nworld\n"),
    );

    let dir = tempfile::TempDir::new().expect("tempdir");
    let target = write_target(&dir, r#"{"tokens":{"access_token":"tok"}}"#);

    let request = UsageRequest {
        target_file: target,
        refresh_on_401: false,
        endpoint: server.url(),
        api_version: "v1internal".to_string(),
        project: "projects/test".to_string(),
        connect_timeout_seconds: 1,
        max_time_seconds: 3,
    };

    let err = match fetch_usage(&request) {
        Ok(_) => panic!("expected fetch_usage to error"),
        Err(err) => err.to_string(),
    };
    assert!(err.contains("HTTP 500"));
    assert!(err.contains("body:"));
    assert!(err.contains("hello world"));
}

#[test]
fn rate_limits_client_fetch_usage_errors_without_body_when_empty() {
    let server = LoopbackServer::new().expect("server");
    server.add_route(
        "POST",
        "/v1internal:retrieveUserQuota",
        HttpResponse::new(500, ""),
    );

    let dir = tempfile::TempDir::new().expect("tempdir");
    let target = write_target(&dir, r#"{"tokens":{"access_token":"tok"}}"#);

    let request = UsageRequest {
        target_file: target,
        refresh_on_401: false,
        endpoint: server.url(),
        api_version: "v1internal".to_string(),
        project: "projects/test".to_string(),
        connect_timeout_seconds: 1,
        max_time_seconds: 3,
    };

    let err = match fetch_usage(&request) {
        Ok(_) => panic!("expected fetch_usage to error"),
        Err(err) => err.to_string(),
    };
    assert!(err.contains("HTTP 500"));
    assert!(!err.contains("body:"));
}

#[test]
fn rate_limits_client_fetch_usage_returns_raw_body_without_json_validation() {
    let server = LoopbackServer::new().expect("server");
    server.add_route(
        "POST",
        "/v1internal:retrieveUserQuota",
        HttpResponse::new(200, "not-json"),
    );

    let dir = tempfile::TempDir::new().expect("tempdir");
    let target = write_target(&dir, r#"{"tokens":{"access_token":"tok"}}"#);

    let request = UsageRequest {
        target_file: target,
        refresh_on_401: false,
        endpoint: server.url(),
        api_version: "v1internal".to_string(),
        project: "projects/test".to_string(),
        connect_timeout_seconds: 1,
        max_time_seconds: 3,
    };

    let response = fetch_usage(&request).expect("usage");
    assert_eq!(response.body, "not-json");
}

#[test]
fn rate_limits_client_fetch_usage_refreshes_on_401_when_enabled() {
    let lock = GlobalStateLock::new();
    let _auth = EnvGuard::remove(&lock, "GEMINI_AUTH_FILE");
    let _secrets = EnvGuard::remove(&lock, "GEMINI_SECRET_DIR");

    let server = LoopbackServer::new().expect("server");
    server.add_route(
        "POST",
        "/v1internal:retrieveUserQuota",
        HttpResponse::new(401, ""),
    );

    let dir = tempfile::TempDir::new().expect("tempdir");
    let target = write_target(&dir, r#"{"tokens":{"access_token":"tok"}}"#);

    let request = UsageRequest {
        target_file: target,
        refresh_on_401: true,
        endpoint: server.url(),
        api_version: "v1internal".to_string(),
        project: "projects/test".to_string(),
        connect_timeout_seconds: 1,
        max_time_seconds: 3,
    };

    let err = match fetch_usage(&request) {
        Ok(_) => panic!("expected fetch_usage to error"),
        Err(err) => err.to_string(),
    };
    assert!(err.contains("HTTP 401"));

    let requests = server.take_requests();
    assert_eq!(
        requests
            .iter()
            .filter(|r| r.method == "POST" && r.path == "/v1internal:retrieveUserQuota")
            .count(),
        2
    );
}