mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
- Initialize git repository - Add comprehensive .gitignore for Go projects - Install golangci-lint v2.6.0 (latest v2) globally - Configure .golangci.yml with appropriate linters and formatters - Fix all formatting issues (gofmt) - Fix all errcheck issues (unchecked errors) - Adjust complexity threshold for validation functions - All checks passing: build, test, vet, lint
307 lines
9.4 KiB
Go
307 lines
9.4 KiB
Go
package revenue
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/damirmukimov/city_resource_graph/models/customer"
|
|
"github.com/damirmukimov/city_resource_graph/models/params"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestCalculateRevenue(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
year int
|
|
custMetrics customer.CustomerMetrics
|
|
tierDist customer.TierDistribution
|
|
expectedTotal float64
|
|
expectedSub float64
|
|
expectedTrans float64
|
|
expectedMun float64
|
|
expectedImpl float64
|
|
}{
|
|
{
|
|
name: "Year 1 revenue calculation",
|
|
year: 1,
|
|
custMetrics: customer.CustomerMetrics{
|
|
TotalOrgs: 500,
|
|
PayingOrgs: 150,
|
|
},
|
|
tierDist: customer.TierDistribution{
|
|
Basic: 90,
|
|
Business: 45,
|
|
Enterprise: 15,
|
|
Total: 150,
|
|
},
|
|
expectedSub: 216360.0, // Using actual tier mix from YAML: 0.60/0.30/0.10
|
|
expectedTrans: 91500.0, // 200*550*0.35 + 300000*0.15 + 200000*0.04
|
|
expectedMun: 60000.0, // 1*60000 + 0
|
|
expectedImpl: 312500.0, // 500*0.5*0.25*5000
|
|
expectedTotal: 680360.0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
p := createTestParams(tt.year)
|
|
|
|
result := CalculateRevenue(tt.year, tt.custMetrics, tt.tierDist, p)
|
|
|
|
assert.InDelta(t, tt.expectedSub, result.Subscription.TotalARR, 0.01, "Subscription revenue should match")
|
|
assert.InDelta(t, tt.expectedTrans, result.Transaction.Total, 0.01, "Transaction revenue should match")
|
|
assert.InDelta(t, tt.expectedMun, result.Municipal.Total, 0.01, "Municipal revenue should match")
|
|
assert.InDelta(t, tt.expectedImpl, result.Implementation.Total, 0.01, "Implementation revenue should match")
|
|
assert.InDelta(t, tt.expectedTotal, result.Total, 0.01, "Total revenue should match")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculateSubscriptionRevenue(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
year int
|
|
tierDist customer.TierDistribution
|
|
expectedBasic float64
|
|
expectedBusiness float64
|
|
expectedEnterprise float64
|
|
expectedTotal float64
|
|
}{
|
|
{
|
|
name: "Year 1 subscription revenue",
|
|
year: 1,
|
|
tierDist: customer.TierDistribution{
|
|
Basic: 90,
|
|
Business: 45,
|
|
Enterprise: 15,
|
|
},
|
|
expectedBasic: 45360.0, // 90 * 35 * 12 * 1.20
|
|
expectedBusiness: 81000.0, // 45 * 120 * 12 * 1.25
|
|
expectedEnterprise: 90000.0, // 15 * 400 * 12 * 1.25
|
|
expectedTotal: 216360.0,
|
|
},
|
|
{
|
|
name: "Year 3 subscription revenue",
|
|
year: 3,
|
|
tierDist: customer.TierDistribution{
|
|
Basic: 810,
|
|
Business: 570,
|
|
Enterprise: 120,
|
|
},
|
|
expectedBasic: 408240.0, // 810 * 35 * 12 * 1.20 = 408240
|
|
expectedBusiness: 1026000.0, // 570 * 120 * 12 * 1.25 = 1026000
|
|
expectedEnterprise: 720000.0, // 120 * 400 * 12 * 1.25 = 720000
|
|
expectedTotal: 2154240.0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
p := createTestParams(tt.year)
|
|
|
|
result := calculateSubscriptionRevenue(tt.year, tt.tierDist, p)
|
|
|
|
assert.InDelta(t, tt.expectedBasic, result.BasicARR, 0.01, "Basic ARR should match")
|
|
assert.InDelta(t, tt.expectedBusiness, result.BusinessARR, 0.01, "Business ARR should match")
|
|
assert.InDelta(t, tt.expectedEnterprise, result.EnterpriseARR, 0.01, "Enterprise ARR should match")
|
|
assert.InDelta(t, tt.expectedTotal, result.TotalARR, 0.01, "Total ARR should match")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculateTransactionRevenue(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
year int
|
|
expectedIntro float64
|
|
expectedService float64
|
|
expectedGroup float64
|
|
expectedTotal float64
|
|
}{
|
|
{
|
|
name: "Year 1 transaction revenue",
|
|
year: 1,
|
|
expectedIntro: 38500.0, // 200 * 550 * 0.35
|
|
expectedService: 45000.0, // 300000 * 0.15
|
|
expectedGroup: 8000.0, // 200000 * 0.04
|
|
expectedTotal: 91500.0,
|
|
},
|
|
{
|
|
name: "Year 2 transaction revenue",
|
|
year: 2,
|
|
expectedIntro: 83600.0, // 400 * 550 * 0.38
|
|
expectedService: 120000.0, // 800000 * 0.15
|
|
expectedGroup: 16000.0, // 400000 * 0.04
|
|
expectedTotal: 219600.0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
p := createTestParams(tt.year)
|
|
|
|
result := calculateTransactionRevenue(tt.year, p)
|
|
|
|
assert.InDelta(t, tt.expectedIntro, result.IntroRevenue, 0.01, "Intro revenue should match")
|
|
assert.InDelta(t, tt.expectedService, result.ServiceRevenue, 0.01, "Service revenue should match")
|
|
assert.InDelta(t, tt.expectedGroup, result.GroupRevenue, 0.01, "Group revenue should match")
|
|
assert.InDelta(t, tt.expectedTotal, result.Total, 0.01, "Total transaction revenue should match")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculateMunicipalRevenue(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
year int
|
|
expectedLicense float64
|
|
expectedData float64
|
|
expectedTotal float64
|
|
}{
|
|
{
|
|
name: "Year 1 municipal revenue",
|
|
year: 1,
|
|
expectedLicense: 60000.0, // 1 * 60000
|
|
expectedData: 0.0, // 0
|
|
expectedTotal: 60000.0,
|
|
},
|
|
{
|
|
name: "Year 2 municipal revenue",
|
|
year: 2,
|
|
expectedLicense: 180000.0, // 2 * 90000
|
|
expectedData: 50000.0, // 50000
|
|
expectedTotal: 230000.0,
|
|
},
|
|
{
|
|
name: "Year 3 municipal revenue",
|
|
year: 3,
|
|
expectedLicense: 360000.0, // 4 * 90000 (YAML has 90000 for year 3)
|
|
expectedData: 100000.0, // 100000 (YAML has 100000 for year 3)
|
|
expectedTotal: 460000.0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
p := createTestParams(tt.year)
|
|
|
|
result := calculateMunicipalRevenue(tt.year, p)
|
|
|
|
assert.InDelta(t, tt.expectedLicense, result.LicenseRevenue, 0.01, "License revenue should match")
|
|
assert.InDelta(t, tt.expectedData, result.DataLicensing, 0.01, "Data licensing revenue should match")
|
|
assert.InDelta(t, tt.expectedTotal, result.Total, 0.01, "Total municipal revenue should match")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculateImplementationRevenue(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
custMetrics customer.CustomerMetrics
|
|
expectedMatches float64
|
|
expectedPaidImpl float64
|
|
expectedRevenue float64
|
|
}{
|
|
{
|
|
name: "Implementation revenue calculation",
|
|
custMetrics: customer.CustomerMetrics{
|
|
TotalOrgs: 500,
|
|
},
|
|
expectedMatches: 250.0, // 500 * 0.5
|
|
expectedPaidImpl: 62.5, // 250 * 0.25
|
|
expectedRevenue: 312500.0, // 62.5 * 5000
|
|
},
|
|
{
|
|
name: "Different org count",
|
|
custMetrics: customer.CustomerMetrics{
|
|
TotalOrgs: 1000,
|
|
},
|
|
expectedMatches: 500.0, // 1000 * 0.5
|
|
expectedPaidImpl: 125.0, // 500 * 0.25
|
|
expectedRevenue: 625000.0, // 125 * 5000
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
p := createTestParams(1)
|
|
|
|
result := calculateImplementationRevenue(tt.custMetrics, p)
|
|
|
|
assert.InDelta(t, tt.expectedMatches, result.Matches, 0.01, "Matches should match")
|
|
assert.InDelta(t, tt.expectedPaidImpl, result.PaidImpls, 0.01, "Paid implementations should match")
|
|
assert.InDelta(t, tt.expectedRevenue, result.Total, 0.01, "Implementation revenue should match")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetPlatformOnlyRevenue(t *testing.T) {
|
|
rev := RevenueBreakdown{
|
|
Subscription: SubscriptionRevenue{TotalARR: 100000.0},
|
|
Transaction: TransactionRevenue{Total: 50000.0},
|
|
Municipal: MunicipalRevenue{Total: 25000.0},
|
|
Implementation: ImplementationRevenue{Total: 15000.0},
|
|
Total: 190000.0,
|
|
}
|
|
|
|
result := GetPlatformOnlyRevenue(rev)
|
|
expected := 125000.0 // Subscription + Municipal
|
|
|
|
assert.InDelta(t, expected, result, 0.01, "Platform-only revenue should be subscription + municipal")
|
|
}
|
|
|
|
func TestGetFullRevenue(t *testing.T) {
|
|
rev := RevenueBreakdown{
|
|
Subscription: SubscriptionRevenue{TotalARR: 100000.0},
|
|
Transaction: TransactionRevenue{Total: 50000.0},
|
|
Municipal: MunicipalRevenue{Total: 25000.0},
|
|
Implementation: ImplementationRevenue{Total: 15000.0},
|
|
Total: 190000.0,
|
|
}
|
|
|
|
result := GetFullRevenue(rev)
|
|
assert.InDelta(t, 190000.0, result, 0.01, "Full revenue should equal total revenue")
|
|
}
|
|
|
|
func createTestParams(year int) *params.Params {
|
|
yearKey := params.YearKey(year)
|
|
|
|
return ¶ms.Params{
|
|
Pricing: params.PricingParams{
|
|
Basic: 35.0,
|
|
Business: 120.0,
|
|
Enterprise: 400.0,
|
|
BlendedUplift: params.BlendedUplift{
|
|
Basic: 0.20,
|
|
Business: 0.25,
|
|
Enterprise: 0.25,
|
|
},
|
|
TierMix: params.YearlyTierMix{
|
|
yearKey: params.TierMix{
|
|
Basic: 0.60,
|
|
Business: 0.30,
|
|
Enterprise: 0.10,
|
|
},
|
|
},
|
|
},
|
|
Transactions: params.TransactionParams{
|
|
AvgIntroFee: 550.0,
|
|
IntrosPerYear: params.YearlyInt{yearKey: 200 + (year-1)*200}, // 200, 400, 600
|
|
IntroConversion: params.YearlyFloat{yearKey: 0.35 + float64(year-1)*0.03}, // 0.35, 0.38, 0.40
|
|
ServiceGMV: params.YearlyFloat{yearKey: float64(300000 + (year-1)*500000)}, // 300k, 800k, 1.5M
|
|
ServiceCommission: 0.15,
|
|
GroupGMV: params.YearlyFloat{yearKey: float64(200000 + (year-1)*200000)}, // 200k, 400k, 800k
|
|
GroupCommission: 0.04,
|
|
},
|
|
Municipal: params.MunicipalParams{
|
|
Cities: params.YearlyInt{yearKey: year}, // 1, 2, 3, 4
|
|
AvgLicense: params.YearlyFloat{yearKey: 60000.0 + float64(year-1)*30000.0}, // 60k, 90k, 110k
|
|
DataLicensing: params.YearlyFloat{yearKey: float64((year - 1) * 50000)}, // 0, 50k, 150k
|
|
},
|
|
ImplServices: params.ImplementationParams{
|
|
MatchesPerOrg: 0.5,
|
|
PaidShare: 0.25,
|
|
AvgFee: 5000.0,
|
|
},
|
|
}
|
|
}
|