feat: add sync context and operation state
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Tests for sync operation context.
|
||||
*
|
||||
* @package WPContentSync
|
||||
*/
|
||||
|
||||
namespace WPContentSync\Tests\Unit\Sync;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use WPContentSync\Sync\SyncContext;
|
||||
|
||||
class SyncContextTest extends TestCase {
|
||||
public function test_it_builds_import_context_from_package_sites(): void {
|
||||
$context = SyncContext::forImport(
|
||||
array( 'site_url' => 'https://source.test' ),
|
||||
array( 'site_url' => 'https://destination.test' ),
|
||||
'last_write_wins',
|
||||
'operation-1'
|
||||
);
|
||||
|
||||
self::assertSame( 'import', $context->direction() );
|
||||
self::assertSame( 'operation-1', $context->operationId() );
|
||||
self::assertSame( 'last_write_wins', $context->conflictStrategy() );
|
||||
self::assertSame( 'https://source.test', $context->sourceUrl() );
|
||||
self::assertSame( 'https://destination.test', $context->destinationUrl() );
|
||||
self::assertSame(
|
||||
array( 'https://source.test' => 'https://destination.test' ),
|
||||
$context->urlMappings()
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_falls_back_to_last_write_wins_for_invalid_strategy(): void {
|
||||
$context = SyncContext::forImport( array(), array(), 'surprise', 'operation-2' );
|
||||
|
||||
self::assertSame( 'last_write_wins', $context->conflictStrategy() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* Tests for sync state persistence.
|
||||
*
|
||||
* @package WPContentSync
|
||||
*/
|
||||
|
||||
namespace WPContentSync\Tests\Unit\Sync;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use WPContentSync\Sync\SyncOperationState;
|
||||
use WPContentSync\Sync\SyncStateRepository;
|
||||
|
||||
class SyncStateRepositoryTest extends TestCase {
|
||||
protected function tearDown(): void {
|
||||
unset( $GLOBALS['wpcs_test_transients'], $GLOBALS['wpcs_test_transient_expiration'] );
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_it_saves_and_reads_operation_state(): void {
|
||||
$repository = new SyncStateRepository();
|
||||
$state = SyncOperationState::running( 'operation-1', 'posts', 2, 10 );
|
||||
|
||||
$repository->save( $state );
|
||||
|
||||
$loaded = $repository->get( 'operation-1' );
|
||||
|
||||
self::assertInstanceOf( SyncOperationState::class, $loaded );
|
||||
self::assertSame( 'operation-1', $loaded->operationId() );
|
||||
self::assertSame( 'posts', $loaded->currentBucket() );
|
||||
self::assertSame( 2, $loaded->processed() );
|
||||
self::assertSame( 10, $loaded->total() );
|
||||
self::assertSame( 'running', $loaded->status() );
|
||||
}
|
||||
|
||||
public function test_it_deletes_operation_state(): void {
|
||||
$repository = new SyncStateRepository();
|
||||
$repository->save( SyncOperationState::completed( 'operation-1', 10, 10 ) );
|
||||
|
||||
$repository->delete( 'operation-1' );
|
||||
|
||||
self::assertNull( $repository->get( 'operation-1' ) );
|
||||
self::assertArrayNotHasKey( 'wpcs_sync_state_operation-1', $GLOBALS['wpcs_test_transient_expiration'] );
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,18 @@ if ( ! function_exists( 'sanitize_text_field' ) ) {
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'sanitize_key' ) ) {
|
||||
/**
|
||||
* Minimal WordPress-compatible key sanitizer for unit tests.
|
||||
*
|
||||
* @param mixed $key Key to sanitize.
|
||||
* @return string
|
||||
*/
|
||||
function sanitize_key( $key ) {
|
||||
return strtolower( preg_replace( '/[^a-zA-Z0-9_\-]/', '', (string) $key ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'wp_strip_all_tags' ) ) {
|
||||
/**
|
||||
* Minimal tag stripper for unit tests.
|
||||
@@ -223,6 +235,36 @@ if ( ! function_exists( 'delete_transient' ) ) {
|
||||
*/
|
||||
function delete_transient( $name ) {
|
||||
unset( $GLOBALS['wpcs_test_transients'][ $name ] );
|
||||
unset( $GLOBALS['wpcs_test_transient_expiration'][ $name ] );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'get_transient' ) ) {
|
||||
/**
|
||||
* Minimal WordPress transient reader for unit tests.
|
||||
*
|
||||
* @param string $name Transient name.
|
||||
* @return mixed
|
||||
*/
|
||||
function get_transient( $name ) {
|
||||
return $GLOBALS['wpcs_test_transients'][ $name ] ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'set_transient' ) ) {
|
||||
/**
|
||||
* Minimal WordPress transient writer for unit tests.
|
||||
*
|
||||
* @param string $name Transient name.
|
||||
* @param mixed $value Transient value.
|
||||
* @param int $expiration Expiration in seconds.
|
||||
* @return bool
|
||||
*/
|
||||
function set_transient( $name, $value, $expiration = 0 ) {
|
||||
$GLOBALS['wpcs_test_transients'][ $name ] = $value;
|
||||
$GLOBALS['wpcs_test_transient_expiration'][ $name ] = $expiration;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user