Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Tests\Unit;
4
5use Orchestra\Testbench\TestCase;
6use SocialDept\AtpSchema\Data\LexiconDocument;
7use SocialDept\AtpSchema\Generator\DTOGenerator;
8use SocialDept\AtpSchema\Parser\SchemaLoader;
9use SocialDept\AtpSchema\SchemaManager;
10use SocialDept\AtpSchema\Validation\LexiconValidator;
11
12class SchemaManagerTest extends TestCase
13{
14 protected string $fixturesPath;
15
16 protected SchemaLoader $loader;
17
18 protected LexiconValidator $validator;
19
20 protected DTOGenerator $generator;
21
22 protected SchemaManager $manager;
23
24 protected function setUp(): void
25 {
26 parent::setUp();
27
28 $this->fixturesPath = __DIR__.'/../fixtures';
29
30 // Create mock dependencies
31 $this->loader = new SchemaLoader([$this->fixturesPath], false);
32 $this->validator = new LexiconValidator($this->loader);
33 $this->generator = new DTOGenerator($this->loader, 'App\\Lexicon', 'app/Lexicon');
34
35 // Create manager
36 $this->manager = new SchemaManager(
37 $this->loader,
38 $this->validator,
39 $this->generator
40 );
41 }
42
43 public function test_it_loads_schema(): void
44 {
45 $schema = $this->manager->load('app.bsky.feed.post');
46
47 $this->assertInstanceOf(LexiconDocument::class, $schema);
48 $this->assertSame('app.bsky.feed.post', $schema->getNsid());
49 }
50
51 public function test_it_checks_if_schema_exists(): void
52 {
53 $this->assertTrue($this->manager->exists('app.bsky.feed.post'));
54 $this->assertFalse($this->manager->exists('nonexistent.schema'));
55 }
56
57 public function test_it_parses_schema_into_document(): void
58 {
59 $document = $this->manager->load('app.bsky.feed.post');
60
61 $this->assertInstanceOf(LexiconDocument::class, $document);
62 $this->assertSame('app.bsky.feed.post', $document->getNsid());
63 $this->assertSame(1, $document->lexicon);
64 }
65
66 public function test_it_validates_data_against_schema(): void
67 {
68 $validData = [
69 'text' => 'Hello, world!',
70 'createdAt' => '2024-01-01T12:00:00Z',
71 ];
72
73 $result = $this->manager->validate('app.bsky.feed.post', $validData);
74
75 $this->assertTrue($result);
76 }
77
78 public function test_it_validates_data_with_errors(): void
79 {
80 $invalidData = [
81 'text' => '', // Required field
82 ];
83
84 $errors = $this->manager->validateWithErrors('app.bsky.feed.post', $invalidData);
85
86 $this->assertIsArray($errors);
87 $this->assertNotEmpty($errors);
88 }
89
90 public function test_it_generates_dto_code(): void
91 {
92 $code = $this->manager->generate('app.bsky.feed.post');
93
94 $this->assertIsString($code);
95 $this->assertStringContainsString('namespace', $code);
96 $this->assertStringContainsString('class', $code);
97 }
98
99 public function test_it_throws_when_generator_not_available(): void
100 {
101 $managerWithoutGenerator = new SchemaManager(
102 $this->loader,
103 $this->validator,
104 null
105 );
106
107 $this->expectException(\RuntimeException::class);
108 $this->expectExceptionMessage('Generator not available');
109
110 $managerWithoutGenerator->generate('app.bsky.feed.post');
111 }
112
113 public function test_it_clears_cache_for_specific_nsid(): void
114 {
115 // Load to cache
116 $this->manager->load('app.bsky.feed.post');
117
118 $this->assertContains('app.bsky.feed.post', $this->loader->getCachedNsids());
119
120 // Clear cache
121 $this->manager->clearCache('app.bsky.feed.post');
122
123 $this->assertNotContains('app.bsky.feed.post', $this->loader->getCachedNsids());
124 }
125
126 public function test_it_clears_all_cache(): void
127 {
128 // Load multiple schemas
129 $this->manager->load('app.bsky.feed.post');
130 $this->manager->load('com.atproto.repo.getRecord');
131
132 $this->assertCount(2, $this->loader->getCachedNsids());
133
134 // Clear all
135 $this->manager->clearCache();
136
137 $this->assertCount(0, $this->loader->getCachedNsids());
138 }
139
140 public function test_it_gets_loader(): void
141 {
142 $loader = $this->manager->getLoader();
143
144 $this->assertSame($this->loader, $loader);
145 }
146
147 public function test_it_gets_validator(): void
148 {
149 $validator = $this->manager->getValidator();
150
151 $this->assertSame($this->validator, $validator);
152 }
153
154 public function test_it_gets_generator(): void
155 {
156 $generator = $this->manager->getGenerator();
157
158 $this->assertSame($this->generator, $generator);
159 }
160
161 public function test_it_sets_generator(): void
162 {
163 $newGenerator = new DTOGenerator($this->loader, 'Custom\\Namespace', 'custom/path');
164
165 $this->manager->setGenerator($newGenerator);
166
167 $this->assertSame($newGenerator, $this->manager->getGenerator());
168 }
169
170 public function test_it_gets_null_generator_when_not_set(): void
171 {
172 $managerWithoutGenerator = new SchemaManager(
173 $this->loader,
174 $this->validator,
175 null
176 );
177
178 $this->assertNull($managerWithoutGenerator->getGenerator());
179 }
180}