#include "network/proclamation_sync.h" #include using namespace radiantwave::db; using namespace radiantwave::sync; TEST(ProclamationSyncTest, ParseProclamationsJson) { const std::string body = R"([{"id":"p1","name":"Peace","content":"

I am peace.

","categoryId":3,"groupId":7,"solfeggioFrequencyId":2}, {"id":"p2","name":"Joy & Calm","content":"text","categoryId":0,"groupId":0,"solfeggioFrequencyId":0}, {"notAnObject":true}])"; std::vector out; EXPECT_TRUE(parseProclamationsJson(body, out)); ASSERT_EQ(out.size(), 2u); EXPECT_EQ(out[0].id, "p1"); EXPECT_EQ(out[0].title, "Peace"); EXPECT_EQ(out[0].content, "I am peace."); EXPECT_EQ(out[0].category_id, 3); EXPECT_EQ(out[0].group_id, 7); EXPECT_EQ(out[0].solfeggio_frequency_id, 2); EXPECT_EQ(out[1].id, "p2"); EXPECT_EQ(out[1].title, "Joy & Calm"); EXPECT_EQ(out[1].content, "text"); EXPECT_EQ(out[1].category_id, 0); EXPECT_EQ(out[1].group_id, 0); EXPECT_EQ(out[1].solfeggio_frequency_id, 0); } TEST(ProclamationSyncTest, ParseIgnoresLegacySubscriptionLevelField) { std::vector out; EXPECT_TRUE(parseProclamationsJson( R"([{"id":"a","name":"A","subscriptionLevelId":0}, {"id":"b","name":"B","subscriptionLevelId":2}])", out)); ASSERT_EQ(out.size(), 2u); EXPECT_EQ(out[0].id, "a"); EXPECT_EQ(out[1].id, "b"); } TEST(ProclamationSyncTest, ParseCategoriesJson) { // Categories are flat now; a legacy parentId field is simply ignored. const std::string body = R"([{"id":3,"name":"Spiritual","sortOrder":1}, {"id":7,"name":"Physical","parentId":3,"sortOrder":2}])"; std::vector out; EXPECT_TRUE(parseCategoriesJson(body, out)); ASSERT_EQ(out.size(), 2u); EXPECT_EQ(out[0].id, 3); EXPECT_EQ(out[0].name, "Spiritual"); EXPECT_EQ(out[0].sort_order, 1); EXPECT_EQ(out[1].id, 7); EXPECT_EQ(out[1].name, "Physical"); EXPECT_EQ(out[1].sort_order, 2); } TEST(ProclamationSyncTest, ParseGroupsJson) { const std::string body = R"([{"id":5,"name":"Prayers","sortOrder":0}, {"id":8,"name":"Affirmations","sortOrder":3}])"; std::vector out; EXPECT_TRUE(parseGroupsJson(body, out)); ASSERT_EQ(out.size(), 2u); EXPECT_EQ(out[0].id, 5); EXPECT_EQ(out[0].name, "Prayers"); EXPECT_EQ(out[0].sort_order, 0); EXPECT_EQ(out[1].id, 8); EXPECT_EQ(out[1].name, "Affirmations"); EXPECT_EQ(out[1].sort_order, 3); } TEST(ProclamationSyncTest, RejectMalformedJson) { std::vector procs; std::vector cats; std::vector groups; EXPECT_FALSE(parseProclamationsJson("not json", procs)); EXPECT_FALSE(parseProclamationsJson(R"({"id":"p1"})", procs)); // object, not array EXPECT_FALSE(parseProclamationsJson(R"([{"id":"p1"}", ])", procs)); EXPECT_FALSE(parseCategoriesJson("garbage", cats)); // Non-numeric ids are tolerated (row skipped), not fatal. EXPECT_TRUE(parseCategoriesJson(R"([{"id":"x"}])", cats)); EXPECT_EQ(cats.size(), 0u); EXPECT_FALSE(parseGroupsJson("garbage", groups)); EXPECT_TRUE(parseGroupsJson(R"([{"id":"x"}])", groups)); EXPECT_EQ(groups.size(), 0u); } TEST(ProclamationSyncTest, ParseSkipsMissingIdAndZeroCategory) { std::vector procs; EXPECT_TRUE(parseProclamationsJson(R"([{"name":"NoId"},{"id":"ok","name":"Fine","categoryId":4}])", procs)); ASSERT_EQ(procs.size(), 1u); EXPECT_EQ(procs[0].id, "ok"); std::vector cats; EXPECT_TRUE(parseCategoriesJson(R"([{"id":0,"name":"Bad"},{"id":9,"name":"Good","sortOrder":1}])", cats)); ASSERT_EQ(cats.size(), 1u); EXPECT_EQ(cats[0].id, 9); std::vector groups; EXPECT_TRUE(parseGroupsJson(R"([{"id":0,"name":"Bad"},{"id":4,"name":"Good","sortOrder":1}])", groups)); ASSERT_EQ(groups.size(), 1u); EXPECT_EQ(groups[0].id, 4); } TEST(ProclamationSyncTest, StripHtmlEntities) { std::vector out; const std::string body = R"([{"id":"p1","name":"N","content":"A & B <C> 'quoted'  end"}] )"; EXPECT_TRUE(parseProclamationsJson(body, out)); ASSERT_EQ(out.size(), 1u); EXPECT_EQ(out[0].content, "A & B 'quoted' end"); }