package billing import ( "context" "errors" "testing" "time" ) func TestLedgerChargesAndRefundsThroughWalletPosterWithFrozenKeys(t *testing.T) { poster := &recordingPoster{result: WalletPosting{LedgerID: "ledger-1", CreatedAt: time.Unix(1, 0)}} ledger := Ledger{Poster: poster, NewID: func() string { return "new-ledger" }} charge, err := ledger.Charge(context.Background(), ChargeRequest{OrganizationID: "org-1", AccountID: "a", JobID: "job-1", AmountFen: 125}) if err != nil || charge.LedgerID != "ledger-1" || poster.params.IdempotencyKey != "job-charge:job-1" || poster.params.DeltaFen != -125 || poster.params.Currency != "CNY" { t.Fatalf("charge = %#v params=%#v err=%v", charge, poster.params, err) } _, err = ledger.Refund(context.Background(), RefundRequest{OrganizationID: "org-1", AccountID: "a", JobID: "job-1", AmountFen: 125}) if err != nil || poster.params.IdempotencyKey != "job-refund:job-1" || poster.params.DeltaFen != 125 { t.Fatalf("refund params=%#v err=%v", poster.params, err) } } func TestLedgerMapsWalletFailuresToFrozenStatuses(t *testing.T) { for _, test := range []struct { db error want error status int }{{errors.New("BILLING_INSUFFICIENT_BALANCE"), ErrInsufficientBalance, 402}, {errors.New("BILLING_IDEMPOTENCY_PAYLOAD_MISMATCH"), ErrIdempotencyConflict, 409}} { ledger := Ledger{Poster: &recordingPoster{err: test.db}, NewID: func() string { return "id" }} _, err := ledger.Charge(context.Background(), ChargeRequest{OrganizationID: "o", JobID: "j", AmountFen: 1}) if !errors.Is(err, test.want) || HTTPStatus(err) != test.status { t.Fatalf("error=%v status=%d", err, HTTPStatus(err)) } } } type recordingPoster struct { params WalletPostParams result WalletPosting err error } func (p *recordingPoster) PostWalletEntry(_ context.Context, v WalletPostParams) (WalletPosting, error) { p.params = v return p.result, p.err }