Skip to content

fix(marketplace): fail loud instead of listing no-upfront RIs at $0 - #1493

Merged
cristim merged 1 commit into
mainfrom
fix/808-zero-price-marketplace-listing
Jul 22, 2026
Merged

cristim merged 1 commit into
mainfrom
fix/808-zero-price-marketplace-listing

Conversation

@cristim

@cristim cristim commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to #808 (Sell-on-Marketplace feature), found during an adversarial review sweep. resolveMarketplacePriceSchedule's default-schedule branch (used when the caller supplies no explicit price_schedule) silently clamped a negative computed list price to 0 instead of rejecting it:

if listPrice < 0 {
    listPrice = 0
}

A no-upfront RI (upfrontCost <= 0) or one with an unknown/elapsed term (originalTerm <= 0) makes marketplaceResidualPerUnit return 0, so the RI would be listed on AWS Marketplace for free instead of erroring out the way the supplied-schedule branch already does for a non-positive price ("price must be positive").

The sell-consent-modal preview in frontend/src/history.ts had a parallel bug: it computed the preview price with a different formula than the backend (it included recurring monthly_cost, which the backend deliberately excludes since the buyer assumes the recurring obligation post-transfer, and it didn't divide by instance count). This meant the preview could show a nonzero price for exactly the case the backend now rejects, and diverge from the real listing price for multi-count RIs.

Fix

  • internal/api/handler_marketplace.go: the default-schedule branch now returns an explicit error ("cannot compute a default listing price for this RI (no upfront cost or unknown term to prorate); supply an explicit price_schedule") instead of silently listing at $0.
  • frontend/src/history.ts: the consent-modal preview now mirrors marketplaceResidualPerUnit + resolveMarketplacePriceSchedule's default branch exactly (upfront-only, per-unit, prorated by remaining/original term), and shows "Default list price: unavailable (no upfront cost or unknown term)" instead of a fabricated or zero price when no default can be computed.

Tests

  • TestResolveMarketplacePriceSchedule_ZeroDefaultPriceRejected (backend): asserts an error for both upfrontCost=0 and originalTerm=0.
  • Two new tests in history-marketplace-sell-button.test.ts (frontend):
    • pins the corrected per-unit formula against the old (wrong) recurring-inclusive row-total formula for a multi-count RI,
    • asserts the no-upfront case shows "unavailable" and never "$0".

Verified both the Go and frontend regression tests fail on the pre-fix code and pass after the fix.

Test plan

  • go build ./...
  • go vet ./...
  • go test ./internal/api/... (full package, includes new test)
  • golangci-lint run (v2.10.1, CI-pinned) - 0 issues
  • gocyclo -over 10 on changed files - clean
  • npx jest src/__tests__/history-marketplace-sell-button.test.ts - 10/10 pass
  • npx tsc --noEmit - clean
  • npx eslint on changed frontend files - clean
  • Confirmed both new regression tests fail on pre-fix code and pass post-fix

resolveMarketplacePriceSchedule's default-schedule branch silently
clamped a negative computed list price to 0 instead of rejecting it.
A no-upfront RI (upfrontCost <= 0) or one with an unknown/elapsed term
(originalTerm <= 0) makes marketplaceResidualPerUnit return 0, so the
RI would be listed on AWS Marketplace for free instead of erroring
out like the supplied-schedule branch already does for a non-positive
price. The backend now returns an explicit error asking the caller to
supply a price_schedule.

The sell-consent-modal preview in frontend/src/history.ts had a
parallel bug: it computed the preview price with a different formula
than the backend (including recurring monthly cost, which the backend
deliberately excludes, and not dividing by instance count), so it
could show a nonzero preview price for exactly the case the backend
now rejects, and diverge from the real listing price for multi-count
RIs. The preview now mirrors marketplaceResidualPerUnit and
resolveMarketplacePriceSchedule's default branch exactly, and shows
"Default list price: unavailable (no upfront cost or unknown term)"
instead of a fabricated or zero price when no default can be computed.

Adds TestResolveMarketplacePriceSchedule_ZeroDefaultPriceRejected
(backend) and two regression tests in
history-marketplace-sell-button.test.ts (frontend): one pinning the
per-unit formula against the old recurring-inclusive row-total formula
for a multi-count RI, one asserting the no-upfront case shows
"unavailable" and never "$0".

Follow-up to #808, found during an adversarial review sweep.
@cristim cristim added triaged Item has been triaged priority/p1 Next up; this sprint severity/high Significant harm urgency/this-sprint Within the current sprint impact/many Affects most users type/bug Defect labels Jul 22, 2026
@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac62cdbd-c17c-4b34-87e3-1fd3464acfd8

📥 Commits

Reviewing files that changed from the base of the PR and between f8cdea6 and 99e7c22.

📒 Files selected for processing (4)
  • frontend/src/__tests__/history-marketplace-sell-button.test.ts
  • frontend/src/history.ts
  • internal/api/handler_marketplace.go
  • internal/api/handler_marketplace_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/808-zero-price-marketplace-listing

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

@cristim

cristim commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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 merged commit 6a8ee07 into main Jul 22, 2026
20 checks passed
@cristim
cristim deleted the fix/808-zero-price-marketplace-listing branch July 27, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

impact/many Affects most users priority/p1 Next up; this sprint severity/high Significant harm triaged Item has been triaged type/bug Defect urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant