0, 'scenario' => 'unit_test_scenario_' . uniqid(), 'format' => 'html', 'name' => 'Test scenario', 'subject' => 'Xin chào {{full_name}}', 'message' => '
Chào {{full_name}}, mời truy cập {{login_url}}
', 'metadata' => [ 'variables' => [ ['key' => 'full_name', 'label' => 'Họ tên', 'type' => 'string', 'example' => 'Quý khách'], ['key' => 'login_url', 'label' => 'Link đăng nhập', 'type' => 'url', 'example' => 'https://example.com'], ], ], 'status' => 1, ], $overrides)); } public function test_send_dispatches_job_to_queue(): void { Bus::fake(); $template = $this->makeTemplate(); app(EmailTemplateService::class)->send($template->scenario, 'user@example.com', [ 'full_name' => 'Nguyễn Văn A', 'login_url' => 'https://portal.tgs.com.vn/login', ]); Bus::assertDispatched(SendTemplateEmailJob::class, fn (SendTemplateEmailJob $job) => $job->scenario === $template->scenario && $job->to === ['user@example.com'] && $job->data['full_name'] === 'Nguyễn Văn A'); } public function test_job_renders_and_sends_mail_with_template_sender_config(): void { Mail::fake(); $template = $this->makeTemplate([ 'metadata' => [ 'variables' => [ ['key' => 'full_name', 'label' => 'Họ tên', 'type' => 'string'], ['key' => 'login_url', 'label' => 'Link', 'type' => 'url'], ], 'mail' => [ 'from_email' => 'noreply@tgs.com.vn', 'from_name' => 'Thế Giới Số', 'reply_to' => 'support@tgs.com.vn', 'reply_to_name' => 'TGS Support', ], ], ]); (new SendTemplateEmailJob($template->scenario, ['user@example.com'], [ 'full_name' => 'Nguyễn Văn A', 'login_url' => 'https://portal.tgs.com.vn/login', ]))->handle(app(EmailTemplateService::class), app(\App\Services\Email\EmailTemplateRenderer::class)); Mail::assertSent(GenericTemplateMail::class, function (GenericTemplateMail $mail) { $mail->assertTo('user@example.com'); $envelope = $mail->envelope(); return $envelope->subject === 'Xin chào Nguyễn Văn A' && $envelope->from->address === 'noreply@tgs.com.vn' && $envelope->replyTo[0]->address === 'support@tgs.com.vn' && str_contains($mail->renderedHtml, 'Nguyễn Văn A'); }); } public function test_job_falls_back_to_system_default_sender_when_template_has_no_mail_config(): void { Mail::fake(); $template = $this->makeTemplate(); (new SendTemplateEmailJob($template->scenario, ['user@example.com'], [ 'full_name' => 'A', 'login_url' => 'https://example.com', ]))->handle(app(EmailTemplateService::class), app(\App\Services\Email\EmailTemplateRenderer::class)); Mail::assertSent(GenericTemplateMail::class, function (GenericTemplateMail $mail) { return $mail->envelope()->from === null; }); } public function test_preview_throws_for_undeclared_variable(): void { $template = $this->makeTemplate([ 'message' => '{{full_name}} {{not_declared}}
', ]); $this->expectException(InvalidTemplateVariableException::class); app(EmailTemplateService::class)->preview($template->scenario, ['full_name' => 'A']); } public function test_load_template_is_cached_by_scenario(): void { $template = $this->makeTemplate(); $service = app(EmailTemplateService::class); $first = $service->loadTemplate($template->scenario); $this->assertSame('Xin chào {{full_name}}', $first->subject); $template->update(['subject' => 'Đã đổi']); // model event must bust the cache $second = $service->loadTemplate($template->scenario); $this->assertSame('Đã đổi', $second->subject); } }