Files
portal-api/tests/Unit/EmailTemplateRendererTest.php
T

166 lines
6.1 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']);
}
public function test_renewal_variables_are_escaped_exactly_once(): void
{
$template = $this->makeTemplate(
'Gia hạn {{product_name}}',
'<p>{{client_name}} - {{admin_name}}{{server_name_suffix}}</p>',
[
['key' => 'product_name', 'label' => 'Dịch vụ', 'type' => 'string'],
['key' => 'client_name', 'label' => 'Khách', 'type' => 'string'],
['key' => 'admin_name', 'label' => 'Nhân viên', 'type' => 'string'],
['key' => 'server_name_suffix', 'label' => 'Máy chủ', 'type' => 'html'],
],
);
$result = (new EmailTemplateRenderer())->render($template, [
'product_name' => '<script>x</script>',
'client_name' => "A & B's <b>",
'admin_name' => '<img src=x onerror=1>',
'server_name_suffix' => ' (' . e('<i>vps</i>') . ')',
]);
$this->assertSame('Gia hạn &lt;script&gt;x&lt;/script&gt;', $result['subject']);
$this->assertSame(
'<p>A &amp; B&#039;s &lt;b&gt; - &lt;img src=x onerror=1&gt; (&lt;i&gt;vps&lt;/i&gt;)</p>',
$result['html'],
);
}
}