Files
portal-api/tests/Unit/EmailTemplateRendererTest.php
T
2026-10-05 09:30:24 +07:00

139 lines
4.9 KiB
PHP

<?php
namespace Tests\Unit;
use App\Exceptions\InvalidTemplateVariableException;
use App\Models\Template;
use App\Services\Email\EmailTemplateRenderer;
use Tests\TestCase;
class EmailTemplateRendererTest extends TestCase
{
private function makeTemplate(string $subject, string $message, array $variables): Template
{
$template = new Template([
'scenario' => 'unit_test_scenario',
'subject' => $subject,
'message' => $message,
'metadata' => ['variables' => $variables],
]);
return $template;
}
public function test_it_replaces_known_placeholders_in_subject_and_html(): void
{
$template = $this->makeTemplate(
'Xin chào {{full_name}}',
'<p>Chào mừng {{full_name}} đến với {{app_name}}</p>',
[
['key' => 'full_name', 'label' => 'Họ tên', 'type' => 'string'],
['key' => 'app_name', 'label' => 'Tên app', 'type' => 'string'],
],
);
$result = (new EmailTemplateRenderer())->render($template, [
'full_name' => 'Nguyễn Văn A',
'app_name' => 'Thế Giới Số',
]);
$this->assertSame('Xin chào Nguyễn Văn A', $result['subject']);
$this->assertSame('<p>Chào mừng Nguyễn Văn A đến với Thế Giới Số</p>', $result['html']);
}
public function test_it_falls_back_to_example_value_when_data_missing(): void
{
$template = $this->makeTemplate(
'Mã: {{code}}',
'<p>{{code}}</p>',
[['key' => 'code', 'label' => 'Mã', 'type' => 'string', 'example' => '000000']],
);
$result = (new EmailTemplateRenderer())->render($template, []);
$this->assertSame('Mã: 000000', $result['subject']);
$this->assertSame('<p>000000</p>', $result['html']);
}
public function test_it_html_escapes_string_type_by_default(): void
{
$template = $this->makeTemplate(
'subj',
'<p>{{name}}</p>',
[['key' => 'name', 'label' => 'Tên', 'type' => 'string']],
);
$result = (new EmailTemplateRenderer())->render($template, ['name' => '<script>alert(1)</script>']);
$this->assertStringNotContainsString('<script>', $result['html']);
$this->assertStringContainsString('&lt;script&gt;', $result['html']);
}
public function test_it_rejects_invalid_url_type(): void
{
$template = $this->makeTemplate(
'subj',
'<a href="{{link}}">go</a>',
[['key' => 'link', 'label' => 'Link', 'type' => 'url']],
);
$result = (new EmailTemplateRenderer())->render($template, ['link' => 'javascript:alert(1)']);
$this->assertStringNotContainsString('javascript:', $result['html']);
}
public function test_it_throws_for_undeclared_variable(): void
{
$template = $this->makeTemplate(
'subj',
'<p>{{full_name}} {{secret_admin_flag}}</p>',
[['key' => 'full_name', 'label' => 'Họ tên', 'type' => 'string']],
);
$this->expectException(InvalidTemplateVariableException::class);
$this->expectExceptionMessage('secret_admin_flag');
(new EmailTemplateRenderer())->render($template, ['full_name' => 'A']);
}
public function test_it_never_executes_placeholder_content_as_code(): void
{
$template = $this->makeTemplate(
'subj',
'<p>{{note}}</p>',
[['key' => 'note', 'label' => 'Note', 'type' => 'string']],
);
$result = (new EmailTemplateRenderer())->render($template, [
'note' => '{{ 7*7 }} @php(system("id")) <?php echo 1; ?>',
]);
$this->assertStringNotContainsString('49', $result['html']);
$this->assertStringContainsString('&lt;?php', $result['html']);
}
public function test_dollar_placeholders_are_declared_on_sync_and_rendered_with_default(): void
{
$template = $this->makeTemplate(
'Ưu đãi {{$promo_code}}',
'<p>{{ $promo_code }} cho {{full_name}}</p>{{$content}}',
[
['key' => 'full_name', 'label' => 'Họ tên', 'type' => 'string'],
['key' => 'old_custom', 'label' => 'Cũ', 'type' => 'string', 'custom' => true],
],
);
$template->syncVariables([['key' => 'promo_code', 'label' => 'Mã KM', 'example' => 'TGS10']]);
$variables = $template->variables();
$this->assertSame(['full_name', 'promo_code'], array_keys($variables));
$this->assertSame('Mã KM', $variables['promo_code']['label']);
$this->assertTrue($variables['promo_code']['custom']);
$result = (new EmailTemplateRenderer())->render($template, ['full_name' => 'An']);
$this->assertSame('Ưu đãi TGS10', $result['subject']);
$this->assertSame('<p>TGS10 cho An</p>{{$content}}', $result['html']);
}
}