Files
daily-meal-api/tests/Feature/CertificationTest.php
T
leonm 8b60283eeb
CI / 🧪 Tests Laravel (push) Successful in 2m34s
CI / 🐳 Build & Push Images (push) Successful in 2m26s
feat: translation codes
2026-08-14 12:46:29 +02:00

198 lines
7.5 KiB
PHP

<?php
use App\Actions\ReviewIdentityVerification;
use App\Enums\IdentityVerificationStatus;
use App\Enums\UserRole;
use App\Filament\Resources\IdentityVerificationRequests\Pages\ListIdentityVerificationRequests;
use App\Models\IdentityVerificationRequest;
use App\Models\User;
use App\Policies\IdentityVerificationRequestPolicy;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Laravel\Sanctum\Sanctum;
use Livewire\Livewire;
uses(RefreshDatabase::class);
it('requires the lifetime purchase before accepting an identity document', function () {
Storage::fake('identity_documents');
Sanctum::actingAs(User::factory()->create());
$this->post('/api/certification/identity-document', [
'document' => UploadedFile::fake()->image('identity.jpg'),
])->assertForbidden();
expect(IdentityVerificationRequest::query()->count())->toBe(0);
});
it('stores identity documents privately without exposing their path', function () {
Storage::fake('identity_documents');
$user = User::factory()->create([
'certification_purchased_at' => now(),
]);
Sanctum::actingAs($user);
$response = $this->post('/api/certification/identity-document', [
'document' => UploadedFile::fake()->image('my-passport.jpg'),
]);
$response
->assertOk()
->assertJsonPath('data.entitlementPurchased', true)
->assertJsonPath('data.canSubmitIdentity', false)
->assertJsonPath('data.verification.status', IdentityVerificationStatus::PENDING->value)
->assertJsonMissingPath('data.verification.documentPath');
$verificationRequest = IdentityVerificationRequest::query()->sole();
Storage::disk('identity_documents')->assertExists($verificationRequest->document_path);
expect($verificationRequest->document_path)->toStartWith('submissions/')
->not->toContain('my-passport')
->and(Storage::disk('identity_documents')->getVisibility($verificationRequest->document_path))
->toBe('private');
});
it('prevents replacing a document while it is under review', function () {
Storage::fake('identity_documents');
$user = User::factory()->create([
'certification_purchased_at' => now(),
]);
IdentityVerificationRequest::factory()->for($user)->create();
Sanctum::actingAs($user);
$this
->withHeader('Accept', 'application/json')
->post('/api/certification/identity-document', [
'document' => UploadedFile::fake()->image('identity.jpg'),
])
->assertConflict()
->assertJsonPath('code', 'CERTIFICATION_ALREADY_PENDING');
});
it('approves a certification and removes the reviewed identity document', function () {
Storage::fake('identity_documents');
$user = User::factory()->create([
'certification_purchased_at' => now(),
]);
$admin = User::factory()->create([
'role' => UserRole::ADMIN,
]);
$verificationRequest = IdentityVerificationRequest::factory()->for($user)->create();
Storage::disk('identity_documents')->put($verificationRequest->document_path, 'identity');
app(ReviewIdentityVerification::class)->execute(
$verificationRequest,
IdentityVerificationStatus::APPROVED,
$admin,
);
Storage::disk('identity_documents')->assertMissing($verificationRequest->document_path);
expect($user->fresh()->account_verified_at)->not->toBeNull()
->and($verificationRequest->fresh()->status)->toBe(IdentityVerificationStatus::APPROVED)
->and($verificationRequest->fresh()->document_path)->toBeNull()
->and($verificationRequest->fresh()->document_deleted_at)->not->toBeNull();
});
it('allows a new submission after rejection without another purchase', function () {
Storage::fake('identity_documents');
$user = User::factory()->create([
'certification_purchased_at' => now(),
]);
$admin = User::factory()->create([
'role' => UserRole::ADMIN,
]);
$verificationRequest = IdentityVerificationRequest::factory()->for($user)->create();
Storage::disk('identity_documents')->put($verificationRequest->document_path, 'identity');
app(ReviewIdentityVerification::class)->execute(
$verificationRequest,
IdentityVerificationStatus::REJECTED,
$admin,
'Le document est illisible.',
);
Sanctum::actingAs($user);
$this->post('/api/certification/identity-document', [
'document' => UploadedFile::fake()->image('identity-new.jpg'),
])
->assertOk()
->assertJsonPath('data.verification.status', IdentityVerificationStatus::PENDING->value);
expect($verificationRequest->fresh()->rejection_reason)->toBeNull()
->and($verificationRequest->fresh()->document_path)->not->toBeNull();
});
it('restricts identity review records to administrators', function () {
$policy = new IdentityVerificationRequestPolicy;
$request = IdentityVerificationRequest::factory()->make();
$admin = User::factory()->make(['role' => UserRole::ADMIN]);
$moderator = User::factory()->make(['role' => UserRole::MODERATOR]);
expect($policy->viewAny($admin))->toBeTrue()
->and($policy->view($admin, $request))->toBeTrue()
->and($policy->viewAny($moderator))->toBeFalse()
->and($policy->view($moderator, $request))->toBeFalse();
});
it('lets an administrator approve the identity request from filament', function () {
app()->setLocale('fr');
Filament::setCurrentPanel(Filament::getPanel('dashboard'));
Storage::fake('identity_documents');
$admin = User::factory()->create([
'role' => UserRole::ADMIN,
]);
$user = User::factory()->create([
'certification_purchased_at' => now(),
]);
$verificationRequest = IdentityVerificationRequest::factory()->for($user)->create();
Storage::disk('identity_documents')->put($verificationRequest->document_path, 'identity');
$this->actingAs($admin);
Livewire::test(ListIdentityVerificationRequests::class)
->assertCanSeeTableRecords([$verificationRequest])
->callTableAction('approve', $verificationRequest);
expect($verificationRequest->fresh()->status)->toBe(IdentityVerificationStatus::APPROVED)
->and($user->fresh()->account_verified_at)->not->toBeNull();
});
it('grants the lifetime certification entitlement from RevenueCat without a trial', function () {
config()->set('billing.revenuecat.secret_key', 'rc-secret');
config()->set('billing.revenuecat.certification_entitlement', 'certification');
$user = User::factory()->create();
$purchaseDate = now()->subDay()->startOfSecond();
Sanctum::actingAs($user);
Http::fake([
"api.revenuecat.com/v1/subscribers/{$user->id}" => Http::response([
'subscriber' => [
'entitlements' => [
'certification' => [
'expires_date' => null,
'grace_period_expires_date' => null,
'product_identifier' => 'lifetime',
'purchase_date' => $purchaseDate->toISOString(),
],
],
'subscriptions' => [],
],
]),
]);
$this->postJson('/api/billing/sync')
->assertOk()
->assertJsonPath('data.certificationPurchased', true)
->assertJsonPath('data.analysisAccessLevel', 'free');
expect($user->fresh()->certification_purchased_at?->equalTo($purchaseDate))->toBeTrue()
->and($user->fresh()->subscription_is_trial)->toBeFalse();
});