feat: add rest transport client
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace WPContentSync\Tests\Unit\Transport;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use WPContentSync\Package\ContentPackage;
|
||||
use WPContentSync\Package\PackageChecksum;
|
||||
use WPContentSync\Transport\RestTransportClient;
|
||||
use WPContentSync\Transport\RestTransportException;
|
||||
|
||||
class RestTransportClientTest extends TestCase {
|
||||
protected function tearDown(): void {
|
||||
unset( $GLOBALS['wpcs_http_response'], $GLOBALS['wpcs_last_http_request'] );
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_it_tests_connections_with_application_password_auth(): void {
|
||||
$client = new RestTransportClient();
|
||||
|
||||
self::assertTrue( $client->testConnection( 'https://destination.test', 'codex', 'app-pass' ) );
|
||||
|
||||
self::assertSame( 'GET', $GLOBALS['wpcs_last_http_request']['method'] );
|
||||
self::assertSame( 'https://destination.test/wp-json/wp-content-sync/v1/status', $GLOBALS['wpcs_last_http_request']['url'] );
|
||||
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Expected Basic auth value for application-password requests.
|
||||
self::assertSame( 'Basic ' . base64_encode( 'codex:app-pass' ), $GLOBALS['wpcs_last_http_request']['args']['headers']['Authorization'] );
|
||||
}
|
||||
|
||||
public function test_it_sends_packages_to_receive_endpoint(): void {
|
||||
$client = new RestTransportClient();
|
||||
|
||||
self::assertTrue( $client->sendPackage( 'https://destination.test/', 'codex', 'app-pass', $this->package() ) );
|
||||
|
||||
self::assertSame( 'POST', $GLOBALS['wpcs_last_http_request']['method'] );
|
||||
self::assertSame( 'https://destination.test/wp-json/wp-content-sync/v1/package', $GLOBALS['wpcs_last_http_request']['url'] );
|
||||
self::assertStringContainsString( '"package"', $GLOBALS['wpcs_last_http_request']['args']['body'] );
|
||||
self::assertSame( 'application/json', $GLOBALS['wpcs_last_http_request']['args']['headers']['Content-Type'] );
|
||||
}
|
||||
|
||||
public function test_it_throws_authentication_failures_for_unauthorized_status(): void {
|
||||
$GLOBALS['wpcs_http_response'] = array(
|
||||
'response' => array( 'code' => 401 ),
|
||||
'body' => '{"message":"Unauthorized"}',
|
||||
);
|
||||
$client = new RestTransportClient();
|
||||
|
||||
$this->expectException( RestTransportException::class );
|
||||
$this->expectExceptionMessage( 'REST authentication failed.' );
|
||||
|
||||
$client->testConnection( 'https://destination.test', 'codex', 'bad-pass' );
|
||||
}
|
||||
|
||||
public function test_it_throws_remote_rejected_for_invalid_package_response(): void {
|
||||
$GLOBALS['wpcs_http_response'] = array(
|
||||
'response' => array( 'code' => 400 ),
|
||||
'body' => '{"message":"Invalid package"}',
|
||||
);
|
||||
$client = new RestTransportClient();
|
||||
|
||||
$this->expectException( RestTransportException::class );
|
||||
$this->expectExceptionMessage( 'Invalid package' );
|
||||
|
||||
$client->sendPackage( 'https://destination.test', 'codex', 'app-pass', $this->package() );
|
||||
}
|
||||
|
||||
private function package(): ContentPackage {
|
||||
$records = array(
|
||||
'posts' => array(),
|
||||
'terms' => array(),
|
||||
'media' => array(),
|
||||
'custom_post_types' => array(),
|
||||
);
|
||||
|
||||
return ContentPackage::fromArray(
|
||||
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 ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
* @package WPContentSync
|
||||
*/
|
||||
|
||||
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- WordPress class and function stubs share this test bootstrap.
|
||||
|
||||
require_once dirname( __DIR__ ) . '/vendor/autoload.php';
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
@@ -23,6 +25,20 @@ if ( ! defined( 'WPCS_VERSION' ) ) {
|
||||
define( 'WPCS_VERSION', '0.1.0' );
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'WP_Error' ) ) {
|
||||
class WP_Error {
|
||||
private string $message;
|
||||
|
||||
public function __construct( string $code, string $message ) {
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
public function get_error_message(): string {
|
||||
return $this->message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'sanitize_text_field' ) ) {
|
||||
/**
|
||||
* Minimal WordPress-compatible text sanitizer for unit tests.
|
||||
@@ -370,6 +386,86 @@ if ( ! function_exists( 'wp_safe_redirect' ) ) {
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'wp_remote_get' ) ) {
|
||||
/**
|
||||
* Minimal HTTP GET helper for unit tests.
|
||||
*
|
||||
* @param string $url Request URL.
|
||||
* @param array<string, mixed> $args Request arguments.
|
||||
* @return array<string, mixed>|\WP_Error
|
||||
*/
|
||||
function wp_remote_get( $url, array $args = array() ) {
|
||||
$GLOBALS['wpcs_last_http_request'] = array(
|
||||
'method' => 'GET',
|
||||
'url' => $url,
|
||||
'args' => $args,
|
||||
);
|
||||
|
||||
return $GLOBALS['wpcs_http_response'] ?? array(
|
||||
'response' => array( 'code' => 200 ),
|
||||
'body' => '{"ok":true}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'wp_remote_post' ) ) {
|
||||
/**
|
||||
* Minimal HTTP POST helper for unit tests.
|
||||
*
|
||||
* @param string $url Request URL.
|
||||
* @param array<string, mixed> $args Request arguments.
|
||||
* @return array<string, mixed>|\WP_Error
|
||||
*/
|
||||
function wp_remote_post( $url, array $args = array() ) {
|
||||
$GLOBALS['wpcs_last_http_request'] = array(
|
||||
'method' => 'POST',
|
||||
'url' => $url,
|
||||
'args' => $args,
|
||||
);
|
||||
|
||||
return $GLOBALS['wpcs_http_response'] ?? array(
|
||||
'response' => array( 'code' => 200 ),
|
||||
'body' => '{"accepted":true}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'wp_remote_retrieve_response_code' ) ) {
|
||||
/**
|
||||
* Minimal response code helper for unit tests.
|
||||
*
|
||||
* @param array<string, mixed> $response HTTP response.
|
||||
* @return int
|
||||
*/
|
||||
function wp_remote_retrieve_response_code( array $response ) {
|
||||
return (int) ( $response['response']['code'] ?? 0 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'wp_remote_retrieve_body' ) ) {
|
||||
/**
|
||||
* Minimal response body helper for unit tests.
|
||||
*
|
||||
* @param array<string, mixed> $response HTTP response.
|
||||
* @return string
|
||||
*/
|
||||
function wp_remote_retrieve_body( array $response ) {
|
||||
return (string) ( $response['body'] ?? '' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'is_wp_error' ) ) {
|
||||
/**
|
||||
* Minimal WP_Error checker for unit tests.
|
||||
*
|
||||
* @param mixed $value Value to check.
|
||||
* @return bool
|
||||
*/
|
||||
function is_wp_error( $value ) {
|
||||
return $value instanceof WP_Error;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'admin_url' ) ) {
|
||||
/**
|
||||
* Minimal admin URL helper for unit tests.
|
||||
|
||||
Reference in New Issue
Block a user