Wave 6: Integration tests, golden tests, index invariant tests, diff command (bd-rex, bd-2gp, bd-1ck)

This commit is contained in:
teernisse
2026-02-12 14:58:25 -05:00
parent 346fef9135
commit 398311ca4c
27 changed files with 2122 additions and 172 deletions

View File

@@ -104,3 +104,521 @@ fn test_fetch_minimal_fixture() {
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);
}