94 lines
3.5 KiB
PHP
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');
|
|
});
|