Files
WP-Content-Sync/tests/Unit/Rest/RestPackageControllerTest.php
2026-05-01 15:23:25 -05:00

270 lines
6.8 KiB
PHP

<?php
/**
* Tests for REST package receive/status controller.
*
* @package WPContentSync
*/
namespace WPContentSync\Tests\Unit\Rest;
use PHPUnit\Framework\TestCase;
use WPContentSync\Content\ContentHandlerInterface;
use WPContentSync\Content\ContentHandlerRegistry;
use WPContentSync\Logging\LoggerInterface;
use WPContentSync\Package\PackageChecksum;
use WPContentSync\Package\PackageValidator;
use WPContentSync\Rest\RestPackageController;
use WPContentSync\Settings\SettingsRepository;
use WPContentSync\Sync\SyncContext;
use WPContentSync\Sync\SyncEngine;
use WPContentSync\Sync\SyncResult;
use WPContentSync\Sync\SyncStateRepository;
class RestPackageControllerTest extends TestCase {
protected function setUp(): void {
parent::setUp();
unset( $GLOBALS['wpcs_rest_routes'], $GLOBALS['wpcs_current_user_can'], $GLOBALS['wpcs_test_actions'] );
}
protected function tearDown(): void {
unset(
$GLOBALS['wpcs_rest_routes'],
$GLOBALS['wpcs_current_user_can'],
$GLOBALS['wpcs_test_actions'],
$GLOBALS['wpcs_test_options'],
$GLOBALS['wpcs_test_option_autoloads'],
$GLOBALS['wpcs_test_transients'],
$GLOBALS['wpcs_test_transient_expiration']
);
parent::tearDown();
}
public function test_it_hooks_route_registration_to_rest_api_init(): void {
$controller = $this->controller();
$controller->register();
self::assertSame(
array( $controller, 'registerRoutes' ),
$GLOBALS['wpcs_test_actions']['rest_api_init'][0]
);
}
public function test_it_registers_status_and_package_routes(): void {
$controller = $this->controller();
$controller->registerRoutes();
self::assertArrayHasKey( 'wp-content-sync/v1/status', $GLOBALS['wpcs_rest_routes'] );
self::assertArrayHasKey( 'wp-content-sync/v1/package', $GLOBALS['wpcs_rest_routes'] );
}
public function test_it_requires_manage_options_permission(): void {
$GLOBALS['wpcs_current_user_can']['manage_options'] = false;
$controller = $this->controller();
self::assertFalse( $controller->canReceivePackage() );
}
public function test_it_returns_status_payload(): void {
$controller = $this->controller();
self::assertSame(
array(
'ok' => true,
'plugin' => 'wp-content-sync',
'version' => WPCS_VERSION,
),
$controller->status()
);
}
public function test_it_accepts_valid_packages(): void {
$controller = $this->controller();
self::assertSame(
$this->acceptedResponse(),
$controller->receivePackage(
array(
'package' => $this->validPackage(),
)
)
);
}
public function test_it_accepts_rest_request_like_objects(): void {
$controller = $this->controller();
$request = new class(
array(
'package' => $this->validPackage(),
)
) {
/** @var array<string, mixed> */
private array $params;
/**
* @param array<string, mixed> $params Request params.
*/
public function __construct( array $params ) {
$this->params = $params;
}
/**
* @return array<string, mixed>
*/
public function get_json_params(): array {
return $this->params;
}
};
self::assertSame( $this->acceptedResponse(), $controller->receivePackage( $request ) );
}
public function test_it_rejects_invalid_package_shapes(): void {
$controller = $this->controller();
self::assertSame(
array(
'accepted' => false,
'errors' => array( 'package is required and must be an object.' ),
),
$controller->receivePackage( array() )
);
}
public function test_it_returns_rejected_response_when_sync_import_fails(): void {
$controller = $this->controller( SyncResult::failure( array( 'Posts failed.' ) ) );
self::assertSame(
array(
'accepted' => false,
'schema_version' => '1.0',
'manifest' => array(
'posts' => 0,
'terms' => 0,
'media' => 0,
'custom_post_types' => 0,
),
'import' => array(
'successful' => false,
'created' => 0,
'updated' => 0,
'skipped' => 0,
'conflicts' => 0,
'errors' => array( 'Posts failed.' ),
),
),
$controller->receivePackage(
array(
'package' => $this->validPackage(),
)
)
);
}
/**
* @return array<string, mixed>
*/
private function acceptedResponse(): array {
return array(
'accepted' => true,
'schema_version' => '1.0',
'manifest' => array(
'posts' => 0,
'terms' => 0,
'media' => 0,
'custom_post_types' => 0,
),
'import' => array(
'successful' => true,
'created' => 0,
'updated' => 0,
'skipped' => 0,
'conflicts' => 0,
'errors' => array(),
),
);
}
private function controller( ?SyncResult $result = null ): RestPackageController {
return new RestPackageController(
new PackageValidator(),
$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() implements LoggerInterface {
public function error( string $message, array $context = array() ): void {}
public function warning( string $message, array $context = array() ): void {}
public function info( string $message, array $context = array() ): void {}
public function debug( string $message, array $context = array() ): void {}
};
}
/**
* @return array<string, mixed>
*/
private function validPackage(): array {
$records = array(
'posts' => array(),
'terms' => array(),
'media' => array(),
'custom_post_types' => array(),
);
return array(
'schema_version' => '1.0',
'generated_at' => '2026-04-28T12:00:00+00:00',
'source' => array(
'site_url' => 'https://example.test',
'name' => 'Example',
),
'destination' => array(
'site_url' => 'https://destination.test',
'name' => 'Destination',
),
'manifest' => array(
'posts' => 0,
'terms' => 0,
'media' => 0,
'custom_post_types' => 0,
),
'records' => $records,
'checksums' => array(
'records' => PackageChecksum::records( $records ),
),
);
}
}