post = new WP_Post([ 'ID' => 5, 'post_type' => 'page', 'post_status' => 'publish', 'post_title' => 'Team', 'post_name' => 'team', 'post_content' => '

Alt

', ]); Functions\when('get_post')->justReturn($this->post); Functions\when('current_user_can')->justReturn(true); } private function schema(): ContentSchema { $schema = Mockery::mock(ContentSchema::class); $schema->allows('isManaged')->andReturn(true); return $schema; } private function ability(PostWriter $writer, ?ChangeLog $log = null): ContentUpdateAbility { return new ContentUpdateAbility($this->schema(), $writer, $log ?? Mockery::mock(ChangeLog::class)); } public function testRevIsRequired(): void { $writer = Mockery::mock(PostWriter::class); Functions\expect('wp_update_post')->never(); self::assertContains('rev', $this->ability($writer)->inputSchema()['required']); self::assertSame( 'lit_rev_mismatch', $this->ability($writer)->execute(['post_id' => 5, 'title' => 'Neu'])->get_error_code() ); } public function testStaleRevIsRejectedWithoutWriting(): void { Functions\expect('wp_update_post')->never(); $result = $this->ability(Mockery::mock(PostWriter::class)) ->execute(['post_id' => 5, 'rev' => 'stale', 'title' => 'Neu']); self::assertSame('lit_rev_mismatch', $result->get_error_code()); self::assertStringContainsString('lit-content-get', $result->get_error_data()['hint']); } public function testAContentOnlyRevIsStaleBecauseTitleAndStatusCountToo(): void { $result = $this->ability(Mockery::mock(PostWriter::class)) ->execute(['post_id' => 5, 'rev' => Rev::of('

Alt

'), 'title' => 'Neu']); self::assertSame('lit_rev_mismatch', $result->get_error_code()); } public function testRawContentOnDiviPagesIsUnsupported(): void { Functions\when('get_post_meta')->justReturn('on'); $writer = Mockery::mock(PostWriter::class); $writer->expects('prepare')->never(); $result = $this->ability($writer) ->execute(['post_id' => 5, 'rev' => Rev::ofPost($this->post), 'content' => '

Neu

']); self::assertSame('lit_unsupported', $result->get_error_code()); self::assertStringContainsString('lit-divi-outline', $result->get_error_message()); } public function testNothingToChangeIsAValidationError(): void { $writer = Mockery::mock(PostWriter::class); $writer->allows('postData')->andReturn([]); $writer->allows('hasExtras')->andReturn(false); $result = $this->ability($writer)->execute(['post_id' => 5, 'rev' => Rev::ofPost($this->post)]); self::assertSame('lit_validation_failed', $result->get_error_code()); } public function testUpdateKeepsTheStatusAndCapturesARestorePointFirst(): void { $order = []; $prepared = ['fields' => [], 'terms' => [], 'warnings' => []]; $input = ['post_id' => 5, 'rev' => Rev::ofPost($this->post), 'title' => 'Unser Team']; $writer = Mockery::mock(PostWriter::class); $writer->allows('postData')->andReturn(['post_title' => 'Unser Team']); $writer->allows('hasExtras')->andReturn(false); $writer->allows('prepare')->with('page', $input, false)->andReturn($prepared); $writer->allows('snapshot')->andReturn(['fields' => [], 'terms' => []]); $writer->expects('writeExtras')->with(5, 'page', $prepared, $input); Functions\expect('wp_save_post_revision')->once()->with(5)->andReturnUsing(static function () use (&$order) { $order[] = 'revision'; return 811; }); Functions\when('wp_get_post_revisions')->justReturn([811 => new WP_Post(['ID' => 811])]); Functions\expect('wp_update_post')->once()->with(['post_title' => 'Unser Team', 'ID' => 5], true) ->andReturnUsing(static function () use (&$order): int { $order[] = 'update'; return 5; }); Actions\expectDone('lit_mcp_after_save')->once()->with(5, 'lit/content-update', $input); Functions\when('get_preview_post_link')->justReturn('https://kunde.de/team/'); Functions\when('get_edit_post_link')->justReturn('https://kunde.de/wp-admin/post.php?post=5&action=edit'); $log = Mockery::mock(ChangeLog::class); $log->expects('record')->with('lit/content-update', 'page', '5', Mockery::type('string'), 811)->andReturn(4711); $result = $this->ability($writer, $log)->execute($input); self::assertSame(['revision', 'update'], $order); self::assertSame(811, $result['revision_id']); self::assertSame(4711, $result['log_id']); self::assertSame('updated', $result['changed'][0]['action']); self::assertSame(Rev::ofPost($this->post), $result['rev']); self::assertStringContainsString('Status unverändert: publish', $result['summary']); } /** * @param array $data * @return array{PostWriter, array, array} */ private function failingWriter(array $data): array { $input = ['post_id' => 5, 'rev' => Rev::ofPost($this->post), 'title' => 'Unser Team', 'fields' => ['a' => 1]]; $snapshot = ['fields' => ['a' => 0], 'terms' => []]; $writer = Mockery::mock(PostWriter::class); $writer->allows('postData')->andReturn($data); $writer->allows('prepare')->andReturn(['fields' => ['a' => 1], 'terms' => [], 'warnings' => []]); $writer->expects('snapshot')->with(5, 'page', Mockery::type('array'), $input)->andReturn($snapshot); $writer->expects('writeExtras')->andThrow(new RuntimeException('meta kaputt')); $writer->expects('restore')->with(5, 'page', $snapshot); Functions\when('wp_save_post_revision')->justReturn(811); Functions\when('wp_get_post_revisions')->justReturn([811 => new WP_Post(['ID' => 811])]); Functions\when('get_page_template_slug')->justReturn(''); Actions\expectDone('lit_mcp_after_save')->never(); return [$writer, $input, $snapshot]; } /** @param array $input */ private function executeExpectingRollback(PostWriter $writer, array $input): void { $log = Mockery::mock(ChangeLog::class); $log->expects('record')->never(); try { $this->ability($writer, $log)->execute($input); self::fail('RuntimeException expected'); } catch (RuntimeException $e) { self::assertStringContainsString('meta kaputt', $e->getMessage()); self::assertStringContainsString('rolled back', $e->getMessage()); } } public function testFailingExtrasRestoreTheRevisionAndTheOldValues(): void { [$writer, $input] = $this->failingWriter(['post_title' => 'Unser Team', 'post_name' => 'unser-team']); $updates = []; Functions\when('wp_update_post')->alias(static function (array $data) use (&$updates): int { $updates[] = $data; return 5; }); Functions\when('wp_revisions_enabled')->justReturn(true); Functions\expect('wp_restore_post_revision')->once()->with(811, ['post_title'])->andReturn(5); $this->executeExpectingRollback($writer, $input); self::assertSame([ ['post_title' => 'Unser Team', 'post_name' => 'unser-team', 'ID' => 5], ['post_name' => 'team', 'ID' => 5], ], $updates); } public function testWithoutRevisionsTheOldColumnValuesAreWrittenBack(): void { [$writer, $input] = $this->failingWriter(['post_title' => 'Unser Team', 'page_template' => 'tpl-wide.php']); $updates = []; Functions\when('wp_update_post')->alias(static function (array $data) use (&$updates): int { $updates[] = $data; return 5; }); Functions\when('wp_revisions_enabled')->justReturn(false); Functions\expect('wp_restore_post_revision')->never(); $this->executeExpectingRollback($writer, $input); self::assertSame([ ['post_title' => 'Unser Team', 'page_template' => 'tpl-wide.php', 'ID' => 5], ['post_title' => 'Team', 'page_template' => 'default', 'ID' => 5], ], $updates); } }