'Full Administrator', 'guard_name' => 'admin']); $this->admin = User::factory()->create(); $this->admin->assignRole('Full Administrator'); } private function campaign(string $status, int $sent = 0, int $failed = 0): MailCampaign { return MailCampaign::create([ 'name' => 'C', 'subject' => 'S', 'message' => '
x
', 'recipient_type' => 'selected', 'total_recipients' => 2, 'sent_count' => $sent, 'failed_count' => $failed, 'status' => $status, ]); } public function test_index_filters_by_search_status_and_date(): void { $match = $this->campaign('completed'); $match->update(['name' => 'Khuyến mãi Tết']); $this->campaign('failed')->update(['name' => 'Khuyến mãi hè']); $this->campaign('completed')->update(['name' => 'Bảo trì']); $old = $this->campaign('completed'); $old->update(['name' => 'Khuyến mãi cũ']); $old->forceFill(['created_at' => now()->subMonth()])->saveQuietly(); $this->actingAs($this->admin, 'admin') ->getJson('/api/v1/administrator/mail-campaigns?' . http_build_query([ 'search' => 'khuyến mãi', 'status' => ['completed'], 'date_from' => now()->subDay()->toDateString(), ])) ->assertOk() ->assertJsonPath('data.total', 1) ->assertJsonPath('data.items.0.uuid', $match->uuid); } public function test_sender_reports_smtp_from_address(): void { Setting::updateOrCreate(['key' => 'smtp'], ['value' => json_encode([ 'host' => 'smtp.example.com', 'user' => 'u@example.com', 'from_address' => 'noreply@tgs.test', 'from_name' => 'TGS', ])]); Cache::forget(MailSettings::cacheKey()); MailSettings::apply(); $this->actingAs($this->admin, 'admin') ->getJson('/api/v1/administrator/mail-campaigns/sender') ->assertOk() ->assertJsonPath('data.from_address', 'noreply@tgs.test') ->assertJsonPath('data.from_name', 'TGS') ->assertJsonPath('data.smtp_configured', true); } public function test_cancel_stops_pending_jobs_and_keeps_finished_campaigns(): void { MailFacade::fake(); $sending = $this->campaign('sending'); $done = $this->campaign('completed', 2); $this->actingAs($this->admin, 'admin') ->postJson('/api/v1/administrator/mail-campaigns/bulk-action', ['action' => 'cancel', 'uuids' => [$sending->uuid, $done->uuid]]) ->assertOk(); $this->assertSame('cancelled', $sending->fresh()->status); $this->assertSame('completed', $done->fresh()->status); (new SendMailCampaignRecipient($sending->id, null, 'a@example.com'))->handle(app(\App\Services\Email\MailCampaignRenderer::class)); MailFacade::assertNothingSent(); $this->assertSame(0, $sending->fresh()->sent_count); } public function test_retry_requeues_only_failed_recipients(): void { Queue::fake(); $campaign = $this->campaign('completed', 1, 1); foreach (['ok@example.com' => 'sent', 'bad@example.com' => 'failed'] as $to => $status) { Mail::create(['campaign_id' => $campaign->id, 'template_id' => 0, 'subject' => 'S', 'to' => $to, 'message' => 'x', 'status' => $status]); } $this->actingAs($this->admin, 'admin') ->postJson('/api/v1/administrator/mail-campaigns/bulk-action', ['action' => 'retry-failed', 'uuids' => [$campaign->uuid]]) ->assertOk(); $campaign->refresh(); $this->assertSame('sending', $campaign->status); $this->assertSame(0, $campaign->failed_count); $this->assertDatabaseMissing('mail', ['to' => 'bad@example.com']); Queue::assertPushed(SendMailCampaignRecipient::class, 1); } public function test_delete_skips_campaigns_still_sending(): void { $sending = $this->campaign('queued'); $done = $this->campaign('failed', 0, 2); $this->actingAs($this->admin, 'admin') ->postJson('/api/v1/administrator/mail-campaigns/bulk-delete', ['uuids' => [$sending->uuid, $done->uuid]]) ->assertOk(); $this->assertNotNull($sending->fresh()); $this->assertNull($done->fresh()); } }