Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at dev 4.2 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Tests\Unit\Parser; 4 5use Orchestra\Testbench\TestCase; 6use SocialDept\AtpSchema\Data\LexiconDocument; 7use SocialDept\AtpSchema\Data\Types\ObjectType; 8use SocialDept\AtpSchema\Data\Types\StringType; 9use SocialDept\AtpSchema\Exceptions\TypeResolutionException; 10use SocialDept\AtpSchema\Parser\TypeParser; 11 12class TypeParserTest extends TestCase 13{ 14 protected TypeParser $parser; 15 16 protected function setUp(): void 17 { 18 parent::setUp(); 19 20 $this->parser = new TypeParser(); 21 } 22 23 public function test_it_parses_primitive_types(): void 24 { 25 $type = $this->parser->parse(['type' => 'string']); 26 27 $this->assertInstanceOf(StringType::class, $type); 28 } 29 30 public function test_it_parses_complex_types(): void 31 { 32 $type = $this->parser->parse(['type' => 'object']); 33 34 $this->assertInstanceOf(ObjectType::class, $type); 35 } 36 37 public function test_it_throws_on_missing_type(): void 38 { 39 $this->expectException(TypeResolutionException::class); 40 $this->expectExceptionMessage('Unknown Lexicon type: (missing type field)'); 41 42 $this->parser->parse([]); 43 } 44 45 public function test_it_throws_on_unknown_type(): void 46 { 47 $this->expectException(TypeResolutionException::class); 48 $this->expectExceptionMessage('Unknown Lexicon type: nonexistent'); 49 50 $this->parser->parse(['type' => 'nonexistent']); 51 } 52 53 public function test_it_resolves_local_reference(): void 54 { 55 $document = LexiconDocument::fromArray([ 56 'lexicon' => 1, 57 'id' => 'com.example.test', 58 'defs' => [ 59 'main' => ['type' => 'object'], 60 'other' => ['type' => 'string'], 61 ], 62 ]); 63 64 $type = $this->parser->resolveReference('#other', $document); 65 66 $this->assertInstanceOf(StringType::class, $type); 67 } 68 69 public function test_it_throws_on_unresolvable_local_reference(): void 70 { 71 $document = LexiconDocument::fromArray([ 72 'lexicon' => 1, 73 'id' => 'com.example.test', 74 'defs' => [ 75 'main' => ['type' => 'object'], 76 ], 77 ]); 78 79 $this->expectException(TypeResolutionException::class); 80 $this->expectExceptionMessage('Cannot resolve reference #nonexistent in schema com.example.test'); 81 82 $this->parser->resolveReference('#nonexistent', $document); 83 } 84 85 public function test_it_caches_resolved_types(): void 86 { 87 $document = LexiconDocument::fromArray([ 88 'lexicon' => 1, 89 'id' => 'com.example.test', 90 'defs' => [ 91 'main' => ['type' => 'object'], 92 'other' => ['type' => 'string'], 93 ], 94 ]); 95 96 $type1 = $this->parser->resolveReference('#other', $document); 97 $type2 = $this->parser->resolveReference('#other', $document); 98 99 $this->assertSame($type1, $type2); 100 $this->assertCount(1, $this->parser->getResolvedTypes()); 101 } 102 103 public function test_it_clears_cache(): void 104 { 105 $document = LexiconDocument::fromArray([ 106 'lexicon' => 1, 107 'id' => 'com.example.test', 108 'defs' => [ 109 'main' => ['type' => 'object'], 110 'other' => ['type' => 'string'], 111 ], 112 ]); 113 114 $this->parser->resolveReference('#other', $document); 115 $this->assertCount(1, $this->parser->getResolvedTypes()); 116 117 $this->parser->clearCache(); 118 $this->assertCount(0, $this->parser->getResolvedTypes()); 119 } 120 121 public function test_it_throws_on_external_reference_without_loader(): void 122 { 123 $document = LexiconDocument::fromArray([ 124 'lexicon' => 1, 125 'id' => 'com.example.test', 126 'defs' => [ 127 'main' => ['type' => 'object'], 128 ], 129 ]); 130 131 $this->expectException(\RuntimeException::class); 132 $this->expectExceptionMessage('Cannot resolve external reference without SchemaLoader'); 133 134 $this->parser->resolveReference('com.atproto.repo.getRecord', $document); 135 } 136}