Skip to content

test(purchase): refuse a mixed-account single-account execute - #388

Merged
cristim merged 1 commit into
mainfrom
test/232-mixed-account-single-execute-refused
Sep 28, 2026
Merged

cristim merged 1 commit into
mainfrom
test/232-mixed-account-single-execute-refused

Conversation

@cristim

@cristim cristim commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds a regression test that drives a mixed-account single-account purchase
execution end to end and asserts it is refused, closing the A05-002 gap
described in #232.

Root cause

SingleCloudAccountIDFromRecs and its caller resolveSingleAccountProvider
already refuse a batch whose selected recommendations span more than one
cloud account (added in #2072 pre-split). But the only test covering the
two-distinct-accounts case, TestSingleCloudAccountIDFromRecs, called the
helper directly in isolation. Nothing drove the error through the real
executePurchase -> executeSingleAccount -> resolveSingleAccountProvider
path and asserted the provider factory (and therefore any purchase) was
never touched, so a regression that silently picked one account instead of
refusing would have shipped through a green suite.

Fix

Test-only change: added
TestExecutePurchase_SingleAccount_MixedAccountRecs_Refused in
internal/purchase/execution_test.go. It builds a direct-execute purchase
(PlanID: "") with two selected recommendations naming two distinct
CloudAccountIDs, calls manager.executePurchase directly, and asserts:

  • the call returns an error wrapping errAmbiguousAccountScope with the
    expected account list in the message
  • CreateAndValidateProvider is never called (registered with .Maybe()
    first so the AssertNotCalled is a meaningful check, not a vacuous one)
  • SavePurchaseHistory is never called

No production code changed; the guard was already correct.

Regression test proof (mutation check)

Confirmed the new test has teeth by temporarily breaking the guard:

 	switch {
-	case len(ids) > 1:
-		return nil, fmt.Errorf("%w: %d selected recommendation(s) target %d cloud accounts (%s); submit one purchase per account",
-			errAmbiguousAccountScope, selected, len(ids), strings.Join(ids, ", "))
 	case len(ids) == 1 && unattributed > 0:

With that mutation, SingleCloudAccountIDFromRecs silently returns the last
seen account instead of refusing. Re-ran the new test:

[FAIL] TestExecutePurchase_SingleAccount_MixedAccountRecs_Refused
Error: Target error should be in err chain: expected "selected recommendations do not resolve to a single cloud account"

Reverted the mutation and confirmed the test passes again (1 passed in 1 packages).

Verification

All run with GOTOOLCHAIN=go1.26.6 GOWORK=off from the repo root:

  • go build ./... - ok
  • go vet ./internal/purchase/... - ok
  • go test -race -short ./internal/purchase/... - 312 passed in 1 packages
  • go mod tidy -diff - empty
  • golangci-lint run --timeout=10m ./internal/purchase/... at the exact
    CI-pinned version (v2.10.1) - 0 issues

Closes #232

Summary by CodeRabbit

  • Tests
    • Added a regression test to verify that purchases with recommendations spanning multiple cloud accounts are rejected before provider activity or purchase history changes.

Add TestExecutePurchase_SingleAccount_MixedAccountRecs_Refused, which
drives a direct-execute purchase whose selected recommendations name
two distinct cloud accounts through the real executePurchase ->
executeSingleAccount -> resolveSingleAccountProvider path and asserts
the purchase is refused before the provider factory is ever touched.

The existing TestSingleCloudAccountIDFromRecs only exercised the
helper in isolation and labeled the two-account case "not this path",
so nothing proved the guard actually stops an end-to-end execution.
Confirmed the new test fails (mutation: dropped the len(ids) > 1
branch in SingleCloudAccountIDFromRecs so it silently returns the last
seen account) and passes again once restored.

Closes #232
@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: LeanerCloud/cloud-commitments-platform/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: 58e984ce-cdba-47a6-8a45-f2cac720b1ff

📥 Commits

Reviewing files that changed from the base of the PR and between 5bfa1db and 8f25b2e.

📒 Files selected for processing (1)
  • internal/purchase/execution_test.go

Included review availability: This review used your included allowance. 1 included review remains after this review. Your included PR review attempts over the past 7 days set your current allowance at 4 reviews per hour.


📝 Walkthrough

Walkthrough

A regression test covers plan-less execution with recommendations assigned to two cloud accounts. It checks for an ambiguous-account-scope error and confirms that execution does not create a provider or save purchase history.

Changes

Purchase execution

Layer / File(s) Summary
Mixed-account refusal test
internal/purchase/execution_test.go
Adds a test that checks mixed-account recommendations return errAmbiguousAccountScope before provider creation or purchase-history saving.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Other

Merge Risk: ⚪ Minimal · up to 8f25b

The new regression test targets mixed-account purchase refusal. No merge-blocking issue is identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #232 requires a regression test that drives the single-account execution path with selected recommendations from two cloud accounts and verifies refusal before provider creation. The added `Test…
Out of Scope Changes check ✅ Passed The pull request adds only the regression test in internal/purchase/execution_test.go. The test assertions and mock setup support issue #232 by checking refusal before provider creation and history …
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a purchase regression test that refuses mixed-account recommendations on the single-account execution path.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@cristim cristim added urgency/this-sprint Within the current sprint impact/internal Team-internal only type/chore Maintenance / non-user-visible priority/p1 Next up; this sprint severity/high Significant harm effort/s Hours triaged Item has been triaged labels Sep 28, 2026
@cristim

cristim commented Sep 28, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Already reviewed the last commit. Use @coderabbitai full review to rerun a review of the entire changeset.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Sep 28, 2026

Copy link
Copy Markdown
Member Author

Independent review + local verification: MERGE at 8f25b2e. Test-only; mutation-checked: disabling scope-error propagation in resolveSingleAccountProvider makes the test fail through the real executePurchase path.

@cristim
cristim merged commit 680e4c2 into main Sep 28, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/internal Team-internal only priority/p1 Next up; this sprint severity/high Significant harm triaged Item has been triaged type/chore Maintenance / non-user-visible urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(purchase): no test refuses a mixed-account single-account execute

1 participant