Initial commit: breakpilot-core - Shared Infrastructure
Docker Compose with 24+ services: - PostgreSQL (PostGIS), Valkey, MinIO, Qdrant - Vault (PKI/TLS), Nginx (Reverse Proxy) - Backend Core API, Consent Service, Billing Service - RAG Service, Embedding Service - Gitea, Woodpecker CI/CD - Night Scheduler, Health Aggregator - Jitsi (Web/XMPP/JVB/Jicofo), Mailpit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
319
billing-service/internal/models/models_test.go
Normal file
319
billing-service/internal/models/models_test.go
Normal file
@@ -0,0 +1,319 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user