#include #include "fgc/Config.h" #include #include using namespace fgc; TEST_CASE("ConfigLoader maps and defaults typed values") { unsetenv("FGC_MQTT_USER"); unsetenv("FGC_MQTT_PW"); std::map kv = { {"General.tower_name", "Staeffelsberg"}, {"General.image_interval", "3"}, {"General.debug", "1"}, {"Network.zkms_server_ip", "10.0.0.5"}, {"Network.mqtt_user", "fileuser"}, {"Camera.id_Cam1", "DEV_1"}, {"Camera.id_Cam3", "DEV_3"}, {"Features.enable_mqtt", "false"}, }; AppConfig c = ConfigLoader::fromMap(kv); CHECK(c.general.tower_name == "Staeffelsberg"); CHECK(c.general.image_interval == 3); CHECK(c.general.debug == true); CHECK(c.network.broker_ip == "10.0.0.5"); CHECK(c.network.mqtt_user == "fileuser"); CHECK(c.camera.ids.size() == 2); // blank id_Cam2 skipped CHECK(c.camera.ids[1] == "DEV_3"); CHECK(c.features.enable_mqtt == false); CHECK(!c.paths.output_dir.empty()); // defaulted CHECK(std::abs(c.image_rate() - 1.0 / 3.0) < 1e-9); } TEST_CASE("environment overrides file credentials") { setenv("FGC_MQTT_USER", "envuser", 1); AppConfig c = ConfigLoader::fromMap({{"Network.mqtt_user", "fileuser"}}); CHECK(c.network.mqtt_user == "envuser"); unsetenv("FGC_MQTT_USER"); } TEST_CASE("ConfigLoader validates input") { CHECK_THROWS(ConfigLoader::fromMap({{"General.image_interval", "0"}})); CHECK_THROWS(ConfigLoader::fromMap({{"General.image_interval", "abc"}})); CHECK_THROWS(ConfigLoader::fromMap({{"General.debug", "maybe"}})); } TEST_CASE("updateIniSectionKeys replaces in-section keys, leaving others intact") { const std::string in = "[General]\n" "tower_name = Foo\n" "[Motor]\n" "; calibrate me\n" "yaw_counts_per_deg = 983.33\n" "yaw_zero_count = 500000\n" "yaw_min_deg = -90\n" "[Scan]\n" "yaw_min_deg = -90\n"; // same key name, different section — must NOT change auto out = updateIniSectionKeys(in, "Motor", {{"yaw_counts_per_deg", "1000"}, {"yaw_zero_count", "12345"}}); CHECK(out.find("yaw_counts_per_deg = 1000") != std::string::npos); CHECK(out.find("yaw_zero_count = 12345") != std::string::npos); CHECK(out.find("yaw_counts_per_deg = 983.33") == std::string::npos); // replaced CHECK(out.find("; calibrate me") != std::string::npos); // comment kept CHECK(out.find("tower_name = Foo") != std::string::npos); // other section kept CHECK(out.find("[Motor]\nyaw_min_deg") == std::string::npos); // Motor's other key kept in place CHECK(out.find("[Scan]\nyaw_min_deg = -90") != std::string::npos); // Scan untouched } TEST_CASE("updateIniSectionKeys appends keys missing from the section") { const std::string in = "[Motor]\nyaw_counts_per_deg = 1\n"; auto out = updateIniSectionKeys(in, "Motor", {{"pitch_zero_count", "7"}}); // re-parsing must see the appended key under [Motor] CHECK(out.find("pitch_zero_count = 7") != std::string::npos); }