feat: history and calculate needs for user
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
<?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');
|
||||
});
|
||||
Reference in New Issue
Block a user