Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at dev 6.7 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Tests\Unit\Parser; 4 5use Illuminate\Support\Facades\Cache; 6use Orchestra\Testbench\TestCase; 7use SocialDept\AtpSchema\Data\LexiconDocument; 8use SocialDept\AtpSchema\Exceptions\SchemaNotFoundException; 9use SocialDept\AtpSchema\Exceptions\SchemaParseException; 10use SocialDept\AtpSchema\Parser\SchemaLoader; 11 12class SchemaLoaderTest extends TestCase 13{ 14 protected string $fixturesPath; 15 16 protected function setUp(): void 17 { 18 parent::setUp(); 19 20 $this->fixturesPath = __DIR__ . '/../../fixtures'; 21 22 // Clear cache before each test 23 Cache::flush(); 24 } 25 26 public function test_it_loads_schema_from_hierarchical_json(): void 27 { 28 $loader = new SchemaLoader([$this->fixturesPath], false); 29 30 $schema = $loader->load('app.bsky.feed.post'); 31 32 $this->assertInstanceOf(LexiconDocument::class, $schema); 33 $this->assertSame(1, $schema->lexicon); 34 $this->assertSame('app.bsky.feed.post', $schema->getNsid()); 35 } 36 37 public function test_it_loads_schema_from_flat_php(): void 38 { 39 $loader = new SchemaLoader([$this->fixturesPath], false); 40 41 $schema = $loader->load('com.atproto.repo.getRecord'); 42 43 $this->assertInstanceOf(LexiconDocument::class, $schema); 44 $this->assertSame(1, $schema->lexicon); 45 $this->assertSame('com.atproto.repo.getRecord', $schema->getNsid()); 46 } 47 48 public function test_it_checks_if_schema_exists(): void 49 { 50 $loader = new SchemaLoader([$this->fixturesPath], false); 51 52 $this->assertTrue($loader->exists('app.bsky.feed.post')); 53 $this->assertTrue($loader->exists('com.atproto.repo.getRecord')); 54 $this->assertFalse($loader->exists('nonexistent.schema')); 55 } 56 57 public function test_it_throws_when_schema_not_found(): void 58 { 59 $loader = new SchemaLoader([$this->fixturesPath], false); 60 61 $this->expectException(SchemaNotFoundException::class); 62 $this->expectExceptionMessage('Schema not found for NSID: nonexistent.schema'); 63 64 $loader->load('nonexistent.schema'); 65 } 66 67 public function test_it_caches_schemas_in_memory(): void 68 { 69 $loader = new SchemaLoader([$this->fixturesPath], false); 70 71 // First load 72 $schema1 = $loader->load('app.bsky.feed.post'); 73 74 // Second load should come from memory 75 $schema2 = $loader->load('app.bsky.feed.post'); 76 77 $this->assertSame($schema1, $schema2); 78 $this->assertContains('app.bsky.feed.post', $loader->getCachedNsids()); 79 } 80 81 public function test_it_caches_schemas_in_laravel_cache(): void 82 { 83 $loader = new SchemaLoader([$this->fixturesPath], true, 3600, 'schema'); 84 85 // First load - should store in cache 86 $schema = $loader->load('app.bsky.feed.post'); 87 88 // Clear memory cache to force Laravel cache lookup 89 $loader->clearCache('app.bsky.feed.post'); 90 91 // Manually put raw array back in Laravel cache 92 Cache::put('schema:parsed:app.bsky.feed.post', $schema->toArray(), 3600); 93 94 // This should retrieve from Laravel cache 95 $cached = $loader->load('app.bsky.feed.post'); 96 97 // The schemas should be equivalent (different object instances but same data) 98 $this->assertEquals($schema->toArray(), $cached->toArray()); 99 } 100 101 public function test_it_retrieves_from_laravel_cache(): void 102 { 103 $loader = new SchemaLoader([$this->fixturesPath], true); 104 105 // First load to cache it 106 $originalSchema = $loader->load('app.bsky.feed.post'); 107 108 // Clear memory cache 109 $loader->clearCache('app.bsky.feed.post'); 110 111 // Second load should come from Laravel cache 112 $cachedSchema = $loader->load('app.bsky.feed.post'); 113 114 $this->assertEquals($originalSchema->toArray(), $cachedSchema->toArray()); 115 $this->assertSame('app.bsky.feed.post', $cachedSchema->getNsid()); 116 } 117 118 public function test_it_clears_specific_schema_cache(): void 119 { 120 $loader = new SchemaLoader([$this->fixturesPath], true); 121 122 // Load to populate caches 123 $loader->load('app.bsky.feed.post'); 124 125 $this->assertContains('app.bsky.feed.post', $loader->getCachedNsids()); 126 127 // Clear cache 128 $loader->clearCache('app.bsky.feed.post'); 129 130 // Memory cache should be cleared 131 $this->assertNotContains('app.bsky.feed.post', $loader->getCachedNsids()); 132 133 // Laravel cache should also be cleared (verify by loading again and checking it comes from file) 134 $this->assertFalse(Cache::has('schema:parsed:app.bsky.feed.post')); 135 } 136 137 public function test_it_clears_all_memory_cache(): void 138 { 139 $loader = new SchemaLoader([$this->fixturesPath], false); 140 141 // Load multiple schemas 142 $loader->load('app.bsky.feed.post'); 143 $loader->load('com.atproto.repo.getRecord'); 144 145 $this->assertCount(2, $loader->getCachedNsids()); 146 147 // Clear all 148 $loader->clearCache(); 149 150 $this->assertCount(0, $loader->getCachedNsids()); 151 } 152 153 public function test_it_searches_multiple_sources_in_order(): void 154 { 155 $source1 = $this->fixturesPath . '/source1'; 156 $source2 = $this->fixturesPath; 157 158 // Schema only exists in source2 159 $loader = new SchemaLoader([$source1, $source2], false); 160 161 $schema = $loader->load('app.bsky.feed.post'); 162 163 $this->assertSame('app.bsky.feed.post', $schema->getNsid()); 164 } 165 166 public function test_it_throws_on_invalid_json(): void 167 { 168 $invalidPath = $this->fixturesPath . '/invalid'; 169 @mkdir($invalidPath, 0755, true); 170 file_put_contents($invalidPath . '/invalid.json', '{invalid json}'); 171 172 $loader = new SchemaLoader([$invalidPath], false); 173 174 $this->expectException(SchemaParseException::class); 175 $this->expectExceptionMessage('Failed to parse schema JSON'); 176 177 try { 178 $loader->load('invalid'); 179 } finally { 180 @unlink($invalidPath . '/invalid.json'); 181 @rmdir($invalidPath); 182 } 183 } 184 185 public function test_it_throws_on_php_file_not_returning_array(): void 186 { 187 $invalidPath = $this->fixturesPath . '/invalid'; 188 @mkdir($invalidPath, 0755, true); 189 file_put_contents($invalidPath . '/invalid.php', '<?php return "not an array";'); 190 191 $loader = new SchemaLoader([$invalidPath], false); 192 193 $this->expectException(SchemaParseException::class); 194 $this->expectExceptionMessage('PHP file must return an array'); 195 196 try { 197 $loader->load('invalid'); 198 } finally { 199 @unlink($invalidPath . '/invalid.php'); 200 @rmdir($invalidPath); 201 } 202 } 203 204}