test: Fix Carbon month overflow in date calculations (#87)
## Summary - Replace `subMonths()` with `subMonthsNoOverflow()` across controllers, commands, and tests to prevent date overflow on months with 31 days - On Jan 31, `subMonths(2)` overflows: Nov 31 doesn't exist → Carbon rolls forward to Dec 1, producing wrong date ranges and off-by-one month counts ## Root cause Carbon's `subMonths()` allows day overflow. When the target month has fewer days than the source date, it rolls into the next month. For example: - `2026-01-31 subMonths(2)` → Nov 31 → **2025-12-01** (expected Nov 30) - `2026-01-31 subMonths(11)` → Feb 31 → **2025-03-03** (expected Feb 28) `subMonthsNoOverflow()` clamps to the last valid day of the target month instead. ## Files changed - `app/Http/Controllers/Api/CashflowAnalyticsController.php` — cashflow trend start date calculation - `app/Console/Commands/ResetDemoAccountCommand.php` — balance history date generation (was causing duplicate `balance_date` entries) - `tests/Feature/AccountControllerTest.php` — balance evolution `from` param - `tests/Feature/CashflowAnalyticsTest.php` — transaction date generation in loop - `tests/Feature/DashboardAnalyticsTest.php` — net worth evolution `from` param ## Test plan - [x] `account balance evolution returns data` — expects 3 months, was getting 2 - [x] `cashflow trend returns monthly data` — expects 3 months, was getting 2 - [x] `cashflow trend defaults to 12 months` — expects 12 months, was getting 11 - [x] `net worth evolution returns monthly data points` — expects 3 months, was getting 2 - [x] `demo:reset creates demo user` — was throwing UniqueConstraintViolation on duplicate balance dates
This commit is contained in:
parent
10bd7da5db
commit
cc666a3111
|
|
@ -276,7 +276,7 @@ class ResetDemoAccountCommand extends Command
|
|||
$balances = [];
|
||||
|
||||
for ($i = 0; $i <= 12; $i++) {
|
||||
$date = now()->subMonths($i)->endOfMonth();
|
||||
$date = now()->subMonthsNoOverflow($i)->endOfMonth();
|
||||
|
||||
if ($i === 0) {
|
||||
$date = now();
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class CashflowAnalyticsController extends Controller
|
|||
$userId = $request->user()->id;
|
||||
|
||||
$end = Carbon::now()->endOfMonth();
|
||||
$start = Carbon::now()->subMonths($months - 1)->startOfMonth();
|
||||
$start = Carbon::now()->subMonthsNoOverflow($months - 1)->startOfMonth();
|
||||
|
||||
$data = [];
|
||||
$current = $start->copy();
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ test('account balance evolution returns data for single account', function () {
|
|||
]);
|
||||
|
||||
$response = $this->getJson('/api/dashboard/account/'.$account->id.'/balance-evolution?'.http_build_query([
|
||||
'from' => now()->subMonths(2)->startOfMonth()->toDateString(),
|
||||
'from' => now()->subMonthsNoOverflow(2)->startOfMonth()->toDateString(),
|
||||
'to' => now()->endOfMonth()->toDateString(),
|
||||
]));
|
||||
|
||||
|
|
|
|||
|
|
@ -223,14 +223,14 @@ test('cashflow trend returns monthly data for specified months', function () {
|
|||
'account_id' => $account->id,
|
||||
'category_id' => $incomeCategory->id,
|
||||
'amount' => 100000 + ($i * 10000),
|
||||
'transaction_date' => now()->subMonths($i),
|
||||
'transaction_date' => now()->subMonthsNoOverflow($i),
|
||||
]);
|
||||
Transaction::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $account->id,
|
||||
'category_id' => $expenseCategory->id,
|
||||
'amount' => -(50000 + ($i * 5000)),
|
||||
'transaction_date' => now()->subMonths($i),
|
||||
'transaction_date' => now()->subMonthsNoOverflow($i),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ test('net worth evolution returns monthly data points with per-account balances'
|
|||
]);
|
||||
|
||||
$response = $this->getJson('/api/dashboard/net-worth-evolution?'.http_build_query([
|
||||
'from' => now()->subMonths(2)->startOfMonth()->toDateString(),
|
||||
'from' => now()->subMonthsNoOverflow(2)->startOfMonth()->toDateString(),
|
||||
'to' => now()->endOfMonth()->toDateString(),
|
||||
]));
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue