feat: stripe
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\CreditProductType;
|
||||
use App\Models\CreditProduct;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('lists active credit products ordered for the mobile app', function () {
|
||||
Sanctum::actingAs(User::factory()->create());
|
||||
|
||||
CreditProduct::create([
|
||||
'name' => 'Pack 10',
|
||||
'type' => CreditProductType::ONE_TIME,
|
||||
'credits' => 10,
|
||||
'amount' => 499,
|
||||
'currency' => 'eur',
|
||||
'stripe_price_id' => 'price_pack_10',
|
||||
'sort_order' => 20,
|
||||
]);
|
||||
CreditProduct::create([
|
||||
'name' => 'Mensuel 30',
|
||||
'type' => CreditProductType::MONTHLY,
|
||||
'credits' => 30,
|
||||
'amount' => 999,
|
||||
'currency' => 'eur',
|
||||
'stripe_price_id' => 'price_monthly_30',
|
||||
'sort_order' => 10,
|
||||
]);
|
||||
CreditProduct::create([
|
||||
'name' => 'Inactive',
|
||||
'type' => CreditProductType::ONE_TIME,
|
||||
'credits' => 5,
|
||||
'amount' => 299,
|
||||
'currency' => 'eur',
|
||||
'stripe_price_id' => 'price_inactive',
|
||||
'is_active' => false,
|
||||
]);
|
||||
|
||||
$this
|
||||
->getJson('/api/billing/products')
|
||||
->assertOk()
|
||||
->assertJsonCount(2, 'data')
|
||||
->assertJsonPath('data.0.name', 'Mensuel 30')
|
||||
->assertJsonPath('data.0.type', CreditProductType::MONTHLY->value)
|
||||
->assertJsonPath('data.0.credits', 30)
|
||||
->assertJsonPath('data.0.formattedAmount', '9,99 EUR')
|
||||
->assertJsonPath('data.1.name', 'Pack 10');
|
||||
});
|
||||
@@ -3,9 +3,11 @@
|
||||
use App\Ai\Agents\MealImageAnalyzer;
|
||||
use App\Enums\AiUsageStatus;
|
||||
use App\Enums\AiUsageType;
|
||||
use App\Enums\CreditLedgerEntryType;
|
||||
use App\Enums\IngredientUnit;
|
||||
use App\Enums\MealPostType;
|
||||
use App\Models\AiUsage;
|
||||
use App\Models\CreditLedgerEntry;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
@@ -64,6 +66,7 @@ it('analyzes a meal image into a draft', function () {
|
||||
|
||||
$response
|
||||
->assertOk()
|
||||
->assertJsonPath('credits.balance', 2)
|
||||
->assertJsonPath('data.title', 'Bowl saumon avocat')
|
||||
->assertJsonPath('data.type', MealPostType::LUNCH->value)
|
||||
->assertJsonPath('data.calories', 612)
|
||||
@@ -106,6 +109,15 @@ it('analyzes a meal image into a draft', function () {
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$user->refresh();
|
||||
$ledgerEntry = CreditLedgerEntry::query()->sole();
|
||||
|
||||
expect($user->ai_credits_balance)->toBe(2)
|
||||
->and($ledgerEntry->user_id)->toBe($user->id)
|
||||
->and($ledgerEntry->type)->toBe(CreditLedgerEntryType::USAGE)
|
||||
->and($ledgerEntry->credits)->toBe(-1)
|
||||
->and($ledgerEntry->balance_after)->toBe(2);
|
||||
});
|
||||
|
||||
it('records a failed meal image analysis attempt', function () {
|
||||
@@ -139,6 +151,34 @@ it('records a failed meal image analysis attempt', function () {
|
||||
])
|
||||
->and($usage->output)->toBeNull()
|
||||
->and($usage->error_message)->toContain('Provider unavailable');
|
||||
|
||||
expect($user->fresh()->ai_credits_balance)->toBe(3);
|
||||
});
|
||||
|
||||
it('requires available credits to analyze a meal image', function () {
|
||||
$user = User::factory()->create([
|
||||
'ai_credits_balance' => 0,
|
||||
]);
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
MealImageAnalyzer::fake([
|
||||
[
|
||||
'title' => 'Bowl',
|
||||
],
|
||||
]);
|
||||
|
||||
$this
|
||||
->withHeader('Accept', 'application/json')
|
||||
->post('/api/meal-posts/analyze-image', [
|
||||
'image' => fakeMealAnalysisImage(),
|
||||
])
|
||||
->assertStatus(402)
|
||||
->assertJsonPath('message', __('api.meal_image_analysis.insufficient_credits'))
|
||||
->assertJsonPath('credits.balance', 0);
|
||||
|
||||
expect(AiUsage::query()->count())->toBe(0)
|
||||
->and(CreditLedgerEntry::query()->count())->toBe(0);
|
||||
});
|
||||
|
||||
it('requires authentication to analyze a meal image', function () {
|
||||
|
||||
Reference in New Issue
Block a user