127 lines
5.1 KiB
Java
127 lines
5.1 KiB
Java
package com.accounting.util;
|
|
|
|
import org.junit.jupiter.api.DisplayName;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import java.math.BigDecimal;
|
|
import java.time.LocalDate;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
/**
|
|
* 减重计划计算测试。
|
|
*
|
|
* <p>这块是「设置目标 → 每周该减多少」的核心算术,也是最容易出现
|
|
* 边界问题的地方:同日、超期、目标比起始还重、除零。所以边界用例
|
|
* 写得比正常用例还多。</p>
|
|
*/
|
|
class WeightPlanCalculatorTest {
|
|
|
|
@Test
|
|
@DisplayName("三个月 65→58kg:总减 7kg 分到 13 周,每周 0.54kg")
|
|
void weeklyLossForThreeMonths() {
|
|
LocalDate start = LocalDate.of(2026, 1, 1);
|
|
LocalDate target = LocalDate.of(2026, 4, 1); // 90 天
|
|
|
|
int weeks = WeightPlanCalculator.totalWeeks(start, target);
|
|
assertEquals(13, weeks);
|
|
|
|
BigDecimal weekly = WeightPlanCalculator.weeklyLoss(
|
|
new BigDecimal("65"), new BigDecimal("58"), weeks);
|
|
assertEquals(new BigDecimal("0.54"), weekly);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("总天数除不尽 7 时向上取整 —— 少算一周会让每周目标偏轻")
|
|
void roundsUpPartialWeek() {
|
|
// 8 天 = 1 周 + 1 天,应该算 2 周而不是 1 周
|
|
assertEquals(2, WeightPlanCalculator.totalWeeks(
|
|
LocalDate.of(2026, 1, 1), LocalDate.of(2026, 1, 9)));
|
|
// 6 天算 1 周
|
|
assertEquals(1, WeightPlanCalculator.totalWeeks(
|
|
LocalDate.of(2026, 1, 1), LocalDate.of(2026, 1, 7)));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("同日或倒填日期都算 1 周,不除零")
|
|
void sameDayFallsBackToOneWeek() {
|
|
LocalDate day = LocalDate.of(2026, 1, 1);
|
|
|
|
assertEquals(1, WeightPlanCalculator.totalWeeks(day, day));
|
|
// 目标日期早于开始日期也不该崩
|
|
assertEquals(1, WeightPlanCalculator.totalWeeks(day, day.minusDays(5)));
|
|
|
|
// 1 周减 7kg 是个荒谬的值,但必须是个数、不能是异常
|
|
assertEquals(new BigDecimal("7.00"), WeightPlanCalculator.weeklyLoss(
|
|
new BigDecimal("65"), new BigDecimal("58"), 1));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("目标比起始还重时每周应减为 0,不能是负数")
|
|
void gainingWeightGivesZeroNotNegative() {
|
|
// 负数会让进度条宽度算反、也会让「每周应减」显示成 -0.5kg
|
|
assertEquals(BigDecimal.ZERO, WeightPlanCalculator.weeklyLoss(
|
|
new BigDecimal("58"), new BigDecimal("65"), 13));
|
|
assertEquals(BigDecimal.ZERO, WeightPlanCalculator.weeklyLoss(
|
|
new BigDecimal("60"), new BigDecimal("60"), 13));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("理想体重曲线:第 0 周是起始体重,最后一周落在目标上")
|
|
void idealCurveEndsAtTarget() {
|
|
BigDecimal start = new BigDecimal("65");
|
|
BigDecimal target = new BigDecimal("58");
|
|
BigDecimal weekly = new BigDecimal("0.54");
|
|
|
|
assertEquals(new BigDecimal("65.0"),
|
|
WeightPlanCalculator.idealWeightAt(start, target, weekly, 0));
|
|
|
|
// 第 13 周:65 - 0.54*13 = 57.98,低于目标 58 → 应该钉在 58
|
|
assertEquals(new BigDecimal("58"),
|
|
WeightPlanCalculator.idealWeightAt(start, target, weekly, 13));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("理想体重不会跌破目标 —— 否则参照线会穿到目标下方")
|
|
void idealWeightNeverGoesBelowTarget() {
|
|
BigDecimal target = new BigDecimal("58");
|
|
// 刻意给一个过大的每周减重
|
|
BigDecimal ideal = WeightPlanCalculator.idealWeightAt(
|
|
new BigDecimal("65"), target, new BigDecimal("2.0"), 10);
|
|
assertEquals(target, ideal);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("时间进度:未开始 0、进行中在 0~1、超期封顶 1.0")
|
|
void timeProgressIsClamped() {
|
|
LocalDate start = LocalDate.of(2026, 1, 1);
|
|
LocalDate target = LocalDate.of(2026, 1, 11); // 10 天
|
|
|
|
// 还没开始
|
|
assertEquals(0.0, WeightPlanCalculator.timeProgress(start, target,
|
|
LocalDate.of(2025, 12, 20)));
|
|
// 正中间
|
|
assertEquals(0.5, WeightPlanCalculator.timeProgress(start, target,
|
|
LocalDate.of(2026, 1, 6)), 0.001);
|
|
// 已超期:封顶 1.0,不是 1.5 —— 进度条宽度不能溢出
|
|
assertEquals(1.0, WeightPlanCalculator.timeProgress(start, target,
|
|
LocalDate.of(2026, 3, 1)));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("每周超过 1kg 会被标记成不健康")
|
|
void flagsUnhealthyPace() {
|
|
// 65→58 只用 3 周 = 每周 2.33kg
|
|
BigDecimal weekly = WeightPlanCalculator.weeklyLoss(
|
|
new BigDecimal("65"), new BigDecimal("58"), 3);
|
|
assertTrue(weekly.compareTo(WeightPlanCalculator.HEALTHY_WEEKLY_LOSS_KG) > 0);
|
|
|
|
// 正常节奏不该被标
|
|
BigDecimal healthy = WeightPlanCalculator.weeklyLoss(
|
|
new BigDecimal("65"), new BigDecimal("58"), 13);
|
|
assertFalse(healthy.compareTo(WeightPlanCalculator.HEALTHY_WEEKLY_LOSS_KG) > 0);
|
|
}
|
|
}
|