Remove subscription levels; license valid = subscribed
This commit is contained in:
@@ -72,19 +72,19 @@ TEST(ApiClientTest, ParseFrequenciesJsonSkipsMissingId) {
|
||||
EXPECT_EQ(out[0].id, 3);
|
||||
}
|
||||
|
||||
TEST(ApiClientTest, ParseLicenseJsonLegacyStringLevel) {
|
||||
TEST(ApiClientTest, ParseLicenseJson) {
|
||||
const std::string body =
|
||||
R"({"key":"ABCD-EFGH-IJKL-MNOP","email_address":"user@example.com","subscription_level":"Basic"})";
|
||||
R"({"key":"ABCD-EFGH-IJKL-MNOP","email_address":"user@example.com"})";
|
||||
|
||||
LicenseInfo info;
|
||||
EXPECT_TRUE(parseLicenseJson(body, info));
|
||||
EXPECT_EQ(info.key, "ABCD-EFGH-IJKL-MNOP");
|
||||
EXPECT_EQ(info.email_address, "user@example.com");
|
||||
EXPECT_EQ(info.subscription_level.name, "Basic");
|
||||
EXPECT_EQ(info.subscription_level.id, 0);
|
||||
}
|
||||
|
||||
TEST(ApiClientTest, ParseLicenseJsonObjectLevel) {
|
||||
TEST(ApiClientTest, ParseLicenseJsonIgnoresSubscriptionFields) {
|
||||
// The active/inactive model has no subscription level. Legacy responses
|
||||
// that still carry subscription_level must parse; the field is ignored.
|
||||
const std::string body =
|
||||
R"({"key":"ABCD-EFGH-IJKL-MNOP","email_address":"user@example.com",)"
|
||||
R"("subscription_level":{"id":2,"name":"Advanced","description":"General + Specific"}})";
|
||||
@@ -93,22 +93,6 @@ TEST(ApiClientTest, ParseLicenseJsonObjectLevel) {
|
||||
EXPECT_TRUE(parseLicenseJson(body, info));
|
||||
EXPECT_EQ(info.key, "ABCD-EFGH-IJKL-MNOP");
|
||||
EXPECT_EQ(info.email_address, "user@example.com");
|
||||
EXPECT_EQ(info.subscription_level.id, 2);
|
||||
EXPECT_EQ(info.subscription_level.name, "Advanced");
|
||||
EXPECT_EQ(info.subscription_level.description, "General + Specific");
|
||||
}
|
||||
|
||||
TEST(ApiClientTest, ParseLicenseJsonFlatLevel) {
|
||||
const std::string body =
|
||||
R"({"key":"ABCD-EFGH-IJKL-MNOP","email_address":"user@example.com",)"
|
||||
R"("subscription_level":"Elite","subscription_level_id":3,)"
|
||||
R"("subscription_level_description":"General + Specific + Precise"})";
|
||||
|
||||
LicenseInfo info;
|
||||
EXPECT_TRUE(parseLicenseJson(body, info));
|
||||
EXPECT_EQ(info.subscription_level.id, 3);
|
||||
EXPECT_EQ(info.subscription_level.name, "Elite");
|
||||
EXPECT_EQ(info.subscription_level.description, "General + Specific + Precise");
|
||||
}
|
||||
|
||||
TEST(ApiClientTest, ParseLicenseJsonRejectsMalformed) {
|
||||
@@ -147,7 +131,7 @@ TEST(ApiClientTest, ExtractErrorMessage) {
|
||||
|
||||
// ─── Offline: real-world payload shapes ────────────────────────────────
|
||||
|
||||
TEST(ApiClientTest, ParseProclamationsHandlesSubscriptionLevelIdNull) {
|
||||
TEST(ApiClientTest, ParseProclamationsToleratesLegacyLevelField) {
|
||||
const std::string body =
|
||||
R"([{"id":"a","name":"Peace","content":"<p>I am peace.</p>","categoryId":5,
|
||||
"solfeggioFrequencyId":5,"subscriptionLevelId":null},
|
||||
@@ -256,33 +240,17 @@ TEST(ApiClientLiveTest, ValidateLicenseSuccessLive) {
|
||||
<< " error=" << result.error_message;
|
||||
EXPECT_EQ(result.info.key, key);
|
||||
EXPECT_EQ(result.info.email_address, email);
|
||||
EXPECT_FALSE(result.info.subscription_level.name.empty());
|
||||
}
|
||||
|
||||
// ─── Live: POST /automations/create/account error paths (harmless) ─────
|
||||
|
||||
TEST(ApiClientLiveTest, CreateAccountMissingEmailLive) {
|
||||
AccountResult result = createAccount("", "Basic", "", liveApiKey());
|
||||
AccountResult result = createAccount("", "", liveApiKey());
|
||||
ASSERT_FALSE(result.ok());
|
||||
EXPECT_EQ(result.status, 400);
|
||||
EXPECT_EQ(result.error_message, "email is required");
|
||||
}
|
||||
|
||||
TEST(ApiClientLiveTest, CreateAccountMissingLevelLive) {
|
||||
AccountResult result = createAccount("test@example.com", "", "", liveApiKey());
|
||||
ASSERT_FALSE(result.ok());
|
||||
EXPECT_EQ(result.status, 400);
|
||||
EXPECT_EQ(result.error_message, "subscription_level is required");
|
||||
}
|
||||
|
||||
TEST(ApiClientLiveTest, CreateAccountInvalidLevelLive) {
|
||||
AccountResult result =
|
||||
createAccount("test@example.com", "NoSuchLevel123", "", liveApiKey());
|
||||
ASSERT_FALSE(result.ok());
|
||||
EXPECT_EQ(result.status, 400);
|
||||
EXPECT_NE(result.error_message.find("not found"), std::string::npos);
|
||||
}
|
||||
|
||||
// ─── Live: POST /automations/create/account success (creates a real
|
||||
// ─── account + emails the user; requires RW_LIVE_ACCOUNT_EMAIL) ────────
|
||||
|
||||
@@ -292,7 +260,7 @@ TEST(ApiClientLiveTest, CreateAccountSuccessLive) {
|
||||
GTEST_SKIP() << "RW_LIVE_ACCOUNT_EMAIL not set";
|
||||
}
|
||||
|
||||
AccountResult result = createAccount(email, "Basic", "RadiantWave Test", liveApiKey());
|
||||
AccountResult result = createAccount(email, "RadiantWave Test", liveApiKey());
|
||||
ASSERT_TRUE(result.ok()) << "status=" << result.status
|
||||
<< " error=" << result.error_message;
|
||||
EXPECT_TRUE(result.body.success);
|
||||
|
||||
@@ -142,32 +142,6 @@ TEST(DatabaseManagerTest, SyncProclamationsPreservesSelectionAndDeletesRemoved)
|
||||
EXPECT_TRUE(all[0].selected);
|
||||
}
|
||||
|
||||
TEST(DatabaseManagerTest, RequiredLevelIdRoundTripsThroughSync) {
|
||||
const std::string defId = "default-1";
|
||||
DbFixture fx("test_rw_db_level.db");
|
||||
|
||||
ProclamationRecord def = makeProc(defId, "Default", 1);
|
||||
def.required_level_id = 1;
|
||||
ProclamationRecord elite = makeProc("elite", "Elite Only", 2);
|
||||
elite.required_level_id = 3;
|
||||
|
||||
ASSERT_TRUE(fx.db.syncProclamations({def, elite}, defId));
|
||||
|
||||
auto all = fx.db.getAllProclamations();
|
||||
ASSERT_EQ(all.size(), 2u);
|
||||
for (const auto& p : all) {
|
||||
EXPECT_EQ(p.required_level_id, p.id == "elite" ? 3 : 1);
|
||||
}
|
||||
|
||||
// A re-sync with the same records keeps the level (and selection).
|
||||
ASSERT_TRUE(fx.db.syncProclamations({def, elite}, defId));
|
||||
auto again = fx.db.getAllProclamations();
|
||||
ASSERT_EQ(again.size(), 2u);
|
||||
for (const auto& p : again) {
|
||||
EXPECT_EQ(p.required_level_id, p.id == "elite" ? 3 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DatabaseManagerTest, SyncForcesDefaultProclamationSelected) {
|
||||
const std::string defId = "default-1";
|
||||
DbFixture fx("test_rw_db_def.db");
|
||||
|
||||
@@ -47,23 +47,18 @@ TEST(LicenseServiceTest, NotConfiguredWhenNoEmail) {
|
||||
TempDb tmp;
|
||||
tmp.db.setKV(KV_LICENSE_KEY, "ABCD-EFGH-IJKL-MNOP");
|
||||
|
||||
std::string levelName = "stale";
|
||||
std::string errorMessage = "stale";
|
||||
EXPECT_EQ(validateStoredLicense(tmp.db, "", &levelName, &errorMessage),
|
||||
EXPECT_EQ(validateStoredLicense(tmp.db, "", &errorMessage),
|
||||
Result::NotConfigured);
|
||||
EXPECT_EQ(levelName, "");
|
||||
EXPECT_EQ(errorMessage, "");
|
||||
EXPECT_FALSE(tmp.db.hasKV(KV_SUBSCRIPTION_LEVEL_NAME));
|
||||
}
|
||||
|
||||
TEST(LicenseServiceTest, NotConfiguredWhenNoLicense) {
|
||||
TempDb tmp;
|
||||
tmp.db.setKV(KV_EMAIL_ADDRESS, "user@example.com");
|
||||
|
||||
std::string levelName = "stale";
|
||||
EXPECT_EQ(validateStoredLicense(tmp.db, "", &levelName, nullptr),
|
||||
EXPECT_EQ(validateStoredLicense(tmp.db, "", nullptr),
|
||||
Result::NotConfigured);
|
||||
EXPECT_EQ(levelName, "");
|
||||
}
|
||||
|
||||
TEST(LicenseServiceTest, NotConfiguredWhenBothMissing) {
|
||||
@@ -71,35 +66,6 @@ TEST(LicenseServiceTest, NotConfiguredWhenBothMissing) {
|
||||
EXPECT_EQ(validateStoredLicense(tmp.db), Result::NotConfigured);
|
||||
}
|
||||
|
||||
// ─── Level helpers (no network) ─────────────────────────────────────────
|
||||
|
||||
TEST(LicenseServiceTest, StoredLevelIdReadsPersistedValue) {
|
||||
TempDb tmp;
|
||||
EXPECT_EQ(getStoredSubscriptionLevelId(tmp.db), 0);
|
||||
|
||||
tmp.db.setKV(KV_SUBSCRIPTION_LEVEL_ID, "2");
|
||||
EXPECT_EQ(getStoredSubscriptionLevelId(tmp.db), 2);
|
||||
|
||||
tmp.db.setKV(KV_SUBSCRIPTION_LEVEL_ID, "not-a-number");
|
||||
EXPECT_EQ(getStoredSubscriptionLevelId(tmp.db), 0);
|
||||
}
|
||||
|
||||
TEST(LicenseServiceTest, LevelNames) {
|
||||
EXPECT_EQ(subscriptionLevelName(1), "Basic");
|
||||
EXPECT_EQ(subscriptionLevelName(2), "Advanced");
|
||||
EXPECT_EQ(subscriptionLevelName(3), "Elite");
|
||||
EXPECT_EQ(subscriptionLevelName(4), "Subscription Level 4");
|
||||
}
|
||||
|
||||
TEST(LicenseServiceTest, LevelIdForName) {
|
||||
EXPECT_EQ(subscriptionLevelIdForName("Basic"), 1);
|
||||
EXPECT_EQ(subscriptionLevelIdForName("advanced"), 2);
|
||||
EXPECT_EQ(subscriptionLevelIdForName("ELITE"), 3);
|
||||
EXPECT_EQ(subscriptionLevelIdForName(" Elite "), 0); // unknown/whitespace
|
||||
EXPECT_EQ(subscriptionLevelIdForName("Enterprise"), 0);
|
||||
EXPECT_EQ(subscriptionLevelIdForName(""), 0);
|
||||
}
|
||||
|
||||
// ─── Offline grace period ───────────────────────────────────────────────
|
||||
|
||||
long long epochSeconds(long long offsetSeconds = 0) {
|
||||
@@ -139,7 +105,7 @@ TEST(LicenseServiceTest, GracePeriodIgnoresCorruptStamp) {
|
||||
|
||||
// ─── Live: full validation + persistence (requires env credentials) ─────
|
||||
|
||||
TEST(LicenseServiceLiveTest, ValidatesAndStoresSubscriptionLevel) {
|
||||
TEST(LicenseServiceLiveTest, ValidatesAndGrantsSubscribedState) {
|
||||
const std::string key = env("RW_LIVE_LICENSE_KEY");
|
||||
const std::string email = env("RW_LIVE_LICENSE_EMAIL");
|
||||
if (key.empty() || email.empty()) {
|
||||
@@ -150,20 +116,14 @@ TEST(LicenseServiceLiveTest, ValidatesAndStoresSubscriptionLevel) {
|
||||
tmp.db.setKV(KV_LICENSE_KEY, key);
|
||||
tmp.db.setKV(KV_EMAIL_ADDRESS, email);
|
||||
|
||||
std::string levelName;
|
||||
std::string errorMessage;
|
||||
const Result result = validateStoredLicense(tmp.db, "", &levelName, &errorMessage);
|
||||
const Result result = validateStoredLicense(tmp.db, "", &errorMessage);
|
||||
EXPECT_EQ(result, Result::Ok) << "error=" << errorMessage;
|
||||
|
||||
// The subscription level name (and id/description when available) must be
|
||||
// persisted to the local database.
|
||||
EXPECT_EQ(levelName, tmp.db.getKV(KV_SUBSCRIPTION_LEVEL_NAME));
|
||||
EXPECT_FALSE(tmp.db.getKV(KV_SUBSCRIPTION_LEVEL_NAME).empty());
|
||||
ASSERT_TRUE(tmp.db.hasKV(KV_SUBSCRIPTION_LEVEL_ID));
|
||||
EXPECT_GT(std::atoi(tmp.db.getKV(KV_SUBSCRIPTION_LEVEL_ID).c_str()), 0);
|
||||
|
||||
// A successful validation must record the "last verified" stamp that drives
|
||||
// the offline grace period.
|
||||
// A successful validation is the "subscribed" flag; no subscription-level
|
||||
// state is persisted. Only the "last verified" stamp that drives the
|
||||
// offline grace window is written.
|
||||
EXPECT_FALSE(tmp.db.hasKV("subscription_level_name"));
|
||||
EXPECT_TRUE(tmp.db.hasKV(KV_LICENSE_LAST_VERIFIED));
|
||||
EXPECT_TRUE(isLicenseWithinGracePeriod(tmp.db));
|
||||
}
|
||||
@@ -174,8 +134,7 @@ TEST(LicenseServiceLiveTest, InvalidPairReportsInvalid) {
|
||||
tmp.db.setKV(KV_EMAIL_ADDRESS, "nobody@example.com");
|
||||
|
||||
std::string errorMessage;
|
||||
EXPECT_EQ(validateStoredLicense(tmp.db, "", nullptr, &errorMessage),
|
||||
EXPECT_EQ(validateStoredLicense(tmp.db, "", &errorMessage),
|
||||
Result::Invalid);
|
||||
EXPECT_FALSE(errorMessage.empty());
|
||||
EXPECT_FALSE(tmp.db.hasKV(KV_SUBSCRIPTION_LEVEL_NAME));
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using namespace radiantwave::sync;
|
||||
|
||||
TEST(ProclamationSyncTest, ParseProclamationsJson) {
|
||||
const std::string body =
|
||||
R"([{"id":"p1","name":"Peace","content":"<p>I am <b>peace</b>.</p>","categoryId":3,"solfeggioFrequencyId":2,"subscriptionLevelId":3},
|
||||
R"([{"id":"p1","name":"Peace","content":"<p>I am <b>peace</b>.</p>","categoryId":3,"solfeggioFrequencyId":2},
|
||||
{"id":"p2","name":"Joy & Calm","content":"text","categoryId":0,"solfeggioFrequencyId":0},
|
||||
{"notAnObject":true}])";
|
||||
|
||||
@@ -19,24 +19,22 @@ TEST(ProclamationSyncTest, ParseProclamationsJson) {
|
||||
EXPECT_EQ(out[0].content, "I am peace.");
|
||||
EXPECT_EQ(out[0].category_id, 3);
|
||||
EXPECT_EQ(out[0].solfeggio_frequency_id, 2);
|
||||
EXPECT_EQ(out[0].required_level_id, 3);
|
||||
|
||||
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);
|
||||
// Missing subscriptionLevelId defaults to the least privileged level.
|
||||
EXPECT_EQ(out[1].required_level_id, 1);
|
||||
EXPECT_EQ(out[1].solfeggio_frequency_id, 0);
|
||||
}
|
||||
|
||||
TEST(ProclamationSyncTest, ParseLevelIdDefaultsToBasic) {
|
||||
TEST(ProclamationSyncTest, ParseIgnoresLegacySubscriptionLevelField) {
|
||||
std::vector<ProclamationRecord> 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].required_level_id, 1);
|
||||
EXPECT_EQ(out[1].required_level_id, 2);
|
||||
EXPECT_EQ(out[0].id, "a");
|
||||
EXPECT_EQ(out[1].id, "b");
|
||||
}
|
||||
|
||||
TEST(ProclamationSyncTest, ParseCategoriesJson) {
|
||||
|
||||
Reference in New Issue
Block a user