#include #include #include "gadm_reader/gadm_reader.h" #include using namespace gadm; using Catch::Matchers::WithinAbs; using Catch::Matchers::WithinRel; // ── Helper: fixtures path ─────────────────────────────────────────────────── // Tests are run with WORKING_DIRECTORY = CMAKE_SOURCE_DIR (server/cpp) static const std::string CACHE_DIR = "cache/gadm"; // ── country_code ──────────────────────────────────────────────────────────── TEST_CASE("country_code: simple ISO3", "[gadm][util]") { REQUIRE(country_code("ABW") == "ABW"); } TEST_CASE("country_code: dotted GID", "[gadm][util]") { REQUIRE(country_code("AFG.1.1_1") == "AFG"); REQUIRE(country_code("ESP.6.1_1") == "ESP"); } // ── infer_level ───────────────────────────────────────────────────────────── TEST_CASE("infer_level: level 0 (country)", "[gadm][util]") { REQUIRE(infer_level("ABW") == 0); REQUIRE(infer_level("AFG") == 0); } TEST_CASE("infer_level: level 1", "[gadm][util]") { REQUIRE(infer_level("AFG.1_1") == 1); } TEST_CASE("infer_level: level 2", "[gadm][util]") { REQUIRE(infer_level("AFG.1.1_1") == 2); } TEST_CASE("infer_level: level 3", "[gadm][util]") { REQUIRE(infer_level("ESP.6.1.4_1") == 3); } // ── load_boundary_file: ABW level 0 ──────────────────────────────────────── TEST_CASE("Load ABW level 0: basic structure", "[gadm][file]") { auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json"); REQUIRE(res.error.empty()); REQUIRE(res.features.size() == 1); const auto& f = res.features[0]; REQUIRE(f.gid == "ABW"); REQUIRE(f.name == "Aruba"); REQUIRE(f.level == 0); REQUIRE(f.isOuter == true); } TEST_CASE("Load ABW level 0: has rings", "[gadm][file]") { auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json"); REQUIRE(res.error.empty()); const auto& f = res.features[0]; REQUIRE(f.rings.size() >= 1); REQUIRE(f.rings[0].size() > 10); // ABW has ~55 coords } TEST_CASE("Load ABW level 0: GHS population data", "[gadm][file]") { auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json"); REQUIRE(res.error.empty()); const auto& f = res.features[0]; REQUIRE_THAT(f.ghsPopulation, WithinRel(104847.0, 0.01)); REQUIRE(f.ghsPopCenters.size() == 5); // First pop center: [-70.04183, 12.53341, 104.0] REQUIRE_THAT(f.ghsPopCenters[0][0], WithinAbs(-70.04183, 0.0001)); REQUIRE_THAT(f.ghsPopCenters[0][1], WithinAbs(12.53341, 0.0001)); REQUIRE_THAT(f.ghsPopCenters[0][2], WithinAbs(104.0, 0.1)); } TEST_CASE("Load ABW level 0: GHS built data", "[gadm][file]") { auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json"); REQUIRE(res.error.empty()); const auto& f = res.features[0]; REQUIRE_THAT(f.ghsBuiltWeight, WithinRel(22900682.0, 0.01)); REQUIRE(f.ghsBuiltCenters.size() == 5); REQUIRE_THAT(f.ghsBuiltCenter.lon, WithinAbs(-69.99304, 0.001)); REQUIRE_THAT(f.ghsBuiltCenter.lat, WithinAbs(12.51234, 0.001)); } TEST_CASE("Load ABW level 0: computed bbox", "[gadm][file]") { auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json"); REQUIRE(res.error.empty()); const auto& f = res.features[0]; // ABW bbox should be roughly in the Caribbean REQUIRE(f.bbox.minLon < -69.8); REQUIRE(f.bbox.maxLon > -70.1); REQUIRE(f.bbox.minLat > 12.4); REQUIRE(f.bbox.maxLat < 12.7); } TEST_CASE("Load ABW level 0: computed area", "[gadm][file]") { auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json"); REQUIRE(res.error.empty()); const auto& f = res.features[0]; // Aruba is ~180 km² REQUIRE_THAT(f.areaSqKm, WithinRel(180.0, 0.15)); // 15% tolerance } // ── load_boundary_file: AFG level 2 ──────────────────────────────────────── TEST_CASE("Load AFG.1.1_1 level 2: basic structure", "[gadm][file]") { auto res = load_boundary_file(CACHE_DIR + "/boundary_AFG.1.1_1_2.json"); REQUIRE(res.error.empty()); REQUIRE(res.features.size() == 1); const auto& f = res.features[0]; REQUIRE(f.gid == "AFG.1.1_1"); REQUIRE(f.name == "Baharak"); REQUIRE(f.level == 2); } TEST_CASE("Load AFG.1.1_1 level 2: has GHS data", "[gadm][file]") { auto res = load_boundary_file(CACHE_DIR + "/boundary_AFG.1.1_1_2.json"); REQUIRE(res.error.empty()); const auto& f = res.features[0]; REQUIRE(f.ghsPopCenters.size() == 5); REQUIRE(f.ghsBuiltCenters.size() == 5); REQUIRE(f.ghsPopulation > 0); } // ── load_boundary: path resolution ────────────────────────────────────────── TEST_CASE("load_boundary: direct GID match", "[gadm][resolve]") { auto res = load_boundary("ABW", 0, CACHE_DIR); REQUIRE(res.error.empty()); REQUIRE(res.features.size() == 1); REQUIRE(res.features[0].gid == "ABW"); } TEST_CASE("load_boundary: sub-region GID", "[gadm][resolve]") { auto res = load_boundary("AFG.1.1_1", 2, CACHE_DIR); REQUIRE(res.error.empty()); REQUIRE(res.features[0].gid == "AFG.1.1_1"); } TEST_CASE("load_boundary: missing file returns error", "[gadm][resolve]") { auto res = load_boundary("DOESNOTEXIST", 0, CACHE_DIR); REQUIRE(!res.error.empty()); REQUIRE(res.features.empty()); } // ── Error handling ────────────────────────────────────────────────────────── TEST_CASE("load_boundary_file: nonexistent file", "[gadm][error]") { auto res = load_boundary_file("nonexistent.json"); REQUIRE(!res.error.empty()); REQUIRE(res.features.empty()); }