alias(static fn (string $value): string => 'kses(' . $value . ')'); Functions\when('esc_url_raw')->alias(static fn (string $value): string => 'url(' . $value . ')'); } public function testEditorsGetKsesForRichTextAndCode(): void { Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap !== 'unfiltered_html'); $sanitizer = new WpValueSanitizer(); self::assertSame('kses()', $sanitizer->html('')); self::assertSame('kses()', $sanitizer->code('')); self::assertFalse($sanitizer->allowsUnfilteredHtml()); } public function testAdministratorsKeepRichTextAndCodeUnfiltered(): void { // Ruling S4: html() depends on unfiltered_html like code() – admin embeds survive updates. Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap === 'unfiltered_html'); $sanitizer = new WpValueSanitizer(); self::assertSame('', $sanitizer->html('')); self::assertSame('', $sanitizer->code('')); self::assertTrue($sanitizer->allowsUnfilteredHtml()); } public function testTextAndUrlDoNotDependOnTheRole(): void { Functions\when('current_user_can')->justReturn(true); $sanitizer = new WpValueSanitizer(); self::assertSame('Hallo', $sanitizer->text('Hallo')); self::assertSame('url(https://x.test/a b)', $sanitizer->url(' https://x.test/a b ')); } public function testCanReadPostAsksWordPress(): void { Functions\when('current_user_can')->alias(static fn (string $cap, int $id = 0): bool => $cap === 'read_post' && $id === 12); $sanitizer = new WpValueSanitizer(); self::assertTrue($sanitizer->canReadPost(12)); self::assertFalse($sanitizer->canReadPost(13)); } /** Ruling P10: the full form uses the {{link:}} targets – attachments, trash and unreadable entries are none. */ public function testLinkTargetTypeFollowsTheLinkTargets(): void { Functions\when('get_post')->alias(static fn (int $id): ?WP_Post => match ($id) { 12 => new WP_Post(['ID' => 12, 'post_type' => 'page', 'post_status' => 'draft']), 13 => new WP_Post(['ID' => 13, 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_parent' => 12]), 14 => new WP_Post(['ID' => 14, 'post_type' => 'page', 'post_status' => 'trash']), 16 => new WP_Post(['ID' => 16, 'post_type' => 'page', 'post_status' => 'private']), 17 => new WP_Post(['ID' => 17, 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_parent' => 0]), 18 => new WP_Post(['ID' => 18, 'post_type' => 'attachment', 'post_status' => 'trash', 'post_parent' => 0]), 19 => new WP_Post(['ID' => 19, 'post_type' => 'attachment', 'post_status' => 'private', 'post_parent' => 0]), 20 => new WP_Post(['ID' => 20, 'post_type' => 'post', 'post_status' => 'publish']), default => null, }); Functions\when('current_user_can')->alias(static fn (string $cap, int $id): bool => $id !== 16); $sanitizer = new WpValueSanitizer(new WpLinkTargets(static fn (string $type): bool => $type !== 'wp_block')); self::assertSame('page', $sanitizer->linkTargetType(12)); self::assertSame('post', $sanitizer->linkTargetType(20)); self::assertNull($sanitizer->linkTargetType(13), 'attached attachment'); self::assertNull($sanitizer->linkTargetType(14), 'trashed'); self::assertNull($sanitizer->linkTargetType(15), 'missing'); self::assertNull($sanitizer->linkTargetType(16), 'unreadable'); self::assertNull($sanitizer->linkTargetType(17), 'unattached attachment'); self::assertNull($sanitizer->linkTargetType(18), 'trashed attachment'); self::assertNull($sanitizer->linkTargetType(19), 'attachment with an own status'); } }