Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Tests\Unit\Generator;
4
5use Orchestra\Testbench\TestCase;
6use SocialDept\AtpSchema\Data\LexiconDocument;
7use SocialDept\AtpSchema\Generator\DTOGenerator;
8use SocialDept\AtpSchema\Parser\SchemaLoader;
9
10class DTOGeneratorTest extends TestCase
11{
12 protected DTOGenerator $generator;
13
14 protected string $tempDir;
15
16 protected function setUp(): void
17 {
18 parent::setUp();
19
20 $fixturesPath = __DIR__.'/../../fixtures';
21 $loader = new SchemaLoader([$fixturesPath], false);
22
23 $this->tempDir = sys_get_temp_dir().'/schema-gen-test-'.uniqid();
24
25 $this->generator = new DTOGenerator(
26 schemaLoader: $loader,
27 baseNamespace: 'Test\\Generated',
28 outputDirectory: $this->tempDir
29 );
30 }
31
32 protected function tearDown(): void
33 {
34 if (is_dir($this->tempDir)) {
35 $this->deleteDirectory($this->tempDir);
36 }
37
38 parent::tearDown();
39 }
40
41 protected function deleteDirectory(string $dir): void
42 {
43 if (! is_dir($dir)) {
44 return;
45 }
46
47 $items = scandir($dir);
48
49 foreach ($items as $item) {
50 if ($item === '.' || $item === '..') {
51 continue;
52 }
53
54 $path = $dir.'/'.$item;
55
56 if (is_dir($path)) {
57 $this->deleteDirectory($path);
58 } else {
59 unlink($path);
60 }
61 }
62
63 rmdir($dir);
64 }
65
66 public function test_it_generates_from_nsid(): void
67 {
68 $files = $this->generator->generateByNsid('app.bsky.feed.post', ['dryRun' => true]);
69
70 $this->assertNotEmpty($files);
71 $this->assertIsArray($files);
72 }
73
74 public function test_it_generates_code_from_document(): void
75 {
76 $document = LexiconDocument::fromArray([
77 'lexicon' => 1,
78 'id' => 'test.example.record',
79 'defs' => [
80 'main' => [
81 'type' => 'record',
82 'record' => [
83 'type' => 'object',
84 'required' => ['name'],
85 'properties' => [
86 'name' => ['type' => 'string'],
87 ],
88 ],
89 ],
90 ],
91 ]);
92
93 $code = $this->generator->generate($document);
94
95 $this->assertIsString($code);
96 $this->assertStringContainsString('class Record', $code);
97 }
98
99 public function test_it_previews_generated_code(): void
100 {
101 $document = LexiconDocument::fromArray([
102 'lexicon' => 1,
103 'id' => 'test.example.record',
104 'defs' => [
105 'main' => [
106 'type' => 'record',
107 'record' => [
108 'type' => 'object',
109 'required' => ['name'],
110 'properties' => [
111 'name' => ['type' => 'string'],
112 ],
113 ],
114 ],
115 ],
116 ]);
117
118 $code = $this->generator->preview($document);
119
120 $this->assertIsString($code);
121 $this->assertStringContainsString('<?php', $code);
122 }
123
124 public function test_it_gets_metadata(): void
125 {
126 $metadata = $this->generator->getMetadata('app.bsky.feed.post');
127
128 $this->assertArrayHasKey('nsid', $metadata);
129 $this->assertArrayHasKey('namespace', $metadata);
130 $this->assertArrayHasKey('className', $metadata);
131 $this->assertArrayHasKey('fullyQualifiedName', $metadata);
132 $this->assertArrayHasKey('type', $metadata);
133
134 $this->assertSame('app.bsky.feed.post', $metadata['nsid']);
135 $this->assertSame('Test\\Generated\\App\\Bsky\\Feed', $metadata['namespace']);
136 $this->assertSame('Post', $metadata['className']);
137 }
138
139 public function test_it_validates_generated_code(): void
140 {
141 $validCode = '<?php class Test {}';
142
143 $this->assertTrue($this->generator->validate($validCode));
144 }
145
146 public function test_it_sets_options(): void
147 {
148 $this->generator->setOptions([
149 'baseNamespace' => 'Custom\\Namespace',
150 'outputDirectory' => '/custom/path',
151 ]);
152
153 $metadata = $this->generator->getMetadata('app.bsky.feed.post');
154
155 $this->assertStringStartsWith('Custom\\Namespace', $metadata['namespace']);
156 }
157}