pub mod app_validator;
pub mod diagnostics;
pub mod report;
pub mod scenario;
pub mod scenario_executor;
pub mod scenario_generator;
pub mod tester;
pub mod validators;
pub use app_validator::{AppValidationMode, AppValidator};
pub use pmcp::client::oauth;
pub use pmcp::client::oauth::{OAuthConfig, OAuthHelper};
pub use report::{OutputFormat, TestReport, TestResult, TestStatus};
pub use scenario::TestScenario;
pub use scenario_executor::ScenarioExecutor;
pub use scenario_generator::ScenarioGenerator;
pub use tester::ServerTester;
use anyhow::{Context, Result};
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct GenerateOptions {
pub all_tools: bool,
pub with_resources: bool,
pub with_prompts: bool,
}
impl Default for GenerateOptions {
fn default() -> Self {
Self {
all_tools: true,
with_resources: false,
with_prompts: false,
}
}
}
pub async fn generate_scenarios(
server_url: &str,
output_path: &str,
options: GenerateOptions,
) -> Result<()> {
generate_scenarios_with_transport(server_url, output_path, options, None).await
}
pub async fn generate_scenarios_with_transport(
server_url: &str,
output_path: &str,
options: GenerateOptions,
transport: Option<&str>,
) -> Result<()> {
let mut tester = ServerTester::new(
server_url,
Duration::from_secs(30),
false, None, transport, None, )?;
let generator = ScenarioGenerator::new(
server_url.to_string(),
options.all_tools,
options.with_resources,
options.with_prompts,
);
generator.generate(&mut tester, output_path).await
}
pub async fn run_scenario(scenario_path: &str, server_url: &str, detailed: bool) -> Result<()> {
run_scenario_with_transport(scenario_path, server_url, detailed, None).await
}
pub async fn run_scenario_with_transport(
scenario_path: &str,
server_url: &str,
detailed: bool,
transport: Option<&str>,
) -> Result<()> {
use colored::*;
let scenario_content = std::fs::read_to_string(scenario_path)
.with_context(|| format!("Failed to read scenario file: {}", scenario_path))?;
let scenario: TestScenario = if scenario_path.ends_with(".json") {
serde_json::from_str(&scenario_content)?
} else {
serde_yaml::from_str(&scenario_content)?
};
let mut tester = ServerTester::new(
server_url,
Duration::from_secs(30),
false, None, transport, None, )?;
let mut executor = ScenarioExecutor::new(&mut tester, detailed);
if detailed {
println!(
"\n{}",
"Running scenario with detailed output..."
.bright_cyan()
.bold()
);
}
let result = executor.execute(scenario).await?;
if result.success {
println!("\n{} Scenario passed!", "✓".green().bold());
Ok(())
} else {
println!("\n{} Scenario failed!", "✗".red().bold());
anyhow::bail!("Scenario execution failed");
}
}
pub fn create_tester(
url: &str,
timeout_secs: u64,
insecure: bool,
api_key: Option<String>,
) -> Result<ServerTester> {
ServerTester::new(
url,
Duration::from_secs(timeout_secs),
insecure,
api_key.as_deref(),
None, None, )
}