use utoipa::OpenApi;
#[derive(OpenApi)]
#[openapi(
paths(
crate::api::rest::handlers::create_session,
crate::api::rest::handlers::get_next_step,
crate::api::rest::handlers::replace_session,
crate::api::rest::handlers::update_session,
crate::api::rest::handlers::delete_session,
),
components(
schemas(
crate::api::rest::responses::OptionContractResponse,
crate::api::rest::responses::OptionPriceResponse,
crate::api::rest::responses::SessionInfoResponse,
crate::api::rest::requests::CreateSessionRequest,
crate::api::rest::requests::UpdateSessionRequest,
crate::api::rest::models::SessionId,
)
),
tags(
(name = "Options-Simulator", description = "Options Simulator endpoints")
)
)]
pub(crate) struct ApiDoc;
#[cfg(test)]
mod tests {
use super::*;
use serde_json::Value;
use utoipa::OpenApi;
#[test]
fn test_openapi_spec_generation() {
let openapi = ApiDoc::openapi();
assert!(
!openapi.to_json().expect("REASON").is_empty(),
"OpenAPI spec should not be empty"
);
}
#[test]
fn test_openapi_paths() {
let openapi = ApiDoc::openapi();
let json = openapi.to_json().unwrap();
let parsed: Value = serde_json::from_str(&json).expect("Failed to parse OpenAPI JSON");
assert!(parsed.get("paths").is_some(), "Paths section should exist");
let paths = parsed.get("paths").unwrap();
let expected_paths = vec![
"/api/v1/chain", ];
for path in expected_paths {
assert!(
paths.get(path).is_some(),
"Path {} should be defined in OpenAPI spec",
path
);
}
}
#[test]
fn test_openapi_schemas() {
let openapi = ApiDoc::openapi();
let json = openapi.to_json().unwrap();
let parsed: Value = serde_json::from_str(&json).expect("Failed to parse OpenAPI JSON");
let components = parsed
.get("components")
.expect("Components section should exist");
let schemas = components
.get("schemas")
.expect("Schemas section should exist");
let expected_schemas = vec![
"OptionContractResponse",
"OptionPriceResponse",
"SessionInfoResponse",
"CreateSessionRequest",
"UpdateSessionRequest",
"SessionId",
];
for schema_name in expected_schemas {
assert!(
schemas.get(schema_name).is_some(),
"Schema {} should be defined in OpenAPI spec",
schema_name
);
}
}
#[test]
fn test_openapi_tags() {
let openapi = ApiDoc::openapi();
let json = openapi.to_json().unwrap();
let parsed: Value = serde_json::from_str(&json).expect("Failed to parse OpenAPI JSON");
let tags = parsed.get("tags").expect("Tags section should exist");
assert!(tags.is_array(), "Tags should be an array");
let tag_exists = tags
.as_array()
.unwrap()
.iter()
.any(|tag| tag.get("name").and_then(|n| n.as_str()) == Some("Options-Simulator"));
assert!(tag_exists, "Options-Simulator tag should be defined");
}
#[test]
fn test_openapi_json_deserializability() {
let openapi = ApiDoc::openapi();
let json = openapi.to_json().unwrap();
let result: Result<serde_json::Value, _> = serde_json::from_str(&json);
assert!(result.is_ok(), "OpenAPI JSON should be valid JSON");
}
#[test]
fn test_no_sensitive_info_in_spec() {
let openapi = ApiDoc::openapi();
let json = openapi.to_json().unwrap();
assert!(
!json.contains("localhost")
&& !json.contains("127.0.0.1")
&& !json.contains("password")
&& !json.contains("secret"),
"OpenAPI spec should not contain sensitive information"
);
}
#[test]
fn test_openapi_version() {
let openapi = ApiDoc::openapi();
let json = openapi.to_json().unwrap();
let parsed: Value = serde_json::from_str(&json).expect("Failed to parse OpenAPI JSON");
assert!(
parsed.get("openapi").is_some(),
"OpenAPI version should be specified"
);
if let Some(version) = parsed.get("openapi") {
assert!(
version.as_str().is_some_and(|v| v.starts_with("3.")),
"OpenAPI version should be 3.x"
);
}
}
}