> */ private array $logs = array(); protected function tearDown(): void { unset( $GLOBALS['wpcs_test_posts'], $GLOBALS['wpcs_test_next_post_id'], $GLOBALS['wpcs_test_post_meta'] ); $this->logs = array(); parent::tearDown(); } public function test_it_creates_new_post_records(): void { $result = $this->handler()->importRecords( array( $this->postRecord(), ), $this->context( 'last_write_wins' ) ); self::assertTrue( $result->isSuccessful() ); self::assertSame( 1, $result->created() ); self::assertSame( 'Imported Title', get_post( 1 )['post_title'] ); self::assertSame( 42, get_post_meta( 1, '_wpcs_source_id', true ) ); self::assertSame( 'https://source.test', get_post_meta( 1, '_wpcs_source_site', true ) ); } public function test_it_updates_existing_posts_with_last_write_wins(): void { $post_id = wp_insert_post( array( 'post_title' => 'Old Title', 'post_type' => 'post', ), true ); update_post_meta( $post_id, '_wpcs_source_id', 42 ); $result = $this->handler()->importRecords( array( $this->postRecord( array( 'post_title' => 'New Title' ) ), ), $this->context( 'last_write_wins' ) ); self::assertTrue( $result->isSuccessful() ); self::assertSame( 1, $result->updated() ); self::assertSame( 'New Title', get_post( $post_id )['post_title'] ); } public function test_it_skips_existing_posts_with_manual_review_conflict(): void { $post_id = wp_insert_post( array( 'post_title' => 'Old Title', 'post_type' => 'post', ), true ); update_post_meta( $post_id, '_wpcs_source_id', 42 ); $result = $this->handler()->importRecords( array( $this->postRecord( array( 'post_title' => 'New Title' ) ), ), $this->context( 'manual_review' ) ); self::assertTrue( $result->isSuccessful() ); self::assertSame( 1, $result->skipped() ); self::assertSame( 1, $result->conflicts() ); self::assertSame( 'Old Title', get_post( $post_id )['post_title'] ); self::assertSame( 'Skipped post import because manual review is required.', $this->logs[0]['message'] ); } public function test_it_rewrites_post_content_excerpt_and_meta_urls(): void { $result = $this->handler()->importRecords( array( $this->postRecord( array( 'post_content' => 'Page', 'post_excerpt' => 'Read https://source.test/page', 'meta' => array( '_source_url' => 'https://source.test/page', '_json_links' => '{"url":"https://source.test/page"}', ), ) ), ), $this->context( 'last_write_wins' ) ); self::assertTrue( $result->isSuccessful() ); self::assertSame( 'Page', get_post( 1 )['post_content'] ); self::assertSame( 'Read https://destination.test/page', get_post( 1 )['post_excerpt'] ); self::assertSame( 'https://destination.test/page', get_post_meta( 1, '_source_url', true ) ); self::assertSame( '{"url":"https:\/\/destination.test\/page"}', get_post_meta( 1, '_json_links', true ) ); } public function test_it_returns_failure_when_wordpress_rejects_post_save(): void { $result = $this->handler()->importRecords( array( $this->postRecord( array( 'id' => 0, 'post_type' => '', ) ), ), $this->context( 'last_write_wins' ) ); self::assertFalse( $result->isSuccessful() ); self::assertSame( array( 'Post import failed for source ID 0.' ), $result->errors() ); self::assertSame( array(), get_post_meta( 0, '_wpcs_source_id', false ) ); } private function handler(): PostContentHandler { return new PostContentHandler( new ContentRecordNormalizer(), new UrlTransformer(), new MetadataUrlTransformer( new UrlTransformer() ), $this->logger() ); } private function context( string $conflict_strategy ): SyncContext { return SyncContext::forImport( array( 'site_url' => 'https://source.test' ), array( 'site_url' => 'https://destination.test' ), $conflict_strategy, 'operation-1' ); } /** * @param array $overrides Record overrides. * @return array */ private function postRecord( array $overrides = array() ): array { return array_merge( array( 'id' => 42, 'post_type' => 'post', 'post_title' => 'Imported Title', 'post_content' => 'Imported content', 'post_excerpt' => 'Imported excerpt', 'post_status' => 'publish', 'post_name' => 'imported-title', 'post_parent' => 0, 'menu_order' => 0, 'meta' => array(), ), $overrides ); } private function logger(): LoggerInterface { return new class( $this->logs ) implements LoggerInterface { /** @var array> */ private array $logs; /** * @param array> $logs Logs. */ public function __construct( array &$logs ) { $this->logs = &$logs; } public function error( string $message, array $context = array() ): void { $this->record( 'error', $message, $context ); } public function warning( string $message, array $context = array() ): void { $this->record( 'warning', $message, $context ); } public function info( string $message, array $context = array() ): void { $this->record( 'info', $message, $context ); } public function debug( string $message, array $context = array() ): void { $this->record( 'debug', $message, $context ); } /** * @param array $context Context. */ private function record( string $level, string $message, array $context ): void { $this->logs[] = array( 'level' => $level, 'message' => $message, 'context' => $context, ); } }; } }