fix(growth): harden deterministic report evidence

This commit is contained in:
2026-08-21 00:32:21 +03:30
parent 1d8d4edf92
commit 6c80f71847
11 changed files with 369 additions and 103 deletions

View File

@@ -74,13 +74,25 @@ func TestDetectRisksRequiresTwentyPercentDropAndBaselineSize(t *testing.T) {
}
}
func TestDetectRisksCoalescesAdjacentDaysAndRanksDeepestFirst(t *testing.T) {
forecast := Forecast{Confidence: ConfidenceHigh, Points: []ForecastPoint{
{Date: date("2026-07-01"), BaselineSessions: 200, BaselineAmountRial: 1_000, PaidSessions: Range{P50: 150}, PaidAmountRial: Range{P50: 750}},
{Date: date("2026-07-02"), BaselineSessions: 200, BaselineAmountRial: 1_000, PaidSessions: Range{P50: 100}, PaidAmountRial: Range{P50: 500}},
{Date: date("2026-07-04"), BaselineSessions: 200, BaselineAmountRial: 1_000, PaidSessions: Range{P50: 120}, PaidAmountRial: Range{P50: 600}},
}}
risks := DetectRisks(forecast, 3)
if len(risks) != 2 || risks[0].Date != date("2026-07-01") || risks[0].EndDate != date("2026-07-02") || risks[0].SessionDeclineBPS != 5_000 {
t.Fatalf("unexpected ranked/coalesced risks: %+v", risks)
}
}
func TestEvaluatePeerAppliesPrivacyThresholdAndEqualMerchantWeight(t *testing.T) {
peers := make([]PeerMerchantValue, 10)
for i := range peers {
peers[i] = PeerMerchantValue{PaidSessions: int64(i + 1), SessionCount: 100}
}
result := EvaluatePeer(PeerInput{MerchantPaidSessions: 8, Peers: peers})
if result.State != PeerReady || result.MedianPaidSessions != 5 || result.PercentileBPS != 8_000 {
if result.State != PeerReady || result.MedianPaidSessions != 5 || result.PercentileBPS != 7_500 {
t.Fatalf("unexpected peer result: %+v", result)
}
suppressed := EvaluatePeer(PeerInput{TerminalScoped: true, Peers: peers})
@@ -90,7 +102,7 @@ func TestEvaluatePeerAppliesPrivacyThresholdAndEqualMerchantWeight(t *testing.T)
}
func TestFingerprintIsStableAndVersionSensitive(t *testing.T) {
input := FingerprintInput{MerchantID: 7, DatasetWatermark: "run-1", ForecastStart: date("2026-07-01"), HorizonDays: 90, CatalogVersion: "1", AnalyticsVersion: "1", PromptVersion: "1", SchemaVersion: "1", Model: "mini"}
input := FingerprintInput{MerchantID: 7, DatasetWatermark: "run-1", ForecastStart: date("2026-07-01"), HorizonDays: 90, CatalogVersion: "1", AnalyticsVersion: "1", PromptVersion: "1", SchemaVersion: "1", Model: "mini", ModelConfigVersion: "cfg-1"}
a := Fingerprint(input)
b := Fingerprint(input)
input.Model = "other"
@@ -99,6 +111,26 @@ func TestFingerprintIsStableAndVersionSensitive(t *testing.T) {
}
}
func TestPeerPercentileUsesMidRankForTies(t *testing.T) {
peers := make([]PeerMerchantValue, 10)
for index := range peers {
peers[index] = PeerMerchantValue{PaidSessions: 50, SessionCount: 100}
}
result := EvaluatePeer(PeerInput{MerchantPaidSessions: 50, Peers: peers})
if result.PercentileBPS != 5_000 {
t.Fatalf("all-tie percentile = %d, want 5000", result.PercentileBPS)
}
}
func TestFingerprintIncludesModelConfigurationPolicy(t *testing.T) {
input := FingerprintInput{MerchantID: 7, ForecastStart: date("2026-07-01"), HorizonDays: 90, Model: "mini", ModelConfigVersion: "cfg-1"}
first := Fingerprint(input)
input.ModelConfigVersion = "cfg-2"
if first == Fingerprint(input) {
t.Fatal("model configuration policy must invalidate fingerprint")
}
}
func TestTehranRefreshBoundary(t *testing.T) {
last := time.Date(2026, 8, 20, 20, 31, 0, 0, time.UTC) // 00:01 Tehran on Aug 21.
next := NextRefreshAt(last)