mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-07 00:37:36 +00:00
348 lines
12 KiB
Go
348 lines
12 KiB
Go
package licensing
|
|
|
|
import "testing"
|
|
|
|
func TestDefaultBillingState(t *testing.T) {
|
|
state := DefaultBillingState()
|
|
if state == nil {
|
|
t.Fatal("expected non-nil default billing state")
|
|
}
|
|
if state.SubscriptionState != SubStateTrial {
|
|
t.Fatalf("subscription_state=%q, want %q", state.SubscriptionState, SubStateTrial)
|
|
}
|
|
if state.PlanVersion != string(SubStateTrial) {
|
|
t.Fatalf("plan_version=%q, want %q", state.PlanVersion, string(SubStateTrial))
|
|
}
|
|
if state.Capabilities == nil || len(state.Capabilities) != 0 {
|
|
t.Fatalf("expected empty capabilities slice, got %#v", state.Capabilities)
|
|
}
|
|
if state.MetersEnabled == nil || len(state.MetersEnabled) != 0 {
|
|
t.Fatalf("expected empty meters_enabled slice, got %#v", state.MetersEnabled)
|
|
}
|
|
if state.Limits == nil || len(state.Limits) != 0 {
|
|
t.Fatalf("expected empty limits map, got %#v", state.Limits)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeBillingState(t *testing.T) {
|
|
trialStarted := int64(100)
|
|
trialEnds := int64(200)
|
|
input := &BillingState{
|
|
Capabilities: []string{" a ", "b"},
|
|
Limits: map[string]int64{"max_monitored_systems": 10},
|
|
MetersEnabled: []string{"meter_a"},
|
|
PlanVersion: " ",
|
|
SubscriptionState: SubscriptionState(" ACTIVE "),
|
|
TrialStartedAt: &trialStarted,
|
|
TrialEndsAt: &trialEnds,
|
|
StripeCustomerID: " cus_123 ",
|
|
}
|
|
|
|
normalized := NormalizeBillingState(input)
|
|
if normalized.SubscriptionState != SubStateActive {
|
|
t.Fatalf("subscription_state=%q, want %q", normalized.SubscriptionState, SubStateActive)
|
|
}
|
|
if normalized.PlanVersion != "" {
|
|
t.Fatalf("plan_version=%q, want empty", normalized.PlanVersion)
|
|
}
|
|
if normalized.StripeCustomerID != "cus_123" {
|
|
t.Fatalf("stripe_customer_id=%q, want %q", normalized.StripeCustomerID, "cus_123")
|
|
}
|
|
if normalized.Limits["max_monitored_systems"] != 10 {
|
|
t.Fatalf("limits[max_nodes]=%d, want 10", normalized.Limits["max_monitored_systems"])
|
|
}
|
|
|
|
input.Capabilities[0] = "changed"
|
|
input.MetersEnabled[0] = "changed"
|
|
input.Limits["max_monitored_systems"] = 99
|
|
if normalized.Capabilities[0] != " a " {
|
|
t.Fatalf("expected capabilities to be copied")
|
|
}
|
|
if normalized.MetersEnabled[0] != "meter_a" {
|
|
t.Fatalf("expected meters_enabled to be copied")
|
|
}
|
|
if normalized.Limits["max_monitored_systems"] != 10 {
|
|
t.Fatalf("expected limits map to be copied")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeBillingStateNil(t *testing.T) {
|
|
normalized := NormalizeBillingState(nil)
|
|
if normalized.SubscriptionState != SubStateTrial {
|
|
t.Fatalf("subscription_state=%q, want %q", normalized.SubscriptionState, SubStateTrial)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeBillingState_PreservesAllFields(t *testing.T) {
|
|
trialStarted := int64(100)
|
|
trialEnds := int64(200)
|
|
trialExtended := int64(150)
|
|
|
|
input := &BillingState{
|
|
Capabilities: []string{"relay", "ai"},
|
|
Limits: map[string]int64{"max_monitored_systems": 50},
|
|
MetersEnabled: []string{"active_agents"},
|
|
PlanVersion: "pro-v2",
|
|
SubscriptionState: SubStateActive,
|
|
TrialStartedAt: &trialStarted,
|
|
TrialEndsAt: &trialEnds,
|
|
TrialExtendedAt: &trialExtended,
|
|
Integrity: "deadbeef",
|
|
StripeCustomerID: "cus_123",
|
|
StripeSubscriptionID: "sub_456",
|
|
StripePriceID: "price_789",
|
|
}
|
|
|
|
normalized := NormalizeBillingState(input)
|
|
|
|
// Verify every field survives normalization.
|
|
if len(normalized.Capabilities) != 2 {
|
|
t.Fatalf("capabilities: got %v", normalized.Capabilities)
|
|
}
|
|
if normalized.Limits["max_monitored_systems"] != 50 {
|
|
t.Fatalf("limits[max_nodes]: got %d", normalized.Limits["max_monitored_systems"])
|
|
}
|
|
if normalized.MetersEnabled[0] != "active_agents" {
|
|
t.Fatalf("meters_enabled: got %v", normalized.MetersEnabled)
|
|
}
|
|
if normalized.PlanVersion != "pro-v2" {
|
|
t.Fatalf("plan_version: got %q", normalized.PlanVersion)
|
|
}
|
|
if normalized.SubscriptionState != SubStateActive {
|
|
t.Fatalf("subscription_state: got %q", normalized.SubscriptionState)
|
|
}
|
|
if normalized.TrialStartedAt == nil || *normalized.TrialStartedAt != 100 {
|
|
t.Fatalf("trial_started_at: got %v", normalized.TrialStartedAt)
|
|
}
|
|
if normalized.TrialEndsAt == nil || *normalized.TrialEndsAt != 200 {
|
|
t.Fatalf("trial_ends_at: got %v", normalized.TrialEndsAt)
|
|
}
|
|
if normalized.TrialExtendedAt == nil || *normalized.TrialExtendedAt != 150 {
|
|
t.Fatalf("trial_extended_at: got %v", normalized.TrialExtendedAt)
|
|
}
|
|
if normalized.Integrity != "deadbeef" {
|
|
t.Fatalf("integrity: got %q", normalized.Integrity)
|
|
}
|
|
if normalized.StripeCustomerID != "cus_123" {
|
|
t.Fatalf("stripe_customer_id: got %q", normalized.StripeCustomerID)
|
|
}
|
|
if normalized.StripeSubscriptionID != "sub_456" {
|
|
t.Fatalf("stripe_subscription_id: got %q", normalized.StripeSubscriptionID)
|
|
}
|
|
if normalized.StripePriceID != "price_789" {
|
|
t.Fatalf("stripe_price_id: got %q", normalized.StripePriceID)
|
|
}
|
|
|
|
// Verify pointer fields are deep-copied (not aliased).
|
|
*input.TrialStartedAt = 999
|
|
if *normalized.TrialStartedAt != 100 {
|
|
t.Fatal("trial_started_at was aliased, not cloned")
|
|
}
|
|
*input.TrialExtendedAt = 999
|
|
if *normalized.TrialExtendedAt != 150 {
|
|
t.Fatal("trial_extended_at was aliased, not cloned")
|
|
}
|
|
}
|
|
|
|
func TestCloneBillingState_PreservesAllFields(t *testing.T) {
|
|
trialStarted := int64(100)
|
|
trialEnds := int64(200)
|
|
trialExtended := int64(150)
|
|
|
|
input := BillingState{
|
|
Capabilities: []string{"relay", "ai"},
|
|
Limits: map[string]int64{"max_monitored_systems": 50},
|
|
MetersEnabled: []string{"active_agents"},
|
|
PlanVersion: "pro-v2",
|
|
SubscriptionState: SubStateActive,
|
|
TrialStartedAt: &trialStarted,
|
|
TrialEndsAt: &trialEnds,
|
|
TrialExtendedAt: &trialExtended,
|
|
Integrity: "deadbeef",
|
|
StripeCustomerID: "cus_123",
|
|
StripeSubscriptionID: "sub_456",
|
|
StripePriceID: "price_789",
|
|
}
|
|
|
|
cloned := cloneBillingState(input)
|
|
|
|
if cloned.Integrity != "deadbeef" {
|
|
t.Fatalf("integrity: got %q", cloned.Integrity)
|
|
}
|
|
if cloned.StripeCustomerID != "cus_123" {
|
|
t.Fatalf("stripe_customer_id: got %q", cloned.StripeCustomerID)
|
|
}
|
|
if cloned.StripeSubscriptionID != "sub_456" {
|
|
t.Fatalf("stripe_subscription_id: got %q", cloned.StripeSubscriptionID)
|
|
}
|
|
if cloned.StripePriceID != "price_789" {
|
|
t.Fatalf("stripe_price_id: got %q", cloned.StripePriceID)
|
|
}
|
|
if cloned.TrialExtendedAt == nil || *cloned.TrialExtendedAt != 150 {
|
|
t.Fatalf("trial_extended_at: got %v", cloned.TrialExtendedAt)
|
|
}
|
|
|
|
// Verify deep copy.
|
|
*input.TrialExtendedAt = 999
|
|
input.Capabilities[0] = "changed"
|
|
if *cloned.TrialExtendedAt != 150 {
|
|
t.Fatal("trial_extended_at was aliased")
|
|
}
|
|
if cloned.Capabilities[0] != "relay" {
|
|
t.Fatal("capabilities were aliased")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeThenClone_RoundTrip(t *testing.T) {
|
|
trialStarted := int64(100)
|
|
trialEnds := int64(200)
|
|
trialExtended := int64(150)
|
|
|
|
original := &BillingState{
|
|
Capabilities: []string{"relay"},
|
|
Limits: map[string]int64{"max_monitored_systems": 50},
|
|
MetersEnabled: []string{"active_agents"},
|
|
PlanVersion: "pro-v2",
|
|
SubscriptionState: SubStateActive,
|
|
TrialStartedAt: &trialStarted,
|
|
TrialEndsAt: &trialEnds,
|
|
TrialExtendedAt: &trialExtended,
|
|
Integrity: "abc123",
|
|
StripeCustomerID: "cus_1",
|
|
StripeSubscriptionID: "sub_2",
|
|
StripePriceID: "price_3",
|
|
}
|
|
|
|
normalized := NormalizeBillingState(original)
|
|
cloned := cloneBillingState(*normalized)
|
|
|
|
// Every field must survive the full normalize → clone chain.
|
|
if cloned.Integrity != "abc123" {
|
|
t.Fatalf("integrity lost in round-trip: got %q", cloned.Integrity)
|
|
}
|
|
if cloned.StripeCustomerID != "cus_1" {
|
|
t.Fatalf("stripe_customer_id lost: got %q", cloned.StripeCustomerID)
|
|
}
|
|
if cloned.TrialExtendedAt == nil || *cloned.TrialExtendedAt != 150 {
|
|
t.Fatalf("trial_extended_at lost: got %v", cloned.TrialExtendedAt)
|
|
}
|
|
}
|
|
|
|
func TestIsValidBillingSubscriptionState(t *testing.T) {
|
|
valid := []SubscriptionState{
|
|
SubStateTrial,
|
|
SubStateActive,
|
|
SubStateGrace,
|
|
SubStateExpired,
|
|
SubStateSuspended,
|
|
SubStateCanceled,
|
|
}
|
|
for _, state := range valid {
|
|
if !IsValidBillingSubscriptionState(state) {
|
|
t.Fatalf("expected %q to be valid", state)
|
|
}
|
|
}
|
|
if IsValidBillingSubscriptionState(SubscriptionState("invalid")) {
|
|
t.Fatal("expected invalid state to be rejected")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeBillingState_MaxNodesToMaxMonitoredSystemsMigration(t *testing.T) {
|
|
t.Run("legacy_key_migrated", func(t *testing.T) {
|
|
state := &BillingState{
|
|
Limits: map[string]int64{"max_nodes": 10},
|
|
}
|
|
normalized := NormalizeBillingState(state)
|
|
if normalized.Limits["max_monitored_systems"] != 10 {
|
|
t.Fatalf("expected max_monitored_systems=10, got %d", normalized.Limits["max_monitored_systems"])
|
|
}
|
|
if _, hasOld := normalized.Limits["max_nodes"]; hasOld {
|
|
t.Fatal("expected max_nodes to be deleted after migration")
|
|
}
|
|
})
|
|
|
|
t.Run("new_key_preserved_legacy_deleted", func(t *testing.T) {
|
|
state := &BillingState{
|
|
Limits: map[string]int64{"max_monitored_systems": 15, "max_nodes": 5},
|
|
}
|
|
normalized := NormalizeBillingState(state)
|
|
if normalized.Limits["max_monitored_systems"] != 15 {
|
|
t.Fatalf("expected max_monitored_systems=15, got %d", normalized.Limits["max_monitored_systems"])
|
|
}
|
|
if _, hasOld := normalized.Limits["max_nodes"]; hasOld {
|
|
t.Fatal("expected max_nodes to be deleted when max_monitored_systems exists")
|
|
}
|
|
})
|
|
|
|
t.Run("no_legacy_key_no_change", func(t *testing.T) {
|
|
state := &BillingState{
|
|
Limits: map[string]int64{"max_monitored_systems": 20},
|
|
}
|
|
normalized := NormalizeBillingState(state)
|
|
if normalized.Limits["max_monitored_systems"] != 20 {
|
|
t.Fatalf("expected max_monitored_systems=20, got %d", normalized.Limits["max_monitored_systems"])
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestNormalizeBillingState_CanonicalizesCloudPlanVersionAndLimits(t *testing.T) {
|
|
state := &BillingState{
|
|
PlanVersion: " cloud-v1 ",
|
|
Limits: map[string]int64{
|
|
"max_monitored_systems": 999,
|
|
"max_nodes": 5,
|
|
},
|
|
}
|
|
|
|
normalized := NormalizeBillingState(state)
|
|
if normalized.PlanVersion != "cloud_starter" {
|
|
t.Fatalf("plan_version=%q, want %q", normalized.PlanVersion, "cloud_starter")
|
|
}
|
|
if got := normalized.Limits["max_monitored_systems"]; got != 10 {
|
|
t.Fatalf("limits[max_monitored_systems]=%d, want %d", got, 10)
|
|
}
|
|
if _, hasOld := normalized.Limits["max_nodes"]; hasOld {
|
|
t.Fatal("expected max_nodes to be deleted during normalization")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeBillingState_PreservesNonCloudPlanLimits(t *testing.T) {
|
|
state := &BillingState{
|
|
PlanVersion: "pro-v2",
|
|
Limits: map[string]int64{
|
|
"max_monitored_systems": 50,
|
|
},
|
|
}
|
|
|
|
normalized := NormalizeBillingState(state)
|
|
if normalized.PlanVersion != "pro-v2" {
|
|
t.Fatalf("plan_version=%q, want %q", normalized.PlanVersion, "pro-v2")
|
|
}
|
|
if got := normalized.Limits["max_monitored_systems"]; got != 50 {
|
|
t.Fatalf("limits[max_monitored_systems]=%d, want %d", got, 50)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeBillingState_StripsEntitlementsForRevokedSubscriptions(t *testing.T) {
|
|
state := &BillingState{
|
|
Capabilities: []string{"relay"},
|
|
Limits: map[string]int64{"max_monitored_systems": 999},
|
|
MetersEnabled: []string{"api_requests"},
|
|
PlanVersion: "cloud_starter",
|
|
SubscriptionState: SubStateCanceled,
|
|
}
|
|
|
|
normalized := NormalizeBillingState(state)
|
|
if len(normalized.Capabilities) != 0 {
|
|
t.Fatalf("capabilities=%v, want empty", normalized.Capabilities)
|
|
}
|
|
if len(normalized.Limits) != 0 {
|
|
t.Fatalf("limits=%v, want empty", normalized.Limits)
|
|
}
|
|
if len(normalized.MetersEnabled) != 0 {
|
|
t.Fatalf("meters_enabled=%v, want empty", normalized.MetersEnabled)
|
|
}
|
|
if normalized.PlanVersion != "cloud_starter" {
|
|
t.Fatalf("plan_version=%q, want %q", normalized.PlanVersion, "cloud_starter")
|
|
}
|
|
}
|