No way existed to log body weight over time - only the single
self-reported value from the signup profile - and the dashboard showed
session activity but nothing about physical progress, which is usually
the main reason people keep using a fitness app.
Adds a WeightLog model (one entry per user per day, re-logging the same
day updates it rather than duplicating), a quick-add form and trend
summary on the dashboard, and delete support.
Found and fixed a real bug along the way: the first implementation used
WeightLog::updateOrCreate() with a raw ['logged_at' => $date] match
array, which silently failed to find the same-day entry because the
`date` cast stores full datetime strings ('2026-01-01 00:00:00') while
the match array holds a plain 'Y-m-d' string - so re-logging the same
day hit a unique-constraint violation instead of updating. Fixed by
using a whereDate() lookup instead, which is comparison-format-safe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
90 lines
2.8 KiB
PHP
90 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\User;
|
|
use App\Models\WeightLog;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class WeightLogTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_guest_cannot_log_weight(): void
|
|
{
|
|
$response = $this->post(route('weight-logs.store'), ['weight_kg' => 80]);
|
|
|
|
$response->assertRedirect(route('login'));
|
|
}
|
|
|
|
public function test_user_can_log_weight_for_today(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
|
|
$response = $this->actingAs($user)->post(route('weight-logs.store'), [
|
|
'weight_kg' => 82.5,
|
|
]);
|
|
|
|
$response->assertRedirect();
|
|
$entry = WeightLog::where('user_id', $user->id)->first();
|
|
$this->assertNotNull($entry);
|
|
$this->assertEquals(82.5, $entry->weight_kg);
|
|
$this->assertEquals(now()->toDateString(), $entry->logged_at->toDateString());
|
|
}
|
|
|
|
public function test_logging_twice_for_the_same_date_updates_the_entry(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
|
|
$this->actingAs($user)->post(route('weight-logs.store'), [
|
|
'weight_kg' => 80,
|
|
'logged_at' => '2026-01-01',
|
|
]);
|
|
$this->actingAs($user)->post(route('weight-logs.store'), [
|
|
'weight_kg' => 81,
|
|
'logged_at' => '2026-01-01',
|
|
]);
|
|
|
|
$this->assertEquals(1, WeightLog::where('user_id', $user->id)->count());
|
|
$entry = WeightLog::where('user_id', $user->id)->first();
|
|
$this->assertEquals(81, $entry->weight_kg);
|
|
$this->assertEquals('2026-01-01', $entry->logged_at->toDateString());
|
|
}
|
|
|
|
public function test_future_date_is_rejected(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
|
|
$response = $this->actingAs($user)->post(route('weight-logs.store'), [
|
|
'weight_kg' => 80,
|
|
'logged_at' => now()->addDay()->toDateString(),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('logged_at');
|
|
}
|
|
|
|
public function test_owner_can_delete_their_entry(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$log = WeightLog::factory()->create(['user_id' => $user->id]);
|
|
|
|
$response = $this->actingAs($user)->delete(route('weight-logs.destroy', $log));
|
|
|
|
$response->assertRedirect();
|
|
$this->assertDatabaseMissing('weight_logs', ['id' => $log->id]);
|
|
}
|
|
|
|
public function test_other_user_cannot_delete_someone_elses_entry(): void
|
|
{
|
|
$owner = User::factory()->create();
|
|
$other = User::factory()->create();
|
|
$log = WeightLog::factory()->create(['user_id' => $owner->id]);
|
|
|
|
$response = $this->actingAs($other)->delete(route('weight-logs.destroy', $log));
|
|
|
|
$response->assertForbidden();
|
|
$this->assertDatabaseHas('weight_logs', ['id' => $log->id]);
|
|
}
|
|
}
|