Fix critical bug: cycle phase boundaries now scale with cycle length

CRITICAL BUG FIX:
- Phase boundaries were hardcoded for 31-day cycle, breaking correct
  phase calculations for users with different cycle lengths (28, 35, etc.)
- Added getPhaseBoundaries(cycleLength) function in cycle.ts
- Updated getPhase() to accept cycleLength parameter (default 31)
- Updated all callers (API routes, components) to pass cycleLength
- Added 13 new tests for phase boundaries with 28, 31, and 35-day cycles

ICS IMPROVEMENTS:
- Fixed emojis to match calendar.md spec: 🩸🌱🌸🌙🌑
- Added CATEGORIES field for calendar app colors per spec:
  MENSTRUAL=Red, FOLLICULAR=Green, OVULATION=Pink,
  EARLY_LUTEAL=Yellow, LATE_LUTEAL=Orange
- Added 5 new tests for CATEGORIES

Updated IMPLEMENTATION_PLAN.md with discovered issues and test counts.

825 tests passing (up from 807)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-11 22:39:09 +00:00
parent 58f6c5605a
commit a977934c23
15 changed files with 337 additions and 148 deletions

View File

@@ -118,13 +118,14 @@ describe("GET /api/cycle/current", () => {
expect(body.phaseConfig.name).toBe("FOLLICULAR");
expect(body.phaseConfig.weeklyLimit).toBe(120);
expect(body.phaseConfig.trainingType).toBe("Strength + rebounding");
expect(body.phaseConfig.days).toEqual([4, 14]);
// Phase configs days are for reference; actual boundaries are calculated dynamically
expect(body.phaseConfig.days).toEqual([4, 15]);
expect(body.phaseConfig.dailyAvg).toBe(17);
});
it("calculates daysUntilNextPhase correctly", async () => {
// Cycle day 10, in FOLLICULAR (days 4-14)
// Days until OVULATION starts (day 15): 15 - 10 = 5
// Cycle day 10, in FOLLICULAR (days 4-15 for 31-day cycle)
// Days until OVULATION starts (day 16): 16 - 10 = 6
currentMockUser = createMockUser({
lastPeriodDate: new Date("2025-01-01"),
cycleLength: 31,
@@ -135,7 +136,7 @@ describe("GET /api/cycle/current", () => {
expect(response.status).toBe(200);
const body = await response.json();
expect(body.daysUntilNextPhase).toBe(5);
expect(body.daysUntilNextPhase).toBe(6);
});
it("returns correct data for MENSTRUAL phase", async () => {
@@ -157,10 +158,11 @@ describe("GET /api/cycle/current", () => {
});
it("returns correct data for OVULATION phase", async () => {
// Set lastPeriodDate so cycle day = 15 (start of OVULATION)
// If current is 2025-01-10, need lastPeriodDate = 2024-12-27 (14 days ago)
// For 31-day cycle, OVULATION is days 16-17
// Set lastPeriodDate so cycle day = 16 (start of OVULATION)
// If current is 2025-01-10, need lastPeriodDate = 2024-12-26 (15 days ago)
currentMockUser = createMockUser({
lastPeriodDate: new Date("2024-12-27"),
lastPeriodDate: new Date("2024-12-26"),
cycleLength: 31,
});
@@ -169,10 +171,10 @@ describe("GET /api/cycle/current", () => {
expect(response.status).toBe(200);
const body = await response.json();
expect(body.cycleDay).toBe(15);
expect(body.cycleDay).toBe(16);
expect(body.phase).toBe("OVULATION");
expect(body.phaseConfig.weeklyLimit).toBe(80);
expect(body.daysUntilNextPhase).toBe(2); // Day 17 is EARLY_LUTEAL
expect(body.daysUntilNextPhase).toBe(2); // Day 18 is EARLY_LUTEAL
});
it("returns correct data for LATE_LUTEAL phase", async () => {