Files
portal-api/tests/Feature/Api/Admin/MailCampaignBulkActionTest.php
T
baodev0andClaude Opus 5.5 40bf6b54f9 feat(admin): star/tags marks, service product-type filter, campaign bulk actions, SMTP test
- Star (is_starred) and colored tags for services, customers, orders, invoices:
  Markable trait, tags/taggables tables, PATCH administrator/marks/{type}/{key},
  tag CRUD, list filters starred / tag_ids[]
- GET administrator/services accepts product_type (falls back to product type
  for legacy rows without services.type)
- Mail campaigns: bulk cancel / retry failed / delete; cancelled campaigns are
  skipped by SendMailCampaignRecipient
- POST administrator/settings/test-smtp sends a test email through the saved
  mail transport

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:53:41 +07:00

89 lines
3.2 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Feature\Api\Admin;
use App\Jobs\Mail\SendMailCampaignRecipient;
use App\Models\Mail;
use App\Models\MailCampaign;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Mail as MailFacade;
use Illuminate\Support\Facades\Queue;
use Spatie\Permission\Models\Role;
use Tests\TestCase;
class MailCampaignBulkActionTest extends TestCase
{
use RefreshDatabase;
private User $admin;
protected function setUp(): void
{
parent::setUp();
Role::firstOrCreate(['name' => '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' => '<p>x</p>', 'recipient_type' => 'selected',
'total_recipients' => 2, 'sent_count' => $sent, 'failed_count' => $failed, 'status' => $status,
]);
}
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());
}
}