$mirror->id, DiviAdapters::mirrors()); $signatureKeys = []; foreach (DiviAdapters::manifests() as $manifest) { foreach ($manifest->calls as $call) { $signatureKeys[] = $call->signatureKey(); } } $keys = array_values(array_unique(array_merge($mirrorIds, $signatureKeys))); sort($keys); $expected = DiviReference::expected(); self::assertSame($keys, array_keys($expected)); foreach ($expected as $key => $hash) { self::assertMatchesRegularExpression('/^[0-9a-f]{64}$/', $hash, $key); } foreach ($mirrorIds as $id) { self::assertNotNull(DiviReference::readSource($id, (string) DiviReference::sourceDir()), $id); } } public function testEverySignatureKeyStartsWithThePrefixNoMirrorIdUses(): void { foreach (DiviAdapters::mirrors() as $mirror) { self::assertStringStartsNotWith(DiviReference::SIGNATURE_PREFIX, $mirror->id); } self::assertSame('sig:et_pb_is_allowed', (new Call('et_pb_is_allowed', 1))->signatureKey()); self::assertSame( 'sig:ET_Core_PageResource::remove_static_resources', (new Call('\\ET_Core_PageResource::remove_static_resources', 3))->signatureKey() ); } public function testCallsListsEverySymbolOnceAcrossTheManifests(): void { $symbols = array_map(static fn (Call $call): string => ltrim($call->symbol, '\\'), DiviAdapters::calls()); self::assertSame($symbols, array_values(array_unique($symbols))); self::assertContains('ET_Core_PageResource::remove_static_resources', $symbols); self::assertCount(1, array_keys($symbols, 'ET_Core_PageResource::remove_static_resources', true)); self::assertContains('et_pb_is_allowed', $symbols); } public function testCurrentSignaturesAreSortedAndKeyedLikeTheReference(): void { $signatures = DiviReference::currentSignatures([ new Call('strlen', 1), new Call('\\str_replace', 3), new Call('strlen', 1), ]); self::assertSame(['sig:str_replace', 'sig:strlen'], array_keys($signatures)); self::assertSame(SignatureFingerprint::of(new Call('strlen')), $signatures['sig:strlen']); } public function testCurrentSignaturesRefuseASymbolThatIsNotLoaded(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('signature of lit_mcp_missing_function(): symbol is not loaded'); DiviReference::currentSignatures([new Call('strlen', 1), new Call('lit_mcp_missing_function', 0)]); } public function testSignatureKeysAreWrittenAndReadLikeMirrorIds(): void { $file = (string) tempnam(sys_get_temp_dir(), 'lit'); try { DiviReference::writeExpected([ 'sig:Z\\Klass::run' => str_repeat('c', 64), 'tb-api-save' => str_repeat('b', 64), 'sig:a_function' => str_repeat('a', 64), ], $file); self::assertSame( ['sig:Z\\Klass::run', 'sig:a_function', 'tb-api-save'], array_keys(DiviReference::expected($file)) ); } finally { unlink($file); } } public function testWritesAndReadsExpectedFingerprintsSorted(): void { $file = (string) tempnam(sys_get_temp_dir(), 'lit'); try { DiviReference::writeExpected(['b' => str_repeat('b', 64), 'a' => str_repeat('a', 64)], $file); self::assertSame(['a' => str_repeat('a', 64), 'b' => str_repeat('b', 64)], DiviReference::expected($file)); self::assertStringEndsWith("\n", (string) file_get_contents($file)); } finally { unlink($file); } } public function testMissingOrBrokenFileMeansNoExpectations(): void { self::assertSame([], DiviReference::expected('/nonexistent/fingerprints.json')); } }