AuthControllerTest — Resultados
312ms
✎ test_users_can_register
2 asserts
18ms
✎ test_users_can_login
2 asserts
24ms
✎ test_users_cannot_login_with_wrong_password
1 assert
9ms
✎ test_token_bearer_authenticates_requests
2 asserts
15ms
✎ test_unauthenticated_requests_are_rejected
1 assert
6ms
✎ test_duplicate_email_registration_fails
2 asserts
12ms
✎ test_logout_revokes_token
2 asserts
11ms
✎ test_password_reset_email_is_sent
1 assert
8ms
📄
phpunit.xml
Configuración
1<?xml version="1.0" encoding="UTF-8"?> 2<phpunit bootstrap="vendor/autoload.php" 3 colors="true" 4 stopOnFailure="false"> 5 <testsuites> 6 <testsuite name="Unit"> 7 <directory suffix="Test.php">tests/Unit</directory> 8 </testsuite> 9 <testsuite name="Feature"> 10 <directory suffix="Test.php">tests/Feature</directory> 11 </testsuite> 12 </testsuites> 13 <php> 14 <env name="APP_ENV" value="testing"/> 15 <env name="BCRYPT_ROUNDS" value="4"/> <!-- Faster hashing in tests --> 16 <env name="CACHE_STORE" value="array"/> 17 <env name="DB_CONNECTION" value="sqlite"/> 18 <env name="DB_DATABASE" value=":memory:"/> <!-- In-memory SQLite --> 19 <env name="MAIL_MAILER" value="array"/> 20 <env name="QUEUE_CONNECTION" value="sync"/> 21 </php> 22</phpunit>
🧪
tests/Feature/Http/Controllers/Api/AuthControllerTest.php
Feature
1namespace Tests\Feature\Http\Controllers\Api; 3use App\Models\User; 4use Illuminate\Foundation\Testing\RefreshDatabase; 5use Illuminate\Support\Facades\Mail; 6use App\Mail\PasswordResetMail; 7use Tests\TestCase; 9class AuthControllerTest extends TestCase 10{ 11 use RefreshDatabase; 13 // ─── RED → GREEN: Registro de nueva agencia ─────────────────────── 14 public function test_users_can_register(): void 15 { 16 $response = $this->postJson('/api/register', [ 17 'name' => 'Agencia Moderna SL', 18 'email' => 'hola@agenciamoderna.es', 19 'password' => 'Secreto123!', 20 'password_confirmation' => 'Secreto123!', 21 ]); 23 $response->assertCreated(); 24 $response->assertJsonStructure(['data' => ['user', 'token']]); 25 } 27 public function test_users_can_login(): void 28 { 29 User::factory()->create([ 30 'email' => 'ceo@cultivamarket.io', 31 'password' => Hash::make('Cultiva2024!'), 32 ]); 34 $response = $this->postJson('/api/login', [ 35 'email' => 'ceo@cultivamarket.io', 36 'password' => 'Cultiva2024!', 37 ]); 39 $response->assertOk(); 40 $response->assertJsonStructure(['data' => ['token']]); 41 } 43 // ─── Mail Fake: reset de contraseña ─────────────────────────────── 44 public function test_password_reset_email_is_sent(): void 45 { 46 Mail::fake(); 47 $user = User::factory()->create(); 49 $this->postJson('/api/forgot-password', ['email' => $user->email]) 50 ->assertOk(); 52 Mail::assertSent(PasswordResetMail::class, fn($m) => $m->hasTo($user->email)); 53 } 54}
🧪
tests/Feature/Http/Controllers/Api/CampaignControllerTest.php
Feature
1namespace Tests\Feature\Http\Controllers\Api; 3use App\Models\{Agency, Campaign, User}; 4use Illuminate\Foundation\Testing\RefreshDatabase; 5use Tests\TestCase; 7class CampaignControllerTest extends TestCase 8{ 9 use RefreshDatabase; 11 public function test_agency_can_create_campaign(): void 12 { 13 $user = $this->actingAsUser(); 15 $response = $this->postJson('/api/campaigns', [ 16 'name' => 'Campaña Q3 2024 — Cultiva IA', 17 'budget' => 5000_00, // céntimos 18 'channel' => 'meta_ads', 19 'starts_at' => '2024-07-01', 20 'ends_at' => '2024-09-30', 21 ]); 23 $response->assertCreated(); 24 $response->assertJsonPath('data.name', 'Campaña Q3 2024 — Cultiva IA'); 25 $this->assertDatabaseHas('campaigns', ['user_id' => $user->id]); 26 } 28 public function test_agencies_cannot_read_others_campaigns(): void 29 { 30 $owner = User::factory()->create(); 31 $attacker = User::factory()->create(); 32 $campaign = Campaign::factory()->create(['user_id' => $owner->id]); 34 $this->actingAs($attacker) 35 ->getJson("/api/campaigns/{$campaign->id}") 36 ->assertForbidden(); 37 } 39 public function test_it_paginates_campaigns(): void 40 { 41 $user = $this->actingAsUser(); 42 Campaign::factory()->count(20)->create(['user_id' => $user->id]); 44 $this->getJson('/api/campaigns?page=1') 45 ->assertOk() 46 ->assertJsonCount(15, 'data') // 15 por página 47 ->assertJsonStructure([ 48 'data' => [['id','name','budget','channel','status']], 49 'meta' => ['current_page','last_page','total'], 50 ]); 51 } 52}
🧪
tests/Unit/Services/PaymentServiceTest.php
Unit
1namespace Tests\Unit\Services; 3use App\Services\PaymentService; 4use App\Exceptions\PaymentFailedException; 5use Illuminate\Support\Facades\Http; 6use Tests\TestCase; 8class PaymentServiceTest extends TestCase 9{ 10 // ─── RED: test escrito antes del servicio ───────────────────────── 12 public function test_it_handles_successful_stripe_charge(): void 13 { 14 Http::fake([ 15 'api.stripe.com/*' => Http::response([ 16 'id' => 'pi_3Q2AABCDE12345', 17 'status' => 'succeeded', 18 'amount' => 2999, 19 ], 200), 20 ]); 22 $result = (new PaymentService())->charge(2999); 24 $this->assertTrue($result->success); 25 $this->assertEquals('pi_3Q2AABCDE12345', $result->paymentIntentId); 26 } 28 public function test_it_throws_on_card_declined(): void 29 { 30 Http::fake([ 31 'api.stripe.com/*' => Http::response([ 32 'error' => ['code' => 'card_declined'] 33 ], 402), 34 ]); 36 $this->expectException(PaymentFailedException::class); 37 (new PaymentService())->charge(2999); 38 } 40 public function test_it_retries_on_timeout(): void 41 { 42 Http::fake([ 43 'api.stripe.com/*' => Http::sequence() 44 ->pushStatus(408) // primer intento: timeout 45 ->push(['id'=>'pi_retry_ok','status'=>'succeeded'], 200), 46 ]); 48 $result = (new PaymentService())->charge(2999); 49 $this->assertTrue($result->success); 50 } 51}
🧪
tests/Feature/campaigns.pest.php
Pest
1use App\Models\{Campaign, User}; 2use Illuminate\Support\Facades\Queue; 3use App\Jobs\GenerateCampaignReport; 5uses(Illuminate\Foundation\Testing\RefreshDatabase::class); 7beforeEach(function () { 8 $this->user = User::factory()->create(); 9 $this->actingAs($this->user); 10}); 12it('lists only own campaigns', function () { 13 Campaign::factory()->count(3)->create(['user_id' => $this->user->id]); 14 Campaign::factory()->count(5)->create(); // otras agencias 16 $this->getJson('/api/campaigns') 17 ->assertOk() 18 ->assertJsonCount(3, 'data'); // solo las 3 propias 19}); 21it('dispatches report job when campaign ends', function () { 22 Queue::fake(); 23 $campaign = Campaign::factory()->create(['user_id' => $this->user->id]); 25 $this->patchJson("/api/campaigns/{$campaign->id}/finish") 26 ->assertOk(); 28 Queue::assertPushed(GenerateCampaignReport::class, 29 fn($job) => $job->campaign->id === $campaign->id 30 ); 31}); 33it('validates budget is positive', function () { 34 $this->postJson('/api/campaigns', ['budget' => -100]) 35 ->assertUnprocessable() 36 ->assertJsonValidationErrors(['name', 'budget']); 37});
$ vendor/bin/pest --coverage --min=85
PASS Tests\Unit\Models\CampaignModelTest
✓ active scope filters correctly 4ms
✓ it belongs to an agency 3ms
✓ budget accessor returns formatted eur 2ms
PASS Tests\Unit\Services\PaymentServiceTest
✓ it handles successful stripe charge 6ms
✓ it throws on card declined 4ms
✓ it retries on timeout 5ms
PASS Tests\Feature\Http\Controllers\Api\AuthControllerTest
✓ users can register 18ms
✓ users can login 24ms
✓ users cannot login with wrong password 9ms
✓ token bearer authenticates requests 15ms
✓ logout revokes token 11ms
✓ password reset email is sent 8ms
PASS Tests\Feature\Http\Controllers\Api\CampaignControllerTest
✓ agency can create campaign 22ms
✓ agencies cannot read others campaigns 14ms
✓ it paginates campaigns 31ms
PASS Tests\Feature (Pest)
✓ lists only own campaigns 19ms
✓ dispatches report job when campaign ends 17ms
✓ validates budget is positive 8ms
⚠ generate report pdf skipped (xdebug missing in CI) —
────────────────────────────────────────────────────────
Tests: 42 passed, 1 skipped
Duration: 1.24s
Coverage: 87.3% (min: 85%) ✓