feat: save ai usage

This commit is contained in:
2026-05-24 11:12:04 +02:00
parent 24ec4165a6
commit 8715267361
11 changed files with 629 additions and 40 deletions
+70 -1
View File
@@ -1,11 +1,15 @@
<?php
use App\Ai\Agents\MealImageAnalyzer;
use App\Enums\AiUsageStatus;
use App\Enums\AiUsageType;
use App\Enums\IngredientUnit;
use App\Enums\MealPostType;
use App\Models\AiUsage;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Exceptions;
use Laravel\Sanctum\Sanctum;
uses(RefreshDatabase::class);
@@ -22,7 +26,9 @@ function fakeMealAnalysisImage(): UploadedFile
}
it('analyzes a meal image into a draft', function () {
Sanctum::actingAs(User::factory()->create());
$user = User::factory()->create();
Sanctum::actingAs($user);
MealImageAnalyzer::fake([
[
@@ -70,6 +76,69 @@ it('analyzes a meal image into a draft', function () {
fn ($prompt): bool => str_contains($prompt->prompt, "Analyse l'image")
&& $prompt->attachments->count() === 1
);
$usage = AiUsage::query()->sole();
expect($usage->user_id)->toBe($user->id)
->and($usage->type)->toBe(AiUsageType::MEAL_IMAGE_ANALYSIS)
->and($usage->status)->toBe(AiUsageStatus::SUCCESS)
->and($usage->input['image'])->toMatchArray([
'client_original_name' => 'meal.png',
'mime_type' => 'image/png',
])
->and($usage->output)->toMatchArray([
'title' => 'Bowl saumon avocat',
'type' => MealPostType::LUNCH->value,
'calories' => 612,
'proteins' => 39.4,
'ingredients' => [
[
'position' => 1,
'ingredient' => 'Riz',
'quantity' => 150,
'unit' => IngredientUnit::GRAM->value,
],
[
'position' => 2,
'ingredient' => 'Saumon',
'quantity' => 120,
'unit' => IngredientUnit::GRAM->value,
],
],
]);
});
it('records a failed meal image analysis attempt', function () {
$user = User::factory()->create();
Sanctum::actingAs($user);
Exceptions::fake();
MealImageAnalyzer::fake(
fn () => throw new \RuntimeException('Provider unavailable')
);
$this
->withHeader('Accept', 'application/json')
->post('/api/meal-posts/analyze-image', [
'image' => fakeMealAnalysisImage(),
])
->assertStatus(502)
->assertJsonPath('message', __('api.meal_image_analysis.failed'));
Exceptions::assertReported(\RuntimeException::class);
$usage = AiUsage::query()->sole();
expect($usage->user_id)->toBe($user->id)
->and($usage->type)->toBe(AiUsageType::MEAL_IMAGE_ANALYSIS)
->and($usage->status)->toBe(AiUsageStatus::FAILED)
->and($usage->input['image'])->toMatchArray([
'client_original_name' => 'meal.png',
'mime_type' => 'image/png',
])
->and($usage->output)->toBeNull()
->and($usage->error_message)->toContain('Provider unavailable');
});
it('requires authentication to analyze a meal image', function () {