Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at main 379 lines 11 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Tests\Unit\Services; 4 5use Illuminate\Http\UploadedFile; 6use Illuminate\Support\Facades\Storage; 7use Orchestra\Testbench\TestCase; 8use SocialDept\AtpSchema\Data\BlobReference; 9use SocialDept\AtpSchema\Exceptions\RecordValidationException; 10use SocialDept\AtpSchema\Services\BlobHandler; 11 12class BlobHandlerTest extends TestCase 13{ 14 protected BlobHandler $handler; 15 16 protected function setUp(): void 17 { 18 parent::setUp(); 19 20 Storage::fake('local'); 21 $this->handler = new BlobHandler('local'); 22 } 23 24 public function test_it_stores_uploaded_file(): void 25 { 26 $file = UploadedFile::fake()->image('photo.jpg', 100, 100); 27 28 $blob = $this->handler->store($file); 29 30 $this->assertInstanceOf(BlobReference::class, $blob); 31 $this->assertStringStartsWith('bafyrei', $blob->ref); 32 $this->assertEquals('image/jpeg', $blob->mimeType); 33 $this->assertGreaterThan(0, $blob->size); 34 $this->assertTrue($this->handler->exists($blob->ref)); 35 } 36 37 public function test_it_stores_file_with_valid_constraints(): void 38 { 39 $file = UploadedFile::fake()->image('photo.jpg', 100, 100); 40 41 $constraints = [ 42 'accept' => ['image/*'], 43 'maxSize' => 1024 * 1024 * 10, // 10MB 44 ]; 45 46 $blob = $this->handler->store($file, $constraints); 47 48 $this->assertInstanceOf(BlobReference::class, $blob); 49 $this->assertTrue($this->handler->exists($blob->ref)); 50 } 51 52 public function test_it_rejects_file_with_invalid_mime_type(): void 53 { 54 $this->expectException(RecordValidationException::class); 55 56 $file = UploadedFile::fake()->image('photo.jpg'); 57 58 $constraints = [ 59 'accept' => ['video/*'], 60 ]; 61 62 $this->handler->store($file, $constraints); 63 } 64 65 public function test_it_rejects_file_exceeding_max_size(): void 66 { 67 $this->expectException(RecordValidationException::class); 68 69 $file = UploadedFile::fake()->create('large.pdf', 2000); // 2MB 70 71 $constraints = [ 72 'maxSize' => 1024, // 1KB 73 ]; 74 75 $this->handler->store($file, $constraints); 76 } 77 78 public function test_it_rejects_file_below_min_size(): void 79 { 80 $this->expectException(RecordValidationException::class); 81 82 $file = UploadedFile::fake()->create('small.txt', 1); 83 84 $constraints = [ 85 'minSize' => 1024 * 1024, // 1MB 86 ]; 87 88 $this->handler->store($file, $constraints); 89 } 90 91 public function test_it_stores_from_string(): void 92 { 93 $content = 'Hello, world!'; 94 95 $blob = $this->handler->storeFromString($content, 'text/plain'); 96 97 $this->assertInstanceOf(BlobReference::class, $blob); 98 $this->assertEquals('text/plain', $blob->mimeType); 99 $this->assertEquals(strlen($content), $blob->size); 100 $this->assertTrue($this->handler->exists($blob->ref)); 101 } 102 103 public function test_it_stores_string_with_valid_constraints(): void 104 { 105 $content = 'Test content'; 106 107 $constraints = [ 108 'accept' => ['text/*'], 109 'maxSize' => 1024, 110 ]; 111 112 $blob = $this->handler->storeFromString($content, 'text/plain', $constraints); 113 114 $this->assertInstanceOf(BlobReference::class, $blob); 115 } 116 117 public function test_it_rejects_string_with_invalid_mime_type(): void 118 { 119 $this->expectException(RecordValidationException::class); 120 121 $constraints = [ 122 'accept' => ['image/*'], 123 ]; 124 125 $this->handler->storeFromString('content', 'text/plain', $constraints); 126 } 127 128 public function test_it_retrieves_blob_content(): void 129 { 130 $content = 'Test content'; 131 $blob = $this->handler->storeFromString($content, 'text/plain'); 132 133 $retrieved = $this->handler->get($blob->ref); 134 135 $this->assertEquals($content, $retrieved); 136 } 137 138 public function test_it_returns_null_for_nonexistent_blob(): void 139 { 140 $content = $this->handler->get('nonexistent-cid'); 141 142 $this->assertNull($content); 143 } 144 145 public function test_it_checks_blob_existence(): void 146 { 147 $blob = $this->handler->storeFromString('content', 'text/plain'); 148 149 $this->assertTrue($this->handler->exists($blob->ref)); 150 $this->assertFalse($this->handler->exists('nonexistent-cid')); 151 } 152 153 public function test_it_deletes_blob(): void 154 { 155 $blob = $this->handler->storeFromString('content', 'text/plain'); 156 157 $this->assertTrue($this->handler->exists($blob->ref)); 158 159 $result = $this->handler->delete($blob->ref); 160 161 $this->assertTrue($result); 162 $this->assertFalse($this->handler->exists($blob->ref)); 163 } 164 165 public function test_it_returns_false_when_deleting_nonexistent_blob(): void 166 { 167 $result = $this->handler->delete('nonexistent-cid'); 168 169 $this->assertFalse($result); 170 } 171 172 public function test_it_gets_blob_size(): void 173 { 174 $content = 'Test content'; 175 $blob = $this->handler->storeFromString($content, 'text/plain'); 176 177 $size = $this->handler->size($blob->ref); 178 179 $this->assertEquals(strlen($content), $size); 180 } 181 182 public function test_it_returns_null_size_for_nonexistent_blob(): void 183 { 184 $size = $this->handler->size('nonexistent-cid'); 185 186 $this->assertNull($size); 187 } 188 189 public function test_it_validates_blob_reference(): void 190 { 191 $blob = new BlobReference('cid', 'image/png', 1024); 192 193 $constraints = [ 194 'accept' => ['image/*'], 195 'maxSize' => 2048, 196 ]; 197 198 $this->handler->validate($blob, $constraints); 199 200 $this->assertTrue(true); // No exception thrown 201 } 202 203 public function test_it_rejects_blob_with_invalid_mime_type(): void 204 { 205 $this->expectException(RecordValidationException::class); 206 207 $blob = new BlobReference('cid', 'text/plain', 1024); 208 209 $constraints = [ 210 'accept' => ['image/*'], 211 ]; 212 213 $this->handler->validate($blob, $constraints); 214 } 215 216 public function test_it_rejects_blob_exceeding_max_size(): void 217 { 218 $this->expectException(RecordValidationException::class); 219 220 $blob = new BlobReference('cid', 'image/png', 2048); 221 222 $constraints = [ 223 'maxSize' => 1024, 224 ]; 225 226 $this->handler->validate($blob, $constraints); 227 } 228 229 public function test_it_rejects_blob_below_min_size(): void 230 { 231 $this->expectException(RecordValidationException::class); 232 233 $blob = new BlobReference('cid', 'image/png', 512); 234 235 $constraints = [ 236 'minSize' => 1024, 237 ]; 238 239 $this->handler->validate($blob, $constraints); 240 } 241 242 public function test_it_validates_multiple_mime_types(): void 243 { 244 $blob = new BlobReference('cid', 'image/png', 1024); 245 246 $constraints = [ 247 'accept' => ['image/jpeg', 'image/png', 'image/webp'], 248 ]; 249 250 $this->handler->validate($blob, $constraints); 251 252 $this->assertTrue(true); 253 } 254 255 public function test_it_validates_wildcard_mime_types(): void 256 { 257 $imageBlob = new BlobReference('cid1', 'image/png', 1024); 258 $videoBlob = new BlobReference('cid2', 'video/mp4', 1024); 259 260 $imageConstraints = ['accept' => ['image/*']]; 261 $videoConstraints = ['accept' => ['video/*']]; 262 263 $this->handler->validate($imageBlob, $imageConstraints); 264 $this->handler->validate($videoBlob, $videoConstraints); 265 266 $this->assertTrue(true); 267 } 268 269 public function test_it_generates_consistent_cids_for_same_content(): void 270 { 271 $content = 'Test content'; 272 273 $blob1 = $this->handler->storeFromString($content, 'text/plain'); 274 $blob2 = $this->handler->storeFromString($content, 'text/plain'); 275 276 $this->assertEquals($blob1->ref, $blob2->ref); 277 } 278 279 public function test_it_generates_different_cids_for_different_content(): void 280 { 281 $blob1 = $this->handler->storeFromString('Content 1', 'text/plain'); 282 $blob2 = $this->handler->storeFromString('Content 2', 'text/plain'); 283 284 $this->assertNotEquals($blob1->ref, $blob2->ref); 285 } 286 287 public function test_it_uses_directory_partitioning_for_storage(): void 288 { 289 $blob = $this->handler->storeFromString('content', 'text/plain'); 290 291 // CID should be used to create partitioned path 292 $prefix = substr($blob->ref, 0, 2); 293 $middle = substr($blob->ref, 2, 2); 294 $expectedPath = "blobs/{$prefix}/{$middle}/{$blob->ref}"; 295 296 Storage::disk('local')->assertExists($expectedPath); 297 } 298 299 public function test_it_can_change_storage_disk(): void 300 { 301 Storage::fake('custom'); 302 303 $this->handler->setDisk('custom'); 304 305 $blob = $this->handler->storeFromString('content', 'text/plain'); 306 307 $prefix = substr($blob->ref, 0, 2); 308 $middle = substr($blob->ref, 2, 2); 309 $expectedPath = "blobs/{$prefix}/{$middle}/{$blob->ref}"; 310 311 Storage::disk('custom')->assertExists($expectedPath); 312 } 313 314 public function test_it_can_change_base_path(): void 315 { 316 $this->handler->setBasePath('custom-path'); 317 318 $blob = $this->handler->storeFromString('content', 'text/plain'); 319 320 $prefix = substr($blob->ref, 0, 2); 321 $middle = substr($blob->ref, 2, 2); 322 $expectedPath = "custom-path/{$prefix}/{$middle}/{$blob->ref}"; 323 324 Storage::disk('local')->assertExists($expectedPath); 325 } 326 327 public function test_it_gets_disk_name(): void 328 { 329 $this->assertEquals('local', $this->handler->getDisk()); 330 331 $this->handler->setDisk('custom'); 332 333 $this->assertEquals('custom', $this->handler->getDisk()); 334 } 335 336 public function test_it_gets_base_path(): void 337 { 338 $this->assertEquals('blobs', $this->handler->getBasePath()); 339 340 $this->handler->setBasePath('custom-path'); 341 342 $this->assertEquals('custom-path', $this->handler->getBasePath()); 343 } 344 345 public function test_it_handles_file_without_mime_type(): void 346 { 347 $file = UploadedFile::fake()->create('unknown.bin'); 348 349 $blob = $this->handler->store($file); 350 351 // Should default to application/octet-stream if mime type is null 352 $this->assertNotNull($blob->mimeType); 353 } 354 355 public function test_it_accepts_constraints_as_array(): void 356 { 357 $blob = new BlobReference('cid', 'image/png', 1024); 358 359 $constraints = [ 360 'accept' => ['image/png', 'image/jpeg'], // Array of types 361 'maxSize' => 2048, 362 ]; 363 364 $this->handler->validate($blob, $constraints); 365 366 $this->assertTrue(true); 367 } 368 369 public function test_it_stores_binary_content(): void 370 { 371 $binaryContent = random_bytes(1024); 372 373 $blob = $this->handler->storeFromString($binaryContent, 'application/octet-stream'); 374 375 $retrieved = $this->handler->get($blob->ref); 376 377 $this->assertEquals($binaryContent, $retrieved); 378 } 379}