mod helpers; #[test] fn test_petstore_json_is_valid() { let path = helpers::fixture_path("petstore.json"); let content = std::fs::read_to_string(&path).unwrap(); let json: serde_json::Value = serde_json::from_str(&content).unwrap(); assert!(json.get("openapi").is_some()); assert!(json.get("paths").is_some()); assert!(json.get("components").is_some()); } #[test] fn test_petstore_yaml_is_valid() { let path = helpers::fixture_path("petstore.yaml"); let content = std::fs::read_to_string(&path).unwrap(); let yaml: serde_json::Value = serde_yaml::from_str(&content).unwrap(); assert!(yaml.get("openapi").is_some()); assert!(yaml.get("paths").is_some()); assert!(yaml.get("components").is_some()); } #[test] fn test_minimal_json_is_valid() { let path = helpers::fixture_path("minimal.json"); let content = std::fs::read_to_string(&path).unwrap(); let json: serde_json::Value = serde_json::from_str(&content).unwrap(); assert_eq!(json["openapi"], "3.0.3"); assert_eq!(json["info"]["title"], "Minimal API"); let paths = json["paths"].as_object().unwrap(); assert_eq!(paths.len(), 2); // /items and /items/{id} // Count total operations: GET /items, POST /items, GET /items/{id} = 3 let items_ops = json["paths"]["/items"].as_object().unwrap(); let item_by_id_ops = json["paths"]["/items/{id}"].as_object().unwrap(); let op_count = items_ops.len() + item_by_id_ops.len(); // /items has get+post (2), /items/{id} has get+parameters (but parameters isn't an op) assert!(op_count >= 3); let schemas = json["components"]["schemas"].as_object().unwrap(); assert_eq!(schemas.len(), 2); // Item and ItemList } #[test] fn test_petstore_yaml_matches_json_structure() { let json_path = helpers::fixture_path("petstore.json"); let yaml_path = helpers::fixture_path("petstore.yaml"); let json_content = std::fs::read_to_string(&json_path).unwrap(); let yaml_content = std::fs::read_to_string(&yaml_path).unwrap(); let json_val: serde_json::Value = serde_json::from_str(&json_content).unwrap(); let yaml_val: serde_json::Value = serde_yaml::from_str(&yaml_content).unwrap(); // Both should have the same top-level keys assert_eq!(json_val["openapi"], yaml_val["openapi"]); assert_eq!(json_val["info"]["title"], yaml_val["info"]["title"]); assert_eq!(json_val["info"]["version"], yaml_val["info"]["version"]); // Same number of paths let json_paths = json_val["paths"].as_object().unwrap(); let yaml_paths = yaml_val["paths"].as_object().unwrap(); assert_eq!(json_paths.len(), yaml_paths.len()); // Same schemas let json_schemas = json_val["components"]["schemas"].as_object().unwrap(); let yaml_schemas = yaml_val["components"]["schemas"].as_object().unwrap(); assert_eq!(json_schemas.len(), yaml_schemas.len()); } #[test] fn test_fetch_and_list_fixture() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["list", "petstore", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); assert!(!json["data"]["endpoints"].as_array().unwrap().is_empty()); assert!(json["data"]["total"].as_u64().unwrap() > 0); } #[test] fn test_fetch_yaml_fixture() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.yaml", "petstore-yaml"); let assert = helpers::run_cmd(&env, &["list", "petstore-yaml", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); } #[test] fn test_fetch_minimal_fixture() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "minimal.json", "minimal"); let assert = helpers::run_cmd(&env, &["list", "minimal", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); assert_eq!(json["data"]["total"], 3); } // =========================================================================== // Fetch tests // =========================================================================== #[test] fn test_fetch_success_robot() { let env = helpers::TestEnv::new(); let fixture = helpers::fixture_path("petstore.json"); let assert = helpers::run_cmd( &env, &[ "fetch", fixture.to_str().unwrap(), "--alias", "ps", "--robot", ], ) .success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); assert_eq!(json["data"]["alias"], "ps"); assert!(json["data"]["endpoint_count"].as_u64().unwrap() > 0); assert!(json["data"]["schema_count"].as_u64().unwrap() > 0); assert!( json["data"]["content_hash"] .as_str() .unwrap() .starts_with("sha256:") ); assert_eq!(json["data"]["source_format"], "json"); assert_eq!(json["meta"]["command"], "fetch"); assert!(json["meta"]["schema_version"].as_u64().is_some()); assert!(json["meta"]["duration_ms"].as_u64().is_some()); } #[test] fn test_fetch_invalid_json() { let env = helpers::TestEnv::new(); // Create a non-JSON, non-YAML file that is not a valid OpenAPI spec let bad_file = env.home_dir.join("not-a-spec.json"); std::fs::write(&bad_file, b"this is not json or yaml").unwrap(); let assert = helpers::run_cmd( &env, &[ "fetch", bad_file.to_str().unwrap(), "--alias", "bad", "--robot", ], ); // Should fail -- the file is not valid JSON or YAML let output = assert.get_output(); assert!(!output.status.success()); } #[test] fn test_fetch_alias_exists_error() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "dupe"); let fixture = helpers::fixture_path("petstore.json"); let assert = helpers::run_cmd( &env, &[ "fetch", fixture.to_str().unwrap(), "--alias", "dupe", "--robot", ], ); let output = assert.get_output(); assert!(!output.status.success()); // Exit code 6 = ALIAS_EXISTS let stderr_json = helpers::parse_robot_json(&output.stderr); assert_eq!(stderr_json["ok"], false); assert_eq!(stderr_json["error"]["code"], "ALIAS_EXISTS"); } #[test] fn test_fetch_force_overwrite() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "overwrite-me"); let fixture = helpers::fixture_path("minimal.json"); let assert = helpers::run_cmd( &env, &[ "fetch", fixture.to_str().unwrap(), "--alias", "overwrite-me", "--force", "--robot", ], ) .success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); assert_eq!(json["data"]["alias"], "overwrite-me"); // After force overwrite, the title should be from minimal.json assert_eq!(json["data"]["title"], "Minimal API"); } #[test] fn test_fetch_yaml_success() { let env = helpers::TestEnv::new(); let fixture = helpers::fixture_path("petstore.yaml"); let assert = helpers::run_cmd( &env, &[ "fetch", fixture.to_str().unwrap(), "--alias", "yaml-test", "--robot", ], ) .success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); assert_eq!(json["data"]["source_format"], "yaml"); assert!(json["data"]["endpoint_count"].as_u64().unwrap() > 0); } #[test] fn test_fetch_stdin() { let env = helpers::TestEnv::new(); let fixture = helpers::fixture_path("petstore.json"); let content = std::fs::read(&fixture).unwrap(); #[allow(deprecated)] let assert = assert_cmd::Command::cargo_bin("swagger-cli") .expect("binary not found") .env("SWAGGER_CLI_HOME", &env.home_dir) .args(["fetch", "-", "--alias", "stdin-test", "--robot"]) .write_stdin(content) .assert() .success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); assert_eq!(json["data"]["alias"], "stdin-test"); } // =========================================================================== // List tests // =========================================================================== #[test] fn test_list_filter_by_method() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["list", "petstore", "--method", "GET", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); let endpoints = json["data"]["endpoints"].as_array().unwrap(); assert!(!endpoints.is_empty()); for ep in endpoints { assert_eq!(ep["method"], "GET"); } } #[test] fn test_list_filter_by_tag() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["list", "petstore", "--tag", "store", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); let endpoints = json["data"]["endpoints"].as_array().unwrap(); assert!(!endpoints.is_empty()); for ep in endpoints { let tags = ep["tags"].as_array().unwrap(); let tag_strings: Vec<&str> = tags.iter().filter_map(|t| t.as_str()).collect(); assert!( tag_strings .iter() .any(|t| t.to_lowercase().contains("store")), "Expected 'store' tag in {tag_strings:?}" ); } } #[test] fn test_list_path_regex() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["list", "petstore", "--path", "pet.*", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); let endpoints = json["data"]["endpoints"].as_array().unwrap(); assert!(!endpoints.is_empty()); for ep in endpoints { let path = ep["path"].as_str().unwrap(); assert!( path.contains("pet"), "Expected path to match 'pet.*', got: {path}" ); } } #[test] fn test_list_invalid_regex_error() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["list", "petstore", "--path", "[invalid", "--robot"]); let output = assert.get_output(); assert!(!output.status.success()); let stderr_json = helpers::parse_robot_json(&output.stderr); assert_eq!(stderr_json["ok"], false); assert_eq!(stderr_json["error"]["code"], "USAGE_ERROR"); } #[test] fn test_list_limit() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["list", "petstore", "--limit", "2", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); let endpoints = json["data"]["endpoints"].as_array().unwrap(); assert!(endpoints.len() <= 2); } #[test] fn test_list_all_flag() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["list", "petstore", "--all", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); let endpoints = json["data"]["endpoints"].as_array().unwrap(); let total = json["data"]["total"].as_u64().unwrap() as usize; // With --all, endpoints shown should equal total (no truncation) assert_eq!(endpoints.len(), total); } // =========================================================================== // Show tests // =========================================================================== #[test] fn test_show_endpoint_details() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd( &env, &["show", "petstore", "/pets", "--method", "GET", "--robot"], ) .success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); assert_eq!(json["data"]["path"], "/pets"); assert_eq!(json["data"]["method"], "GET"); assert!(json["data"]["summary"].as_str().is_some()); } #[test] fn test_show_method_disambiguation() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); // /pets has both GET and POST -- omitting --method should error let assert = helpers::run_cmd(&env, &["show", "petstore", "/pets", "--robot"]); let output = assert.get_output(); assert!(!output.status.success()); let stderr_json = helpers::parse_robot_json(&output.stderr); assert_eq!(stderr_json["ok"], false); assert_eq!(stderr_json["error"]["code"], "USAGE_ERROR"); } // =========================================================================== // Search tests // =========================================================================== #[test] fn test_search_basic() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["search", "petstore", "pet", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); let total = json["data"]["total"].as_u64().unwrap(); assert!(total > 0, "Expected search results for 'pet'"); } #[test] fn test_search_no_results() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd( &env, &["search", "petstore", "zzzznonexistent99999", "--robot"], ) .success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); assert_eq!(json["data"]["total"], 0); assert!(json["data"]["results"].as_array().unwrap().is_empty()); } #[test] fn test_search_case_insensitive() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); // Default search is case-insensitive; "PET" should match "pet" paths let assert = helpers::run_cmd(&env, &["search", "petstore", "PET", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); let total = json["data"]["total"].as_u64().unwrap(); assert!( total > 0, "Expected case-insensitive search for 'PET' to find results" ); } // =========================================================================== // Schemas tests // =========================================================================== #[test] fn test_schemas_list() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["schemas", "petstore", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); let schemas = json["data"]["schemas"].as_array().unwrap(); assert!(!schemas.is_empty()); let names: Vec<&str> = schemas.iter().filter_map(|s| s["name"].as_str()).collect(); assert!( names.contains(&"Pet"), "Expected 'Pet' schema, got: {names:?}" ); assert!( names.contains(&"Error"), "Expected 'Error' schema, got: {names:?}" ); } #[test] fn test_schemas_show() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["schemas", "petstore", "--show", "Pet", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); assert_eq!(json["data"]["name"], "Pet"); assert!(json["data"]["schema"].is_object()); } // =========================================================================== // Tags tests // =========================================================================== #[test] fn test_tags_list() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "petstore"); let assert = helpers::run_cmd(&env, &["tags", "petstore", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); let tags = json["data"]["tags"].as_array().unwrap(); assert!(!tags.is_empty()); let names: Vec<&str> = tags.iter().filter_map(|t| t["name"].as_str()).collect(); assert!( names.contains(&"pets"), "Expected 'pets' tag, got: {names:?}" ); assert!( names.contains(&"store"), "Expected 'store' tag, got: {names:?}" ); // Each tag should have an endpoint_count for tag in tags { assert!( tag["endpoint_count"].as_u64().is_some(), "Expected endpoint_count on tag: {tag}" ); } } // =========================================================================== // Aliases tests // =========================================================================== #[test] fn test_aliases_list() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "alias-one"); helpers::fetch_fixture(&env, "minimal.json", "alias-two"); let assert = helpers::run_cmd(&env, &["aliases", "--list", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); let aliases = json["data"]["aliases"].as_array().unwrap(); let names: Vec<&str> = aliases.iter().filter_map(|a| a["name"].as_str()).collect(); assert!( names.contains(&"alias-one"), "Expected 'alias-one', got: {names:?}" ); assert!( names.contains(&"alias-two"), "Expected 'alias-two', got: {names:?}" ); assert_eq!(json["data"]["count"].as_u64().unwrap(), 2); } #[test] fn test_aliases_set_default() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "my-default"); let assert = helpers::run_cmd(&env, &["aliases", "--set-default", "my-default", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); assert_eq!(json["data"]["name"], "my-default"); // Verify by listing let list_assert = helpers::run_cmd(&env, &["aliases", "--list", "--robot"]).success(); let list_json = helpers::parse_robot_json(&list_assert.get_output().stdout); assert_eq!(list_json["data"]["default_alias"], "my-default"); } // =========================================================================== // Doctor tests // =========================================================================== #[test] fn test_doctor_healthy() { let env = helpers::TestEnv::new(); helpers::fetch_fixture(&env, "petstore.json", "healthy-spec"); let assert = helpers::run_cmd(&env, &["doctor", "--robot"]).success(); let json = helpers::parse_robot_json(&assert.get_output().stdout); assert_eq!(json["ok"], true); // health should be "healthy" or "warning" (warning is ok if stale threshold is very low) let health = json["data"]["health"].as_str().unwrap(); assert!( health == "healthy" || health == "warning", "Expected healthy or warning, got: {health}" ); let aliases = json["data"]["aliases"].as_array().unwrap(); assert!(!aliases.is_empty()); let spec_report = aliases .iter() .find(|a| a["name"] == "healthy-spec") .unwrap(); assert_eq!(spec_report["status"], "healthy"); assert!(spec_report["endpoint_count"].as_u64().unwrap() > 0); }