#include #include #include #include #include #include #include #include #include #include "../../src/cmd_gridsearch.h" #include "logger/logger.h" // ── Helpers ────────────────────────────────────────────────────────────────── static std::string read_file_contents(const std::string &path) { std::ifstream f(path); if (!f.is_open()) return ""; std::stringstream ss; ss << f.rdbuf(); return ss.str(); } /// Read a JSON config file and inject test-safe overrides: /// - configPath = "config/postgres.toml" /// - enrich = false (no live HTTP / thread-pool in tests) /// - persistencePostgres = false static std::string load_test_payload(const std::string &config_path) { std::string raw = read_file_contents(config_path); if (raw.empty()) return ""; rapidjson::Document doc; doc.Parse(raw.c_str()); if (doc.HasParseError()) return ""; auto &alloc = doc.GetAllocator(); // Remove-then-add ensures no double-add assertion from rapidjson auto inject_bool = [&](const char *key, bool val) { if (doc.HasMember(key)) doc.RemoveMember(key); doc.AddMember(rapidjson::Value(key, alloc), rapidjson::Value(val), alloc); }; auto inject_str = [&](const char *key, const char *val) { if (doc.HasMember(key)) doc.RemoveMember(key); doc.AddMember(rapidjson::Value(key, alloc), rapidjson::Value(val, alloc), alloc); }; inject_str("configPath", "config/postgres.toml"); inject_str("cacheDir", "../../packages/gadm/cache/gadm"); // server/cache/gadm inject_bool("enrich", false); // no live enrichment in tests inject_bool("persistencePostgres", false); rapidjson::StringBuffer buf; rapidjson::Writer writer(buf); doc.Accept(writer); return buf.GetString(); } // ── Tests // ───────────────────────────────────────────────────────────────────── TEST_CASE("E2E: Gridsearch Country Boundary Filter (Lamu/KEN)", "[e2e][gridsearch][boundary]") { REQUIRE_NOTHROW(logger::init("test-gridsearch")); // Lamu, Kenya — SerpAPI often returns US results for obscure African regions. // boundary_KEN_0.json should filter them out. std::string payload = load_test_payload("config/gridsearch-lamu.json"); REQUIRE(!payload.empty()); std::vector location_events; int error_count = 0; polymech::GridsearchCallbacks cb; cb.onEvent = [&](const std::string &type, const std::string &json) { if (type == "location") { location_events.push_back(json); } else if (type == "error") { error_count++; std::cout << "[ERROR EVENT]: " << json << "\n"; } }; int result = polymech::run_cmd_gridsearch_ipc(payload, "test-lamu-job", cb, false, ""); REQUIRE(result == 0); REQUIRE(error_count == 0); // All returned locations must be within Kenya (no USA coords). // Verify: no location has lng < -30 (Americas) or lng > 60 (not Africa/Asia) // and lat outside [-5, 5] for Lamu county bounds. int outside_kenya = 0; for (const auto &loc_json : location_events) { rapidjson::Document loc; loc.Parse(loc_json.c_str()); if (loc.HasParseError()) continue; if (loc.HasMember("gps") && loc["gps"].IsObject()) { double lng = loc["gps"].HasMember("lng") ? loc["gps"]["lng"].GetDouble() : 0; // Kenya longitude range: ~34..42; USA is roughly -130..-60 if (lng < 20.0 || lng > 55.0) outside_kenya++; } } CHECK(outside_kenya == 0); std::cout << "Lamu boundary test: " << location_events.size() << " locations kept, " << outside_kenya << " outside Kenya.\n"; } TEST_CASE("E2E: Gridsearch Type Filter (Sample/ABW)", "[e2e][gridsearch][filter]") { std::string payload = load_test_payload("config/gridsearch-sample.json"); REQUIRE(!payload.empty()); std::vector location_events; int error_count = 0; polymech::GridsearchCallbacks cb; cb.onEvent = [&](const std::string &type, const std::string &json) { if (type == "location") location_events.push_back(json); else if (type == "error") error_count++; }; int result = polymech::run_cmd_gridsearch_ipc(payload, "test-sample-job", cb, false, ""); REQUIRE(result == 0); REQUIRE(error_count == 0); std::cout << "Sample (ABW) type filter test: " << location_events.size() << " locations.\n"; }