test: payment coverage — gift card daily caps, buildSplitRecords, ValidateAmount edge cases, SquareRefundStatusToLocal
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
//go:build test && dev
|
||||
|
||||
package payments
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestValidateAmount_ExtraEdgeCases extends the existing ValidateAmount coverage
|
||||
// with additional boundary and edge cases for the money-validation function.
|
||||
func TestValidateAmount_ExtraEdgeCases(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
amount int64
|
||||
wantErr bool
|
||||
desc string
|
||||
}{
|
||||
// Zero and negative — must be rejected
|
||||
{0, true, "zero is not allowed"},
|
||||
{-1, true, "negative one penny"},
|
||||
{-100, true, "negative £1"},
|
||||
{-1000000, true, "negative £10,000 (must be rejected early, not overflow)"},
|
||||
|
||||
// Normal positive amounts — must pass
|
||||
{1, false, "minimum valid: 1 penny"},
|
||||
{50, false, "50p"},
|
||||
{100, false, "£1"},
|
||||
{5000, false, "£50"},
|
||||
{50000, false, "£500"},
|
||||
{100000, false, "£1,000"},
|
||||
{500000, false, "£5,000"},
|
||||
{999999, false, "£9,999.99 — just under max"},
|
||||
{1000000, false, "£10,000 exactly — boundary allowed"},
|
||||
|
||||
// Above maximum — must be rejected
|
||||
{1000001, true, "£10,000.01 — one penny over max"},
|
||||
{1000050, true, "£10,000.50 — 50p over max"},
|
||||
{2000000, true, "£20,000 — double max"},
|
||||
{99999999, true, "very large amount"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("%s (%d)", tt.desc, tt.amount), func(t *testing.T) {
|
||||
err := ValidateAmount(tt.amount)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("expected error for amount %d (%s), got nil", tt.amount, tt.desc)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error for amount %d (%s): %v", tt.amount, tt.desc, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidateAmount_ErrorMessage locks the error messages for the two
|
||||
// rejection paths so callers depending on string matching do not silently
|
||||
// break.
|
||||
func TestValidateAmount_ErrorMessage(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if err := ValidateAmount(0); err == nil || err.Error() != "amount must be greater than 0" {
|
||||
t.Errorf("zero amount error message mismatch: %v", err)
|
||||
}
|
||||
if err := ValidateAmount(1000001); err == nil || err.Error() != "amount exceeds maximum (£10,000)" {
|
||||
t.Errorf("over-max error message mismatch: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidatePartialAmount_ErrorMessage locks the error message format.
|
||||
func TestValidatePartialAmount_ErrorMessage(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := ValidatePartialAmount(5000, 2500)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for partial exceeding remaining")
|
||||
}
|
||||
expected := "partial amount (£50.00) exceeds remaining balance (£25.00)"
|
||||
if err.Error() != expected {
|
||||
t.Errorf("error message mismatch:\n got: %s\n want: %s", err.Error(), expected)
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidatePaymentType_AllValidTypes verifies every entry in the
|
||||
// validPaymentTypes map is accepted and unknown types are rejected.
|
||||
func TestValidatePaymentType_AllValidTypes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
valid := []string{"deposit", "full", "tip", "balance", "partial"}
|
||||
for _, pt := range valid {
|
||||
if err := ValidatePaymentType(pt); err != nil {
|
||||
t.Errorf("expected valid payment type %q to be accepted, got: %v", pt, err)
|
||||
}
|
||||
}
|
||||
|
||||
invalid := []string{"", "unknown", "deposits", "FULL", "gift_card"}
|
||||
for _, pt := range invalid {
|
||||
if err := ValidatePaymentType(pt); err == nil {
|
||||
t.Errorf("expected invalid payment type %q to be rejected", pt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidateRefundReason_Empty verifies an empty refund reason is rejected.
|
||||
func TestValidateRefundReason_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if err := ValidateRefundReason(""); err == nil {
|
||||
t.Error("expected an error for empty refund reason")
|
||||
}
|
||||
if err := ValidateRefundReason("customer changed mind"); err != nil {
|
||||
t.Errorf("unexpected error for non-empty refund reason: %v", err)
|
||||
}
|
||||
if err := ValidateRefundReason(" "); err != nil {
|
||||
t.Errorf("whitespace-only is arguably a reason — should not error: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user