397 lines
15 KiB
C++
397 lines
15 KiB
C++
|
|
/**
|
|||
|
|
* @file test_license.cpp
|
|||
|
|
* @brief ViewDesignEngine 许可证管理单元测试
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#include <gtest/gtest.h>
|
|||
|
|
#include "vde/core/license_manager.h"
|
|||
|
|
#include <string>
|
|||
|
|
#include <ctime>
|
|||
|
|
|
|||
|
|
using namespace vde::core;
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
// LicenseTier 测试
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
TEST(LicenseTier, NameConversion) {
|
|||
|
|
EXPECT_STREQ(license_tier_name(LicenseTier::Community), "Community");
|
|||
|
|
EXPECT_STREQ(license_tier_name(LicenseTier::Professional), "Professional");
|
|||
|
|
EXPECT_STREQ(license_tier_name(LicenseTier::Enterprise), "Enterprise");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseTier, ParseName) {
|
|||
|
|
EXPECT_EQ(parse_license_tier("Community"), 0);
|
|||
|
|
EXPECT_EQ(parse_license_tier("Professional"), 1);
|
|||
|
|
EXPECT_EQ(parse_license_tier("Enterprise"), 2);
|
|||
|
|
EXPECT_EQ(parse_license_tier("community"), 0);
|
|||
|
|
EXPECT_EQ(parse_license_tier("unknown"), -1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
// Feature 枚举测试
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
TEST(FeatureEnum, FeatureName) {
|
|||
|
|
EXPECT_STREQ(feature_name(Feature::BASIC_BREP), "BASIC_BREP");
|
|||
|
|
EXPECT_STREQ(feature_name(Feature::CAM_5AXIS), "CAM_5AXIS");
|
|||
|
|
EXPECT_STREQ(feature_name(Feature::TECHNICAL_SUPPORT), "TECHNICAL_SUPPORT");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(FeatureEnum, ParseFeature) {
|
|||
|
|
EXPECT_EQ(parse_feature("BASIC_BREP"), 0);
|
|||
|
|
EXPECT_EQ(parse_feature("CAM_5AXIS"), 10);
|
|||
|
|
EXPECT_EQ(parse_feature("TECHNICAL_SUPPORT"), 30);
|
|||
|
|
EXPECT_EQ(parse_feature("NONEXISTENT"), -1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
// Tier → Feature 映射测试
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
TEST(TierFeatures, CommunityLevel) {
|
|||
|
|
auto feats = features_for_tier(LicenseTier::Community);
|
|||
|
|
EXPECT_GE(feats.size(), 6u); // 至少6个基础功能
|
|||
|
|
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::BASIC_BREP, LicenseTier::Community));
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::STEP_IMPORT_EXPORT, LicenseTier::Community));
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::IGES_IMPORT_EXPORT, LicenseTier::Community));
|
|||
|
|
|
|||
|
|
// Community 不应该有 Professional 功能
|
|||
|
|
EXPECT_FALSE(feature_available_in_tier(Feature::CAM_5AXIS, LicenseTier::Community));
|
|||
|
|
EXPECT_FALSE(feature_available_in_tier(Feature::GD_T, LicenseTier::Community));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(TierFeatures, ProfessionalLevel) {
|
|||
|
|
auto feats = features_for_tier(LicenseTier::Professional);
|
|||
|
|
EXPECT_GE(feats.size(), 12u);
|
|||
|
|
|
|||
|
|
// 包含 Community 的全部
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::BASIC_BREP, LicenseTier::Professional));
|
|||
|
|
// 包含 Professional 特有
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::CAM_5AXIS, LicenseTier::Professional));
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::ADVANCED_SURFACING, LicenseTier::Professional));
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::GD_T, LicenseTier::Professional));
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::SHEET_METAL, LicenseTier::Professional));
|
|||
|
|
|
|||
|
|
// 不应包含 Enterprise 功能
|
|||
|
|
EXPECT_FALSE(feature_available_in_tier(Feature::TECHNICAL_SUPPORT, LicenseTier::Professional));
|
|||
|
|
EXPECT_FALSE(feature_available_in_tier(Feature::GPU_ACCELERATION, LicenseTier::Professional));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(TierFeatures, EnterpriseLevel) {
|
|||
|
|
auto feats = features_for_tier(LicenseTier::Enterprise);
|
|||
|
|
EXPECT_GE(feats.size(), 18u);
|
|||
|
|
|
|||
|
|
// 全部功能
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::BASIC_BREP, LicenseTier::Enterprise));
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::CAM_5AXIS, LicenseTier::Enterprise));
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::TECHNICAL_SUPPORT, LicenseTier::Enterprise));
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::CUSTOM_FORMATS, LicenseTier::Enterprise));
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::GPU_ACCELERATION, LicenseTier::Enterprise));
|
|||
|
|
EXPECT_TRUE(feature_available_in_tier(Feature::LICENSE_MANAGER_SDK, LicenseTier::Enterprise));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
// LicenseInfo 结构体测试
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
TEST(LicenseInfo, DefaultConstruction) {
|
|||
|
|
LicenseInfo info;
|
|||
|
|
EXPECT_EQ(info.tier, LicenseTier::Community);
|
|||
|
|
EXPECT_EQ(info.licensee, "Trial User");
|
|||
|
|
EXPECT_EQ(info.expiry, 0);
|
|||
|
|
EXPECT_EQ(info.version, 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseInfo, IsExpiredPermanent) {
|
|||
|
|
LicenseInfo info;
|
|||
|
|
info.expiry = 0; // 永久
|
|||
|
|
EXPECT_FALSE(info.is_expired());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseInfo, IsExpiredFuture) {
|
|||
|
|
LicenseInfo info;
|
|||
|
|
info.expiry = std::time(nullptr) + 86400 * 365; // 一年后
|
|||
|
|
EXPECT_FALSE(info.is_expired());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseInfo, IsExpiredPast) {
|
|||
|
|
LicenseInfo info;
|
|||
|
|
info.expiry = std::time(nullptr) - 86400; // 昨天
|
|||
|
|
EXPECT_TRUE(info.is_expired());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseInfo, DaysUntilExpiry) {
|
|||
|
|
LicenseInfo info;
|
|||
|
|
|
|||
|
|
// 永久
|
|||
|
|
info.expiry = 0;
|
|||
|
|
EXPECT_EQ(info.days_until_expiry(), -1);
|
|||
|
|
|
|||
|
|
// 未来30天
|
|||
|
|
info.expiry = std::time(nullptr) + 86400 * 30;
|
|||
|
|
EXPECT_EQ(info.days_until_expiry(), 30);
|
|||
|
|
|
|||
|
|
// 未来1天
|
|||
|
|
info.expiry = std::time(nullptr) + 86400;
|
|||
|
|
EXPECT_EQ(info.days_until_expiry(), 1);
|
|||
|
|
|
|||
|
|
// 已过期
|
|||
|
|
info.expiry = std::time(nullptr) - 86400;
|
|||
|
|
EXPECT_EQ(info.days_until_expiry(), 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
// LicenseKey 生成/解析/验证测试
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
TEST(LicenseKey, GenerateAndParse) {
|
|||
|
|
// 生成一个 Community 永不过期的 Key
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Community, 0, "TestUser");
|
|||
|
|
EXPECT_FALSE(key.empty());
|
|||
|
|
EXPECT_EQ(key.substr(0, 4), "VDE-");
|
|||
|
|
|
|||
|
|
// 解析
|
|||
|
|
LicenseKey lk;
|
|||
|
|
EXPECT_TRUE(lk.parse(key));
|
|||
|
|
EXPECT_TRUE(lk.signature_valid());
|
|||
|
|
EXPECT_EQ(lk.info().tier, LicenseTier::Community);
|
|||
|
|
EXPECT_EQ(lk.info().expiry, 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseKey, GenerateAndParseProfessional) {
|
|||
|
|
std::time_t fut = std::time(nullptr) + 86400 * 365; // 一年后
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Professional, fut, "ProUser");
|
|||
|
|
|
|||
|
|
LicenseKey lk;
|
|||
|
|
EXPECT_TRUE(lk.parse(key));
|
|||
|
|
EXPECT_TRUE(lk.signature_valid());
|
|||
|
|
EXPECT_EQ(lk.info().tier, LicenseTier::Professional);
|
|||
|
|
EXPECT_EQ(lk.info().expiry, fut);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseKey, GenerateEnterprise) {
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Enterprise, 0, "EntUser");
|
|||
|
|
|
|||
|
|
LicenseKey lk;
|
|||
|
|
EXPECT_TRUE(lk.parse(key));
|
|||
|
|
EXPECT_TRUE(lk.signature_valid());
|
|||
|
|
EXPECT_EQ(lk.info().tier, LicenseTier::Enterprise);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseKey, Validate) {
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Community, 0, "User");
|
|||
|
|
EXPECT_TRUE(LicenseKey::validate(key));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseKey, ValidateExpired) {
|
|||
|
|
// 生成一个已过期的 Key
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Community,
|
|||
|
|
std::time(nullptr) - 86400, "User");
|
|||
|
|
EXPECT_FALSE(LicenseKey::validate(key));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseKey, InvalidFormat) {
|
|||
|
|
EXPECT_FALSE(LicenseKey::validate(""));
|
|||
|
|
EXPECT_FALSE(LicenseKey::validate("INVALID-KEY"));
|
|||
|
|
EXPECT_FALSE(LicenseKey::validate("VDE-!!!!"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseKey, TamperedKey) {
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Community, 0, "User");
|
|||
|
|
|
|||
|
|
// 篡改 key(改一个字符)
|
|||
|
|
if (key.size() > 6) {
|
|||
|
|
key[6] = (key[6] == 'A') ? 'B' : 'A';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
LicenseKey lk;
|
|||
|
|
bool parsed = lk.parse(key);
|
|||
|
|
if (parsed) {
|
|||
|
|
// 签名验证应该失败
|
|||
|
|
EXPECT_FALSE(lk.signature_valid());
|
|||
|
|
}
|
|||
|
|
// 无论如何,整体 validate 应该失败
|
|||
|
|
EXPECT_FALSE(LicenseKey::validate(key));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseKey, MultipleKeysAreDifferent) {
|
|||
|
|
std::string k1 = LicenseKey::generate(LicenseTier::Community, 0, "U1");
|
|||
|
|
std::string k2 = LicenseKey::generate(LicenseTier::Professional, 0, "U2");
|
|||
|
|
EXPECT_NE(k1, k2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseKey, KeyStringStored) {
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Community, 0, "U");
|
|||
|
|
LicenseKey lk;
|
|||
|
|
lk.parse(key);
|
|||
|
|
EXPECT_EQ(lk.key_string(), key);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
// LicenseManager 单例测试
|
|||
|
|
// ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, Singleton) {
|
|||
|
|
LicenseManager& mgr1 = LicenseManager::instance();
|
|||
|
|
LicenseManager& mgr2 = LicenseManager::instance();
|
|||
|
|
EXPECT_EQ(&mgr1, &mgr2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, DefaultCommunity) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
EXPECT_EQ(mgr.current_tier(), LicenseTier::Community);
|
|||
|
|
EXPECT_FALSE(mgr.is_expired());
|
|||
|
|
EXPECT_FALSE(mgr.is_trial());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, HasFeatureBasic) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
// Community 默认应该有基础功能
|
|||
|
|
EXPECT_TRUE(mgr.has_feature("BASIC_BREP"));
|
|||
|
|
EXPECT_TRUE(mgr.has_feature("STEP_IMPORT_EXPORT"));
|
|||
|
|
EXPECT_TRUE(mgr.has_feature(Feature::MESH_OPERATIONS));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, HasFeatureProfessional) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
// Community 不应该有 Professional 功能
|
|||
|
|
EXPECT_FALSE(mgr.has_feature("CAM_5AXIS"));
|
|||
|
|
EXPECT_FALSE(mgr.has_feature("GD_T"));
|
|||
|
|
EXPECT_FALSE(mgr.has_feature("ADVANCED_SURFACING"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, SetLicenseCommunity) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
|
|||
|
|
// 保存原始 license
|
|||
|
|
auto orig = mgr.current_license();
|
|||
|
|
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Community, 0, "Test");
|
|||
|
|
EXPECT_TRUE(mgr.set_license(key));
|
|||
|
|
EXPECT_EQ(mgr.current_tier(), LicenseTier::Community);
|
|||
|
|
EXPECT_FALSE(mgr.is_trial());
|
|||
|
|
|
|||
|
|
// 恢复原始
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Community, 0, "Community User"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, SetLicenseProfessional) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
std::time_t fut = std::time(nullptr) + 86400 * 365;
|
|||
|
|
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Professional, fut, "Pro");
|
|||
|
|
EXPECT_TRUE(mgr.set_license(key));
|
|||
|
|
EXPECT_EQ(mgr.current_tier(), LicenseTier::Professional);
|
|||
|
|
EXPECT_TRUE(mgr.has_feature("CAM_5AXIS"));
|
|||
|
|
EXPECT_TRUE(mgr.has_feature("GD_T"));
|
|||
|
|
|
|||
|
|
// 恢复默认
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Community, 0, "Community User"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, SetLicenseEnterprise) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Enterprise, 0, "Ent");
|
|||
|
|
EXPECT_TRUE(mgr.set_license(key));
|
|||
|
|
EXPECT_EQ(mgr.current_tier(), LicenseTier::Enterprise);
|
|||
|
|
EXPECT_TRUE(mgr.has_feature("TECHNICAL_SUPPORT"));
|
|||
|
|
EXPECT_TRUE(mgr.has_feature("GPU_ACCELERATION"));
|
|||
|
|
EXPECT_TRUE(mgr.has_feature("CUSTOM_FORMATS"));
|
|||
|
|
|
|||
|
|
// 恢复
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Community, 0, "Community User"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, SetLicenseExpired) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
// 先生成一个有效 key 确保非过期状态
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Community, 0, "Reset"));
|
|||
|
|
|
|||
|
|
// 生成已过期的 key
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Professional,
|
|||
|
|
std::time(nullptr) - 86400, "Expired");
|
|||
|
|
// 已过期 key 应该被拒绝
|
|||
|
|
EXPECT_FALSE(mgr.set_license(key));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, SetLicenseInvalid) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
EXPECT_FALSE(mgr.set_license("INVALID-KEY"));
|
|||
|
|
EXPECT_FALSE(mgr.set_license(""));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, TrialMode) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
// 先设置永久 Community key
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Community, 0, "Reset"));
|
|||
|
|
|
|||
|
|
mgr.start_trial(30);
|
|||
|
|
EXPECT_TRUE(mgr.is_trial());
|
|||
|
|
EXPECT_EQ(mgr.current_tier(), LicenseTier::Professional);
|
|||
|
|
EXPECT_EQ(mgr.trial_days_left(), 30);
|
|||
|
|
EXPECT_TRUE(mgr.has_feature("CAM_5AXIS"));
|
|||
|
|
|
|||
|
|
// 但也应该有基础功能
|
|||
|
|
EXPECT_TRUE(mgr.has_feature("BASIC_BREP"));
|
|||
|
|
|
|||
|
|
// 恢复
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Community, 0, "Community User"));
|
|||
|
|
EXPECT_FALSE(mgr.is_trial());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, TrialDaysLeftVarious) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Community, 0, "Reset"));
|
|||
|
|
|
|||
|
|
// 没有试用时
|
|||
|
|
EXPECT_EQ(mgr.trial_days_left(), -1);
|
|||
|
|
|
|||
|
|
// 30天试用
|
|||
|
|
mgr.start_trial(30);
|
|||
|
|
EXPECT_GE(mgr.trial_days_left(), 29); // 可能刚好跨天
|
|||
|
|
EXPECT_LE(mgr.trial_days_left(), 30);
|
|||
|
|
|
|||
|
|
// 恢复
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Community, 0, "Reset"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, CurrentLicense) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
std::string key = LicenseKey::generate(LicenseTier::Professional,
|
|||
|
|
std::time(nullptr) + 86400 * 90, "TestCo");
|
|||
|
|
mgr.set_license(key);
|
|||
|
|
|
|||
|
|
const auto& info = mgr.current_license();
|
|||
|
|
EXPECT_EQ(info.tier, LicenseTier::Professional);
|
|||
|
|
EXPECT_EQ(info.licensee, "Licensee"); // payload 中无 licensee 字段
|
|||
|
|
|
|||
|
|
// 恢复
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Community, 0, "Community User"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, HasFeatureInvalidName) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
EXPECT_FALSE(mgr.has_feature("NONEXISTENT_FEATURE"));
|
|||
|
|
EXPECT_FALSE(mgr.has_feature(""));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(LicenseManager, ExpiredLicenseBlocksFeatures) {
|
|||
|
|
auto& mgr = LicenseManager::instance();
|
|||
|
|
// 恢复有效状态
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Professional,
|
|||
|
|
std::time(nullptr) + 86400 * 365, "Reset"));
|
|||
|
|
EXPECT_TRUE(mgr.has_feature("CAM_5AXIS"));
|
|||
|
|
|
|||
|
|
// 设置已过期的 license
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Professional,
|
|||
|
|
std::time(nullptr) - 86400, "Expired"));
|
|||
|
|
// 过期 license 不会被设置(set_license 返回 false),所以状态不变
|
|||
|
|
// 但如果 is_expired() 返回 true,has_feature 也会 false
|
|||
|
|
|
|||
|
|
// 恢复
|
|||
|
|
mgr.set_license(LicenseKey::generate(LicenseTier::Community, 0, "Community User"));
|
|||
|
|
}
|