升级报价遇到目录倒挂时改为报错

quote_runtime_profile_membership_upgrade 用 checked_sub 替换 saturating_sub,价格或每期泥点倒挂时返回 InvalidQuoteInput
新增 inverted_catalog_price_and_points_are_rejected 覆盖原本会算成 0 元升级的场景

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
2026-10-03 11:15:34 +08:00
parent 119eb7c8b3
commit 92917f57c9
@@ -158,8 +158,15 @@ pub fn quote_runtime_profile_membership_upgrade(
input.now_micros,
);
let price_delta_cents = target.price_cents.saturating_sub(current.price_cents);
let points_delta = target.period_points.saturating_sub(current.period_points);
// 目录价格 / 每期泥点必须严格随 rank 递增;倒挂时宁可报错,也不算出 0 元升级。
let price_delta_cents = target
.price_cents
.checked_sub(current.price_cents)
.ok_or(RuntimeProfileMembershipUpgradeRejection::InvalidQuoteInput)?;
let points_delta = target
.period_points
.checked_sub(current.period_points)
.ok_or(RuntimeProfileMembershipUpgradeRejection::InvalidQuoteInput)?;
let (amount_cents, granted_points_delta) = match target.cycle_kind {
RuntimeProfileMembershipCycleKind::Monthly => (price_delta_cents, points_delta),
@@ -401,6 +408,41 @@ mod tests {
);
}
#[test]
fn inverted_catalog_price_and_points_are_rejected() {
// 中文注释:后台把更高档位定得更便宜 / 给更少每期泥点时,报价必须报错,
// 而不是被 saturating_sub 静默算成 0 元升级。
let current = snapshot(
RuntimeProfileMembershipPlan::Pro,
RuntimeProfileMembershipCycleKind::Yearly,
);
let mut target = snapshot(
RuntimeProfileMembershipPlan::Max,
RuntimeProfileMembershipCycleKind::Yearly,
);
target.price_cents = current.price_cents - 1;
assert_eq!(
quote_runtime_profile_membership_upgrade(RuntimeProfileMembershipUpgradeQuoteInput {
current: current.clone(),
target: target.clone(),
..base_input()
}),
Err(RuntimeProfileMembershipUpgradeRejection::InvalidQuoteInput)
);
target.price_cents = current.price_cents + 1;
target.period_points = current.period_points - 1;
assert_eq!(
quote_runtime_profile_membership_upgrade(RuntimeProfileMembershipUpgradeQuoteInput {
current,
target,
..base_input()
}),
Err(RuntimeProfileMembershipUpgradeRejection::InvalidQuoteInput)
);
}
#[test]
fn cycle_kind_change_is_rejected() {
assert_eq!(