package service import ( "math" "testing" approvalService "gitlab.com/mbugroup/lti-api.git/internal/common/service" ) // production-scope total should sum only parts tagged production_cost (a part // tagged with both scopes still counts once). func TestHppPerFarmProductionScopeTotalPartLevelScopes(t *testing.T) { comp := &approvalService.HppV2Component{ Code: "PAKAN", Parts: []approvalService.HppV2ComponentPart{ {Total: 100, Scopes: []string{"production_cost"}}, {Total: 50, Scopes: []string{"pullet_cost"}}, {Total: 25, Scopes: []string{"production_cost", "pullet_cost"}}, }, } if got := hppPerFarmProductionScopeTotal(comp); got != 125 { t.Fatalf("expected 125, got %v", got) } } // when parts carry no scopes, fall back to the component-level scope. func TestHppPerFarmProductionScopeTotalComponentLevelFallback(t *testing.T) { prod := &approvalService.HppV2Component{ Code: "DIRECT_PULLET_PURCHASE", Scopes: []string{"production_cost"}, Total: 300, Parts: []approvalService.HppV2ComponentPart{{Total: 300}}, } if got := hppPerFarmProductionScopeTotal(prod); got != 300 { t.Fatalf("expected 300 component fallback, got %v", got) } // DOC/pullet is pullet-scope only -> contributes 0 to production cost, // which is exactly why it must not be added to total_cost (depreciation // already expenses the pullet). pulletOnly := &approvalService.HppV2Component{ Code: "DOC_CHICKIN", Scopes: []string{"pullet_cost"}, Total: 999, Parts: []approvalService.HppV2ComponentPart{{Total: 999}}, } if got := hppPerFarmProductionScopeTotal(pulletOnly); got != 0 { t.Fatalf("expected 0 for pullet-only component, got %v", got) } } func TestHppPerFarmProductionScopeTotalsByCode(t *testing.T) { b := &approvalService.HppV2Breakdown{ Components: []approvalService.HppV2Component{ {Code: "PAKAN", Parts: []approvalService.HppV2ComponentPart{{Total: 100, Scopes: []string{"production_cost"}}}}, {Code: "OVK", Parts: []approvalService.HppV2ComponentPart{{Total: 40, Scopes: []string{"production_cost"}}}}, {Code: "DOC_CHICKIN", Scopes: []string{"pullet_cost"}, Total: 500, Parts: []approvalService.HppV2ComponentPart{{Total: 500}}}, {Code: "DEPRECIATION", Scopes: []string{"production_cost"}, Total: 30, Parts: []approvalService.HppV2ComponentPart{{Total: 30, Scopes: []string{"production_cost"}}}}, }, } got := hppPerFarmProductionScopeTotalsByCode(b) if got["PAKAN"] != 100 { t.Fatalf("expected PAKAN 100, got %v", got["PAKAN"]) } if got["OVK"] != 40 { t.Fatalf("expected OVK 40, got %v", got["OVK"]) } if got["DOC_CHICKIN"] != 0 { t.Fatalf("expected DOC_CHICKIN production scope 0, got %v", got["DOC_CHICKIN"]) } if got["DEPRECIATION"] != 30 { t.Fatalf("expected DEPRECIATION 30, got %v", got["DEPRECIATION"]) } } func TestHppPerFarmSafeDiv(t *testing.T) { cases := []struct { num, den, want float64 }{ {100, 4, 25}, {100, 0, 0}, {100, -5, 0}, {0, 0, 0}, } for _, c := range cases { if got := hppPerFarmSafeDiv(c.num, c.den); got != c.want { t.Fatalf("safeDiv(%v,%v)=%v want %v", c.num, c.den, got, c.want) } } if got := hppPerFarmSafeDiv(math.Inf(1), 1); got != 0 { t.Fatalf("expected 0 for inf numerator, got %v", got) } }