Files
daily-meal-api/tests/Feature/NutritionOnboardingTest.php
leonm 69372a49ed
CI / 🧪 Tests Laravel (push) Failing after 2m18s
CI / 🐳 Build & Push Images (push) Has been skipped
feat: history and calculate needs for user
2026-08-14 12:45:37 +02:00

94 lines
3.5 KiB
PHP

<?php
use App\Enums\PacePreference;
use App\Enums\PhysicalActivityLevel;
use App\Enums\UserSex;
use App\Enums\WeightGoal;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Sanctum\Sanctum;
uses(RefreshDatabase::class);
function onboardingPayload(array $overrides = []): array
{
return [
'dateOfBirth' => '1992-04-18',
'sex' => UserSex::WOMAN->value,
'heightCm' => 168,
'weightKg' => 72.4,
'targetWeightKg' => 66,
'physicalActivityLevel' => PhysicalActivityLevel::MODERATELY_ACTIVE->value,
'weightGoal' => WeightGoal::LOSE_WEIGHT->value,
'pacePreference' => PacePreference::NORMAL->value,
'nutritionEstimateAccepted' => true,
...$overrides,
];
}
it('returns an explicit onboarding state and blocks application endpoints', function () {
$user = User::factory()->withoutNutritionOnboarding()->create();
Sanctum::actingAs($user);
$this->getJson('/api/auth/me')
->assertOk()
->assertJsonPath('data.onboardingStatus', 'required')
->assertJsonPath('data.nutritionGoals', null)
->assertJsonPath('data.currentWeight', null);
$this->getJson('/api/meal-posts/stats?period=day&date=2026-08-14')
->assertStatus(409)
->assertJsonPath('code', 'ONBOARDING_REQUIRED')
->assertJsonMissingPath('message');
});
it('completes onboarding atomically and returns the calculated plan', function () {
$user = User::factory()->withoutNutritionOnboarding()->create();
Sanctum::actingAs($user);
$response = $this->postJson('/api/nutrition-onboarding', onboardingPayload());
$response
->assertOk()
->assertJsonPath('data.onboardingStatus', 'completed')
->assertJsonPath('data.height', 168)
->assertJsonPath('data.currentWeight', 72.4)
->assertJsonPath('data.targetWeight', 66)
->assertJsonPath('data.nutritionPlan.formulaVersion', 'mifflin_st_jeor_v1')
->assertJsonPath('data.nutritionPlan.source', 'onboarding');
expect($response->json('data.nutritionGoals.calories'))->toBeGreaterThan(1200);
$this->assertDatabaseCount('weight_entries', 1);
$this->assertDatabaseCount('nutrition_plans', 1);
expect($user->fresh()->onboarding_completed_at)->not->toBeNull();
});
it('is idempotent when the completion response is retried', function () {
$user = User::factory()->withoutNutritionOnboarding()->create();
Sanctum::actingAs($user);
$this->postJson('/api/nutrition-onboarding', onboardingPayload())->assertOk();
$this->postJson('/api/nutrition-onboarding', onboardingPayload())->assertOk();
$this->assertDatabaseCount('weight_entries', 1);
$this->assertDatabaseCount('nutrition_plans', 1);
});
it('validates adult age, formula reference and target direction', function () {
Sanctum::actingAs(User::factory()->withoutNutritionOnboarding()->create());
$this->postJson('/api/nutrition-onboarding', onboardingPayload([
'dateOfBirth' => now()->subYears(17)->toDateString(),
'sex' => UserSex::OTHER->value,
'targetWeightKg' => 80,
]))
->assertUnprocessable()
->assertJsonValidationErrors(['dateOfBirth', 'sex', 'targetWeightKg'])
->assertJsonPath('code', 'VALIDATION_ERROR')
->assertJsonPath('errors.dateOfBirth.0', 'NUTRITION_ADULTS_ONLY')
->assertJsonPath('errors.sex.0', 'NUTRITION_METABOLIC_SEX_REQUIRED')
->assertJsonPath('errors.targetWeightKg.0', 'NUTRITION_TARGET_WEIGHT_MUST_BE_LOWER')
->assertJsonMissingPath('message');
});