use std::path::{Path, PathBuf}; use assert_cmd::Command; use tempfile::TempDir; /// An isolated test environment with its own home, cache, and config directories. /// /// Setting `SWAGGER_CLI_HOME` on each command invocation ensures tests never /// touch the real user cache or config. pub struct TestEnv { // Kept alive for RAII cleanup; not read directly. _temp_dir: TempDir, pub home_dir: PathBuf, pub cache_dir: PathBuf, pub config_dir: PathBuf, } impl TestEnv { /// Creates a fresh isolated test environment backed by a temporary directory. pub fn new() -> Self { let temp_dir = TempDir::new().expect("failed to create temp dir"); let home_dir = temp_dir.path().join("swagger-cli-home"); let cache_dir = home_dir.join("cache"); let config_dir = home_dir.join("config"); std::fs::create_dir_all(&cache_dir).expect("failed to create cache dir"); std::fs::create_dir_all(&config_dir).expect("failed to create config dir"); Self { _temp_dir: temp_dir, home_dir, cache_dir, config_dir, } } } /// Run `swagger-cli` with the given arguments inside the provided test environment. /// /// `SWAGGER_CLI_HOME` is set so all reads/writes go to the temp directory. #[allow(deprecated)] pub fn run_cmd(env: &TestEnv, args: &[&str]) -> assert_cmd::assert::Assert { Command::cargo_bin("swagger-cli") .expect("binary not found -- run `cargo build` first") .env("SWAGGER_CLI_HOME", &env.home_dir) .args(args) .assert() } /// Fetch a fixture file into the test environment's cache under the given alias. /// /// Runs: `swagger-cli fetch --alias ` pub fn fetch_fixture(env: &TestEnv, fixture_name: &str, alias: &str) { let path = fixture_path(fixture_name); assert!( path.exists(), "fixture file does not exist: {}", path.display() ); let path_str = path.to_str().expect("fixture path is not valid UTF-8"); run_cmd(env, &["fetch", path_str, "--alias", alias]).success(); } /// Parse robot-mode JSON output from command stdout bytes. /// /// The robot envelope has the shape: `{"ok": true, "data": {...}, "meta": {...}}` pub fn parse_robot_json(output: &[u8]) -> serde_json::Value { let text = std::str::from_utf8(output).expect("stdout is not valid UTF-8"); serde_json::from_str(text.trim()).expect("stdout is not valid JSON") } /// Return the absolute path to a test fixture file by name. /// /// Fixtures live in `tests/fixtures/` relative to the project root. pub fn fixture_path(name: &str) -> PathBuf { let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set -- run via cargo"); Path::new(&manifest_dir) .join("tests") .join("fixtures") .join(name) } // --------------------------------------------------------------------------- // Self-tests for the helpers // --------------------------------------------------------------------------- #[cfg(test)] mod tests { use super::*; #[test] fn test_env_creates_directories() { let env = TestEnv::new(); assert!(env.home_dir.exists()); assert!(env.cache_dir.exists()); assert!(env.config_dir.exists()); } #[test] fn test_fixture_path_exists() { let path = fixture_path("petstore.json"); assert!(path.exists(), "petstore.json fixture missing"); assert!(path.is_absolute()); } #[test] fn test_fixture_path_yaml() { let path = fixture_path("petstore.yaml"); assert!(path.exists(), "petstore.yaml fixture missing"); } #[test] fn test_fixture_path_minimal() { let path = fixture_path("minimal.json"); assert!(path.exists(), "minimal.json fixture missing"); } #[test] fn test_parse_robot_json_valid() { let input = br#"{"ok":true,"data":{"x":1},"meta":{}}"#; let val = parse_robot_json(input); assert_eq!(val["ok"], true); assert_eq!(val["data"]["x"], 1); } #[test] #[should_panic(expected = "not valid JSON")] fn test_parse_robot_json_invalid() { parse_robot_json(b"not json"); } }