allows('isManaged')->andReturnUsing(static fn (string $type): bool => in_array($type, ['page', 'post'], true)); return $schema; } public function testRejectsInvalidIds(): void { self::assertSame('lit_validation_failed', PostGuard::editable('abc', $this->schema())->get_error_code()); self::assertSame('lit_validation_failed', PostGuard::editable(-3, $this->schema())->get_error_code()); } public function testMissingOrUnmanagedPostsAreNotFound(): void { Functions\when('get_post')->alias(static fn (int $id) => $id === 8 ? new WP_Post(['ID' => 8, 'post_type' => 'et_pb_layout']) : null); self::assertSame('lit_not_found', PostGuard::editable(5, $this->schema())->get_error_code()); self::assertSame('lit_not_found', PostGuard::editable(8, $this->schema())->get_error_code()); } public function testRequiresEditRightsAndReturnsThePost(): void { $post = new WP_Post(['ID' => 5, 'post_type' => 'page']); Functions\when('get_post')->justReturn($post); Functions\when('current_user_can')->alias(static fn (string $cap, int $id = 0): bool => $id === 5); self::assertSame($post, PostGuard::editable('5', $this->schema())); Functions\when('current_user_can')->justReturn(false); self::assertSame('lit_forbidden', PostGuard::editable(5, $this->schema())->get_error_code()); } public function testTrashedPostsAreRefusedUnlessAllowed(): void { $post = new WP_Post(['ID' => 5, 'post_type' => 'page', 'post_status' => 'trash']); Functions\when('get_post')->justReturn($post); Functions\when('current_user_can')->justReturn(true); $error = PostGuard::editable(5, $this->schema(), 'copy_id'); self::assertSame('lit_validation_failed', $error->get_error_code()); self::assertArrayHasKey('copy_id', $error->get_error_data()['fields']); self::assertSame($post, PostGuard::editable(5, $this->schema(), 'post_id', true)); } public function testMissedScheduleIsAFuturePostWhoseDateHasPassed(): void { $past = gmdate('Y-m-d H:i:s', time() - 3600); $future = gmdate('Y-m-d H:i:s', time() + 3600); self::assertTrue(PostGuard::missedSchedule(new WP_Post(['post_status' => 'future', 'post_date_gmt' => $past]))); self::assertFalse(PostGuard::missedSchedule(new WP_Post(['post_status' => 'future', 'post_date_gmt' => $future]))); self::assertFalse(PostGuard::missedSchedule(new WP_Post(['post_status' => 'draft', 'post_date_gmt' => $past]))); Functions\when('get_gmt_from_date')->justReturn(''); self::assertFalse(PostGuard::missedSchedule( new WP_Post(['post_status' => 'future', 'post_date_gmt' => '0000-00-00 00:00:00']) )); } public function testMissedScheduleIncludesTheLastMinuteBeforePublication(): void { // wp_update_post() publishes a future post on save when its date is within the next 60 seconds. $in30s = gmdate('Y-m-d H:i:s', time() + 30); $in5min = gmdate('Y-m-d H:i:s', time() + 300); self::assertTrue(PostGuard::missedSchedule(new WP_Post(['post_status' => 'future', 'post_date_gmt' => $in30s]))); self::assertFalse(PostGuard::missedSchedule(new WP_Post(['post_status' => 'future', 'post_date_gmt' => $in5min]))); } public function testMissedScheduleFallsBackToTheLocalDateWhenTheGmtDateIsZero(): void { Functions\when('get_gmt_from_date')->alias( static fn (string $local): string => $local === '2020-01-01 10:00:00' ? '2020-01-01 09:00:00' : $local ); self::assertTrue(PostGuard::missedSchedule(new WP_Post([ 'post_status' => 'future', 'post_date_gmt' => '0000-00-00 00:00:00', 'post_date' => '2020-01-01 10:00:00', ]))); self::assertFalse(PostGuard::missedSchedule(new WP_Post([ 'post_status' => 'future', 'post_date_gmt' => '0000-00-00 00:00:00', 'post_date' => '2099-01-01 10:00:00', ]))); } public function testStatusStableRefusesAMissedScheduleWithAHint(): void { $post = new WP_Post(['ID' => 5, 'post_status' => 'future', 'post_date_gmt' => gmdate('Y-m-d H:i:s', time() - 60)]); $error = PostGuard::statusStable($post, 'original_id'); self::assertSame('lit_validation_failed', $error->get_error_code()); self::assertArrayHasKey('original_id', $error->get_error_data()['fields']); self::assertStringContainsString('lit-post-publish', $error->get_error_data()['hint']); self::assertStringContainsString('lit/post-schedule', $error->get_error_data()['hint']); self::assertNull(PostGuard::statusStable(new WP_Post(['post_status' => 'publish']))); } public function testOwnPostsOnlyReturnsTheCurrentUserWithoutEditOthersPosts(): void { Functions\when('get_post_type_object')->justReturn( (object) ['cap' => (object) ['edit_others_posts' => 'edit_others_posts']] ); Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap !== 'edit_others_posts'); Functions\when('get_current_user_id')->justReturn(7); self::assertSame(7, PostGuard::ownPostsOnly('page')); } public function testOwnPostsOnlyReturnsNullWithEditOthersPosts(): void { Functions\when('get_post_type_object')->justReturn( (object) ['cap' => (object) ['edit_others_posts' => 'edit_others_posts']] ); Functions\when('current_user_can')->justReturn(true); self::assertNull(PostGuard::ownPostsOnly('page')); } public function testPostTypeChecksAvailabilityAndCapability(): void { $object = (object) ['cap' => (object) ['create_posts' => 'edit_pages']]; Functions\when('get_post_type_object')->justReturn($object); Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap === 'edit_pages'); self::assertSame('lit_not_found', PostGuard::postType('et_pb_layout', $this->schema(), 'create_posts')->get_error_code()); self::assertSame('lit_not_found', PostGuard::postType(null, $this->schema(), 'create_posts')->get_error_code()); self::assertSame($object, PostGuard::postType('page', $this->schema(), 'create_posts')); Functions\when('current_user_can')->justReturn(false); self::assertSame('lit_forbidden', PostGuard::postType('page', $this->schema(), 'create_posts')->get_error_code()); } public function testAssertNoMissedScheduleRefusesAMissedScheduleWithTheCallersMessage(): void { Functions\when('get_post')->alias(static fn (int $id): ?WP_Post => match ($id) { 5 => new WP_Post(['ID' => 5, 'post_status' => 'future', 'post_date_gmt' => '2020-01-01 09:00:00']), 6 => new WP_Post(['ID' => 6, 'post_status' => 'publish']), default => null, }); PostGuard::assertNoMissedSchedule(6, 'nie'); PostGuard::assertNoMissedSchedule(7, 'nie'); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Post 5 not written'); PostGuard::assertNoMissedSchedule(5, 'Post 5 not written'); } }