*/ private array $temporary_files = array(); /** @var array> */ private array $logs = array(); protected function tearDown(): void { foreach ( $this->temporary_files as $file ) { if ( is_file( $file ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink -- Removing a PHPUnit temp file. unlink( $file ); } } unset( $GLOBALS['wpcs_current_user_can'], $GLOBALS['wpcs_nonce_valid'], $GLOBALS['wpcs_redirect_location'], $GLOBALS['wpcs_test_options'], $GLOBALS['wpcs_test_option_autoloads'], $GLOBALS['wpcs_test_transients'], $GLOBALS['wpcs_test_transient_expiration'] ); $_FILES = array(); $this->logs = array(); parent::tearDown(); } public function test_it_rejects_users_without_manage_options(): void { $GLOBALS['wpcs_current_user_can']['manage_options'] = false; $controller = $this->controller(); $this->expectException( \RuntimeException::class ); $this->expectExceptionMessage( 'You do not have permission to import content packages.' ); $controller->handleImport(); } public function test_it_rejects_invalid_nonces(): void { $GLOBALS['wpcs_nonce_valid']['wpcs_import_package']['wpcs_import_package_nonce'] = false; $controller = $this->controller(); $this->expectException( \RuntimeException::class ); $this->expectExceptionMessage( 'The import request could not be verified.' ); $controller->handleImport(); } public function test_it_rejects_missing_uploads(): void { $controller = $this->controller(); $this->expectException( \RuntimeException::class ); $this->expectExceptionMessage( 'Choose a package JSON file before importing.' ); $controller->handleImport(); } public function test_it_rejects_failed_uploads(): void { $_FILES['wpcs_package_file'] = array( 'tmp_name' => '', 'error' => UPLOAD_ERR_INI_SIZE, ); $controller = $this->controller(); $this->expectException( \RuntimeException::class ); $this->expectExceptionMessage( 'The package file could not be uploaded.' ); $controller->handleImport(); } public function test_it_imports_valid_uploaded_packages_with_sync_engine(): void { $file = $this->createTemporaryPackageFile( $this->validJson() ); $_FILES['wpcs_package_file'] = array( 'tmp_name' => $file, 'error' => UPLOAD_ERR_OK, ); $this->controller()->handleImport(); self::assertStringContainsString( 'wpcs_imported=1', $GLOBALS['wpcs_redirect_location'] ); self::assertSame( 'Imported content package.', $this->logs[2]['message'] ); self::assertSame( 0, $this->logs[2]['context']['created'] ); } public function test_it_redirects_with_error_when_sync_engine_import_fails(): void { $file = $this->createTemporaryPackageFile( $this->validJson() ); $_FILES['wpcs_package_file'] = array( 'tmp_name' => $file, 'error' => UPLOAD_ERR_OK, ); $this->controller( SyncResult::failure( array( 'Posts failed.' ) ) )->handleImport(); self::assertStringContainsString( 'wpcs_import_error=', $GLOBALS['wpcs_redirect_location'] ); self::assertStringContainsString( 'Posts+failed.', $GLOBALS['wpcs_redirect_location'] ); } public function test_it_redirects_with_error_for_invalid_uploaded_packages(): void { $file = $this->createTemporaryPackageFile( '{"schema_version":' ); $_FILES['wpcs_package_file'] = array( 'tmp_name' => $file, 'error' => UPLOAD_ERR_OK, ); $this->controller()->handleImport(); self::assertStringContainsString( 'wpcs_import_error=', $GLOBALS['wpcs_redirect_location'] ); self::assertStringContainsString( 'not+valid+JSON', $GLOBALS['wpcs_redirect_location'] ); } private function controller( ?SyncResult $result = null ): FileImportController { $logger = $this->logger(); return new FileImportController( new JsonFileTransport( new PackageValidator() ), $logger, $this->syncEngine( $result ?? SyncResult::success() ) ); } private function syncEngine( SyncResult $result ): SyncEngine { return new SyncEngine( new ContentHandlerRegistry( array( $this->handler( $result ) ) ), new SyncStateRepository(), new SettingsRepository(), $this->logger() ); } private function handler( SyncResult $result ): ContentHandlerInterface { return new class( $result ) implements ContentHandlerInterface { private SyncResult $result; public function __construct( SyncResult $result ) { $this->result = $result; } public function bucket(): string { return 'posts'; } public function importRecords( array $records, SyncContext $context ): SyncResult { return $this->result; } }; } 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, ); } }; } private function validJson(): string { $records = array( 'posts' => array(), 'terms' => array(), 'media' => array(), 'custom_post_types' => array(), ); $json = wp_json_encode( array( 'schema_version' => '1.0', 'generated_at' => '2026-04-26T20:30:00+00:00', 'source' => array( 'site_url' => 'https://example.test', 'name' => 'Example', ), 'destination' => array( 'site_url' => 'https://staging.example.test', 'name' => 'Staging', ), 'manifest' => array( 'posts' => 0, 'terms' => 0, 'media' => 0, 'custom_post_types' => 0, ), 'records' => $records, 'checksums' => array( 'records' => PackageChecksum::records( $records ), ), ) ); if ( false === $json ) { throw new \RuntimeException( 'Unable to create package JSON fixture.' ); } return $json; } private function createTemporaryPackageFile( string $contents ): string { $file = tempnam( sys_get_temp_dir(), 'wpcs-package-' ); if ( false === $file ) { throw new \RuntimeException( 'Unable to create temporary package file.' ); } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Creating a PHPUnit temp fixture. file_put_contents( $file, $contents ); $this->temporary_files[] = $file; return $file; } }