feat(db): nullable MonthlyCost on PurchaseHistoryRecord (closes #255) - #848
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughMake monthly recurring cost nullable end-to-end: DB column becomes nullable, store scans map NULL to nil pointers, execution preserves pointers, API DTOs and aggregation keep nil vs zero semantics, tests updated, and frontend renders “—” for null costs. ChangesMake MonthlyCost nullable throughout the stack
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
Migration 000063 drops NOT NULL on purchase_history.monthly_cost so rows from providers without a monthly recurring breakdown (Azure/GCP all-upfront) can store NULL instead of a misleading 0.0. - PurchaseHistoryRecord.MonthlyCost: float64 -> *float64 - InventoryCommitment.MonthlyCost: float64 -> *float64 - All scan loops use sql.NullFloat64; nil DB value -> nil pointer - execution.go: remove derefFloat64; pass pointer straight through - handler_inventory.go: skip nil MonthlyCost in coverage aggregation - handler_history.go: sumRecommendationMonthlyCostPtr returns nil when every rec has nil MonthlyCost (renders as -- not $0.00) - Frontend: monthly_cost typed number|null; inventory cell renders -- - All test fixtures and assertions updated to use *float64 (pf helper)
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
…e refactor The PurchaseHistoryRecord.MonthlyCost type changed to *float64 in 3cd63c8 but four test literals in handler_inventory_test.go (lines 186, 196, 499, 508) still assigned untyped float constants, breaking go vet with: cannot use 80.0 (untyped float constant 80) as *float64 value in struct literal Switch them to float64Ptr(...) consistent with the rest of the file (lines 77, 90, 145, 430 already use the helper).
|
Fixed CI go-vet failure introduced by the nullable refactor:
Local verification: @coderabbitai review |
|
🧠 Learnings used✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
internal/config/store_postgres_db_test.go (1)
699-737: ⚡ Quick winAdd a DB round-trip case for
MonthlyCost: nil.This subtest now pins
0.0 -> *float64(0), but the slice’s main contract is the distinction betweenNULLand0.0. Please add a companion insert/read assertion withMonthlyCost: nilso the migration + store scan path provesNULL -> nilend to end.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/config/store_postgres_db_test.go` around lines 699 - 737, The test currently only verifies a non-nil MonthlyCost pointer; add a companion subtest that inserts a PurchaseHistoryRecord with MonthlyCost: nil and then reads it back via store.SavePurchaseHistory and store.GetPurchaseHistory to assert the retrieved record's MonthlyCost is nil (verifying NULL -> nil round-trip through the migration and scan). Locate the existing test block that uses PurchaseHistoryRecord, SavePurchaseHistory and GetPurchaseHistory and duplicate the flow in a new t.Run (e.g., "MonthlyCost nil round-trip") setting MonthlyCost to nil, calling SavePurchaseHistory(ctx, record) and then GetPurchaseHistory(ctx, "123456789012", 10) and assert the returned record with PurchaseID == "purchase-001" has MonthlyCost == nil.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/config/types.go`:
- Around line 465-472: The comment on the MonthlyCost field incorrectly
references "GCP all-upfront" which is misleading; update the comment in types.go
(the block describing MonthlyCost and the Migration 000063 note) to remove the
"GCP all-upfront" example and mention only Azure (or a neutral phrasing) so it
reflects that GCP commitments are monthly-only—keep the rest of the explanation
about nil vs zero, frontend rendering, aggregation behavior, and the Migration
000063 NOT NULL change intact.
---
Nitpick comments:
In `@internal/config/store_postgres_db_test.go`:
- Around line 699-737: The test currently only verifies a non-nil MonthlyCost
pointer; add a companion subtest that inserts a PurchaseHistoryRecord with
MonthlyCost: nil and then reads it back via store.SavePurchaseHistory and
store.GetPurchaseHistory to assert the retrieved record's MonthlyCost is nil
(verifying NULL -> nil round-trip through the migration and scan). Locate the
existing test block that uses PurchaseHistoryRecord, SavePurchaseHistory and
GetPurchaseHistory and duplicate the flow in a new t.Run (e.g., "MonthlyCost nil
round-trip") setting MonthlyCost to nil, calling SavePurchaseHistory(ctx,
record) and then GetPurchaseHistory(ctx, "123456789012", 10) and assert the
returned record with PurchaseID == "purchase-001" has MonthlyCost == nil.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c444e917-5e59-4dac-95d4-136fbc28ef55
📒 Files selected for processing (21)
frontend/src/api/types.tsfrontend/src/inventory.tsfrontend/src/types.tsinternal/api/handler_history.gointernal/api/handler_history_test.gointernal/api/handler_inventory.gointernal/api/handler_inventory_test.gointernal/api/handler_per_account_perms_test.gointernal/api/types.gointernal/config/store_postgres.gointernal/config/store_postgres_additional_test.gointernal/config/store_postgres_comprehensive_test.gointernal/config/store_postgres_coverage_test.gointernal/config/store_postgres_db_test.gointernal/config/store_postgres_test.gointernal/config/store_postgres_unit_test.gointernal/config/types.gointernal/config/types_test.gointernal/database/postgres/migrations/000063_purchase_history_monthly_cost_nullable.down.sqlinternal/database/postgres/migrations/000063_purchase_history_monthly_cost_nullable.up.sqlinternal/purchase/execution.go
Address CodeRabbit review on PR #848: - F1 (types.go): remove the misleading "GCP all-upfront" example from the MonthlyCost field comment. In this repo GCP CUD commitments are always monthly-billed (per #709, #717, #718, #829), so the only provider that can legitimately produce a NULL recurring breakdown at the commitment layer is Azure all-upfront. Comment now mentions only Azure and explicitly notes that GCP always populates MonthlyCost. - F2 (store_postgres_db_test.go): add a "MonthlyCost nil round-trip" subtest that inserts a PurchaseHistoryRecord with MonthlyCost: nil and asserts it reads back as nil. This is the contract migration 000063 was meant to establish (NULL -> nil, distinct from 0.0 -> &0), and the frontend renders the dash glyph vs "$0.00" based on it. Also strengthens the existing 0.0 subtest to assert the pointer is non-nil with *MonthlyCost == 0.0, so both sides of the contract are pinned end-to-end. No production code changes; comment + test only.
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
PurchaseHistoryRecord.MonthlyCosta*float64(nullable) so rows from providers that do not return a monthly recurring cost (e.g. Azure/GCP all-upfront) storeNULLinstead of a misleading0.0.NOT NULLconstraint onpurchase_history.monthly_cost; rollback coerces anyNULLs back to0.0before re-adding the constraint.sql.NullFloat64in all three copies of the scan loop (production, comprehensive-test mock, additional-test mock) soNULLDB values materialise as anilpointer and0.0DB values materialise as&0.0, preserving the semantic distinction.derefFloat64helper inexecution.gothat previously flattened nil to 0; now the pointer is passed through to the history record unchanged.monthly_costtyped asnumber | null; inventory commitment cell renders—when null,$X.XXotherwise.MonthlyCostskip nil entries (no distortion of totals).Test plan
go build ./...cleango test github.com/LeanerCloud/CUDly/internal/config/... github.com/LeanerCloud/CUDly/internal/api/...1906 passpurchase_history.monthly_cost; rollback NULLs-to-0 before re-adding constraintMonthlyCostentries rather than adding zeroCloses #255
Summary by CodeRabbit