> */ private array $logs = array(); protected function tearDown(): void { unset( $GLOBALS['wpcs_test_terms'], $GLOBALS['wpcs_test_next_term_id'], $GLOBALS['wpcs_test_term_meta'] ); $this->logs = array(); parent::tearDown(); } public function test_it_creates_new_terms_by_taxonomy_and_slug(): void { $result = $this->handler()->importRecords( array( $this->termRecord(), ), $this->context( 'last_write_wins' ) ); $term = get_term_by( 'slug', 'news', 'category' ); self::assertTrue( $result->isSuccessful() ); self::assertSame( 1, $result->created() ); self::assertSame( 'News', $term->name ); self::assertSame( 42, get_term_meta( $term->term_id, '_wpcs_source_id', true ) ); self::assertSame( 'https://source.test', get_term_meta( $term->term_id, '_wpcs_source_site', true ) ); } public function test_it_updates_existing_terms_with_last_write_wins(): void { $existing = wp_insert_term( 'Old News', 'category', array( 'slug' => 'news' ) ); update_term_meta( $existing['term_id'], '_wpcs_source_id', 42 ); $result = $this->handler()->importRecords( array( $this->termRecord( array( 'name' => 'Updated News' ) ), ), $this->context( 'last_write_wins' ) ); $term = get_term_by( 'id', $existing['term_id'], 'category' ); self::assertTrue( $result->isSuccessful() ); self::assertSame( 1, $result->updated() ); self::assertSame( 'Updated News', $term->name ); } public function test_it_skips_existing_terms_with_manual_review_conflict(): void { $existing = wp_insert_term( 'Old News', 'category', array( 'slug' => 'news' ) ); update_term_meta( $existing['term_id'], '_wpcs_source_id', 42 ); $result = $this->handler()->importRecords( array( $this->termRecord( array( 'name' => 'Updated News' ) ), ), $this->context( 'manual_review' ) ); $term = get_term_by( 'id', $existing['term_id'], 'category' ); self::assertTrue( $result->isSuccessful() ); self::assertSame( 1, $result->skipped() ); self::assertSame( 1, $result->conflicts() ); self::assertSame( 'Old News', $term->name ); self::assertSame( 'Skipped term import because manual review is required.', $this->logs[0]['message'] ); } public function test_it_rewrites_term_description_and_meta_urls(): void { $result = $this->handler()->importRecords( array( $this->termRecord( array( 'description' => 'News', 'meta' => array( 'landing_url' => 'https://source.test/news', 'json_links' => '{"url":"https://source.test/news"}', ), ) ), ), $this->context( 'last_write_wins' ) ); $term = get_term_by( 'slug', 'news', 'category' ); self::assertTrue( $result->isSuccessful() ); self::assertSame( 'News', $term->description ); self::assertSame( 'https://destination.test/news', get_term_meta( $term->term_id, 'landing_url', true ) ); self::assertSame( '{"url":"https:\/\/destination.test\/news"}', get_term_meta( $term->term_id, 'json_links', true ) ); } public function test_it_returns_failure_when_wordpress_rejects_term_save(): void { $result = $this->handler()->importRecords( array( $this->termRecord( array( 'id' => 0, 'taxonomy' => '', 'name' => '', ) ), ), $this->context( 'last_write_wins' ) ); self::assertFalse( $result->isSuccessful() ); self::assertSame( array( 'Term import failed for source ID 0.' ), $result->errors() ); self::assertSame( array(), get_term_meta( 0, '_wpcs_source_id', false ) ); } private function handler(): TermContentHandler { return new TermContentHandler( 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 termRecord( array $overrides = array() ): array { return array_merge( array( 'id' => 42, 'taxonomy' => 'category', 'name' => 'News', 'slug' => 'news', 'description' => 'News description', 'parent' => 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, ); } }; } }