rho-coding-agent 1.5.0

A lightweight agent harness inspired by Pi
Documentation
use tokio::{
    io::{AsyncReadExt, AsyncWriteExt},
    net::TcpListener,
};

use super::{error_for_status, MAX_ERROR_BODY_BYTES};
use crate::model::ModelError;

#[tokio::test]
async fn captures_and_truncates_error_response_bodies() {
    let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
    let address = listener.local_addr().unwrap();
    let body = "x".repeat(MAX_ERROR_BODY_BYTES + 100);
    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.unwrap();
        let mut request = [0_u8; 1024];
        let _ = socket.read(&mut request).await.unwrap();
        socket
            .write_all(
                format!(
                    "HTTP/1.1 400 Bad Request\r\nContent-Length: {}\r\n\r\n{body}",
                    body.len()
                )
                .as_bytes(),
            )
            .await
            .unwrap();
    });

    let response = reqwest::get(format!("http://{address}")).await.unwrap();
    let error = error_for_status(response).await.unwrap_err();
    server.await.unwrap();

    let ModelError::HttpStatus { status, body } = error else {
        panic!("expected HTTP status error");
    };
    assert_eq!(status, reqwest::StatusCode::BAD_REQUEST);
    assert_eq!(body.matches('x').count(), MAX_ERROR_BODY_BYTES);
    assert!(body.ends_with("[response body truncated]"));
}

#[tokio::test]
async fn preserves_status_when_reading_the_error_body_fails() {
    let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
    let address = listener.local_addr().unwrap();
    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await.unwrap();
        let mut request = [0_u8; 1024];
        let _ = socket.read(&mut request).await.unwrap();
        socket
            .write_all(b"HTTP/1.1 401 Unauthorized\r\nContent-Length: 100\r\n\r\npartial")
            .await
            .unwrap();
    });

    let response = reqwest::get(format!("http://{address}")).await.unwrap();
    let error = error_for_status(response).await.unwrap_err();
    server.await.unwrap();

    let ModelError::HttpStatus { status, body } = error else {
        panic!("expected HTTP status error");
    };
    assert_eq!(status, reqwest::StatusCode::UNAUTHORIZED);
    assert!(body.starts_with("partial"));
    assert!(body.ends_with("[response body read failed]"));
}