#![allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "test code: unwrap/expect on assert_cmd assertions is the expected diagnostic"
)]
mod common;
use predicates::prelude::*;
use predicates::str::contains;
use tempfile::tempdir;
use common::aviso;
#[test]
fn rule_1_admin_no_yes_exits_2() {
aviso()
.args(["admin", "wipe-all"])
.assert()
.failure()
.code(2);
}
#[test]
fn rule_1_runtime_server_500_exits_1() {
let dir = tempdir().unwrap();
let cfg = dir.path().join("config.yaml");
std::fs::write(&cfg, "").unwrap();
aviso()
.args([
"--config",
cfg.to_str().unwrap(),
"--base-url",
"http://127.0.0.1:9", "schema",
"list",
])
.assert()
.failure()
.code(1);
}
#[test]
fn rule_2_error_starts_with_one_line_summary() {
aviso()
.args(["admin", "wipe-all"])
.assert()
.failure()
.stderr(contains("error: "));
}
#[test]
fn rule_3_listener_file_parse_error_names_absolute_path() {
let dir = tempdir().unwrap();
let bad = dir.path().join("bad.yaml");
std::fs::write(&bad, "listeners:\n - bogus_field: 1\n").unwrap();
aviso()
.args([
"--base-url",
"http://unused",
"listen",
bad.to_str().unwrap(),
])
.assert()
.failure()
.code(1)
.stderr(contains(bad.display().to_string()));
}
#[test]
fn rule_4_yaml_error_includes_line_position() {
let dir = tempdir().unwrap();
let bad = dir.path().join("bad.yaml");
std::fs::write(&bad, "base_url: foo\nbogus_top_level: 1\n").unwrap();
aviso()
.args(["--config", bad.to_str().unwrap(), "config", "dump"])
.assert()
.failure()
.code(1)
.stderr(contains("line").or(contains("bogus_top_level")));
}
#[test]
fn rule_5_serde_unknown_field_is_named() {
let dir = tempdir().unwrap();
let bad = dir.path().join("config.yaml");
std::fs::write(&bad, "definitely_unknown_field: 1\n").unwrap();
aviso()
.args(["--config", bad.to_str().unwrap(), "config", "dump"])
.assert()
.failure()
.code(1)
.stderr(contains("definitely_unknown_field").or(contains("unknown field")));
}
#[test]
fn rule_6_admin_no_yes_includes_suggestion() {
aviso()
.args(["admin", "wipe-all"])
.assert()
.failure()
.stderr(contains("--yes"));
}
#[test]
fn rule_6_no_listeners_includes_fix_guidance() {
let dir = tempdir().unwrap();
let cfg = dir.path().join("config.yaml");
std::fs::write(&cfg, "").unwrap();
aviso()
.args([
"--config",
cfg.to_str().unwrap(),
"--base-url",
"http://unused",
"listen",
])
.assert()
.failure()
.code(2)
.stderr(contains("pass listener YAML files").or(contains("listeners:")));
}
#[test]
fn rule_6_partial_auth_env_exits_2_with_misconfig_message() {
aviso()
.env("AVISO_USERNAME", "alice")
.args(["--base-url", "http://unused", "schema", "list"])
.assert()
.failure()
.code(2)
.stderr(contains("misconfigured").or(contains("AVISO_PASSWORD")));
}
#[test]
fn rule_6_partial_auth_env_password_only_exits_2_with_misconfig_message() {
aviso()
.env("AVISO_PASSWORD", "wonderland")
.args(["--base-url", "http://unused", "schema", "list"])
.assert()
.failure()
.code(2)
.stderr(contains("misconfigured").or(contains("AVISO_USERNAME")));
}
#[test]
fn rule_7_caused_by_chain_present_for_layered_errors() {
let dir = tempdir().unwrap();
let bad = dir.path().join("config.yaml");
std::fs::write(&bad, "definitely_unknown_field: 1\n").unwrap();
aviso()
.args(["--config", bad.to_str().unwrap(), "config", "dump"])
.assert()
.failure()
.code(1)
.stderr(contains("Caused by:"));
}
#[test]
fn rule_8_token_value_not_leaked_in_error_output() {
aviso()
.args([
"--base-url",
"http://127.0.0.1:9",
"--token",
"do-not-leak-this-secret-in-stderr",
"schema",
"list",
])
.assert()
.failure()
.code(1)
.stderr(predicates::str::contains("do-not-leak-this-secret-in-stderr").not());
}