test(2meet-data-optimizer): add live test runner and failing shadow_read write suite
- Add tests/live: a `wp eval-file` runner (PASS/FAIL/XFAIL/XPASS/SKIP) with fixtures that override entity modes in process (pre_option) and read native / flat values with plain SQL, so WP core meta flows run for real - shadow_write suite reproduces the dev36 finding: in shadow_read, update_*_meta returns false and native meta is never written (3 XFAIL marked for PR-056); the dual_write control passes - Move the dev36 integration harness into tests/live/tools; outputs go to cwd / $TMDO_LIVE_OUT, site URL and path are env-configurable - Verify: wp --path=<dev36> eval-file tests/live/run.php shadow_write → 1 passed, 3 xfail; unit 658 OK, integration 480 OK, phpcs (phpcs.xml) 0, stan OK - PR: PR-052
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/**
|
||||
* Minimal assertion + test registry for the live suite.
|
||||
*
|
||||
* Why not PHPUnit: the live suite runs inside a real WordPress request
|
||||
* (`wp eval-file`) so WP core's update_metadata() flow, WP_Query SQL and
|
||||
* third-party hooks (HivePress, WooCommerce) are exercised for real — the
|
||||
* stubbed integration bootstrap cannot reproduce those.
|
||||
*
|
||||
* @package WP_Data_Optimizer
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Thrown by the assert helpers.
|
||||
*/
|
||||
class TMDO_Live_Failure extends RuntimeException {}
|
||||
|
||||
/**
|
||||
* Thrown to skip a test whose preconditions the site does not meet.
|
||||
*/
|
||||
class TMDO_Live_Skip extends RuntimeException {}
|
||||
|
||||
/**
|
||||
* Test registry and runner.
|
||||
*/
|
||||
final class TMDO_Live {
|
||||
|
||||
/**
|
||||
* Registered tests.
|
||||
*
|
||||
* @var array<int, array{suite:string, name:string, fn:callable, xfail:string}>
|
||||
*/
|
||||
private static array $tests = array();
|
||||
|
||||
/**
|
||||
* Suite currently being loaded.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static string $suite = '';
|
||||
|
||||
/**
|
||||
* Register a test.
|
||||
*
|
||||
* `xfail` marks a known bug: a failure is reported as XFAIL (not counted),
|
||||
* a pass is reported as XPASS and counted as a failure so the marker gets
|
||||
* removed by the PR that fixes the bug.
|
||||
*
|
||||
* @param string $name Test name.
|
||||
* @param callable $fn Test body.
|
||||
* @param array $opts Options: xfail => reason.
|
||||
*/
|
||||
public static function test( string $name, callable $fn, array $opts = array() ): void {
|
||||
self::$tests[] = array(
|
||||
'suite' => self::$suite,
|
||||
'name' => $name,
|
||||
'fn' => $fn,
|
||||
'xfail' => (string) ( $opts['xfail'] ?? '' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run every registered test; return true when nothing failed.
|
||||
*/
|
||||
public static function run(): bool {
|
||||
$counts = array( 'PASS' => 0, 'FAIL' => 0, 'XFAIL' => 0, 'XPASS' => 0, 'SKIP' => 0 );
|
||||
|
||||
foreach ( self::$tests as $t ) {
|
||||
$detail = '';
|
||||
try {
|
||||
( $t['fn'] )();
|
||||
$status = '' !== $t['xfail'] ? 'XPASS' : 'PASS';
|
||||
} catch ( TMDO_Live_Skip $e ) {
|
||||
$status = 'SKIP';
|
||||
$detail = $e->getMessage();
|
||||
} catch ( Throwable $e ) {
|
||||
$status = '' !== $t['xfail'] ? 'XFAIL' : 'FAIL';
|
||||
$detail = ( $e instanceof TMDO_Live_Failure ? '' : get_class( $e ) . ': ' ) . $e->getMessage();
|
||||
} finally {
|
||||
TMDO_Live_Fixtures::teardown();
|
||||
}
|
||||
|
||||
++$counts[ $status ];
|
||||
if ( '' !== $t['xfail'] ) {
|
||||
$detail = trim( $detail . ' [xfail: ' . $t['xfail'] . ']' );
|
||||
}
|
||||
printf( "%-5s %s :: %s%s\n", $status, $t['suite'], $t['name'], '' !== $detail ? "\n " . $detail : '' );
|
||||
}
|
||||
|
||||
printf(
|
||||
"\n%d passed, %d failed, %d xfail, %d xpass, %d skipped\n",
|
||||
$counts['PASS'],
|
||||
$counts['FAIL'],
|
||||
$counts['XFAIL'],
|
||||
$counts['XPASS'],
|
||||
$counts['SKIP']
|
||||
);
|
||||
|
||||
return 0 === $counts['FAIL'] && 0 === $counts['XPASS'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail unless $actual is identical to $expected.
|
||||
*
|
||||
* @param mixed $expected Expected value.
|
||||
* @param mixed $actual Actual value.
|
||||
* @param string $message Context.
|
||||
*/
|
||||
function tmdo_live_assert_same( $expected, $actual, string $message ): void {
|
||||
if ( $expected !== $actual ) {
|
||||
throw new TMDO_Live_Failure(
|
||||
sprintf( '%s — expected %s, got %s', $message, var_export( $expected, true ), var_export( $actual, true ) ) // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail unless $condition is true.
|
||||
*
|
||||
* @param bool $condition Condition.
|
||||
* @param string $message Context.
|
||||
*/
|
||||
function tmdo_live_assert_true( bool $condition, string $message ): void {
|
||||
if ( ! $condition ) {
|
||||
throw new TMDO_Live_Failure( $message );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user