#include "config/config_manager.h" #include "config/yaml_parser.h" #include #include #include #include #include using namespace radiantwave::config; TEST(YamlParserTest, ParseBasicYaml) { std::string yaml = R"( license_key: "KEY-12345" email_address: "test@example.com" provisioning_status: "READY" audio: volume: 95 output_device: "hdmi" proclamation: scroll_speed: 2.5 font_size: 40 )"; YamlNode root = parseYaml(yaml); EXPECT_TRUE(root.hasChild("license_key")); EXPECT_EQ(root.getChild("license_key").asString(), "KEY-12345"); EXPECT_EQ(root.getChild("email_address").asString(), "test@example.com"); EXPECT_TRUE(root.hasChild("audio")); EXPECT_EQ(root.getChild("audio").getChild("volume").asInt(), 95); EXPECT_EQ(root.getChild("audio").getChild("output_device").asString(), "hdmi"); EXPECT_TRUE(root.hasChild("proclamation")); EXPECT_FLOAT_EQ(root.getChild("proclamation").getChild("scroll_speed").asFloat(), 2.5f); EXPECT_EQ(root.getChild("proclamation").getChild("font_size").asInt(), 40); } TEST(YamlParserTest, ConfigRoundTrip) { AppConfig original; original.license_key = "RW-TEST-99"; original.email_address = "admin@radiantwave.com"; original.audio.volume = 60; original.audio.dynamic_audio = false; original.proclamation.scroll_speed = 1.8f; original.proclamation.schedule_active = true; std::string serialized = serializeConfigToYaml(original); AppConfig parsed = parseConfigFromYaml(serialized); EXPECT_EQ(parsed.license_key, original.license_key); EXPECT_EQ(parsed.email_address, original.email_address); EXPECT_EQ(parsed.audio.volume, original.audio.volume); EXPECT_EQ(parsed.audio.dynamic_audio, original.audio.dynamic_audio); EXPECT_FLOAT_EQ(parsed.proclamation.scroll_speed, original.proclamation.scroll_speed); EXPECT_TRUE(parsed.proclamation.schedule_active); } TEST(ConfigManagerTest, DefaultGeneration) { std::string temp_path = (std::filesystem::temp_directory_path() / "test_rw_config1.yaml").string(); if (std::filesystem::exists(temp_path)) { std::filesystem::remove(temp_path); } ConfigManager manager; EXPECT_TRUE(manager.initialize(temp_path)); AppConfig cfg = manager.get(); EXPECT_EQ(cfg.audio.volume, 80); EXPECT_EQ(cfg.audio.dynamic_audio, true); EXPECT_EQ(cfg.proclamation.font_size, 32); EXPECT_FALSE(cfg.proclamation.schedule_active); EXPECT_TRUE(std::filesystem::exists(temp_path)); std::filesystem::remove(temp_path); } TEST(ConfigManagerTest, MergePreservesExistingValues) { std::string temp_path = (std::filesystem::temp_directory_path() / "test_rw_config2.yaml").string(); // Write partial config { std::ofstream file(temp_path); file << "license_key: \"MY-LICENSE\"\n"; file << "audio:\n"; file << " volume: 42\n"; } ConfigManager manager; EXPECT_TRUE(manager.initialize(temp_path)); AppConfig cfg = manager.get(); // Preserved custom values EXPECT_EQ(cfg.license_key, "MY-LICENSE"); EXPECT_EQ(cfg.audio.volume, 42); // Default values populated for missing fields EXPECT_EQ(cfg.proclamation.font_size, 32); std::filesystem::remove(temp_path); } TEST(ConfigManagerTest, HotReloadWatcher) { std::string temp_path = (std::filesystem::temp_directory_path() / "test_rw_config3.yaml").string(); if (std::filesystem::exists(temp_path)) { std::filesystem::remove(temp_path); } ConfigManager manager; EXPECT_TRUE(manager.initialize(temp_path)); std::atomic reloaded{false}; int new_font_size = 0; manager.onChanged([&](const AppConfig& new_cfg) { new_font_size = new_cfg.proclamation.font_size; reloaded = true; }); // Pause briefly then write modified config to disk std::this_thread::sleep_for(std::chrono::milliseconds(600)); { std::ofstream file(temp_path, std::ios::out | std::ios::trunc); file << "proclamation:\n"; file << " font_size: 48\n"; } // Wait for file watcher thread to pick up change for (int i = 0; i < 20; ++i) { if (reloaded) break; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } EXPECT_TRUE(reloaded); EXPECT_EQ(new_font_size, 48); manager.shutdown(); std::filesystem::remove(temp_path); }