prefix; $wpdb->query( "DROP TABLE IF EXISTS `{$p}wpdo_audit`" ); $wpdb->query( "DROP TABLE IF EXISTS `{$p}wpdo_shadow_diffs`" ); $wpdb->query( "DROP TABLE IF EXISTS `{$p}wpdo_site_metrics`" ); $wpdb->query( "DROP TABLE IF EXISTS `{$p}wpdo_uni_options`" ); } public function test_v2_tables_initially_absent(): void { $status = WPDO_Installer::v2_tables_status(); foreach ( $status as $table => $exists ) { $this->assertFalse( $exists, "Expected {$table} to NOT exist initially" ); } } public function test_install_v2_tables_creates_all_four(): void { WPDO_Installer::install_v2_tables(); $status = WPDO_Installer::v2_tables_status(); foreach ( $status as $table => $exists ) { $this->assertTrue( $exists, "Expected {$table} to exist after install_v2_tables()" ); } } public function test_install_v2_tables_is_idempotent(): void { WPDO_Installer::install_v2_tables(); WPDO_Installer::install_v2_tables(); // Second call must not error. WPDO_Installer::install_v2_tables(); // Third for good measure. $status = WPDO_Installer::v2_tables_status(); $this->assertCount( 4, $status ); $this->assertTrue( array_reduce( $status, static fn( $carry, $v ) => $carry && $v, true ) ); } public function test_audit_table_has_required_columns(): void { global $wpdb; $p = $wpdb->prefix; $cols = $wpdb->get_col( "SHOW COLUMNS FROM `{$p}wpdo_audit`" ); // PR-2 spec: op, value_before, value_after, source, trace_id are required. foreach ( array( 'op', 'value_before', 'value_after', 'source', 'trace_id' ) as $required ) { $this->assertContains( $required, $cols, "wpdo_audit missing column {$required}" ); } } public function test_shadow_diffs_table_has_required_columns(): void { global $wpdb; $p = $wpdb->prefix; $cols = $wpdb->get_col( "SHOW COLUMNS FROM `{$p}wpdo_shadow_diffs`" ); foreach ( array( 'entity_type', 'entity_id', 'meta_key', 'postmeta_value', 'zone_value', 'diff_hash' ) as $required ) { $this->assertContains( $required, $cols, "wpdo_shadow_diffs missing column {$required}" ); } } public function test_uni_options_has_unique_index_on_option_name(): void { global $wpdb; $p = $wpdb->prefix; $indexes = $wpdb->get_results( $wpdb->prepare( 'SELECT INDEX_NAME, COLUMN_NAME, NON_UNIQUE FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s', $p . 'wpdo_uni_options' ), ARRAY_A ); $found_unique = false; foreach ( $indexes as $idx ) { if ( 'option_name' === $idx['COLUMN_NAME'] && '0' === (string) $idx['NON_UNIQUE'] ) { $found_unique = true; break; } } $this->assertTrue( $found_unique, 'Expected UNIQUE index on wpdo_uni_options.option_name' ); } public function test_audit_table_indexes_for_query_performance(): void { global $wpdb; $p = $wpdb->prefix; $indexes = $wpdb->get_col( $wpdb->prepare( 'SELECT DISTINCT INDEX_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s', $p . 'wpdo_audit' ) ); // Performance-critical indexes per Part F.3 schema spec. foreach ( array( 'idx_entity', 'idx_meta_key', 'idx_ts', 'idx_trace' ) as $required ) { $this->assertContains( $required, $indexes, "wpdo_audit missing index {$required}" ); } } public static function tearDownAfterClass(): void { // Leave v2 tables in place for subsequent tests / dev convenience. // Cleanup happens via wp wpdo cleanup-uae-tables --confirm in real upgrades. } }