expectException(InvalidArgumentException::class); ModelMetadata::validateTitle($title); } public static function invalidTitles(): array { return [[''], [" \t\n"], ["a\0b"], ["\xff"], [str_repeat('é', 256)]]; } public function testPreservesValidAndUnchangedLegacyValues(): void { $old = ['title' => '', 'notes' => "\xff", 'sourceURL' => 'not a URL']; self::assertSame($old, ModelMetadata::replacement($old, $old)); $new = ['title' => ' ' . str_repeat('é', 253) . ' ', 'notes' => "\xff", 'sourceURL' => 'not a URL']; self::assertSame($new, ModelMetadata::replacement($old, $new)); $old = ['title' => 'T', 'notes' => 'old', 'sourceURL' => 'old']; self::assertSame(['title' => 'T', 'notes' => null, 'sourceURL' => null], ModelMetadata::replacement( $old, ['title' => 'T', 'notes' => '', 'sourceURL' => ''] )); ModelMetadata::validateTitle('é'); self::assertSame(" \n", ModelMetadata::replacement($old, array_replace($old, ['notes' => " \n"]))['notes']); } #[DataProvider('urls')] public function testUrls(string $url, bool $valid): void { $old = ['title' => 'T', 'notes' => null, 'sourceURL' => null]; if (!$valid) { $this->expectException(InvalidArgumentException::class); } $result = ModelMetadata::replacement($old, array_replace($old, ['sourceURL' => $url])); self::assertSame($url, $result['sourceURL']); } public static function urls(): array { $valid = ['https://example.com/a?q=x#f', 'http://127.0.0.1:80/', 'https://[::1]/', 'https://xn--bcher-kva.example/%C3%A9', 'https://example.com/' . str_repeat('a', 2028)]; $invalid = ['relative', 'ftp://example.com', 'https://', 'https://u:p@example.com', 'https://u@example.com', "https://example.com/\n", 'https://example.com/a b', 'https://example.com/\\x', 'https://example.com/%zz', 'https://example.com/%', 'https://example.com/é', 'http://example.com:65536', 'http://example.com:abc', 'https://example.com/' . str_repeat('a', 2030)]; return array_merge( array_map(static fn ($v) => [$v, true], $valid), array_map(static fn ($v) => [$v, false], $invalid) ); } #[DataProvider('badShapes')] public function testExactShape(array $shape): void { $this->expectException(InvalidArgumentException::class); ModelMetadata::validateShape($shape); } public static function badShapes(): array { return [[[]], [['title' => 'T', 'notes' => null]], [['title' => null, 'notes' => null, 'sourceURL' => null]], [['title' => 'T', 'notes' => 1, 'sourceURL' => null]], [['title' => 'T', 'notes' => null, 'sourceURL' => false]], [['title' => 'T', 'notes' => null, 'sourceURL' => null, 'extra' => null]]]; } #[DataProvider('badNotes')] public function testChangedNotesMustBePortable(string $notes): void { $old = ['title' => 'T', 'notes' => null, 'sourceURL' => null]; $this->expectException(InvalidArgumentException::class); ModelMetadata::replacement($old, array_replace($old, ['notes' => $notes])); } public static function badNotes(): array { return [["\xff"], ["a\0b"]]; } }