Files
wpdev d36bb954d1 chore: initial snapshot of 2meet-data-optimizer v0.1.0
Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 05:06:36 +08:00

140 lines
5.5 KiB
PHP

<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* Unit tests for WPDO_CLI_V2::lint_directory() — Anti-EAV strict lint (PR-6).
*
* @covers WPDO_CLI_V2::lint_directory
*/
class CliLintTest extends TestCase {
private string $fixture_dir;
public static function setUpBeforeClass(): void {
// Stub WP_CLI just enough so the cli-v2 file can be require'd without errors.
if ( ! class_exists( 'WP_CLI' ) ) {
eval( 'class WP_CLI { public static function add_command( $name, $callable ) {} public static function log( $msg ) {} public static function warning( $msg ) {} public static function error( $msg ) { throw new \RuntimeException( $msg ); } public static function success( $msg ) {} }' ); // phpcs:ignore Squiz.PHP.Eval -- test-only stub.
}
if ( ! class_exists( 'TMDO_CLI_V2' ) ) {
require_once dirname( __DIR__, 2 ) . '/cli/class-tmdo-cli-v2.php';
}
}
protected function setUp(): void {
$this->fixture_dir = sys_get_temp_dir() . '/wpdo-lint-fixture-' . uniqid();
mkdir( $this->fixture_dir, 0777, true );
}
protected function tearDown(): void {
// Recursive rmdir.
$this->rrmdir( $this->fixture_dir );
}
private function rrmdir( string $dir ): void {
if ( ! is_dir( $dir ) ) {
return;
}
foreach ( scandir( $dir ) as $f ) {
if ( '.' === $f || '..' === $f ) {
continue;
}
$path = $dir . '/' . $f;
is_dir( $path ) ? $this->rrmdir( $path ) : unlink( $path );
}
rmdir( $dir );
}
private function write( string $relative, string $content ): void {
$path = $this->fixture_dir . '/' . $relative;
$dir = dirname( $path );
if ( ! is_dir( $dir ) ) {
mkdir( $dir, 0777, true );
}
file_put_contents( $path, $content );
}
// ── happy path ──────────────────────────────────────────────────────────
public function test_clean_plugin_passes_lint(): void {
$this->write( 'main.php', "<?php\nfunction foo() { return 'bar'; }\n" );
$findings = WPDO_CLI_V2::lint_directory( $this->fixture_dir );
$this->assertEmpty( $findings );
}
// ── direct postmeta SELECT ─────────────────────────────────────────────
public function test_direct_postmeta_select_flagged(): void {
$this->write(
'bad.php',
"<?php\n\$rows = \$wpdb->get_results( \"SELECT meta_value FROM {\$wpdb->prefix}postmeta WHERE meta_key='foo'\" );\n"
);
$findings = WPDO_CLI_V2::lint_directory( $this->fixture_dir );
$this->assertGreaterThanOrEqual( 1, count( $findings ) );
$this->assertSame( 'no-direct-postmeta-select', $findings[0]['rule'] );
}
public function test_direct_usermeta_select_flagged(): void {
$this->write(
'user.php',
"<?php\n\$wpdb->get_var( \"SELECT meta_value FROM wp_usermeta WHERE meta_key='foo'\" );\n"
);
$findings = WPDO_CLI_V2::lint_directory( $this->fixture_dir );
$this->assertGreaterThanOrEqual( 1, count( $findings ) );
$this->assertSame( 'no-direct-usermeta-select', $findings[0]['rule'] );
}
// ── autoload=yes ───────────────────────────────────────────────────────
public function test_autoload_yes_flagged(): void {
$this->write(
'opt.php',
"<?php\nadd_option( 'mykey', 'val', '', 'yes' );\n\$x = array( 'autoload' => 'yes' );\n"
);
$findings = WPDO_CLI_V2::lint_directory( $this->fixture_dir );
$autoload_findings = array_filter( $findings, static fn( $f ) => 'autoload-yes' === $f['rule'] );
$this->assertGreaterThanOrEqual( 1, count( $autoload_findings ) );
}
// ── ignore comment ─────────────────────────────────────────────────────
public function test_phpcs_ignore_comment_skips_finding(): void {
$this->write(
'fallback.php',
"<?php\n// phpcs:ignore WPDO.AntiEAV.PostmetaFallback -- legacy fallback path\n\$rows = \$wpdb->get_results( \"SELECT meta_value FROM wp_postmeta WHERE meta_key='x'\" );\n"
);
$findings = WPDO_CLI_V2::lint_directory( $this->fixture_dir );
$this->assertEmpty( $findings, 'phpcs:ignore WPDO.AntiEAV must suppress findings' );
}
// ── skip dirs ──────────────────────────────────────────────────────────
public function test_skips_vendor_and_node_modules(): void {
// Bad code inside vendor/ MUST be ignored.
$this->write(
'vendor/lib/bad.php',
"<?php\n\$wpdb->get_results( \"SELECT meta_value FROM wp_postmeta\" );\n"
);
$this->write(
'node_modules/foo/bad.php',
"<?php\n\$wpdb->get_results( \"SELECT meta_value FROM wp_usermeta\" );\n"
);
$findings = WPDO_CLI_V2::lint_directory( $this->fixture_dir );
$this->assertEmpty( $findings );
}
// ── reports file + line ────────────────────────────────────────────────
public function test_finding_includes_file_and_line(): void {
$this->write(
'multi.php',
"<?php\n\n\$x = 1;\n\$wpdb->get_var( \"SELECT meta_value FROM wp_postmeta\" );\n"
);
$findings = WPDO_CLI_V2::lint_directory( $this->fixture_dir );
$this->assertNotEmpty( $findings );
$this->assertStringEndsWith( 'multi.php', $findings[0]['file'] );
$this->assertSame( 4, $findings[0]['line'] );
}
}