//go:build test && dev package payments // M18 refund-tier epsilon boundary tests. CalculateRefundForCancellation uses // STRICT comparisons (`> FullRefundThreshold`, `>= PartialRefundThreshold`), // so the exact-boundary tier selection and the just-inside/just-outside // epsilon margins are distinct behaviours. The exact 72h / exact 24h tier // selection is already locked in refunds_test.go // (TestCalculateRefundForCancellation_Exact72hBoundary / // _Exact24hBoundary); these tests pin the epsilon margins AROUND both // boundaries and assert the refundable/kept amounts at the exact boundaries // (which the existing exact-boundary tests only check for the tier string). import ( "testing" "time" ) func TestCalculateRefundForCancellation_Boundary72h_EpsilonMargins(t *testing.T) { t.Parallel() start := time.Date(2099, 12, 31, 10, 0, 0, 0, time.UTC) // 1s past 72h → the > comparison wins: full refund of all pre-payments. over := CalculateRefundForCancellation(100, 50, start.Add(-72*time.Hour-1*time.Second), start) if over.Tier != FullRefundTier { t.Errorf("expected tier %q just past 72h, got %q", FullRefundTier, over.Tier) } if over.RefundableAmount != 50 || over.KeptAmount != 0 { t.Errorf("expected full refund (50 refundable / 0 kept) just past 72h, got %.2f / %.2f", over.RefundableAmount, over.KeptAmount) } // 1s inside 72h → the >= comparison wins: protected deposit kept. under := CalculateRefundForCancellation(100, 50, start.Add(-72*time.Hour+1*time.Second), start) if under.Tier != PartialRefundTier { t.Errorf("expected tier %q 1s inside 72h, got %q", PartialRefundTier, under.Tier) } // Protected deposit = min(50, 100*0.5) = 50 → everything kept. if under.KeptAmount != 50 || under.RefundableAmount != 0 { t.Errorf("expected 50 kept / 0 refundable 1s inside 72h, got %.2f / %.2f", under.KeptAmount, under.RefundableAmount) } } func TestCalculateRefundForCancellation_Boundary24h_EpsilonMargins(t *testing.T) { t.Parallel() start := time.Date(2099, 12, 31, 10, 0, 0, 0, time.UTC) // 1s past 24h → the >= comparison wins: protected deposit kept. over := CalculateRefundForCancellation(100, 80, start.Add(-24*time.Hour-1*time.Second), start) if over.Tier != PartialRefundTier { t.Errorf("expected tier %q 1s past 24h, got %q", PartialRefundTier, over.Tier) } // Protected deposit = min(80, 50) = 50 → 30 refundable / 50 kept. if over.RefundableAmount != 30 || over.KeptAmount != 50 { t.Errorf("expected 30 refundable / 50 kept 1s past 24h, got %.2f / %.2f", over.RefundableAmount, over.KeptAmount) } // 1s inside 24h → the default wins: nothing refundable, everything kept. under := CalculateRefundForCancellation(100, 80, start.Add(-24*time.Hour+1*time.Second), start) if under.Tier != NoRefundTier { t.Errorf("expected tier %q 1s inside 24h, got %q", NoRefundTier, under.Tier) } if under.RefundableAmount != 0 || under.KeptAmount != 80 { t.Errorf("expected 0 refundable / 80 kept 1s inside 24h, got %.2f / %.2f", under.RefundableAmount, under.KeptAmount) } } func TestCalculateRefundForCancellation_ExactBoundaries_Amounts(t *testing.T) { t.Parallel() start := time.Date(2099, 12, 31, 10, 0, 0, 0, time.UTC) // Exactly 72h: NOT > 72h → partial tier. Amounts must match the partial // tier computation (protected deposit kept), not the full tier. at72 := CalculateRefundForCancellation(100, 100, start.Add(-72*time.Hour), start) if at72.Tier != PartialRefundTier { t.Fatalf("expected tier %q at exactly 72h, got %q", PartialRefundTier, at72.Tier) } if at72.ProtectedDeposit != 50 || at72.KeptAmount != 50 || at72.RefundableAmount != 50 { t.Errorf("expected protected 50 / kept 50 / refundable 50 at exactly 72h, got %.2f / %.2f / %.2f", at72.ProtectedDeposit, at72.KeptAmount, at72.RefundableAmount) } // Exactly 24h: >= 24h → partial tier (protected deposit kept). at24 := CalculateRefundForCancellation(100, 60, start.Add(-24*time.Hour), start) if at24.Tier != PartialRefundTier { t.Fatalf("expected tier %q at exactly 24h, got %q", PartialRefundTier, at24.Tier) } // Protected deposit = min(60, 50) = 50 → 10 refundable / 50 kept. if at24.RefundableAmount != 10 || at24.KeptAmount != 50 { t.Errorf("expected 10 refundable / 50 kept at exactly 24h, got %.2f / %.2f", at24.RefundableAmount, at24.KeptAmount) } }