Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).

### Added

- **`common.ErrRefusedBeforePurchase`:** a sentinel for a purchase refused before
the call sent any purchase request, detectable with `errors.Is`.
- **Azure `NewPartialSubscriptionFailure` constructor:** builds a
`PartialSubscriptionFailureError`, optionally with skip reasons readable
through `Skips()`, so code outside the module can construct one, for example
Expand Down
10 changes: 10 additions & 0 deletions pkg/common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ var ErrCurrencyNotAccepted = errors.New("currency not accepted")
// contract until the platform persists a typed flag (platform #778).
var ErrOutcomeUnknown = errors.New("purchase outcome unknown")

// ErrRefusedBeforePurchase wraps any error from the approved-upfront check,
// which runs before the purchase request is built or sent. Its presence is
// sufficient to know that this call sent no purchase request, but its absence
// proves nothing: other refusals do not carry it. It describes only the call
// that returned it and says nothing about earlier calls with the same
// idempotency token. A wrapper that combines several purchase calls must not
// propagate it unless none of them sent a request. Detect it with
// errors.Is(err, ErrRefusedBeforePurchase).
var ErrRefusedBeforePurchase = errors.New("refused before purchase")

// ProviderType identifies the cloud provider.
type ProviderType string

Expand Down
13 changes: 13 additions & 0 deletions pkg/common/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,3 +626,16 @@ func TestErrCurrencyNotAccepted(t *testing.T) {
assert.ErrorIs(t, wrapped, ErrCurrencyNotAccepted)
assert.NotErrorIs(t, errors.New("currency not accepted"), ErrCurrencyNotAccepted)
}

func TestErrRefusedBeforePurchase(t *testing.T) {
wrapped := fmt.Errorf("%w: %w", ErrRefusedBeforePurchase, errors.New("upfront 1200 exceeds approved 1000"))
assert.ErrorIs(t, wrapped, ErrRefusedBeforePurchase)
assert.NotErrorIs(t, errors.New("refused before purchase"), ErrRefusedBeforePurchase)
// Consumers release spend on this sentinel, so it must never match a sibling
// that marks a purchase as possibly made (ErrOutcomeUnknown).
for _, other := range []error{ErrOutcomeUnknown, ErrCurrencyNotAccepted, ErrCommitmentPurchaseNotSupported} {
assert.NotErrorIs(t, ErrRefusedBeforePurchase, other)
assert.NotErrorIs(t, other, ErrRefusedBeforePurchase)
}
assert.Equal(t, "refused before purchase", ErrRefusedBeforePurchase.Error())
}
Loading