use predicates::prelude::PredicateBooleanExt;
use predicates::str::contains;
mod common;
use common::fez_fake_quiet as fez_fake;
#[test]
fn status_reports_default_zone_and_drift() {
fez_fake()
.args(["firewall", "status", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallStatus\""))
.stdout(contains("\"default_zone\":\"public\""))
.stdout(contains("\"panic_mode\":false"))
.stdout(contains("+port 9090/tcp"));
}
#[test]
fn list_shows_all_zones() {
fez_fake()
.args(["firewall", "list", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallZoneList\""))
.stdout(contains("public"))
.stdout(contains("internal"))
.stdout(contains("drop"));
}
#[test]
fn show_public_lists_services_and_ports() {
fez_fake()
.args(["firewall", "show", "public", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallZone\""))
.stdout(contains("ssh"))
.stdout(contains("9090/tcp"));
}
#[test]
fn show_unknown_zone_exits_4() {
fez_fake()
.args(["firewall", "show", "bogus"])
.assert()
.code(4);
}
#[test]
fn services_lists_catalog() {
fez_fake()
.args(["firewall", "services", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallServiceCatalog\""))
.stdout(contains("http"))
.stdout(contains("https"));
}
#[test]
fn add_service_runtime_only_with_hint() {
fez_fake()
.args(["firewall", "add-service", "http", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallChange\""))
.stdout(contains("\"persisted\":false"))
.stdout(contains("fez firewall confirm"));
}
#[test]
fn add_port_with_timeout() {
fez_fake()
.args([
"firewall",
"add-port",
"8080/tcp",
"--timeout",
"60",
"--json",
])
.assert()
.success()
.stdout(contains("\"timeout\":60"));
}
#[test]
fn add_port_bad_spec_exits_4() {
fez_fake()
.args(["firewall", "add-port", "nope"])
.assert()
.code(4);
}
#[test]
fn protected_op_refused_without_force() {
fez_fake()
.args(["firewall", "remove-service", "ssh", "--json"])
.assert()
.code(8)
.stdout(contains("\"code\":\"protected-unit\""));
}
#[test]
fn protected_op_allowed_with_force() {
fez_fake()
.args(["firewall", "remove-service", "ssh", "--force", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallChange\""));
}
#[test]
fn remove_port_succeeds() {
fez_fake()
.args(["firewall", "remove-port", "9090/tcp", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallChange\""))
.stdout(contains("\"persisted\":false"))
.stdout(contains("fez firewall confirm"));
}
#[test]
fn remove_port_gone_from_runtime_after_removal() {
fez_fake()
.env("FEZ_FAKE_PORT_REMOVED", "1")
.args(["firewall", "show", "public", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallZone\""))
.stdout(contains("ssh"))
.stdout(contains("9090/tcp").not());
}
#[test]
fn set_default_zone_requires_force() {
fez_fake()
.args(["firewall", "set-default-zone", "internal"])
.assert()
.code(8);
fez_fake()
.args([
"firewall",
"set-default-zone",
"internal",
"--force",
"--json",
])
.assert()
.success();
}
#[test]
fn panic_on_gated_off_allowed() {
fez_fake()
.args(["firewall", "panic", "on"])
.assert()
.code(8);
fez_fake()
.args(["firewall", "panic", "off", "--json"])
.assert()
.success()
.stdout(contains("\"panic_mode\":false"));
}
#[test]
fn confirm_persists() {
fez_fake()
.args(["firewall", "confirm", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallConfirm\""))
.stdout(contains("\"persisted\":true"));
}
#[test]
fn reload_with_drift_requires_force() {
fez_fake().args(["firewall", "reload"]).assert().code(8);
fez_fake()
.args(["firewall", "reload", "--force", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallChange\""));
}
#[test]
fn reload_with_config_info_denied_requires_force() {
fez_fake()
.env("FEZ_FAKE_CONFIG_INFO_DENIED", "1")
.args(["firewall", "reload", "--json"])
.assert()
.code(8)
.stdout(contains("\"code\":\"protected-unit\""))
.stdout(contains("firewall reload"));
fez_fake()
.env("FEZ_FAKE_CONFIG_INFO_DENIED", "1")
.args(["firewall", "reload", "--force", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallChange\""));
}
#[test]
fn reload_with_other_permanent_config_error_passes_through() {
fez_fake()
.env("FEZ_FAKE_CONFIG_UNKNOWN_METHOD", "1")
.args(["firewall", "reload", "--force", "--json"])
.assert()
.code(12)
.stdout(contains("\"code\":\"unsupported-api\""));
}
#[test]
fn firewalld_absent_exits_9() {
fez_fake()
.env("FEZ_FAKE_NO_FIREWALLD", "1")
.args(["firewall", "status", "--json"])
.assert()
.code(9)
.stdout(contains("\"code\":\"dependency-missing\""))
.stdout(contains("firewalld"));
}
#[test]
fn list_firewalld_absent_exits_9() {
fez_fake()
.env("FEZ_FAKE_NO_FIREWALLD", "1")
.args(["firewall", "list", "--json"])
.assert()
.code(9)
.stdout(contains("\"code\":\"dependency-missing\""));
}
#[test]
fn show_firewalld_absent_exits_9() {
fez_fake()
.env("FEZ_FAKE_NO_FIREWALLD", "1")
.args(["firewall", "show", "public", "--json"])
.assert()
.code(9)
.stdout(contains("\"code\":\"dependency-missing\""));
}
#[test]
fn mutation_without_escalation_exits_11() {
fez_fake()
.env("FEZ_FAKE_BRIDGES", "")
.args(["firewall", "add-service", "http"])
.assert()
.code(11);
}
#[test]
fn list_calls_getzones_on_zone_interface() {
fez_fake()
.args(["firewall", "list", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallZoneList\""))
.stdout(contains("internal"));
}
#[test]
fn show_resolves_zone_via_zone_interface() {
fez_fake()
.args(["firewall", "show", "internal", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallZone\""))
.stdout(contains("\"zone\":\"internal\""));
}
#[test]
fn status_without_escalation_exits_11() {
fez_fake()
.env("FEZ_FAKE_BRIDGES", "")
.args(["firewall", "status", "--json"])
.assert()
.code(11);
}
#[test]
fn status_with_escalation_reports_drift() {
fez_fake()
.args(["firewall", "status", "--json"])
.assert()
.success()
.stdout(contains("+port 9090/tcp"))
.stdout(contains("fez firewall confirm"));
}
#[test]
fn status_with_config_info_denied_reports_runtime_status() {
fez_fake()
.env("FEZ_FAKE_CONFIG_INFO_DENIED", "1")
.args(["firewall", "status", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallStatus\""))
.stdout(contains("\"pending_changes_available\":false"))
.stdout(contains("permanent firewall config was not readable"));
}
#[test]
fn panic_off_when_panic_on() {
fez_fake()
.env("FEZ_FAKE_PANIC", "1")
.args(["firewall", "panic", "off", "--json"])
.assert()
.success();
}
#[test]
fn show_public_reports_masquerade_on() {
fez_fake()
.args(["firewall", "show", "public", "--json"])
.assert()
.success()
.stdout(contains("\"masquerade\":true"));
}
#[test]
fn status_reports_masquerade_drift() {
fez_fake()
.args(["firewall", "status", "--json"])
.assert()
.success()
.stdout(contains("+masquerade"));
}
#[test]
fn masquerade_off_without_force_exits_8() {
fez_fake()
.args(["firewall", "masquerade", "off"])
.assert()
.code(8);
}
#[test]
fn masquerade_off_with_force_succeeds() {
fez_fake()
.args(["firewall", "masquerade", "off", "--force", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallChange\""))
.stdout(contains("\"masquerade\":false"));
}
#[test]
fn masquerade_on_succeeds() {
fez_fake()
.args(["firewall", "masquerade", "on"])
.assert()
.success();
}
#[test]
fn masquerade_on_json_reports_change() {
fez_fake()
.args(["firewall", "masquerade", "on", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallChange\""))
.stdout(contains("\"operation\":\"masquerade\""))
.stdout(contains("\"masquerade\":true"));
}
#[test]
fn masquerade_on_with_timeout_echoes_timeout() {
fez_fake()
.args(["firewall", "masquerade", "on", "--timeout", "60", "--json"])
.assert()
.success()
.stdout(contains("\"kind\":\"FirewallChange\""))
.stdout(contains("\"timeout\":60"));
}
#[test]
fn masquerade_on_without_escalation_exits_11() {
fez_fake()
.env("FEZ_FAKE_BRIDGES", "")
.args(["firewall", "masquerade", "on"])
.assert()
.code(11);
}
#[test]
fn unreachable_firewalld_maps_to_dependency_missing() {
fez_fake()
.env("FEZ_FAKE_FIREWALLD_UNREACHABLE", "1")
.args(["firewall", "list", "--json"])
.assert()
.code(9)
.stdout(contains("\"code\":\"dependency-missing\""))
.stdout(contains("firewalld"))
.stdout(contains("channel problem").not());
}
#[test]
fn unreachable_firewalld_includes_remediation_detail() {
fez_fake()
.env("FEZ_FAKE_FIREWALLD_UNREACHABLE", "1")
.args(["firewall", "status", "--json"])
.assert()
.code(9)
.stdout(contains("\"code\":\"dependency-missing\""))
.stdout(contains("firewalld.service"))
.stdout(contains("\"hints\""))
.stdout(contains("\"remediation\""))
.stdout(contains("fez services status firewalld.service"));
}
#[test]
fn missing_masquerade_method_maps_to_unsupported_api() {
fez_fake()
.env("FEZ_FAKE_NO_MASQUERADE", "1")
.args(["firewall", "status", "--json"])
.assert()
.code(12)
.stdout(contains("\"code\":\"unsupported-api\""))
.stdout(contains("getMasquerade"))
.stdout(contains("unsupported"));
}
#[test]
fn unreachable_firewalld_plain_text_is_actionable() {
fez_fake()
.env("FEZ_FAKE_FIREWALLD_UNREACHABLE", "1")
.args(["firewall", "list"])
.assert()
.code(9)
.stderr(contains("firewalld"))
.stderr(contains("channel problem").not());
}