fold/tests/unit/test_scan_option_catalog.cpp
trvon 231af6dbd2
feat(cli): generous compute budgets for day-scale scans
Given the engine can afford days of compute (fuzzing-grade), widen the
solver budgets so hard queries resolve instead of timing out:
- checkAdaptive escalation ceiling raised to 1 hour per query (was 8x base)
- budget_seconds 7200 -> 604800 (7 days)
- per_tu_timeout 900 -> 86400 (24h)

A single hard query getting up to an hour of escalating compute is cheap
relative to a lost vulnerability.
2026-08-22 13:45:47 -06:00

78 lines
3.3 KiB
C++

#include <catch2/catch_test_macros.hpp>
#include <set>
#include <string>
#include <string_view>
#include "../../include/fold/cli/scan_option_catalog.hpp"
TEST_CASE("scan option catalog has unique canonical and compatibility names",
"[scan-options][contract]") {
std::set<std::string_view> keys;
std::set<std::string_view> cli_flags;
std::set<std::string_view> 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<std::string_view>(mode->default_value) == "auto");
REQUIRE(std::get<int>(budget->default_value) == 604800);
REQUIRE(std::get<int>(per_query->default_value) == 30000);
REQUIRE_FALSE(std::get<bool>(nullable->default_value));
}