#include #include #include #include #include "../../include/fold/cli/scan_option_catalog.hpp" TEST_CASE("scan option catalog has unique canonical and compatibility names", "[scan-options][contract]") { std::set keys; std::set cli_flags; std::set mcp_names; std::size_t mcp_count = 0; const auto& catalog = fold::cli::scanOptionCatalog(); REQUIRE(catalog.size() >= 50); for (const auto& option : catalog) { REQUIRE_FALSE(option.key.empty()); REQUIRE_FALSE(option.description.empty()); REQUIRE(keys.insert(option.key).second); if (option.cli_exposed) REQUIRE(cli_flags.insert(option.cli_flag).second); if (!option.mcp_exposed) continue; ++mcp_count; REQUIRE(mcp_names.insert(option.key).second); for (const auto alias : option.mcp_aliases) REQUIRE(mcp_names.insert(alias).second); } REQUIRE(mcp_count == 19); } TEST_CASE("scan option catalog resolves aliases and applies typed values", "[scan-options][contract]") { const auto* binary = fold::cli::findScanOption("binary_path"); REQUIRE(binary != nullptr); REQUIRE(binary->key == "binary"); REQUIRE(binary->cli_flag == "--binary"); const auto* max_functions = fold::cli::findScanOption("max_functions"); REQUIRE(max_functions != nullptr); REQUIRE(max_functions->key == "binary_max_functions"); fold::cli::ScanOptions options; REQUIRE(fold::cli::applyScanOption(options, "binary_path", std::string("app.bin"))); REQUIRE(fold::cli::applyScanOption(options, "max_functions", 64)); REQUIRE(fold::cli::applyScanOption(options, "nullable_allocations", true)); REQUIRE(fold::cli::applyScanOption(options, "per_tu_timeout", 17U)); REQUIRE(fold::cli::applyScanOption(options, "mode", std::string("binary"))); REQUIRE( fold::cli::applyScanOption(options, "configuration_profile", std::string("profile.json"))); REQUIRE(options.binary_path == "app.bin"); REQUIRE(options.binary_max_functions == 64); REQUIRE(options.nullable_allocations); REQUIRE(options.per_tu_timeout_seconds == 17U); REQUIRE(options.mode == "binary"); REQUIRE(options.configuration_profile_path == "profile.json"); REQUIRE(options.mode_explicit); REQUIRE_FALSE(fold::cli::applyScanOption(options, "budget_seconds", true)); REQUIRE_FALSE(fold::cli::applyScanOption(options, "not_an_option", 1)); } TEST_CASE("scan option catalog defaults match the runtime contract", "[scan-options][contract]") { const auto* mode = fold::cli::findScanOption("mode"); const auto* budget = fold::cli::findScanOption("budget_seconds"); const auto* per_query = fold::cli::findScanOption("per_query_ms"); const auto* nullable = fold::cli::findScanOption("nullable_allocations"); REQUIRE(mode != nullptr); REQUIRE(budget != nullptr); REQUIRE(per_query != nullptr); REQUIRE(nullable != nullptr); REQUIRE(std::get(mode->default_value) == "auto"); REQUIRE(std::get(budget->default_value) == 604800); REQUIRE(std::get(per_query->default_value) == 30000); REQUIRE_FALSE(std::get(nullable->default_value)); }