#include "network/system_name.h" #include "db/db_keys.h" #include "validation.h" #include #include #include using namespace radiantwave::systemname; using namespace radiantwave::db; namespace api = radiantwave::api; namespace { struct TempDb { TempDb() { path = (std::filesystem::temp_directory_path() / ("test_rw_sysname_" + std::to_string(counter++) + ".db")) .string(); std::filesystem::remove(path); db.initialize(path); } ~TempDb() { db.close(); std::filesystem::remove(path); } std::string path; DatabaseManager db; static int counter; }; int TempDb::counter = 0; api::PutNameResult ok() { return api::PutNameResult{200, true}; } api::PutNameResult conflict() { return api::PutNameResult{409, true}; } api::PutNameResult invalid() { return api::PutNameResult{400, true}; } api::PutNameResult unknown() { return api::PutNameResult{404, true}; } api::PutNameResult offline() { return api::PutNameResult{0, false}; } } // namespace // ─── Validation ──────────────────────────────────────────────────────── TEST(SystemNameValidationTest, TrimAndBounds) { EXPECT_EQ(radiantwave::validation::trim(" Den "), "Den"); EXPECT_TRUE(radiantwave::validation::isValidSystemName("Den")); EXPECT_TRUE(radiantwave::validation::isValidSystemName(" Den ")); EXPECT_TRUE(radiantwave::validation::isValidSystemName(std::string(40, 'a'))); EXPECT_FALSE(radiantwave::validation::isValidSystemName("")); EXPECT_FALSE(radiantwave::validation::isValidSystemName(" ")); EXPECT_FALSE(radiantwave::validation::isValidSystemName(std::string(41, 'a'))); } // ─── Local storage ───────────────────────────────────────────────────── TEST(SystemNameLocalTest, StoresTrimmedNameAndMarksPending) { TempDb tmp; std::string error; ASSERT_TRUE(storeLocalName(tmp.db, " Living Room ", &error)); EXPECT_EQ(storedName(tmp.db), "Living Room"); EXPECT_TRUE(pendingSync(tmp.db)); } TEST(SystemNameLocalTest, RejectsInvalidWithoutStoring) { TempDb tmp; std::string error; EXPECT_FALSE(storeLocalName(tmp.db, " ", &error)); EXPECT_EQ(error, INVALID_MESSAGE); EXPECT_FALSE(tmp.db.hasKV(KV_SYSTEM_NAME)); EXPECT_FALSE(pendingSync(tmp.db)); EXPECT_FALSE(storeLocalName(tmp.db, std::string(41, 'a'))); EXPECT_FALSE(tmp.db.hasKV(KV_SYSTEM_NAME)); } // ─── Server status application (K2) ──────────────────────────────────── TEST(SystemNameStatusTest, OkMarksSyncedAndClearsPending) { TempDb tmp; tmp.db.setKV(KV_SYSTEM_NAME, "Den"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); EXPECT_EQ(applyServerStatus(tmp.db, "ok"), Status::Ok); EXPECT_EQ(tmp.db.getKV(KV_SYSTEM_NAME_SYNCED), "Den"); EXPECT_FALSE(pendingSync(tmp.db)); } TEST(SystemNameStatusTest, ConflictRevertsToSyncedAndRecordsMessage) { TempDb tmp; tmp.db.setKV(KV_SYSTEM_NAME_SYNCED, "Living Room"); tmp.db.setKV(KV_SYSTEM_NAME, "Den"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); EXPECT_EQ(applyServerStatus(tmp.db, "conflict"), Status::Conflict); EXPECT_EQ(storedName(tmp.db), "Living Room"); EXPECT_FALSE(pendingSync(tmp.db)); EXPECT_EQ(tmp.db.getKV(KV_SYSTEM_NAME_MESSAGE), CONFLICT_MESSAGE); } TEST(SystemNameStatusTest, ConflictWithNoPreviousNameClearsIt) { TempDb tmp; tmp.db.setKV(KV_SYSTEM_NAME, "Den"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); EXPECT_EQ(applyServerStatus(tmp.db, "conflict"), Status::Conflict); EXPECT_FALSE(tmp.db.hasKV(KV_SYSTEM_NAME)); } TEST(SystemNameStatusTest, InvalidRevertsAndRecordsMessage) { TempDb tmp; tmp.db.setKV(KV_SYSTEM_NAME_SYNCED, "Den"); tmp.db.setKV(KV_SYSTEM_NAME, "Nope"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); EXPECT_EQ(applyServerStatus(tmp.db, "invalid"), Status::Invalid); EXPECT_EQ(storedName(tmp.db), "Den"); EXPECT_EQ(tmp.db.getKV(KV_SYSTEM_NAME_MESSAGE), INVALID_MESSAGE); } TEST(SystemNameStatusTest, UnchangedClearsPending) { TempDb tmp; tmp.db.setKV(KV_SYSTEM_NAME, "Den"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); EXPECT_EQ(applyServerStatus(tmp.db, "unchanged"), Status::Unchanged); EXPECT_FALSE(pendingSync(tmp.db)); } // ─── Background sync (K3) ────────────────────────────────────────────── TEST(SystemNameSyncTest, NotConfiguredWithoutLicenseKey) { TempDb tmp; tmp.db.setKV(KV_SYSTEM_NAME, "Den"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); bool called = false; EXPECT_EQ(syncWithServer(tmp.db, [&](const std::string&, const std::string&) { called = true; return ok(); }), Status::NotConfigured); EXPECT_FALSE(called); } TEST(SystemNameSyncTest, DoesNothingWhenNotPending) { TempDb tmp; tmp.db.setKV(KV_LICENSE_KEY, "ABCD-EFGH-IJKL-MNOP"); tmp.db.setKV(KV_SYSTEM_NAME, "Den"); bool called = false; EXPECT_EQ(syncWithServer(tmp.db, [&](const std::string&, const std::string&) { called = true; return ok(); }), Status::Unchanged); EXPECT_FALSE(called); } TEST(SystemNameSyncTest, PushesNameAndMarksSynced) { TempDb tmp; tmp.db.setKV(KV_LICENSE_KEY, "ABCD-EFGH-IJKL-MNOP"); tmp.db.setKV(KV_SYSTEM_NAME, "Den"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); std::string sentKey; std::string sentName; EXPECT_EQ(syncWithServer(tmp.db, [&](const std::string& k, const std::string& n) { sentKey = k; sentName = n; return ok(); }), Status::Ok); EXPECT_EQ(sentKey, "ABCD-EFGH-IJKL-MNOP"); EXPECT_EQ(sentName, "Den"); EXPECT_FALSE(pendingSync(tmp.db)); EXPECT_EQ(tmp.db.getKV(KV_SYSTEM_NAME_SYNCED), "Den"); } TEST(SystemNameSyncTest, ConflictRevertsAndKeepsOldName) { TempDb tmp; tmp.db.setKV(KV_LICENSE_KEY, "ABCD-EFGH-IJKL-MNOP"); tmp.db.setKV(KV_SYSTEM_NAME_SYNCED, "Living Room"); tmp.db.setKV(KV_SYSTEM_NAME, "Den"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); EXPECT_EQ(syncWithServer(tmp.db, [](const std::string&, const std::string&) { return conflict(); }), Status::Conflict); EXPECT_EQ(storedName(tmp.db), "Living Room"); EXPECT_EQ(tmp.db.getKV(KV_SYSTEM_NAME_MESSAGE), CONFLICT_MESSAGE); } TEST(SystemNameSyncTest, InvalidReverts) { TempDb tmp; tmp.db.setKV(KV_LICENSE_KEY, "ABCD-EFGH-IJKL-MNOP"); tmp.db.setKV(KV_SYSTEM_NAME_SYNCED, "Living Room"); tmp.db.setKV(KV_SYSTEM_NAME, "x"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); EXPECT_EQ(syncWithServer(tmp.db, [](const std::string&, const std::string&) { return invalid(); }), Status::Invalid); EXPECT_EQ(storedName(tmp.db), "Living Room"); } TEST(SystemNameSyncTest, NetworkFailureKeepsPendingForRetry) { TempDb tmp; tmp.db.setKV(KV_LICENSE_KEY, "ABCD-EFGH-IJKL-MNOP"); tmp.db.setKV(KV_SYSTEM_NAME, "Den"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); EXPECT_EQ(syncWithServer(tmp.db, [](const std::string&, const std::string&) { return offline(); }), Status::NetworkError); EXPECT_TRUE(pendingSync(tmp.db)); EXPECT_EQ(storedName(tmp.db), "Den"); } TEST(SystemNameSyncTest, UnknownLicenseKeepsPending) { TempDb tmp; tmp.db.setKV(KV_LICENSE_KEY, "ABCD-EFGH-IJKL-MNOP"); tmp.db.setKV(KV_SYSTEM_NAME, "Den"); tmp.db.setKV(KV_SYSTEM_NAME_PENDING, "1"); EXPECT_EQ(syncWithServer(tmp.db, [](const std::string&, const std::string&) { return unknown(); }), Status::UnknownLicense); EXPECT_TRUE(pendingSync(tmp.db)); } TEST(SystemNameMessageTest, ConsumeReturnsAndClears) { TempDb tmp; tmp.db.setKV(KV_SYSTEM_NAME_MESSAGE, CONFLICT_MESSAGE); EXPECT_EQ(consumeMessage(tmp.db), CONFLICT_MESSAGE); EXPECT_EQ(consumeMessage(tmp.db), ""); }