Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at main 196 lines 5.6 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Tests\Unit\Validation; 4 5use Orchestra\Testbench\TestCase; 6use SocialDept\AtpSchema\Data\LexiconDocument; 7use SocialDept\AtpSchema\Exceptions\RecordValidationException; 8use SocialDept\AtpSchema\Exceptions\SchemaValidationException; 9use SocialDept\AtpSchema\Parser\SchemaLoader; 10use SocialDept\AtpSchema\Validation\LexiconValidator; 11 12class LexiconValidatorTest extends TestCase 13{ 14 protected LexiconValidator $validator; 15 16 protected SchemaLoader $loader; 17 18 protected function setUp(): void 19 { 20 parent::setUp(); 21 22 $fixturesPath = __DIR__.'/../../fixtures'; 23 $this->loader = new SchemaLoader([$fixturesPath], false); 24 $this->validator = new LexiconValidator($this->loader); 25 } 26 27 public function test_it_validates_valid_record(): void 28 { 29 $record = [ 30 'text' => 'Hello, World!', 31 'createdAt' => '2024-01-01T00:00:00Z', 32 ]; 33 34 $this->validator->validateByNsid('app.bsky.feed.post', $record); 35 36 $this->assertTrue(true); 37 } 38 39 public function test_it_throws_on_missing_required_field(): void 40 { 41 $record = [ 42 'text' => 'Hello, World!', 43 // Missing createdAt 44 ]; 45 46 $this->expectException(RecordValidationException::class); 47 48 $this->validator->validateByNsid('app.bsky.feed.post', $record); 49 } 50 51 public function test_it_throws_on_invalid_field_type(): void 52 { 53 $record = [ 54 'text' => 123, // Should be string 55 'createdAt' => '2024-01-01T00:00:00Z', 56 ]; 57 58 $this->expectException(RecordValidationException::class); 59 60 $this->validator->validateByNsid('app.bsky.feed.post', $record); 61 } 62 63 public function test_it_validates_record_with_lexicon_document(): void 64 { 65 $document = LexiconDocument::fromArray([ 66 'lexicon' => 1, 67 'id' => 'com.example.test', 68 'defs' => [ 69 'main' => [ 70 'type' => 'record', 71 'record' => [ 72 'type' => 'object', 73 'required' => ['name'], 74 'properties' => [ 75 'name' => ['type' => 'string'], 76 ], 77 ], 78 ], 79 ], 80 ]); 81 82 $this->validator->validateRecord($document, ['name' => 'John']); 83 84 $this->assertTrue(true); 85 } 86 87 public function test_it_throws_on_non_record_schema(): void 88 { 89 $document = LexiconDocument::fromArray([ 90 'lexicon' => 1, 91 'id' => 'com.example.test', 92 'defs' => [ 93 'main' => [ 94 'type' => 'query', 95 ], 96 ], 97 ]); 98 99 $this->expectException(SchemaValidationException::class); 100 $this->expectExceptionMessage('Schema is not a record type'); 101 102 $this->validator->validateRecord($document, ['name' => 'John']); 103 } 104 105 public function test_it_validates_procedure_input(): void 106 { 107 $document = LexiconDocument::fromArray([ 108 'lexicon' => 1, 109 'id' => 'com.example.test', 110 'defs' => [ 111 'main' => [ 112 'type' => 'procedure', 113 'input' => [ 114 'type' => 'object', 115 'required' => ['name'], 116 'properties' => [ 117 'name' => ['type' => 'string'], 118 ], 119 ], 120 ], 121 ], 122 ]); 123 124 $this->validator->validateProcedure($document, ['name' => 'John']); 125 126 $this->assertTrue(true); 127 } 128 129 public function test_it_validates_with_contract_method(): void 130 { 131 $document = $this->loader->load('app.bsky.feed.post'); 132 133 $record = [ 134 'text' => 'Hello, World!', 135 'createdAt' => '2024-01-01T00:00:00Z', 136 ]; 137 138 $this->assertTrue($this->validator->validate($record, $document)); 139 } 140 141 public function test_it_returns_false_for_invalid_record(): void 142 { 143 $document = $this->loader->load('app.bsky.feed.post'); 144 145 $record = [ 146 'text' => 'Hello, World!', 147 // Missing createdAt 148 ]; 149 150 $this->assertFalse($this->validator->validate($record, $document)); 151 } 152 153 public function test_it_validates_with_errors(): void 154 { 155 $document = $this->loader->load('app.bsky.feed.post'); 156 157 $record = [ 158 'text' => 'Hello, World!', 159 'createdAt' => '2024-01-01T00:00:00Z', 160 ]; 161 162 $errors = $this->validator->validateWithErrors($record, $document); 163 164 $this->assertEmpty($errors); 165 } 166 167 public function test_it_returns_errors_for_invalid_record(): void 168 { 169 $document = $this->loader->load('app.bsky.feed.post'); 170 171 $record = [ 172 'text' => 'Hello, World!', 173 // Missing createdAt 174 ]; 175 176 $errors = $this->validator->validateWithErrors($record, $document); 177 178 $this->assertNotEmpty($errors); 179 $this->assertIsArray($errors); 180 } 181 182 public function test_it_validates_specific_field(): void 183 { 184 $document = $this->loader->load('app.bsky.feed.post'); 185 186 $this->assertTrue($this->validator->validateField('Hello, World!', 'text', $document)); 187 $this->assertFalse($this->validator->validateField(123, 'text', $document)); 188 } 189 190 public function test_it_sets_validation_mode(): void 191 { 192 $this->validator->setMode('lenient'); 193 194 $this->assertTrue(true); 195 } 196}