PromptVault/tests/Unit/PromptRendererTest.php
2026-10-26 15:12:16 -05:00

138 lines
4.5 KiB
PHP

<?php
namespace Tests\Unit;
use App\Exceptions\PromptRenderException;
use App\Services\PromptRenderer;
use PHPUnit\Framework\TestCase;
class PromptRendererTest extends TestCase
{
private PromptRenderer $renderer;
protected function setUp(): void
{
parent::setUp();
$this->renderer = new PromptRenderer();
}
public function test_extract_variables_returns_empty_list_without_variables(): void
{
$this->assertSame([], $this->renderer->extractVariables(''));
$this->assertSame([], $this->renderer->extractVariables('Plain text, no placeholders at all.'));
}
public function test_extract_variables_returns_unique_names_in_first_occurrence_order(): void
{
$body = 'Hello {{name}}, here is {{content}}. Again: {{name}} and {{extra}}.';
$this->assertSame(['name', 'content', 'extra'], $this->renderer->extractVariables($body));
}
public function test_extract_variables_tolerates_inner_whitespace(): void
{
$this->assertSame(
['content'],
$this->renderer->extractVariables('{{content}} {{ content }}')
);
$this->assertSame(
['a_b', 'A-B'],
$this->renderer->extractVariables('{{a_b}} and {{ A-B }}')
);
// `$` is not in the variable charset, so this stays literal.
$this->assertSame([], $this->renderer->extractVariables('{{$a_b}}'));
}
public function test_invalid_tokens_are_not_treated_as_variables(): void
{
$body = '{{}} and {{a b}} and {{a!b}} and {{ }} stay literal';
$this->assertSame([], $this->renderer->extractVariables($body));
}
public function test_render_replaces_every_occurrence(): void
{
$rendered = $this->renderer->render(
'Hi {{name}}! Send {{name}} the file.',
['name' => 'Ada']
);
$this->assertSame('Hi Ada! Send Ada the file.', $rendered);
}
public function test_render_returns_body_unchanged_without_variables(): void
{
$body = 'Nothing to do here.';
$this->assertSame($body, $this->renderer->render($body, ['ignored' => 'x']));
}
public function test_render_is_strict_about_missing_variables(): void
{
try {
$this->renderer->render('A: {{alpha}} B: {{beta}} C: {{gamma}}', ['beta' => 'b']);
$this->fail('Expected PromptRenderException was not thrown.');
} catch (PromptRenderException $exception) {
$this->assertSame(['alpha', 'gamma'], $exception->missingVariables);
$this->assertSame('Missing variables: alpha, gamma', $exception->getMessage());
}
}
public function test_render_treats_non_string_values_as_missing(): void
{
try {
$this->renderer->render('Count: {{count}}', ['count' => 42]);
$this->fail('Expected PromptRenderException was not thrown.');
} catch (PromptRenderException $exception) {
$this->assertSame(['count'], $exception->missingVariables);
}
try {
$this->renderer->render('Count: {{count}}', ['count' => null]);
$this->fail('Expected PromptRenderException was not thrown.');
} catch (PromptRenderException $exception) {
$this->assertSame(['count'], $exception->missingVariables);
}
}
public function test_render_accepts_empty_string_values(): void
{
$this->assertSame(
'Title: ',
$this->renderer->render('Title: {{title}}', ['title' => ''])
);
}
public function test_render_ignores_extra_values(): void
{
$rendered = $this->renderer->render(
'Only {{used}}',
['used' => 'yes', 'extra' => 'no']
);
$this->assertSame('Only yes', $rendered);
}
public function test_inserted_values_are_not_re_scanned_for_variables(): void
{
$rendered = $this->renderer->render(
'Hello {{name}}, and {{other}}.',
['name' => '{{other}}', 'other' => 'world']
);
$this->assertSame('Hello {{other}}, and world.', $rendered);
}
public function test_missing_variables_are_reported_in_template_order(): void
{
try {
$this->renderer->render('{{zulu}} {{alpha}} {{zulu}}', []);
$this->fail('Expected PromptRenderException was not thrown.');
} catch (PromptRenderException $exception) {
$this->assertSame(['zulu', 'alpha'], $exception->missingVariables);
}
}
}