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
+80
View File
@@ -1,11 +1,15 @@
<?php
use App\Ai\Agents\MealMaker;
use App\Enums\AiUsageStatus;
use App\Enums\AiUsageType;
use App\Enums\IngredientUnit;
use App\Enums\MealPostType;
use App\Enums\MealPostVisibility;
use App\Models\AiUsage;
use App\Models\MealPosts;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Exceptions;
use Illuminate\Support\Facades\Storage;
use Laravel\Ai\Image;
@@ -119,4 +123,80 @@ it('generates an ai meal post with an image', function () {
&& $prompt->isLandscape()
&& $prompt->quality === 'medium'
);
$usage = AiUsage::query()->sole();
expect($usage->user_id)->toBe($mealPost->user_id)
->and($usage->type)->toBe(AiUsageType::MEAL_POST_GENERATION)
->and($usage->status)->toBe(AiUsageStatus::SUCCESS)
->and($usage->related_type)->toBe(MealPosts::class)
->and($usage->related_id)->toBe($mealPost->id)
->and($usage->input['image'])->toMatchArray([
'size' => '3:2',
'quality' => 'medium',
'storage_path' => 'meal-posts/ai-generated',
])
->and($usage->output)->toMatchArray([
'title' => 'Bowl de saumon croustillant',
'type' => MealPostType::LUNCH->value,
'calories' => 640,
'image_url' => $mealPost->image_url,
'ingredients' => [
[
'position' => 1,
'ingredient' => 'Riz vinaigre',
'quantity' => 160,
'unit' => IngredientUnit::GRAM->value,
],
[
'position' => 2,
'ingredient' => 'Saumon',
'quantity' => 130,
'unit' => IngredientUnit::GRAM->value,
],
],
]);
});
it('records a failed ai meal post generation attempt', function () {
Storage::fake('public');
Exceptions::fake();
config()->set('meal_posts.ai_generation.user_name', 'Chef IA');
config()->set('meal_posts.ai_generation.user_email', 'chef-ia@example.test');
config()->set('meal_posts.ai_generation.image_quality', 'medium');
MealMaker::fake([
[
'title' => 'Bowl impossible',
'caption' => 'Un bowl qui ne sera pas publie.',
'type' => MealPostType::LUNCH->value,
'calories' => 640,
'proteins' => 42.4,
'carbs' => 62.2,
'fats' => 24.8,
'ingredients' => [],
'image_prompt' => 'Photorealistic impossible bowl',
],
]);
Image::fake(
fn () => throw new \RuntimeException('Image provider unavailable')
);
$this->artisan('meal-posts:generate-ai')
->assertExitCode(1);
Exceptions::assertReported(\RuntimeException::class);
expect(MealPosts::query()->where('ai_generated', true)->count())->toBe(0);
$usage = AiUsage::query()->sole();
expect($usage->user->email)->toBe('chef-ia@example.test')
->and($usage->type)->toBe(AiUsageType::MEAL_POST_GENERATION)
->and($usage->status)->toBe(AiUsageStatus::FAILED)
->and($usage->input['image_prompt'])->toContain('impossible bowl')
->and($usage->output)->toBeNull()
->and($usage->error_message)->toContain('Image provider unavailable');
});