Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at dev 11 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Tests\Unit\Services; 4 5use Orchestra\Testbench\TestCase; 6use SocialDept\AtpSchema\Contracts\Transformer; 7use SocialDept\AtpSchema\Exceptions\SchemaException; 8use SocialDept\AtpSchema\Services\ModelMapper; 9 10class ModelMapperTest extends TestCase 11{ 12 protected ModelMapper $mapper; 13 14 protected function setUp(): void 15 { 16 parent::setUp(); 17 18 $this->mapper = new ModelMapper(); 19 } 20 21 public function test_it_registers_transformer(): void 22 { 23 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 24 25 $this->mapper->register('app.bsky.feed.post', $transformer); 26 27 $this->assertTrue($this->mapper->has('app.bsky.feed.post')); 28 } 29 30 public function test_it_registers_multiple_transformers(): void 31 { 32 $transformer1 = $this->createTestTransformer('app.bsky.feed.post'); 33 $transformer2 = $this->createTestTransformer('app.bsky.feed.repost'); 34 35 $this->mapper->registerMany([ 36 'app.bsky.feed.post' => $transformer1, 37 'app.bsky.feed.repost' => $transformer2, 38 ]); 39 40 $this->assertTrue($this->mapper->has('app.bsky.feed.post')); 41 $this->assertTrue($this->mapper->has('app.bsky.feed.repost')); 42 } 43 44 public function test_it_transforms_from_array(): void 45 { 46 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 47 $this->mapper->register('app.bsky.feed.post', $transformer); 48 49 $data = ['text' => 'Hello, world!']; 50 $model = $this->mapper->fromArray('app.bsky.feed.post', $data); 51 52 $this->assertInstanceOf(\stdClass::class, $model); 53 $this->assertEquals('Hello, world!', $model->text); 54 } 55 56 public function test_it_transforms_to_array(): void 57 { 58 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 59 $this->mapper->register('app.bsky.feed.post', $transformer); 60 61 $model = (object) ['text' => 'Hello, world!']; 62 $data = $this->mapper->toArray('app.bsky.feed.post', $model); 63 64 $this->assertEquals(['text' => 'Hello, world!'], $data); 65 } 66 67 public function test_it_throws_exception_when_transformer_not_found_for_from_array(): void 68 { 69 $this->expectException(SchemaException::class); 70 71 $this->mapper->fromArray('unknown.type', []); 72 } 73 74 public function test_it_throws_exception_when_transformer_not_found_for_to_array(): void 75 { 76 $this->expectException(SchemaException::class); 77 78 $this->mapper->toArray('unknown.type', new \stdClass()); 79 } 80 81 public function test_it_transforms_multiple_items_from_arrays(): void 82 { 83 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 84 $this->mapper->register('app.bsky.feed.post', $transformer); 85 86 $items = [ 87 ['text' => 'First post'], 88 ['text' => 'Second post'], 89 ]; 90 91 $models = $this->mapper->fromArrayMany('app.bsky.feed.post', $items); 92 93 $this->assertCount(2, $models); 94 $this->assertEquals('First post', $models[0]->text); 95 $this->assertEquals('Second post', $models[1]->text); 96 } 97 98 public function test_it_transforms_multiple_items_to_arrays(): void 99 { 100 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 101 $this->mapper->register('app.bsky.feed.post', $transformer); 102 103 $models = [ 104 (object) ['text' => 'First post'], 105 (object) ['text' => 'Second post'], 106 ]; 107 108 $arrays = $this->mapper->toArrayMany('app.bsky.feed.post', $models); 109 110 $this->assertCount(2, $arrays); 111 $this->assertEquals(['text' => 'First post'], $arrays[0]); 112 $this->assertEquals(['text' => 'Second post'], $arrays[1]); 113 } 114 115 public function test_it_gets_transformer(): void 116 { 117 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 118 $this->mapper->register('app.bsky.feed.post', $transformer); 119 120 $retrieved = $this->mapper->getTransformer('app.bsky.feed.post'); 121 122 $this->assertSame($transformer, $retrieved); 123 } 124 125 public function test_it_returns_null_for_missing_transformer(): void 126 { 127 $transformer = $this->mapper->getTransformer('unknown.type'); 128 129 $this->assertNull($transformer); 130 } 131 132 public function test_it_checks_if_has_transformer(): void 133 { 134 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 135 $this->mapper->register('app.bsky.feed.post', $transformer); 136 137 $this->assertTrue($this->mapper->has('app.bsky.feed.post')); 138 $this->assertFalse($this->mapper->has('unknown.type')); 139 } 140 141 public function test_it_unregisters_transformer(): void 142 { 143 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 144 $this->mapper->register('app.bsky.feed.post', $transformer); 145 146 $this->assertTrue($this->mapper->has('app.bsky.feed.post')); 147 148 $this->mapper->unregister('app.bsky.feed.post'); 149 150 $this->assertFalse($this->mapper->has('app.bsky.feed.post')); 151 } 152 153 public function test_it_gets_all_transformers(): void 154 { 155 $transformer1 = $this->createTestTransformer('app.bsky.feed.post'); 156 $transformer2 = $this->createTestTransformer('app.bsky.feed.repost'); 157 158 $this->mapper->registerMany([ 159 'app.bsky.feed.post' => $transformer1, 160 'app.bsky.feed.repost' => $transformer2, 161 ]); 162 163 $all = $this->mapper->all(); 164 165 $this->assertCount(2, $all); 166 $this->assertArrayHasKey('app.bsky.feed.post', $all); 167 $this->assertArrayHasKey('app.bsky.feed.repost', $all); 168 } 169 170 public function test_it_clears_all_transformers(): void 171 { 172 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 173 $this->mapper->register('app.bsky.feed.post', $transformer); 174 175 $this->assertEquals(1, $this->mapper->count()); 176 177 $this->mapper->clear(); 178 179 $this->assertEquals(0, $this->mapper->count()); 180 } 181 182 public function test_it_tries_from_array_with_missing_transformer(): void 183 { 184 $result = $this->mapper->tryFromArray('unknown.type', []); 185 186 $this->assertNull($result); 187 } 188 189 public function test_it_tries_from_array_with_existing_transformer(): void 190 { 191 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 192 $this->mapper->register('app.bsky.feed.post', $transformer); 193 194 $result = $this->mapper->tryFromArray('app.bsky.feed.post', ['text' => 'Hello']); 195 196 $this->assertNotNull($result); 197 $this->assertEquals('Hello', $result->text); 198 } 199 200 public function test_it_tries_to_array_with_missing_transformer(): void 201 { 202 $result = $this->mapper->tryToArray('unknown.type', new \stdClass()); 203 204 $this->assertNull($result); 205 } 206 207 public function test_it_tries_to_array_with_existing_transformer(): void 208 { 209 $transformer = $this->createTestTransformer('app.bsky.feed.post'); 210 $this->mapper->register('app.bsky.feed.post', $transformer); 211 212 $model = (object) ['text' => 'Hello']; 213 $result = $this->mapper->tryToArray('app.bsky.feed.post', $model); 214 215 $this->assertNotNull($result); 216 $this->assertEquals(['text' => 'Hello'], $result); 217 } 218 219 public function test_it_counts_transformers(): void 220 { 221 $this->assertEquals(0, $this->mapper->count()); 222 223 $this->mapper->register('app.bsky.feed.post', $this->createTestTransformer('app.bsky.feed.post')); 224 225 $this->assertEquals(1, $this->mapper->count()); 226 227 $this->mapper->register('app.bsky.feed.repost', $this->createTestTransformer('app.bsky.feed.repost')); 228 229 $this->assertEquals(2, $this->mapper->count()); 230 } 231 232 public function test_it_uses_wildcard_transformer(): void 233 { 234 $transformer = $this->createWildcardTransformer('app.bsky.feed.*'); 235 $this->mapper->register('app.bsky.feed.*', $transformer); 236 237 $this->assertTrue($this->mapper->has('app.bsky.feed.post')); 238 $this->assertTrue($this->mapper->has('app.bsky.feed.repost')); 239 $this->assertFalse($this->mapper->has('app.bsky.graph.follow')); 240 } 241 242 public function test_it_prefers_exact_match_over_wildcard(): void 243 { 244 $wildcardTransformer = $this->createWildcardTransformer('app.bsky.feed.*'); 245 $exactTransformer = $this->createTestTransformer('app.bsky.feed.post'); 246 247 $this->mapper->register('app.bsky.feed.*', $wildcardTransformer); 248 $this->mapper->register('app.bsky.feed.post', $exactTransformer); 249 250 $retrieved = $this->mapper->getTransformer('app.bsky.feed.post'); 251 252 $this->assertSame($exactTransformer, $retrieved); 253 } 254 255 public function test_it_chains_register_calls(): void 256 { 257 $transformer1 = $this->createTestTransformer('type1'); 258 $transformer2 = $this->createTestTransformer('type2'); 259 260 $result = $this->mapper 261 ->register('type1', $transformer1) 262 ->register('type2', $transformer2); 263 264 $this->assertSame($this->mapper, $result); 265 $this->assertTrue($this->mapper->has('type1')); 266 $this->assertTrue($this->mapper->has('type2')); 267 } 268 269 public function test_it_chains_register_many_calls(): void 270 { 271 $result = $this->mapper->registerMany([ 272 'type1' => $this->createTestTransformer('type1'), 273 'type2' => $this->createTestTransformer('type2'), 274 ]); 275 276 $this->assertSame($this->mapper, $result); 277 } 278 279 public function test_it_chains_unregister_calls(): void 280 { 281 $this->mapper->register('type1', $this->createTestTransformer('type1')); 282 283 $result = $this->mapper->unregister('type1'); 284 285 $this->assertSame($this->mapper, $result); 286 } 287 288 public function test_it_chains_clear_calls(): void 289 { 290 $result = $this->mapper->clear(); 291 292 $this->assertSame($this->mapper, $result); 293 } 294 295 protected function createTestTransformer(string $type): Transformer 296 { 297 return new class ($type) implements Transformer { 298 public function __construct(protected string $type) 299 { 300 } 301 302 public function fromArray(array $data): mixed 303 { 304 return (object) $data; 305 } 306 307 public function toArray(mixed $model): array 308 { 309 return (array) $model; 310 } 311 312 public function supports(string $type): bool 313 { 314 return $type === $this->type; 315 } 316 }; 317 } 318 319 protected function createWildcardTransformer(string $pattern): Transformer 320 { 321 return new class ($pattern) implements Transformer { 322 public function __construct(protected string $pattern) 323 { 324 } 325 326 public function fromArray(array $data): mixed 327 { 328 return (object) $data; 329 } 330 331 public function toArray(mixed $model): array 332 { 333 return (array) $model; 334 } 335 336 public function supports(string $type): bool 337 { 338 $regex = '/^'.str_replace('\\*', '.*', preg_quote($this->pattern, '/')).'$/'; 339 340 return (bool) preg_match($regex, $type); 341 } 342 }; 343 } 344}