package licensing import ( "encoding/json" "reflect" "strings" "testing" "time" ) func containsString(values []string, want string) bool { for _, value := range values { if value == want { return true } } return false } func hasLimit(payload EntitlementPayload, key string) bool { for _, limit := range payload.Limits { if limit.Key == key { return true } } return false } func TestBuildEntitlementPayload_ActiveLicenseOmitsRetiredMonitoredSystemLimit(t *testing.T) { status := &LicenseStatus{ Valid: true, Tier: TierCloud, PlanVersion: "cloud_starter", Features: append([]string(nil), TierFeatures[TierPro]...), } payload := BuildEntitlementPayload(status, "") if payload.SubscriptionState != string(SubStateActive) { t.Fatalf("expected subscription_state %q, got %q", SubStateActive, payload.SubscriptionState) } if !reflect.DeepEqual(payload.Capabilities, status.Features) { t.Fatalf("expected capabilities to match status features") } if hasLimit(payload, MaxMonitoredSystemsLicenseGateKey) { t.Fatalf("payload exposed retired monitored-system limit: %+v", payload.Limits) } if len(payload.UpgradeReasons) != 0 { t.Fatalf("expected no upgrade reasons for pro tier, got %d", len(payload.UpgradeReasons)) } } func TestBuildEntitlementPayload_StripsInternalOnlyCapabilities(t *testing.T) { status := &LicenseStatus{ Valid: true, Tier: TierEnterprise, Features: []string{ FeatureRelay, FeatureDemoFixtures, FeatureMultiTenant, }, } payload := BuildEntitlementPayload(status, "") if reflect.DeepEqual(payload.Capabilities, status.Features) { t.Fatalf("expected payload capabilities to filter internal-only features, got %v", payload.Capabilities) } if containsString(payload.Capabilities, FeatureDemoFixtures) { t.Fatalf("payload capabilities leaked internal feature %q: %v", FeatureDemoFixtures, payload.Capabilities) } if !containsString(payload.Capabilities, FeatureRelay) || !containsString(payload.Capabilities, FeatureMultiTenant) { t.Fatalf("payload capabilities lost public features: %v", payload.Capabilities) } } func TestBuildEntitlementPayload_FreeTier(t *testing.T) { status := &LicenseStatus{ Valid: true, Tier: TierFree, Features: append([]string(nil), TierFeatures[TierFree]...), } payload := BuildEntitlementPayload(status, "") if len(payload.UpgradeReasons) == 0 { t.Fatalf("expected upgrade reasons for free tier") } if payload.TrialEligible { t.Fatal("expected retired self-hosted trial eligibility to stay false") } if payload.TrialEligibilityReason != "" { t.Fatalf("TrialEligibilityReason=%q, want empty", payload.TrialEligibilityReason) } for _, reason := range payload.UpgradeReasons { if reason.ActionURL == "" { t.Fatalf("expected action_url for reason %q", reason.Key) } } } func TestBuildEntitlementPayloadWithUsage_CurrentValues(t *testing.T) { status := &LicenseStatus{ Valid: true, Tier: TierCloud, PlanVersion: "cloud_starter", Features: append([]string(nil), TierFeatures[TierPro]...), MaxGuests: 100, } payload := BuildEntitlementPayloadWithUsage(status, "", EntitlementUsageSnapshot{ MonitoredSystems: 12, MonitoredSystemsAvailable: true, Guests: 44, LegacyConnections: LegacyConnectionCounts{ ProxmoxNodes: 2, DockerHosts: 1, KubernetesClusters: 3, }, }, nil) if hasLimit(payload, MaxMonitoredSystemsLicenseGateKey) { t.Fatalf("payload exposed retired monitored-system limit: %+v", payload.Limits) } var guestLimit *LimitStatus for i := range payload.Limits { if payload.Limits[i].Key == "max_guests" { guestLimit = &payload.Limits[i] } } if guestLimit == nil { t.Fatalf("expected max_guests limit") } if guestLimit.Current != 44 { t.Fatalf("expected guest current 44, got %d", guestLimit.Current) } if payload.LegacyConnections.ProxmoxNodes != 2 { t.Fatalf("expected proxmox_nodes 2, got %d", payload.LegacyConnections.ProxmoxNodes) } if payload.LegacyConnections.DockerHosts != 1 { t.Fatalf("expected docker_hosts 1, got %d", payload.LegacyConnections.DockerHosts) } if payload.LegacyConnections.KubernetesClusters != 3 { t.Fatalf( "expected kubernetes_clusters 3, got %d", payload.LegacyConnections.KubernetesClusters, ) } if payload.HasMigrationGap { t.Fatal("expected has_migration_gap=false") } } func TestBuildEntitlementPayload_TrialState(t *testing.T) { expiresAt := time.Now().Add(36 * time.Hour).UTC().Format(time.RFC3339) status := &LicenseStatus{ Valid: true, Tier: TierPro, Features: append([]string(nil), TierFeatures[TierPro]...), ExpiresAt: &expiresAt, } payload := BuildEntitlementPayload(status, string(SubStateTrial)) if payload.SubscriptionState != string(SubStateTrial) { t.Fatalf("expected subscription_state %q, got %q", SubStateTrial, payload.SubscriptionState) } if payload.TrialExpiresAt == nil { t.Fatalf("expected trial_expires_at to be populated for trial state") } if payload.TrialDaysRemaining == nil { t.Fatalf("expected trial_days_remaining to be populated for trial state") } if *payload.TrialDaysRemaining != 2 { t.Fatalf("expected trial_days_remaining 2, got %d", *payload.TrialDaysRemaining) } } func TestBuildEntitlementPayload_CopiesStatusDisplayFields(t *testing.T) { tests := []struct { name string planVersion string }{ {name: "lifetime grandfathered", planVersion: "v5_lifetime_grandfathered"}, {name: "monthly grandfathered", planVersion: "v5_pro_monthly_grandfathered"}, {name: "annual grandfathered", planVersion: "v5_pro_annual_grandfathered"}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { expiresAt := time.Now().Add(72 * time.Hour).UTC().Format(time.RFC3339) graceEnd := time.Now().Add(24 * time.Hour).UTC().Format(time.RFC3339) status := &LicenseStatus{ Valid: true, Tier: TierPro, PlanVersion: tc.planVersion, Email: "owner@example.com", ExpiresAt: &expiresAt, IsLifetime: false, DaysRemaining: 3, InGracePeriod: true, GracePeriodEnd: &graceEnd, Features: append([]string(nil), TierFeatures[TierPro]...), } payload := BuildEntitlementPayload(status, string(SubStateGrace)) if !payload.Valid { t.Fatalf("expected valid=true, got %v", payload.Valid) } if payload.PlanVersion != tc.planVersion { t.Fatalf("expected plan_version %q, got %q", tc.planVersion, payload.PlanVersion) } if payload.LicensedEmail != "owner@example.com" { t.Fatalf("expected licensed_email %q, got %q", "owner@example.com", payload.LicensedEmail) } if payload.ExpiresAt == nil || *payload.ExpiresAt != expiresAt { t.Fatalf("expected expires_at %q, got %v", expiresAt, payload.ExpiresAt) } if payload.DaysRemaining != 3 { t.Fatalf("expected days_remaining 3, got %d", payload.DaysRemaining) } if !payload.InGracePeriod { t.Fatalf("expected in_grace_period=true, got %v", payload.InGracePeriod) } if payload.GracePeriodEnd == nil || *payload.GracePeriodEnd != graceEnd { t.Fatalf("expected grace_period_end %q, got %v", graceEnd, payload.GracePeriodEnd) } }) } } func TestBuildEntitlementPayload_LifetimeOmitsCommercialCaps(t *testing.T) { payload := BuildEntitlementPayloadWithUsage(&LicenseStatus{ Valid: true, Tier: TierLifetime, PlanVersion: "v5_lifetime_grandfathered", IsLifetime: true, Features: append([]string(nil), TierFeatures[TierLifetime]...), MaxGuests: 0, }, string(SubStateActive), EntitlementUsageSnapshot{ MonitoredSystems: 15, MonitoredSystemsAvailable: true, Guests: 4, }, nil) if len(payload.Limits) != 0 { t.Fatalf("expected lifetime entitlements to omit commercial caps, got %+v", payload.Limits) } } func TestBuildEntitlementPayload_MaxHistoryDays(t *testing.T) { tests := []struct { name string tier Tier wantDays int }{ {"free tier gets 7 days", TierFree, 7}, {"relay tier gets 14 days", TierRelay, 14}, {"pro tier gets 90 days", TierPro, 90}, {"pro_plus tier gets 90 days", TierProPlus, 90}, {"pro_annual tier gets 90 days", TierProAnnual, 90}, {"lifetime tier gets 90 days", TierLifetime, 90}, {"cloud tier gets 90 days", TierCloud, 90}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { status := &LicenseStatus{ Valid: true, Tier: tc.tier, Features: append([]string(nil), TierFeatures[tc.tier]...), } payload := BuildEntitlementPayload(status, "") if payload.MaxHistoryDays != tc.wantDays { t.Fatalf("expected MaxHistoryDays=%d for tier %q, got %d", tc.wantDays, tc.tier, payload.MaxHistoryDays) } }) } } func TestBuildEntitlementPayload_NilStatus_MaxHistoryDays(t *testing.T) { payload := BuildEntitlementPayloadWithUsage(nil, "", EntitlementUsageSnapshot{}, nil) if payload.MaxHistoryDays != TierHistoryDays[TierFree] { t.Fatalf("expected MaxHistoryDays=%d for nil status, got %d", TierHistoryDays[TierFree], payload.MaxHistoryDays) } if payload.HasMigrationGap { t.Fatal("expected has_migration_gap=false for nil status payload") } if payload.LegacyConnections.Total() != 0 { t.Fatalf("expected zero legacy connections for nil status payload, got %+v", payload.LegacyConnections) } } func TestBuildCommercialPosturePayloadWithUsage_CurrentValues(t *testing.T) { status := &LicenseStatus{ Valid: true, Tier: TierCloud, PlanVersion: "cloud_starter", Features: append([]string(nil), TierFeatures[TierPro]...), } payload := BuildCommercialPosturePayloadWithUsage(status, "", EntitlementUsageSnapshot{ MonitoredSystems: 7, MonitoredSystemsAvailable: true, LegacyConnections: LegacyConnectionCounts{ ProxmoxNodes: 2, DockerHosts: 1, }, }, nil) if payload.Tier != string(TierCloud) { t.Fatalf("expected tier=%q, got %q", TierCloud, payload.Tier) } if payload.SubscriptionState != string(SubStateActive) { t.Fatalf("expected subscription_state=%q, got %q", SubStateActive, payload.SubscriptionState) } if len(payload.UpgradeReasons) != 0 { t.Fatalf("expected hosted paid posture to omit upgrade reasons, got %v", payload.UpgradeReasons) } if payload.LegacyConnections.ProxmoxNodes != 2 || payload.LegacyConnections.DockerHosts != 1 { t.Fatalf("expected legacy connection counts to be preserved, got %+v", payload.LegacyConnections) } if payload.HasMigrationGap { t.Fatal("expected has_migration_gap=false") } } func TestCommercialPosturePayloadFromEntitlementPayload_StripsBillingIdentityFields(t *testing.T) { expiresAt := time.Now().Add(48 * time.Hour).UTC().Format(time.RFC3339) payload := BuildEntitlementPayloadWithUsage(&LicenseStatus{ Valid: true, Tier: TierCloud, PlanVersion: "cloud_starter", Email: "owner@example.com", ExpiresAt: &expiresAt, IsLifetime: false, DaysRemaining: 2, Features: append([]string(nil), TierFeatures[TierPro]...), }, string(SubStateActive), EntitlementUsageSnapshot{ MonitoredSystems: 12, MonitoredSystemsAvailable: true, LegacyConnections: LegacyConnectionCounts{ ProxmoxNodes: 1, }, }, nil) posture := CommercialPosturePayloadFromEntitlementPayload(payload) if posture.Tier != string(TierCloud) { t.Fatalf("expected tier=%q, got %q", TierCloud, posture.Tier) } if posture.SubscriptionState != string(SubStateActive) { t.Fatalf("expected subscription_state=%q, got %q", SubStateActive, posture.SubscriptionState) } if posture.LegacyConnections.ProxmoxNodes != 1 { t.Fatalf("expected proxmox_nodes=1, got %+v", posture.LegacyConnections) } body, err := json.Marshal(posture) if err != nil { t.Fatalf("marshal commercial posture: %v", err) } jsonBody := string(body) for _, forbidden := range []string{ `"capabilities"`, `"limits"`, `"licensed_email"`, `"plan_version"`, `"hosted_mode"`, `"valid"`, `"max_history_days"`, } { if strings.Contains(jsonBody, forbidden) { t.Fatalf("commercial posture leaked billing/runtime field %s in %s", forbidden, jsonBody) } } } func TestBuildRuntimeCapabilitiesPayloadWithUsage_CurrentValues(t *testing.T) { status := &LicenseStatus{ Valid: true, Tier: TierCloud, PlanVersion: "cloud_starter", Features: append([]string(nil), TierFeatures[TierRelay]...), MaxGuests: 12, } payload := BuildRuntimeCapabilitiesPayloadWithUsage(status, "", EntitlementUsageSnapshot{ Guests: 5, }) if !reflect.DeepEqual(payload.Capabilities, status.Features) { t.Fatalf("expected capabilities to match status features") } if payload.MaxHistoryDays != TierHistoryDays[TierCloud] { t.Fatalf("expected cloud max history days %d, got %d", TierHistoryDays[TierCloud], payload.MaxHistoryDays) } if len(payload.Limits) != 1 { t.Fatalf("expected one runtime limit, got %d", len(payload.Limits)) } if payload.Limits[0].Key != "max_guests" { t.Fatalf("expected runtime limit key max_guests, got %q", payload.Limits[0].Key) } if payload.Limits[0].Current != 5 { t.Fatalf("expected runtime current 5, got %d", payload.Limits[0].Current) } } func TestBuildRuntimeCapabilitiesPayloadWithUsage_StripsInternalOnlyCapabilities(t *testing.T) { status := &LicenseStatus{ Valid: true, Tier: TierEnterprise, Features: []string{ FeatureRelay, FeatureDemoFixtures, }, } payload := BuildRuntimeCapabilitiesPayloadWithUsage(status, "", EntitlementUsageSnapshot{}) if containsString(payload.Capabilities, FeatureDemoFixtures) { t.Fatalf("runtime capabilities leaked internal feature %q: %v", FeatureDemoFixtures, payload.Capabilities) } if !containsString(payload.Capabilities, FeatureRelay) { t.Fatalf("runtime capabilities lost public feature %q: %v", FeatureRelay, payload.Capabilities) } } func TestFilterCapabilitiesForRuntimeIdentity_BlocksPrivateRuntimeCapabilitiesOnCommunityBuild(t *testing.T) { filtered, blocked := FilterCapabilitiesForRuntimeIdentity([]string{ FeatureRelay, FeatureAuditLogging, FeatureRBAC, FeatureAIAutoFix, FeatureAdvancedReporting, }, CommunityRuntimeIdentity()) for _, feature := range []string{FeatureAuditLogging, FeatureRBAC, FeatureAIAutoFix} { if containsString(filtered, feature) { t.Fatalf("community runtime capabilities retained private feature %q: %v", feature, filtered) } } for _, feature := range []string{FeatureRelay, FeatureAdvancedReporting} { if !containsString(filtered, feature) { t.Fatalf("community runtime capabilities lost supported feature %q: %v", feature, filtered) } } if len(blocked) != 3 { t.Fatalf("blocked capability count=%d, want 3: %+v", len(blocked), blocked) } for _, block := range blocked { if block.Reason != "paid_runtime_required" { t.Fatalf("blocked reason=%q, want paid_runtime_required", block.Reason) } if block.ActionURL != PulseProDownloadURL { t.Fatalf("blocked action_url=%q, want %q", block.ActionURL, PulseProDownloadURL) } } } func TestFilterCapabilitiesForRuntimeIdentity_PreservesPrivateRuntimeCapabilitiesOnProBuild(t *testing.T) { capabilities := []string{FeatureRelay, FeatureAuditLogging, FeatureRBAC, FeatureAIAutoFix} filtered, blocked := FilterCapabilitiesForRuntimeIdentity(capabilities, ProRuntimeIdentity()) if !reflect.DeepEqual(filtered, capabilities) { t.Fatalf("pro runtime capabilities=%v, want %v", filtered, capabilities) } if len(blocked) != 0 { t.Fatalf("pro runtime blocked capabilities=%+v, want none", blocked) } } func TestBuildRuntimeCapabilitiesPayload_NilStatusDefaultsToFreeTier(t *testing.T) { payload := BuildRuntimeCapabilitiesPayload(nil, "") if payload.MaxHistoryDays != TierHistoryDays[TierFree] { t.Fatalf("expected MaxHistoryDays=%d for nil status, got %d", TierHistoryDays[TierFree], payload.MaxHistoryDays) } if len(payload.Capabilities) != 0 { t.Fatalf("expected no runtime capabilities for nil status, got %+v", payload.Capabilities) } } func TestBuildRuntimeCapabilitiesPayload_EmptyCollectionsRemainJSONArrays(t *testing.T) { payload := BuildRuntimeCapabilitiesPayloadWithUsage(&LicenseStatus{ Valid: true, Tier: TierFree, Features: []string{}, }, "", EntitlementUsageSnapshot{}) if payload.Capabilities == nil { t.Fatal("expected runtime capabilities to preserve empty capabilities as []") } if payload.Limits == nil { t.Fatal("expected runtime capabilities to preserve empty limits as []") } body, err := json.Marshal(payload) if err != nil { t.Fatalf("marshal runtime capabilities: %v", err) } if strings.Contains(string(body), `"capabilities":null`) { t.Fatalf("runtime capabilities encoded null capabilities: %s", string(body)) } if strings.Contains(string(body), `"limits":null`) { t.Fatalf("runtime capabilities encoded null limits: %s", string(body)) } } func TestLimitState(t *testing.T) { tests := []struct { name string current int64 limit int64 want string }{ {name: "ok_below_threshold", current: 50, limit: 100, want: "ok"}, {name: "warning_at_90_percent", current: 90, limit: 100, want: "warning"}, {name: "enforced_at_limit", current: 100, limit: 100, want: "enforced"}, {name: "enforced_above_limit", current: 110, limit: 100, want: "enforced"}, {name: "ok_unlimited", current: 50, limit: 0, want: "ok"}, {name: "small_limit_ok", current: 3, limit: 5, want: "ok"}, {name: "small_limit_warning_at_n_minus_1", current: 4, limit: 5, want: "warning"}, {name: "small_limit_enforced_at_limit", current: 5, limit: 5, want: "enforced"}, {name: "small_limit_8_warning", current: 7, limit: 8, want: "warning"}, {name: "small_limit_8_ok", current: 6, limit: 8, want: "ok"}, {name: "small_limit_10_warning", current: 9, limit: 10, want: "warning"}, {name: "small_limit_10_ok", current: 8, limit: 10, want: "ok"}, {name: "limit_1_zero_usage_ok", current: 0, limit: 1, want: "ok"}, {name: "limit_1_at_limit_enforced", current: 1, limit: 1, want: "enforced"}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := LimitState(tc.current, tc.limit) if got != tc.want { t.Fatalf("LimitState(%d, %d) = %q, want %q", tc.current, tc.limit, got, tc.want) } }) } }