tinyhumans 0.1.0

Rust SDK for the TinyHumans backend
Documentation
use std::collections::BTreeSet;

use serde_json::json;
use tinyhumans_sdk::api::api_keys::{ApiKeyScope, CreateApiKeyRequest};
use tinyhumans_sdk::api::medulla::{CreateTaskRequest, TaskStatus};
use tinyhumans_sdk::generated_public_routes::PUBLIC_ROUTES;
use tinyhumans_sdk::TinyHumansClient;
use wiremock::matchers::{body_json, method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};

fn ok() -> ResponseTemplate {
    ResponseTemplate::new(200).set_body_json(json!({"success": true, "data": {"ok": true}}))
}

#[tokio::test]
async fn typed_api_key_request_uses_openapi_field_names() {
    let server = MockServer::start().await;
    Mock::given(method("POST"))
        .and(path("/api-keys"))
        .and(body_json(
            json!({"name":"CI","scopes":["inference"],"allowedIps":["10.0.0.0/8"]}),
        ))
        .respond_with(ok())
        .mount(&server)
        .await;
    let request = CreateApiKeyRequest {
        name: "CI".into(),
        scopes: vec![ApiKeyScope::Inference],
        allowed_ips: vec!["10.0.0.0/8".into()],
        expires_at: None,
    };
    TinyHumansClient::new(server.uri())
        .api_keys()
        .create(&request)
        .await
        .unwrap();
}

#[tokio::test]
async fn medulla_task_status_serializes_in_camel_case() {
    let server = MockServer::start().await;
    Mock::given(method("POST"))
        .and(path("/medulla/v1/tasks"))
        .and(body_json(json!({"title":"Ship","status":"inProgress"})))
        .respond_with(ok())
        .mount(&server)
        .await;
    let request = CreateTaskRequest {
        title: "Ship".into(),
        description: None,
        status: Some(TaskStatus::InProgress),
        recurrence: None,
    };
    TinyHumansClient::new(server.uri())
        .medulla()
        .create_task(&request)
        .await
        .unwrap();
}

#[tokio::test]
async fn path_segments_are_encoded_on_new_namespaces() {
    let server = MockServer::start().await;
    Mock::given(method("GET"))
        .and(path("/orchestration/v1/sessions/a%2Fb/messages"))
        .respond_with(ok())
        .mount(&server)
        .await;
    TinyHumansClient::new(server.uri())
        .orchestration()
        .session_messages("a/b", &[])
        .await
        .unwrap();
}

#[test]
fn generated_rust_routes_match_the_public_manifest() {
    let manifest: serde_json::Value =
        serde_json::from_str(include_str!("../api/tinyhumans.backend.json")).unwrap();
    let manifest_routes = manifest["namespaces"]
        .as_array()
        .unwrap()
        .iter()
        .flat_map(|namespace| namespace["routes"].as_array().unwrap())
        .map(|route| {
            let route = route.as_str().unwrap();
            route.split_once(' ').unwrap()
        })
        .collect::<BTreeSet<_>>();
    let rust_routes = PUBLIC_ROUTES.iter().copied().collect::<BTreeSet<_>>();

    assert_eq!(manifest["source"]["operationCount"], 187);
    assert_eq!(manifest["source"]["supplementalOperationCount"], 5);
    assert_eq!(manifest["source"]["excludedAdminOperationCount"], 35);
    assert_eq!(manifest["source"]["excludedWebhookOperationCount"], 18);
    assert_eq!(rust_routes.len(), 187);
    assert_eq!(rust_routes, manifest_routes);
    assert!(rust_routes
        .iter()
        .all(|(_, path)| !path.split('/').any(|segment| segment == "admin")));
    assert!(rust_routes
        .iter()
        .all(|(_, path)| !path.split('/').any(|segment| segment == "webhooks")));
}