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\Generator\ModelMapper;
7
8class ModelMapperTest extends TestCase
9{
10 protected ModelMapper $mapper;
11
12 protected function setUp(): void
13 {
14 parent::setUp();
15
16 $this->mapper = new ModelMapper();
17 }
18
19 public function test_it_generates_to_model_body_for_simple_properties(): void
20 {
21 $body = $this->mapper->generateToModelBody([
22 'name' => ['type' => 'string'],
23 'age' => ['type' => 'integer'],
24 ], 'User');
25
26 $this->assertStringContainsString('return new User([', $body);
27 $this->assertStringContainsString("'name' => \$this->name,", $body);
28 $this->assertStringContainsString("'age' => \$this->age,", $body);
29 }
30
31 public function test_it_generates_from_model_body_for_simple_properties(): void
32 {
33 $body = $this->mapper->generateFromModelBody([
34 'name' => ['type' => 'string'],
35 'age' => ['type' => 'integer'],
36 ]);
37
38 $this->assertStringContainsString('return new static(', $body);
39 $this->assertStringContainsString('name: $model->name ?? null,', $body);
40 $this->assertStringContainsString('age: $model->age ?? null', $body);
41 }
42
43 public function test_it_handles_datetime_in_to_model(): void
44 {
45 $body = $this->mapper->generateToModelBody([
46 'createdAt' => [
47 'type' => 'string',
48 'format' => 'datetime',
49 ],
50 ]);
51
52 $this->assertStringContainsString("\$this->createdAt?->format('Y-m-d H:i:s')", $body);
53 }
54
55 public function test_it_handles_datetime_in_from_model(): void
56 {
57 $body = $this->mapper->generateFromModelBody([
58 'createdAt' => [
59 'type' => 'string',
60 'format' => 'datetime',
61 ],
62 ]);
63
64 $this->assertStringContainsString('$model->createdAt ? new \\DateTime($model->createdAt) : null', $body);
65 }
66
67 public function test_it_handles_blob_in_to_model(): void
68 {
69 $body = $this->mapper->generateToModelBody([
70 'image' => ['type' => 'blob'],
71 ]);
72
73 $this->assertStringContainsString('$this->image?->toArray()', $body);
74 }
75
76 public function test_it_handles_blob_in_from_model(): void
77 {
78 $body = $this->mapper->generateFromModelBody([
79 'image' => ['type' => 'blob'],
80 ]);
81
82 $this->assertStringContainsString('\\SocialDept\\AtpSchema\\Data\\BlobReference::fromArray', $body);
83 }
84
85 public function test_it_handles_ref_in_to_model(): void
86 {
87 $body = $this->mapper->generateToModelBody([
88 'author' => [
89 'type' => 'ref',
90 'ref' => 'app.test.author',
91 ],
92 ]);
93
94 $this->assertStringContainsString('$this->author?->toArray()', $body);
95 }
96
97 public function test_it_handles_ref_in_from_model(): void
98 {
99 $body = $this->mapper->generateFromModelBody([
100 'author' => [
101 'type' => 'ref',
102 'ref' => 'app.test.author',
103 ],
104 ]);
105
106 $this->assertStringContainsString('Author::fromArray', $body);
107 }
108
109 public function test_it_handles_array_of_refs_in_to_model(): void
110 {
111 $body = $this->mapper->generateToModelBody([
112 'posts' => [
113 'type' => 'array',
114 'items' => [
115 'type' => 'ref',
116 'ref' => 'app.test.post',
117 ],
118 ],
119 ]);
120
121 $this->assertStringContainsString('array_map(fn ($item) => $item->toArray()', $body);
122 }
123
124 public function test_it_handles_array_of_refs_in_from_model(): void
125 {
126 $body = $this->mapper->generateFromModelBody([
127 'posts' => [
128 'type' => 'array',
129 'items' => [
130 'type' => 'ref',
131 'ref' => 'app.test.post',
132 ],
133 ],
134 ]);
135
136 $this->assertStringContainsString('array_map(fn ($item) => Post::fromArray($item)', $body);
137 }
138
139 public function test_it_handles_array_of_objects(): void
140 {
141 $body = $this->mapper->generateToModelBody([
142 'settings' => [
143 'type' => 'array',
144 'items' => [
145 'type' => 'object',
146 ],
147 ],
148 ]);
149
150 $this->assertStringContainsString('$this->settings ?? []', $body);
151 }
152
153 public function test_it_generates_empty_to_model_for_no_properties(): void
154 {
155 $body = $this->mapper->generateToModelBody([], 'User');
156
157 $this->assertStringContainsString('return new User();', $body);
158 }
159
160 public function test_it_generates_empty_from_model_for_no_properties(): void
161 {
162 $body = $this->mapper->generateFromModelBody([]);
163
164 $this->assertStringContainsString('return new static();', $body);
165 }
166
167 public function test_it_gets_field_mapping(): void
168 {
169 $mapping = $this->mapper->getFieldMapping([
170 'userName' => ['type' => 'string'],
171 'emailAddress' => ['type' => 'string'],
172 ]);
173
174 $this->assertSame([
175 'userName' => 'user_name',
176 'emailAddress' => 'email_address',
177 ], $mapping);
178 }
179
180 public function test_it_checks_if_datetime_needs_transformer(): void
181 {
182 $this->assertTrue($this->mapper->needsTransformer([
183 'type' => 'string',
184 'format' => 'datetime',
185 ]));
186 }
187
188 public function test_it_checks_if_blob_needs_transformer(): void
189 {
190 $this->assertTrue($this->mapper->needsTransformer(['type' => 'blob']));
191 }
192
193 public function test_it_checks_if_ref_needs_transformer(): void
194 {
195 $this->assertTrue($this->mapper->needsTransformer(['type' => 'ref']));
196 }
197
198 public function test_it_checks_if_array_of_refs_needs_transformer(): void
199 {
200 $this->assertTrue($this->mapper->needsTransformer([
201 'type' => 'array',
202 'items' => ['type' => 'ref'],
203 ]));
204 }
205
206 public function test_it_checks_if_simple_type_needs_transformer(): void
207 {
208 $this->assertFalse($this->mapper->needsTransformer(['type' => 'string']));
209 $this->assertFalse($this->mapper->needsTransformer(['type' => 'integer']));
210 }
211
212 public function test_it_gets_datetime_transformer_type(): void
213 {
214 $type = $this->mapper->getTransformerType([
215 'type' => 'string',
216 'format' => 'datetime',
217 ]);
218
219 $this->assertSame('datetime', $type);
220 }
221
222 public function test_it_gets_blob_transformer_type(): void
223 {
224 $type = $this->mapper->getTransformerType(['type' => 'blob']);
225
226 $this->assertSame('blob', $type);
227 }
228
229 public function test_it_gets_ref_transformer_type(): void
230 {
231 $type = $this->mapper->getTransformerType(['type' => 'ref']);
232
233 $this->assertSame('ref', $type);
234 }
235
236 public function test_it_gets_array_ref_transformer_type(): void
237 {
238 $type = $this->mapper->getTransformerType([
239 'type' => 'array',
240 'items' => ['type' => 'ref'],
241 ]);
242
243 $this->assertSame('array_ref', $type);
244 }
245
246 public function test_it_gets_array_object_transformer_type(): void
247 {
248 $type = $this->mapper->getTransformerType([
249 'type' => 'array',
250 'items' => ['type' => 'object'],
251 ]);
252
253 $this->assertSame('array_object', $type);
254 }
255
256 public function test_it_gets_null_transformer_for_simple_types(): void
257 {
258 $this->assertNull($this->mapper->getTransformerType(['type' => 'string']));
259 $this->assertNull($this->mapper->getTransformerType(['type' => 'integer']));
260 }
261
262 public function test_it_handles_custom_model_class(): void
263 {
264 $body = $this->mapper->generateToModelBody([
265 'name' => ['type' => 'string'],
266 ], 'CustomModel');
267
268 $this->assertStringContainsString('return new CustomModel([', $body);
269 }
270
271 public function test_it_does_not_add_trailing_comma_to_last_property(): void
272 {
273 $body = $this->mapper->generateFromModelBody([
274 'first' => ['type' => 'string'],
275 'last' => ['type' => 'string'],
276 ]);
277
278 // Should not have comma after last property
279 $lines = explode("\n", $body);
280 $lastPropertyLine = '';
281 foreach ($lines as $line) {
282 if (str_contains($line, 'last:')) {
283 $lastPropertyLine = $line;
284
285 break;
286 }
287 }
288
289 $this->assertStringNotContainsString(',', rtrim($lastPropertyLine));
290 }
291}