package models import ( "testing" ) func TestCarryoverMonthsCap(t *testing.T) { // Verify the constant is set correctly if CarryoverMonthsCap != 5 { t.Errorf("CarryoverMonthsCap should be 5, got %d", CarryoverMonthsCap) } } func TestCalculateMaxTaskBalance(t *testing.T) { tests := []struct { name string monthlyAllowance int expected int }{ {"Basic plan", 30, 150}, {"Standard plan", 100, 500}, {"Premium plan", 1000, 5000}, {"Zero allowance", 0, 0}, {"Single task", 1, 5}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := CalculateMaxTaskBalance(tt.monthlyAllowance) if result != tt.expected { t.Errorf("CalculateMaxTaskBalance(%d) = %d, expected %d", tt.monthlyAllowance, result, tt.expected) } }) } } func TestGetDefaultPlans(t *testing.T) { plans := GetDefaultPlans() if len(plans) != 3 { t.Fatalf("Expected 3 plans, got %d", len(plans)) } // Test Basic plan basic := plans[0] if basic.ID != PlanBasic { t.Errorf("First plan should be Basic, got %s", basic.ID) } if basic.PriceCents != 990 { t.Errorf("Basic price should be 990 cents, got %d", basic.PriceCents) } if basic.Features.MonthlyTaskAllowance != 30 { t.Errorf("Basic monthly allowance should be 30, got %d", basic.Features.MonthlyTaskAllowance) } if basic.Features.MaxTaskBalance != 150 { t.Errorf("Basic max balance should be 150, got %d", basic.Features.MaxTaskBalance) } if basic.Features.FairUseMode { t.Error("Basic should not have FairUseMode") } // Test Standard plan standard := plans[1] if standard.ID != PlanStandard { t.Errorf("Second plan should be Standard, got %s", standard.ID) } if standard.PriceCents != 1990 { t.Errorf("Standard price should be 1990 cents, got %d", standard.PriceCents) } if standard.Features.MonthlyTaskAllowance != 100 { t.Errorf("Standard monthly allowance should be 100, got %d", standard.Features.MonthlyTaskAllowance) } if !standard.Features.BatchProcessing { t.Error("Standard should have BatchProcessing") } if !standard.Features.CustomTemplates { t.Error("Standard should have CustomTemplates") } // Test Premium plan premium := plans[2] if premium.ID != PlanPremium { t.Errorf("Third plan should be Premium, got %s", premium.ID) } if premium.PriceCents != 3990 { t.Errorf("Premium price should be 3990 cents, got %d", premium.PriceCents) } if !premium.Features.FairUseMode { t.Error("Premium should have FairUseMode") } if !premium.Features.PrioritySupport { t.Error("Premium should have PrioritySupport") } if !premium.Features.CustomBranding { t.Error("Premium should have CustomBranding") } } func TestPlanIDConstants(t *testing.T) { if PlanBasic != "basic" { t.Errorf("PlanBasic should be 'basic', got '%s'", PlanBasic) } if PlanStandard != "standard" { t.Errorf("PlanStandard should be 'standard', got '%s'", PlanStandard) } if PlanPremium != "premium" { t.Errorf("PlanPremium should be 'premium', got '%s'", PlanPremium) } } func TestSubscriptionStatusConstants(t *testing.T) { statuses := []struct { status SubscriptionStatus expected string }{ {StatusTrialing, "trialing"}, {StatusActive, "active"}, {StatusPastDue, "past_due"}, {StatusCanceled, "canceled"}, {StatusExpired, "expired"}, } for _, tt := range statuses { if string(tt.status) != tt.expected { t.Errorf("Status %s should be '%s'", tt.status, tt.expected) } } } func TestTaskTypeConstants(t *testing.T) { types := []struct { taskType TaskType expected string }{ {TaskTypeCorrection, "correction"}, {TaskTypeLetter, "letter"}, {TaskTypeMeeting, "meeting"}, {TaskTypeBatch, "batch"}, {TaskTypeOther, "other"}, } for _, tt := range types { if string(tt.taskType) != tt.expected { t.Errorf("TaskType %s should be '%s'", tt.taskType, tt.expected) } } } func TestPlanFeatures_CarryoverCalculation(t *testing.T) { plans := GetDefaultPlans() for _, plan := range plans { expectedMax := plan.Features.MonthlyTaskAllowance * CarryoverMonthsCap if plan.Features.MaxTaskBalance != expectedMax { t.Errorf("Plan %s: MaxTaskBalance should be %d (allowance * 5), got %d", plan.ID, expectedMax, plan.Features.MaxTaskBalance) } } } func TestBillingPlan_AllPlansActive(t *testing.T) { plans := GetDefaultPlans() for _, plan := range plans { if !plan.IsActive { t.Errorf("Plan %s should be active", plan.ID) } } } func TestBillingPlan_CurrencyIsEuro(t *testing.T) { plans := GetDefaultPlans() for _, plan := range plans { if plan.Currency != "eur" { t.Errorf("Plan %s currency should be 'eur', got '%s'", plan.ID, plan.Currency) } } } func TestBillingPlan_IntervalIsMonth(t *testing.T) { plans := GetDefaultPlans() for _, plan := range plans { if plan.Interval != "month" { t.Errorf("Plan %s interval should be 'month', got '%s'", plan.ID, plan.Interval) } } } func TestBillingPlan_SortOrder(t *testing.T) { plans := GetDefaultPlans() for i, plan := range plans { expectedOrder := i + 1 if plan.SortOrder != expectedOrder { t.Errorf("Plan %s sort order should be %d, got %d", plan.ID, expectedOrder, plan.SortOrder) } } } func TestTaskUsageInfo_FormatStrings(t *testing.T) { usage := TaskUsageInfo{ TasksAvailable: 45, MaxTasks: 150, InfoText: "Aufgaben verfuegbar: 45 von max. 150", TooltipText: "Aufgaben koennen sich bis zu 5 Monate ansammeln.", } if usage.TasksAvailable != 45 { t.Errorf("TasksAvailable should be 45, got %d", usage.TasksAvailable) } if usage.MaxTasks != 150 { t.Errorf("MaxTasks should be 150, got %d", usage.MaxTasks) } } func TestCheckTaskAllowedResponse_Allowed(t *testing.T) { response := CheckTaskAllowedResponse{ Allowed: true, TasksAvailable: 50, MaxTasks: 150, PlanID: PlanBasic, } if !response.Allowed { t.Error("Response should be allowed") } if response.Message != "" { t.Errorf("Message should be empty for allowed response, got '%s'", response.Message) } } func TestCheckTaskAllowedResponse_NotAllowed(t *testing.T) { response := CheckTaskAllowedResponse{ Allowed: false, TasksAvailable: 0, MaxTasks: 150, PlanID: PlanBasic, Message: "Dein Aufgaben-Kontingent ist aufgebraucht.", } if response.Allowed { t.Error("Response should not be allowed") } if response.TasksAvailable != 0 { t.Errorf("TasksAvailable should be 0, got %d", response.TasksAvailable) } } func TestTaskLimitError(t *testing.T) { err := TaskLimitError{ Error: "TASK_LIMIT_REACHED", CurrentBalance: 0, Plan: PlanBasic, } if err.Error != "TASK_LIMIT_REACHED" { t.Errorf("Error should be 'TASK_LIMIT_REACHED', got '%s'", err.Error) } if err.CurrentBalance != 0 { t.Errorf("CurrentBalance should be 0, got %d", err.CurrentBalance) } if err.Plan != PlanBasic { t.Errorf("Plan should be basic, got '%s'", err.Plan) } } func TestConsumeTaskRequest(t *testing.T) { req := ConsumeTaskRequest{ UserID: "550e8400-e29b-41d4-a716-446655440000", TaskType: TaskTypeCorrection, } if req.UserID == "" { t.Error("UserID should not be empty") } if req.TaskType != TaskTypeCorrection { t.Errorf("TaskType should be correction, got '%s'", req.TaskType) } } func TestConsumeTaskResponse_Success(t *testing.T) { resp := ConsumeTaskResponse{ Success: true, TaskID: "task-123", TasksRemaining: 49, } if !resp.Success { t.Error("Response should be successful") } if resp.TasksRemaining != 49 { t.Errorf("TasksRemaining should be 49, got %d", resp.TasksRemaining) } } func TestEntitlementInfo_Premium(t *testing.T) { premium := GetDefaultPlans()[2] info := EntitlementInfo{ Features: premium.Features.FeatureFlags, MaxTeamMembers: premium.Features.MaxTeamMembers, PrioritySupport: premium.Features.PrioritySupport, CustomBranding: premium.Features.CustomBranding, BatchProcessing: premium.Features.BatchProcessing, CustomTemplates: premium.Features.CustomTemplates, FairUseMode: premium.Features.FairUseMode, } if !info.FairUseMode { t.Error("Premium should have FairUseMode") } if info.MaxTeamMembers != 10 { t.Errorf("Premium MaxTeamMembers should be 10, got %d", info.MaxTeamMembers) } }