fix: never publish a missed schedule, refuse trashed posts, fail on meta read errors
- PostGuard::editable() refuses trashed entries unless the caller opts in (content-get, revisions-list, post-trash); publish/schedule/unpublish/update/ restore/duplicate/replace now refuse them. - PostGuard::statusStable(): content-update, revision-restore and replace-with-copy refuse a future post whose date has passed (any save would publish it); the content-update rollback and PostCloner row writes re-check. - PostCloner::ownMeta() treats a failed SELECT as an error; replace() reads the copy before its first write. - replace-with-copy needs create_posts (backup clone) and reports backup_id in write_failed/partial_write. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -76,6 +76,45 @@ final class RevisionRestoreAbilityTest extends TestCase
|
||||
self::assertArrayHasKey('revision_id', $error->get_error_data()['fields']);
|
||||
}
|
||||
|
||||
public function testATrashedEntryIsRefused(): void
|
||||
{
|
||||
Functions\when('get_post')->alias(static fn (int $id) => match ($id) {
|
||||
810 => new WP_Post(['ID' => 810, 'post_type' => 'revision', 'post_parent' => 5]),
|
||||
5 => new WP_Post(array_merge(self::CURRENT_POST, ['post_status' => 'trash'])),
|
||||
default => null,
|
||||
});
|
||||
Functions\expect('wp_restore_post_revision')->never();
|
||||
|
||||
$error = $this->ability(Mockery::mock(ChangeLog::class))->execute(['revision_id' => 810, 'rev' => 'x', 'confirm' => true]);
|
||||
|
||||
self::assertSame('lit_validation_failed', $error->get_error_code());
|
||||
self::assertArrayHasKey('revision_id', $error->get_error_data()['fields']);
|
||||
}
|
||||
|
||||
public function testAMissedScheduleIsRefusedBecauseRestoringWouldPublish(): void
|
||||
{
|
||||
$current = array_merge(self::CURRENT_POST, [
|
||||
'post_status' => 'future',
|
||||
'post_date_gmt' => gmdate('Y-m-d H:i:s', time() - 600),
|
||||
]);
|
||||
Functions\when('get_post')->alias(static fn (int $id) => match ($id) {
|
||||
810 => new WP_Post(['ID' => 810, 'post_type' => 'revision', 'post_parent' => 5]),
|
||||
5 => new WP_Post($current),
|
||||
default => null,
|
||||
});
|
||||
Functions\expect('wp_save_post_revision')->never();
|
||||
Functions\expect('wp_restore_post_revision')->never();
|
||||
|
||||
$error = $this->ability(Mockery::mock(ChangeLog::class))->execute([
|
||||
'revision_id' => 810,
|
||||
'rev' => Rev::ofPost(new WP_Post($current)),
|
||||
'confirm' => true,
|
||||
]);
|
||||
|
||||
self::assertSame('lit_validation_failed', $error->get_error_code());
|
||||
self::assertStringContainsString('lit-post-publish', $error->get_error_data()['hint']);
|
||||
}
|
||||
|
||||
public function testMissingOrStaleRevIsRevMismatch(): void
|
||||
{
|
||||
Functions\expect('wp_restore_post_revision')->never();
|
||||
|
||||
@@ -69,6 +69,14 @@ final class RevisionsListAbilityTest extends TestCase
|
||||
self::assertSame([PostPresenter::DIVI_HINT], $result['hints']);
|
||||
}
|
||||
|
||||
public function testRevisionsOfATrashedPostCanStillBeListed(): void
|
||||
{
|
||||
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 5, 'post_type' => 'page', 'post_status' => 'trash']));
|
||||
Functions\when('wp_get_post_revisions')->justReturn([]);
|
||||
|
||||
self::assertSame([], $this->ability()->execute(['post_id' => 5])['items']);
|
||||
}
|
||||
|
||||
public function testInvalidCursorIsRejected(): void
|
||||
{
|
||||
$result = $this->ability()->execute(['post_id' => 5, 'cursor' => 'not-a-cursor']);
|
||||
|
||||
Reference in New Issue
Block a user