Files
Gnuboard7/tests/Feature/Rules/ComponentExistsTest.php
2026-05-11 11:29:41 +09:00

340 lines
10 KiB
PHP

<?php
namespace Tests\Feature\Rules;
use App\Rules\ComponentExists;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;
class ComponentExistsTest extends TestCase
{
use RefreshDatabase;
private string $testTemplateDir;
private string $testManifestPath;
protected function setUp(): void
{
parent::setUp();
// 테스트용 템플릿 디렉토리 생성
$this->testTemplateDir = base_path('templates/test-template');
$this->testManifestPath = $this->testTemplateDir.'/components.json';
if (! is_dir($this->testTemplateDir)) {
mkdir($this->testTemplateDir, 0755, true);
}
// 테스트용 components.json 생성
$manifest = [
'basic' => ['Button', 'Input', 'Textarea', 'Label'],
'composite' => ['Card', 'Modal', 'Dropdown', 'DataGrid'],
'layout' => ['Container', 'Section', 'Grid'],
];
file_put_contents($this->testManifestPath, json_encode($manifest, JSON_PRETTY_PRINT));
// 캐시 클리어
Cache::flush();
}
protected function tearDown(): void
{
// 테스트 파일 및 디렉토리 삭제
if (file_exists($this->testManifestPath)) {
unlink($this->testManifestPath);
}
if (is_dir($this->testTemplateDir)) {
rmdir($this->testTemplateDir);
}
Cache::flush();
parent::tearDown();
}
/**
* 등록된 컴포넌트는 검증 통과
*/
public function test_registered_components_pass_validation(): void
{
$rule = new ComponentExists;
$rule->setData(['template_id' => 'test-template']);
$layoutJson = [
'version' => '1.0.0',
'layout_name' => 'test',
'components' => [
[
'id' => 'button-1',
'type' => 'basic',
'name' => 'Button',
'props' => ['label' => 'Click me'],
],
[
'id' => 'card-1',
'type' => 'composite',
'name' => 'Card',
'props' => ['title' => 'Test Card'],
],
],
];
$failed = false;
$rule->validate('layout_json', $layoutJson, function () use (&$failed) {
$failed = true;
});
$this->assertFalse($failed, '등록된 컴포넌트는 검증을 통과해야 합니다.');
}
/**
* 미등록 컴포넌트는 검증 실패
*/
public function test_unregistered_component_fails_validation(): void
{
$rule = new ComponentExists;
$rule->setData(['template_id' => 'test-template']);
$layoutJson = [
'version' => '1.0.0',
'layout_name' => 'test',
'components' => [
[
'id' => 'malicious-1',
'type' => 'basic',
'name' => 'MaliciousComponent',
'props' => [],
],
],
];
$failed = false;
$failMessage = '';
$rule->validate('layout_json', $layoutJson, function ($message) use (&$failed, &$failMessage) {
$failed = true;
$failMessage = $message;
});
$this->assertTrue($failed, '미등록 컴포넌트는 검증에 실패해야 합니다.');
$this->assertStringContainsString('MaliciousComponent', $failMessage);
}
/**
* 중첩된 children 내의 모든 컴포넌트 검증
*/
public function test_validates_nested_children_components(): void
{
$rule = new ComponentExists;
$rule->setData(['template_id' => 'test-template']);
$layoutJson = [
'version' => '1.0.0',
'layout_name' => 'test',
'components' => [
[
'id' => 'container-1',
'type' => 'layout',
'name' => 'Container',
'props' => [],
'children' => [
[
'id' => 'card-1',
'type' => 'composite',
'name' => 'Card',
'props' => [],
'children' => [
[
'id' => 'button-1',
'type' => 'basic',
'name' => 'Button',
'props' => [],
],
],
],
],
],
],
];
$failed = false;
$rule->validate('layout_json', $layoutJson, function () use (&$failed) {
$failed = true;
});
$this->assertFalse($failed, '중첩된 children의 등록된 컴포넌트는 검증을 통과해야 합니다.');
}
/**
* 중첩된 children에 미등록 컴포넌트가 있으면 실패
*/
public function test_fails_with_unregistered_component_in_nested_children(): void
{
$rule = new ComponentExists;
$rule->setData(['template_id' => 'test-template']);
$layoutJson = [
'version' => '1.0.0',
'layout_name' => 'test',
'components' => [
[
'id' => 'container-1',
'type' => 'layout',
'name' => 'Container',
'props' => [],
'children' => [
[
'id' => 'evil-1',
'type' => 'basic',
'name' => 'EvilComponent',
'props' => [],
],
],
],
],
];
$failed = false;
$rule->validate('layout_json', $layoutJson, function () use (&$failed) {
$failed = true;
});
$this->assertTrue($failed, '중첩된 children의 미등록 컴포넌트는 검증에 실패해야 합니다.');
}
/**
* 빈 컴포넌트 이름은 검증 실패
*/
public function test_empty_component_name_fails_validation(): void
{
$rule = new ComponentExists;
$rule->setData(['template_id' => 'test-template']);
$layoutJson = [
'version' => '1.0.0',
'layout_name' => 'test',
'components' => [
[
'id' => 'empty-1',
'type' => 'basic',
'name' => '',
'props' => [],
],
],
];
$failed = false;
$rule->validate('layout_json', $layoutJson, function () use (&$failed) {
$failed = true;
});
$this->assertTrue($failed, '빈 컴포넌트 이름은 검증에 실패해야 합니다.');
}
/**
* template_id가 없으면 검증 실패
*/
public function test_missing_template_id_fails_validation(): void
{
$rule = new ComponentExists;
$rule->setData([]); // template_id 없음
$layoutJson = [
'version' => '1.0.0',
'layout_name' => 'test',
'components' => [
[
'id' => 'button-1',
'type' => 'basic',
'name' => 'Button',
'props' => [],
],
],
];
$failed = false;
$rule->validate('layout_json', $layoutJson, function () use (&$failed) {
$failed = true;
});
$this->assertTrue($failed, 'template_id가 없으면 검증에 실패해야 합니다.');
}
/**
* components.json이 없으면 검증 실패
*/
public function test_missing_manifest_fails_validation(): void
{
$rule = new ComponentExists;
$rule->setData(['template_id' => 'non-existent-template']);
$layoutJson = [
'version' => '1.0.0',
'layout_name' => 'test',
'components' => [
[
'id' => 'button-1',
'type' => 'basic',
'name' => 'Button',
'props' => [],
],
],
];
$failed = false;
$rule->validate('layout_json', $layoutJson, function () use (&$failed) {
$failed = true;
});
$this->assertTrue($failed, 'components.json이 없으면 검증에 실패해야 합니다.');
}
/**
* 캐싱이 정상 동작하는지 확인
*/
public function test_caching_works_correctly(): void
{
$rule1 = new ComponentExists;
$rule1->setData(['template_id' => 'test-template']);
$layoutJson = [
'version' => '1.0.0',
'layout_name' => 'test',
'components' => [
[
'id' => 'button-1',
'type' => 'basic',
'name' => 'Button',
'props' => [],
],
],
];
// 첫 번째 호출 (캐시 미스)
$failed1 = false;
$rule1->validate('layout_json', $layoutJson, function () use (&$failed1) {
$failed1 = true;
});
// 두 번째 호출 (캐시 히트)
$rule2 = new ComponentExists;
$rule2->setData(['template_id' => 'test-template']);
$failed2 = false;
$rule2->validate('layout_json', $layoutJson, function () use (&$failed2) {
$failed2 = true;
});
$this->assertFalse($failed1);
$this->assertFalse($failed2);
// 캐시 키가 올바르게 생성되었는지 확인 (CacheInterface 사용 — ComponentExists.php 참조)
$cache = app(\App\Contracts\Extension\CacheInterface::class);
$cacheKey = 'template.test-template.components_manifest';
$this->assertNotNull($cache->get($cacheKey), '캐시가 생성되어야 합니다.');
}
}