| Crates.io | clawspec-core |
| lib.rs | clawspec-core |
| version | 0.2.0 |
| created_at | 2025-07-14 13:01:32.709704+00 |
| updated_at | 2025-07-19 20:04:26.704851+00 |
| description | Core library for generating OpenAPI specifications from tests |
| homepage | https://github.com/ilaborie/clawspec |
| repository | https://github.com/ilaborie/clawspec |
| max_upload_size | |
| id | 1751670 |
| size | 565,442 |
A Rust library for generating OpenAPI specifications from your HTTP client test code. Write tests, get documentation.
Clawspec automatically generates OpenAPI documentation by observing HTTP client interactions in your tests. Instead of maintaining separate API documentation, your tests become the source of truth.
Add to your Cargo.toml:
[dependencies]
clawspec-core = "0.1.4"
[dev-dependencies]
tokio = { version = "1", features = ["full"] }
use clawspec_core::ApiClient;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
#[derive(Debug, Serialize, Deserialize, ToSchema)]
struct User {
id: u64,
name: String,
email: String,
}
#[tokio::test]
async fn test_user_api() -> Result<(), Box<dyn std::error::Error>> {
// Create API client
let mut client = ApiClient::builder()
.with_host("api.example.com")
.build()?;
// Make requests - schemas are automatically captured
let user: User = client
.get("/users/123")?
.await?
.as_json()
.await?;
// Generate OpenAPI specification
let spec = client.collected_openapi().await;
let yaml = serde_yaml::to_string(&spec)?;
std::fs::write("openapi.yml", yaml)?;
Ok(())
}
For testing complete web applications. See the axum example for a full working implementation:
use clawspec_core::test_client::{TestClient, TestServer};
use std::net::TcpListener;
#[derive(Debug)]
struct MyServer;
impl TestServer for MyServer {
type Error = std::io::Error;
async fn launch(&self, listener: TcpListener) -> Result<(), Self::Error> {
// Start your web server with the provided listener
// Works with Axum, Warp, Actix-web, etc.
todo!("Launch your server")
}
}
#[tokio::test]
async fn test_with_server() -> Result<(), Box<dyn std::error::Error>> {
// Start test server and client
let mut client = TestClient::start(MyServer).await?;
// Test your API
let response = client
.post("/users")?
.json(&User {
id: 1,
name: "Alice".into(),
email: "alice@example.com".into()
})
.await?;
assert_eq!(response.status_code(), 201);
// Write OpenAPI specification
client.write_openapi("docs/api.yml").await?;
Ok(())
}
The main HTTP client that captures request/response schemas:
A test-focused wrapper providing:
use clawspec_core::{ApiClient, CallPath, CallQuery, CallHeaders, CallCookies, ParamValue};
let path = CallPath::from("/users/{id}/posts/{post_id}")
.add_param("id", ParamValue::new(123))
.add_param("post_id", ParamValue::new(456));
let query = CallQuery::new()
.add_param("page", ParamValue::new(1))
.add_param("limit", ParamValue::new(20));
let headers = CallHeaders::new()
.add_header("Authorization", "Bearer token")
.add_header("X-Request-ID", "abc123");
let cookies = CallCookies::new()
.add_cookie("session_id", "abc123")
.add_cookie("user_id", 456);
let response = client
.get(path)?
.with_query(query)
.with_headers(headers)
.with_cookies(cookies)
.exchange()
.await?;
By default, requests expect status codes in the range 200-499. You can customize this:
use clawspec_core::{ApiClient, expected_status_codes};
// Accept specific codes
client.post("/users")?
.with_expected_status_codes(expected_status_codes!(201, 202))
.await?;
// Accept ranges
client.get("/health")?
.with_expected_status_codes(expected_status_codes!(200-299))
.await?;
// Complex patterns
client.delete("/users/123")?
.with_expected_status_codes(expected_status_codes!(204, 404, 400-403))
.await?;
use clawspec_core::{ApiClient, register_schemas};
#[derive(serde::Deserialize, utoipa::ToSchema)]
struct CreateUserRequest {
name: String,
email: String,
}
#[derive(serde::Deserialize, utoipa::ToSchema)]
struct ErrorResponse {
code: String,
message: String,
}
// Register schemas for better documentation
register_schemas!(client, CreateUserRequest, ErrorResponse);
Clawspec supports various authentication methods with enhanced security features:
use clawspec_core::{ApiClient, Authentication};
// Bearer token authentication
let client = ApiClient::builder()
.with_host("api.example.com")
.with_authentication(Authentication::Bearer("my-api-token".into()))
.build()?;
// Basic authentication
let client = ApiClient::builder()
.with_authentication(Authentication::Basic {
username: "user".to_string(),
password: "pass".into(),
})
.build()?;
// API key authentication
let client = ApiClient::builder()
.with_authentication(Authentication::ApiKey {
header_name: "X-API-Key".to_string(),
key: "secret-key".into(),
})
.build()?;
// Per-request authentication override
let response = client
.get("/admin/users")?
.with_authentication(Authentication::Bearer("admin-token".into()))
.await?;
// Disable authentication for public endpoints
let public_data = client
.get("/public/health")?
.with_authentication_none()
.await?;
Bearer abcd...789)Handle cookie parameters with full OpenAPI documentation:
use clawspec_core::{ApiClient, CallCookies};
let cookies = CallCookies::new()
.add_cookie("session_id", "abc123")
.add_cookie("user_preferences", vec!["dark_mode", "notifications"])
.add_cookie("is_admin", true);
let response = client
.get("/dashboard")?
.with_cookies(cookies)
.await?;
For a complete working example, see the axum example implementation.
use axum::{Router, routing::get};
use clawspec_core::test_client::{TestClient, TestServer, HealthStatus};
struct AxumTestServer {
router: Router,
}
impl TestServer for AxumTestServer {
type Error = std::io::Error;
async fn launch(&self, listener: TcpListener) -> Result<(), Self::Error> {
listener.set_nonblocking(true)?;
let listener = tokio::net::TcpListener::from_std(listener)?;
axum::serve(listener, self.router.clone())
.await
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
}
async fn is_healthy(&self, client: &mut ApiClient) -> Result<HealthStatus, Self::Error> {
match client.get("/health").unwrap().await {
Ok(_) => Ok(HealthStatus::Healthy),
Err(_) => Ok(HealthStatus::Unhealthy),
}
}
}
Configure test server behavior:
use clawspec_core::test_client::TestServerConfig;
use std::time::Duration;
let config = TestServerConfig {
min_backoff_delay: Duration::from_millis(10),
max_backoff_delay: Duration::from_secs(1),
backoff_jitter: true,
max_retry_attempts: 10,
..Default::default()
};
The library provides comprehensive error types:
ApiClientError - HTTP client errors (includes authentication errors)AuthenticationError - Granular authentication failure detailsTestAppError - Test server errorsAll errors implement standard error traits and provide detailed context for debugging.
We welcome contributions! Please see our Contributing Guide for details.
Note: This project has been developed with assistance from Claude Code. All AI-generated code has been carefully reviewed, tested, and validated to ensure quality, security, and adherence to Rust best practices.
This project is licensed under either of:
at your option.
Built with excellent crates from the Rust ecosystem: