Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at dev 8.7 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Tests\Unit\Data; 4 5use Orchestra\Testbench\TestCase; 6use SocialDept\AtpSchema\Data\Data; 7 8class DataTest extends TestCase 9{ 10 public function test_it_converts_to_array(): void 11 { 12 $data = new TestData('John', 30); 13 14 $array = $data->toArray(); 15 16 $this->assertSame([ 17 'name' => 'John', 18 'age' => 30, 19 ], $array); 20 } 21 22 public function test_it_converts_to_json(): void 23 { 24 $data = new TestData('John', 30); 25 26 $json = $data->toJson(); 27 28 $this->assertJson($json); 29 $this->assertSame('{"name":"John","age":30}', $json); 30 } 31 32 public function test_it_is_json_serializable(): void 33 { 34 $data = new TestData('John', 30); 35 36 $json = json_encode($data); 37 38 $this->assertSame('{"name":"John","age":30}', $json); 39 } 40 41 public function test_it_converts_to_string(): void 42 { 43 $data = new TestData('John', 30); 44 45 $string = (string) $data; 46 47 $this->assertSame('{"name":"John","age":30}', $string); 48 } 49 50 public function test_it_creates_from_array(): void 51 { 52 $data = TestData::fromArray([ 53 'name' => 'Jane', 54 'age' => 25, 55 ]); 56 57 $this->assertSame('Jane', $data->name); 58 $this->assertSame(25, $data->age); 59 } 60 61 public function test_it_creates_from_json(): void 62 { 63 $data = TestData::fromJson('{"name":"Bob","age":40}'); 64 65 $this->assertSame('Bob', $data->name); 66 $this->assertSame(40, $data->age); 67 } 68 69 public function test_it_throws_on_invalid_json(): void 70 { 71 $this->expectException(\InvalidArgumentException::class); 72 $this->expectExceptionMessage('Invalid JSON'); 73 74 TestData::fromJson('{invalid json}'); 75 } 76 77 public function test_it_creates_from_record(): void 78 { 79 $data = TestData::fromRecord([ 80 'name' => 'Alice', 81 'age' => 35, 82 ]); 83 84 $this->assertSame('Alice', $data->name); 85 $this->assertSame(35, $data->age); 86 } 87 88 public function test_it_converts_to_record(): void 89 { 90 $data = new TestData('John', 30); 91 92 $record = $data->toRecord(); 93 94 $this->assertSame([ 95 'name' => 'John', 96 'age' => 30, 97 '$type' => 'app.test.data', 98 ], $record); 99 } 100 101 public function test_it_checks_equality(): void 102 { 103 $data1 = new TestData('John', 30); 104 $data2 = new TestData('John', 30); 105 $data3 = new TestData('Jane', 25); 106 107 $this->assertTrue($data1->equals($data2)); 108 $this->assertFalse($data1->equals($data3)); 109 } 110 111 public function test_it_generates_hash(): void 112 { 113 $data = new TestData('John', 30); 114 115 $hash = $data->hash(); 116 117 $this->assertIsString($hash); 118 $this->assertSame(64, strlen($hash)); // SHA256 produces 64 hex characters 119 } 120 121 public function test_it_generates_same_hash_for_equal_data(): void 122 { 123 $data1 = new TestData('John', 30); 124 $data2 = new TestData('John', 30); 125 126 $this->assertSame($data1->hash(), $data2->hash()); 127 } 128 129 public function test_it_generates_different_hash_for_different_data(): void 130 { 131 $data1 = new TestData('John', 30); 132 $data2 = new TestData('Jane', 25); 133 134 $this->assertNotSame($data1->hash(), $data2->hash()); 135 } 136 137 public function test_it_gets_property_dynamically(): void 138 { 139 $data = new TestData('John', 30); 140 141 $this->assertSame('John', $data->__get('name')); 142 $this->assertSame(30, $data->__get('age')); 143 } 144 145 public function test_it_throws_on_nonexistent_property(): void 146 { 147 $data = new TestData('John', 30); 148 149 $this->expectException(\InvalidArgumentException::class); 150 $this->expectExceptionMessage('Property nonexistent does not exist'); 151 152 $data->__get('nonexistent'); 153 } 154 155 public function test_it_checks_if_property_exists(): void 156 { 157 $data = new TestData('John', 30); 158 159 $this->assertTrue($data->__isset('name')); 160 $this->assertTrue($data->__isset('age')); 161 $this->assertFalse($data->__isset('nonexistent')); 162 } 163 164 public function test_it_clones_with_modified_properties(): void 165 { 166 $data = new TestData('John', 30); 167 168 $modified = $data->with(['age' => 31]); 169 170 $this->assertSame('John', $modified->name); 171 $this->assertSame(31, $modified->age); 172 $this->assertNotSame($data, $modified); 173 $this->assertSame(30, $data->age); // Original unchanged 174 } 175 176 public function test_it_serializes_nested_data_objects(): void 177 { 178 $nested = new TestData('Inner', 20); 179 $parent = new TestDataWithNested('Outer', $nested); 180 181 $array = $parent->toArray(); 182 183 $this->assertSame([ 184 'name' => 'Outer', 185 'nested' => [ 186 'name' => 'Inner', 187 'age' => 20, 188 '$type' => 'app.test.data', 189 ], 190 ], $array); 191 } 192 193 public function test_it_serializes_arrays_of_data_objects(): void 194 { 195 $items = [ 196 new TestData('First', 10), 197 new TestData('Second', 20), 198 ]; 199 $collection = new TestDataWithArray('Collection', $items); 200 201 $array = $collection->toArray(); 202 203 $this->assertSame([ 204 'name' => 'Collection', 205 'items' => [ 206 ['name' => 'First', 'age' => 10, '$type' => 'app.test.data'], 207 ['name' => 'Second', 'age' => 20, '$type' => 'app.test.data'], 208 ], 209 ], $array); 210 } 211 212 public function test_it_serializes_datetime_objects(): void 213 { 214 $date = new \DateTime('2024-01-01T12:00:00Z'); 215 $data = new TestDataWithDate('Event', $date); 216 217 $array = $data->toArray(); 218 219 $this->assertArrayHasKey('createdAt', $array); 220 $this->assertIsString($array['createdAt']); 221 $this->assertStringContainsString('2024-01-01', $array['createdAt']); 222 } 223 224 public function test_it_returns_lexicon_nsid(): void 225 { 226 $lexicon = TestData::getLexicon(); 227 228 $this->assertSame('app.test.data', $lexicon); 229 } 230 231 public function test_validation_returns_true_when_helper_not_available(): void 232 { 233 $data = new TestData('John', 30); 234 235 // Schema helper may not be available in unit tests 236 $result = $data->validate(); 237 238 $this->assertTrue($result); 239 } 240 241 public function test_validation_errors_returns_empty_when_helper_not_available(): void 242 { 243 $data = new TestData('John', 30); 244 245 // Schema helper may not be available in unit tests 246 $errors = $data->validateWithErrors(); 247 248 $this->assertIsArray($errors); 249 } 250} 251 252// Test implementations 253 254class TestData extends Data 255{ 256 public function __construct( 257 public readonly string $name, 258 public readonly int $age 259 ) { 260 } 261 262 public static function getLexicon(): string 263 { 264 return 'app.test.data'; 265 } 266 267 public static function fromArray(array $data): static 268 { 269 return new static( 270 name: $data['name'], 271 age: $data['age'] 272 ); 273 } 274} 275 276class TestDataWithNested extends Data 277{ 278 public function __construct( 279 public readonly string $name, 280 public readonly TestData $nested 281 ) { 282 } 283 284 public static function getLexicon(): string 285 { 286 return 'app.test.nested'; 287 } 288 289 public static function fromArray(array $data): static 290 { 291 return new static( 292 name: $data['name'], 293 nested: TestData::fromArray($data['nested']) 294 ); 295 } 296} 297 298class TestDataWithArray extends Data 299{ 300 /** 301 * @param array<TestData> $items 302 */ 303 public function __construct( 304 public readonly string $name, 305 public readonly array $items 306 ) { 307 } 308 309 public static function getLexicon(): string 310 { 311 return 'app.test.collection'; 312 } 313 314 public static function fromArray(array $data): static 315 { 316 return new static( 317 name: $data['name'], 318 items: array_map( 319 fn ($item) => TestData::fromArray($item), 320 $data['items'] 321 ) 322 ); 323 } 324} 325 326class TestDataWithDate extends Data 327{ 328 public function __construct( 329 public readonly string $name, 330 public readonly \DateTimeInterface $createdAt 331 ) { 332 } 333 334 public static function getLexicon(): string 335 { 336 return 'app.test.dated'; 337 } 338 339 public static function fromArray(array $data): static 340 { 341 return new static( 342 name: $data['name'], 343 createdAt: new \DateTime($data['createdAt']) 344 ); 345 } 346}