package calc import ( "math" "testing" ) func TestComputeBaseFee_GKG_2025(t *testing.T) { tests := []struct { streitwert float64 isRVG bool want float64 }{ // Minimum fee: first bracket, one increment {100, false, 40}, {300, false, 40}, // First bracket boundary: includes additional step for 300-500 range {500, false, 80}, {501, false, 101}, // enters second bracket: 80 + 21 {1000, false, 101}, // still one step in second bracket {2000, false, 143}, // 80 + 3*21 = 143 // RVG base for 1M EUR {1000000, true, 5553.5}, // GKG base for 1M EUR {1000000, false, 6278}, } for _, tt := range tests { got, err := ComputeBaseFee(tt.streitwert, tt.isRVG, "2025") if err != nil { t.Fatalf("ComputeBaseFee(%v, %v, 2025): %v", tt.streitwert, tt.isRVG, err) } if math.Abs(got-tt.want) > 0.01 { t.Errorf("ComputeBaseFee(%v, isRVG=%v, 2025) = %v, want %v", tt.streitwert, tt.isRVG, got, tt.want) } } } func TestComputeBaseFee_Aktuell_Alias(t *testing.T) { v2025, err := ComputeBaseFee(1000000, false, "2025") if err != nil { t.Fatal(err) } vAktuell, err := ComputeBaseFee(1000000, false, "Aktuell") if err != nil { t.Fatal(err) } if v2025 != vAktuell { t.Errorf("Aktuell alias: got %v, want %v (same as 2025)", vAktuell, v2025) } } func TestComputeBaseFee_UnknownVersion(t *testing.T) { _, err := ComputeBaseFee(1000, false, "1999") if err == nil { t.Error("expected error for unknown version, got nil") } } func TestComputeDEInstance_LG(t *testing.T) { input := InstanceInput{ Enabled: true, FeeVersion: "2025", NumAttorneys: 1, NumPatentAttorneys: 1, NumClients: 1, OralHearing: true, } meta := DEInfringementInstances[0] // LG result, err := ComputeDEInstance(1000000, input, meta, 0.19) if err != nil { t.Fatal(err) } if !result.Enabled { t.Fatal("expected enabled=true") } if result.CourtFeeBasis != "GKG" { t.Errorf("expected GKG, got %s", result.CourtFeeBasis) } // Court fee: 3.0 × 6278 = 18834 if math.Abs(result.CourtFee-18834) > 0.01 { t.Errorf("CourtFee = %v, want 18834", result.CourtFee) } if result.Attorney == nil { t.Fatal("expected attorney breakdown") } if result.PatentAttorney == nil { t.Fatal("expected patent attorney breakdown") } if result.InstanceTotal <= 0 { t.Error("expected positive instance total") } } func TestComputeDEInstance_Disabled(t *testing.T) { input := InstanceInput{Enabled: false} meta := DEInfringementInstances[0] result, err := ComputeDEInstance(1000000, input, meta, 0.19) if err != nil { t.Fatal(err) } if result.Enabled { t.Error("expected enabled=false") } if result.InstanceTotal != 0 { t.Errorf("expected 0 total for disabled, got %v", result.InstanceTotal) } } func TestComputeUPCInstance_Pre2026(t *testing.T) { input := InstanceInput{ Enabled: true, FeeVersion: "pre2026", IsSME: false, } result, err := ComputeUPCInstance(1000000, input, "UPC_FIRST") if err != nil { t.Fatal(err) } // Fixed: 11000, value-based for 1M: 4000 if result.FixedFee != 11000 { t.Errorf("FixedFee = %v, want 11000", result.FixedFee) } if result.ValueBasedFee != 4000 { t.Errorf("ValueBasedFee = %v, want 4000", result.ValueBasedFee) } if result.CourtFeesTotal != 15000 { t.Errorf("CourtFeesTotal = %v, want 15000", result.CourtFeesTotal) } // Recoverable for 1M: 112000 if result.RecoverableCeiling != 112000 { t.Errorf("RecoverableCeiling = %v, want 112000", result.RecoverableCeiling) } } func TestComputeUPCInstance_SME(t *testing.T) { input := InstanceInput{ Enabled: true, FeeVersion: "pre2026", IsSME: true, } result, err := ComputeUPCInstance(1000000, input, "UPC_FIRST") if err != nil { t.Fatal(err) } // SME: 15000 × (1 - 0.4) = 9000 if result.CourtFeesSME != 9000 { t.Errorf("CourtFeesSME = %v, want 9000", result.CourtFeesSME) } // InstanceTotal is the user's own outlay — court fee only, never the // opposing side's R.152 recoverable cap (which stays on RecoverableCeiling). expectedTotal := 9000.0 if math.Abs(result.InstanceTotal-expectedTotal) > 0.01 { t.Errorf("InstanceTotal = %v, want %v", result.InstanceTotal, expectedTotal) } if result.RecoverableCeiling != 112000 { t.Errorf("RecoverableCeiling = %v, want 112000 (separate line item)", result.RecoverableCeiling) } } func TestComputeEPAInstance(t *testing.T) { input := InstanceInput{Enabled: true} result, err := ComputeEPAInstance(input, "EPA_OPPOSITION") if err != nil { t.Fatal(err) } if result.Fee != 880 { t.Errorf("EPA Opposition Fee = %v, want 880", result.Fee) } } func TestComputeEPAInstance_SME(t *testing.T) { input := InstanceInput{Enabled: true, IsSME: true} result, err := ComputeEPAInstance(input, "EPA_APPEAL") if err != nil { t.Fatal(err) } if result.Fee != 1880 { t.Errorf("EPA Appeal SME Fee = %v, want 1880", result.Fee) } } func TestCalculate_FullRequest(t *testing.T) { req := CostRequest{ Streitwert: 1000000, VATRate: 0.19, Instances: map[string]InstanceInput{ "LG": { Enabled: true, FeeVersion: "Aktuell", NumAttorneys: 1, NumPatentAttorneys: 1, NumClients: 1, OralHearing: true, }, "UPC_FIRST": { Enabled: true, FeeVersion: "2026", }, "EPA_OPPOSITION": { Enabled: true, }, }, } resp, err := Calculate(req) if err != nil { t.Fatal(err) } // Should have 6 DE results, 2 UPC results, 2 EPA results if len(resp.DEResults) != 6 { t.Errorf("expected 6 DE results, got %d", len(resp.DEResults)) } if len(resp.UPCResults) != 2 { t.Errorf("expected 2 UPC results, got %d", len(resp.UPCResults)) } if len(resp.EPAResults) != 2 { t.Errorf("expected 2 EPA results, got %d", len(resp.EPAResults)) } // LG should be enabled if !resp.DEResults[0].Enabled { t.Error("LG should be enabled") } // OLG should be disabled if resp.DEResults[1].Enabled { t.Error("OLG should be disabled") } // Grand total should be positive if resp.Totals.GrandTotal <= 0 { t.Error("expected positive grand total") } // EPA fees should include opposition if resp.Totals.EPAFees != 880 { t.Errorf("EPA fees = %v, want 880", resp.Totals.EPAFees) } } func TestAttorneyFees_MultipleClients(t *testing.T) { input := InstanceInput{ Enabled: true, FeeVersion: "2025", NumAttorneys: 1, NumClients: 3, OralHearing: true, } meta := DEInfringementInstances[0] // LG result, err := ComputeDEInstance(1000000, input, meta, 0.19) if err != nil { t.Fatal(err) } // Erhöhung: min((3-1)*0.3, 2.0) × baseFee = 0.6 × 5553.5 = 3332.1 if result.Attorney == nil { t.Fatal("expected attorney breakdown") } if math.Abs(result.Attorney.Erhoehungsgebuehr-3332.1) > 0.01 { t.Errorf("Erhoehungsgebuehr = %v, want 3332.1", result.Attorney.Erhoehungsgebuehr) } }