Files
apskel-pos-backend/internal/processor/game_budget_metrics_processor_test.go
T

62 lines
2.9 KiB
Go
Raw Normal View History

package processor
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/entities"
)
func TestBudgetMetrics(t *testing.T) {
october := &entities.GameBudget{
Scope: constants.GameBudgetScopeGlobal, Amount: 100_000_000,
PeriodStart: time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC), PeriodEnd: time.Date(2026, 10, 31, 0, 0, 0, 0, time.UTC),
}
today := time.Date(2026, 10, 21, 0, 0, 0, 0, time.UTC)
// PRD §30: Rp100M, Rp60M realized, 10 days left, burning Rp5,5M a day → Rp115M.
m := budgetMetrics(october, today, budgetMetricInputs{Realized: 60_000_000, RealizedWindow: 38_500_000, WindowDays: 7, RemainingDays: 10})
assert.EqualValues(t, 40_000_000, m.Remaining)
assert.EqualValues(t, 60, m.UtilizationPercent)
assert.EqualValues(t, 5_500_000, m.DailyBurn)
assert.EqualValues(t, 115_000_000, m.ForecastCost)
assert.EqualValues(t, -15_000_000, m.ForecastRemaining)
assert.EqualValues(t, 115, m.ForecastUtilizationPercent)
assert.Equal(t, constants.GameBudgetCritical, m.Status, "the forecast passes the budget")
assert.EqualValues(t, 70, *m.Thresholds.Warning, "the default when the budget sets none")
assert.EqualValues(t, 90, *m.Thresholds.Critical)
assert.Equal(t, "2026-10-21", m.AsOf)
for name, tc := range map[string]struct {
thresholds string
realized, burn int64
want string
}{
"quiet": {"", 10_000_000, 0, constants.GameBudgetHealthy},
"forecast at warning": {"", 30_000_000, 4_000_000, constants.GameBudgetWarning}, // 30 + 4 × 10 = 70%
"realized at critical": {"", 90_000_000, 0, constants.GameBudgetCritical}, // 90%
"spent": {"", 100_000_000, 0, constants.GameBudgetExhausted}, // 100%
"over": {"", 120_000_000, 0, constants.GameBudgetExhausted}, // 120%
"own thresholds": {`{"warning": 20, "critical": 50}`, 25_000_000, 0, constants.GameBudgetWarning},
"own critical only": {`{"critical": 30}`, 31_000_000, 0, constants.GameBudgetCritical},
"own warning above default": {`{"warning": 80}`, 75_000_000, 0, constants.GameBudgetHealthy},
} {
b := *october
b.Thresholds = entities.JSONDocument(tc.thresholds)
m := budgetMetrics(&b, today, budgetMetricInputs{Realized: tc.realized, RealizedWindow: tc.burn * 7, WindowDays: 7, RemainingDays: 10})
assert.Equal(t, tc.want, m.Status, name)
}
// Not started yet: nothing to average, the forecast is what is realized.
m = budgetMetrics(october, time.Date(2026, 9, 20, 0, 0, 0, 0, time.UTC), budgetMetricInputs{})
assert.Zero(t, m.ForecastCost)
assert.Equal(t, constants.GameBudgetHealthy, m.Status)
// A third of a rupiah per day is not lost: 10 over 3 days, 3 days left → 10 more.
m = budgetMetrics(october, today, budgetMetricInputs{Realized: 10, RealizedWindow: 10, WindowDays: 3, RemainingDays: 3})
assert.EqualValues(t, 20, m.ForecastCost)
assert.EqualValues(t, 3, m.DailyBurn)
}