feat(mail-campaigns): sender info + resolve typed emails against customers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
54c14c41d8
commit
e1efc88fd2
@@ -55,6 +55,21 @@ class LeadApiTest extends TestCase
|
||||
$this->assertSame(0, Lead::count());
|
||||
}
|
||||
|
||||
public function test_index_filters_by_source_and_recorded_date(): void
|
||||
{
|
||||
$admin = $this->admin(['lead.view']);
|
||||
$match = Lead::create(['email' => 'a@example.com', 'source' => 'whmcs_contact']);
|
||||
Lead::create(['email' => 'b@example.com', 'source' => 'manual']);
|
||||
$old = Lead::create(['email' => 'c@example.com', 'source' => 'whmcs_contact']);
|
||||
$old->forceFill(['created_at' => now()->subMonth()])->saveQuietly();
|
||||
|
||||
$this->actingAs($admin, 'admin')
|
||||
->getJson(self::BASE . '?' . http_build_query(['source' => 'whmcs_contact', 'date_from' => now()->subDay()->toDateString(), 'date_to' => now()->toDateString()]))
|
||||
->assertOk()
|
||||
->assertJsonPath('data.total', 1)
|
||||
->assertJsonPath('data.items.0.uuid', $match->uuid);
|
||||
}
|
||||
|
||||
public function test_permissions_are_enforced(): void
|
||||
{
|
||||
$viewer = $this->admin(['lead.view']);
|
||||
|
||||
@@ -7,7 +7,10 @@ namespace Tests\Feature\Api\Admin;
|
||||
use App\Jobs\Mail\SendMailCampaignRecipient;
|
||||
use App\Models\Mail;
|
||||
use App\Models\MailCampaign;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Support\MailSettings;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Mail as MailFacade;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
@@ -36,6 +39,41 @@ class MailCampaignBulkActionTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Api\Admin;
|
||||
|
||||
use App\Jobs\Mail\SendMailCampaignRecipient;
|
||||
use App\Models\Customer;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MailCampaignResolveEmailsTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private function admin(): User
|
||||
{
|
||||
$role = Role::create(['name' => 'role-' . uniqid(), 'guard_name' => 'admin']);
|
||||
$role->givePermissionTo(Permission::firstOrCreate(['name' => 'mail-campaign.create', 'guard_name' => 'admin']));
|
||||
$admin = User::factory()->create();
|
||||
$admin->assignRole($role);
|
||||
|
||||
return $admin;
|
||||
}
|
||||
|
||||
public function test_existing_customer_email_is_matched_case_insensitively(): void
|
||||
{
|
||||
$customer = Customer::factory()->create(['email' => 'Bao.Dev@Example.com']);
|
||||
|
||||
$this->actingAs($this->admin(), 'admin')
|
||||
->postJson('/api/v1/administrator/mail-campaigns/resolve-emails', [
|
||||
'emails' => [' bao.dev@example.COM ', 'new.person@example.com'],
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonCount(1, 'data.customers')
|
||||
->assertJsonPath('data.customers.0.uuid', $customer->uuid)
|
||||
->assertJsonPath('data.unregistered', ['new.person@example.com']);
|
||||
}
|
||||
|
||||
public function test_invalid_emails_are_rejected_with_clear_message(): void
|
||||
{
|
||||
$this->actingAs($this->admin(), 'admin')
|
||||
->postJson('/api/v1/administrator/mail-campaigns/resolve-emails', [
|
||||
'emails' => ['not-an-email'],
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors(['emails.0' => 'Email "not-an-email" không hợp lệ.']);
|
||||
|
||||
$this->actingAs($this->admin(), 'admin')
|
||||
->postJson('/api/v1/administrator/mail-campaigns/resolve-emails', ['emails' => []])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors(['emails' => 'Nhập ít nhất một email.']);
|
||||
}
|
||||
|
||||
public function test_store_attaches_customer_typed_as_email_with_different_case(): void
|
||||
{
|
||||
Queue::fake();
|
||||
$customer = Customer::factory()->create(['email' => 'Bao.Dev@Example.com', 'email_marketing' => 1]);
|
||||
|
||||
$this->actingAs($this->admin(), 'admin')
|
||||
->postJson('/api/v1/administrator/mail-campaigns', [
|
||||
'subject' => 'Tin tức',
|
||||
'message' => '<p>Hi</p>',
|
||||
'recipient_type' => 'selected',
|
||||
'uuids' => [$customer->uuid],
|
||||
'emails' => ['bao.dev@example.com'],
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonPath('data.total_recipients', 1);
|
||||
|
||||
Queue::assertPushed(
|
||||
SendMailCampaignRecipient::class,
|
||||
fn (SendMailCampaignRecipient $job) => $this->jobCustomerId($job) === $customer->id
|
||||
);
|
||||
}
|
||||
|
||||
private function jobCustomerId(SendMailCampaignRecipient $job): ?int
|
||||
{
|
||||
return (fn () => $this->customerId)->call($job);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user