-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
578 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,395 @@ | ||
<?php | ||
|
||
require_once __DIR__ . '/../wp-includes/sqlite-ast/class-wp-sqlite-driver.php'; | ||
require_once __DIR__ . '/../wp-includes/sqlite-ast/class-wp-sqlite-driver-exception.php'; | ||
require_once __DIR__ . '/../wp-includes/sqlite-ast/class-wp-sqlite-information-schema-builder.php'; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
|
||
class WP_SQLite_Driver_Metadata_Tests extends TestCase { | ||
|
||
private $engine; | ||
private $sqlite; | ||
|
||
public static function setUpBeforeClass(): void { | ||
// if ( ! defined( 'PDO_DEBUG' )) { | ||
// define( 'PDO_DEBUG', true ); | ||
// } | ||
if ( ! defined( 'FQDB' ) ) { | ||
define( 'FQDB', ':memory:' ); | ||
define( 'FQDBDIR', __DIR__ . '/../testdb' ); | ||
} | ||
error_reporting( E_ALL & ~E_DEPRECATED ); | ||
if ( ! isset( $GLOBALS['table_prefix'] ) ) { | ||
$GLOBALS['table_prefix'] = 'wptests_'; | ||
} | ||
if ( ! isset( $GLOBALS['wpdb'] ) ) { | ||
$GLOBALS['wpdb'] = new stdClass(); | ||
$GLOBALS['wpdb']->suppress_errors = false; | ||
$GLOBALS['wpdb']->show_errors = true; | ||
} | ||
return; | ||
} | ||
|
||
// Before each test, we create a new database | ||
public function setUp(): void { | ||
global $blog_tables; | ||
$queries = explode( ';', $blog_tables ); | ||
|
||
$this->sqlite = new PDO( 'sqlite::memory:' ); | ||
$this->engine = new WP_SQLite_Driver( | ||
array( | ||
'connection' => $this->sqlite, | ||
'database' => 'wp', | ||
) | ||
); | ||
|
||
$translator = $this->engine; | ||
|
||
try { | ||
$translator->begin_transaction(); | ||
foreach ( $queries as $query ) { | ||
$query = trim( $query ); | ||
if ( empty( $query ) ) { | ||
continue; | ||
} | ||
|
||
$translator->execute_sqlite_query( $query ); | ||
} | ||
$translator->commit(); | ||
} catch ( PDOException $err ) { | ||
$err_data = | ||
$err->errorInfo; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase | ||
$err_code = $err_data[1]; | ||
$translator->rollback(); | ||
$message = sprintf( | ||
'Error occurred while creating tables or indexes...<br />Query was: %s<br />', | ||
var_export( $query, true ) | ||
); | ||
$message .= sprintf( 'Error message is: %s', $err_data[2] ); | ||
wp_die( $message, 'Database Error!' ); | ||
} | ||
} | ||
|
||
public function testCountTables() { | ||
$this->assertQuery( 'CREATE TABLE t1 (id INT)' ); | ||
$this->assertQuery( 'CREATE TABLE t2 (id INT)' ); | ||
|
||
$result = $this->assertQuery( "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'wp'" ); | ||
$this->assertEquals( array( (object) array( 'COUNT ( * )' => '2' ) ), $result ); | ||
|
||
$result = $this->assertQuery( "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'other'" ); | ||
$this->assertEquals( array( (object) array( 'COUNT ( * )' => '0' ) ), $result ); | ||
|
||
// @TODO: The result key should be "COUNT(*)" instead of "COUNT ( * )". | ||
// The spacing was probably inserted by the translator. | ||
} | ||
|
||
public function testInformationSchemaTables() { | ||
$this->assertQuery( | ||
' | ||
CREATE TABLE t (id INT PRIMARY KEY, name TEXT, age INT) | ||
' | ||
); | ||
|
||
$result = $this->assertQuery( "SELECT * FROM information_schema.tables WHERE TABLE_NAME = 't'" ); | ||
$this->assertCount( 1, $result ); | ||
$this->assertRegExp( '/\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/', $result[0]->CREATE_TIME ); | ||
$this->assertEquals( | ||
array( | ||
'TABLE_CATALOG' => 'def', | ||
'TABLE_SCHEMA' => 'wp', | ||
'TABLE_NAME' => 't', | ||
'TABLE_TYPE' => 'BASE TABLE', | ||
'ENGINE' => 'InnoDB', | ||
'VERSION' => '10', | ||
'ROW_FORMAT' => 'Dynamic', | ||
'TABLE_ROWS' => '0', | ||
'AVG_ROW_LENGTH' => '0', | ||
'DATA_LENGTH' => '0', | ||
'MAX_DATA_LENGTH' => '0', | ||
'INDEX_LENGTH' => '0', | ||
'DATA_FREE' => '0', | ||
'AUTO_INCREMENT' => null, | ||
'CREATE_TIME' => $result[0]->CREATE_TIME, | ||
'UPDATE_TIME' => null, | ||
'CHECK_TIME' => null, | ||
'TABLE_COLLATION' => 'utf8mb4_general_ci', | ||
'CHECKSUM' => null, | ||
'CREATE_OPTIONS' => '', | ||
'TABLE_COMMENT' => '', | ||
), | ||
(array) $result[0] | ||
); | ||
|
||
$result = $this->assertQuery( | ||
"SELECT | ||
table_name as 'name', | ||
engine AS 'engine', | ||
FLOOR( data_length / 1024 / 1024 ) 'data' | ||
FROM INFORMATION_SCHEMA.TABLES | ||
WHERE TABLE_NAME = 't' | ||
ORDER BY name ASC" | ||
); | ||
|
||
$this->assertEquals( | ||
array( | ||
'name' => 't', | ||
'engine' => 'InnoDB', | ||
'data' => '0', | ||
), | ||
(array) $result[0] | ||
); | ||
} | ||
|
||
public function testInformationSchemaQueryHidesSqliteSystemTables() { | ||
/** | ||
* By default, system tables are not returned. | ||
*/ | ||
$result = $this->assertQuery( "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sqlite_sequence'" ); | ||
$this->assertEquals( 0, count( $result ) ); | ||
} | ||
|
||
public function testUseStatement() { | ||
$this->assertQuery( 'CREATE TABLE tables (ENGINE TEXT)' ); | ||
$this->assertQuery( "INSERT INTO tables (ENGINE) VALUES ('test')" ); | ||
|
||
$this->assertQuery( 'USE wp' ); | ||
$result = $this->assertQuery( 'SELECT * FROM tables' ); | ||
$this->assertSame( 'test', $result[0]->ENGINE ); | ||
|
||
$this->assertQuery( 'USE information_schema' ); | ||
$result = $this->assertQuery( 'SELECT * FROM tables' ); | ||
$this->assertSame( 'InnoDB', $result[0]->ENGINE ); | ||
} | ||
|
||
private function assertQuery( $sql ) { | ||
$retval = $this->engine->query( $sql ); | ||
$this->assertNotFalse( $retval ); | ||
return $retval; | ||
} | ||
|
||
public function testCheckTable() { | ||
|
||
/* a good table */ | ||
$table_name = 'wp_options'; | ||
$expected_result = array( | ||
(object) array( | ||
'Table' => 'wp.' . $table_name, | ||
'Op' => 'check', | ||
'Msg_type' => 'status', | ||
'Msg_text' => 'OK', | ||
), | ||
); | ||
|
||
$this->assertQuery( | ||
"CHECK TABLE $table_name;" | ||
); | ||
|
||
$this->assertEquals( | ||
$expected_result, | ||
$this->engine->get_query_results() | ||
); | ||
|
||
/* a different good table */ | ||
$table_name = 'wp_postmeta'; | ||
$expected_result = array( | ||
(object) array( | ||
'Table' => 'wp.' . $table_name, | ||
'Op' => 'check', | ||
'Msg_type' => 'status', | ||
'Msg_text' => 'OK', | ||
), | ||
); | ||
|
||
$this->assertQuery( | ||
"CHECK TABLE $table_name;" | ||
); | ||
$this->assertEquals( | ||
$expected_result, | ||
$this->engine->get_query_results() | ||
); | ||
|
||
/* a bogus, missing, table */ | ||
$table_name = 'wp_sqlite_rocks'; | ||
$expected_result = array( | ||
(object) array( | ||
'Table' => 'wp.' . $table_name, | ||
'Op' => 'check', | ||
'Msg_type' => 'Error', | ||
'Msg_text' => "Table '$table_name' doesn't exist", | ||
), | ||
(object) array( | ||
'Table' => 'wp.' . $table_name, | ||
'Op' => 'check', | ||
'Msg_type' => 'status', | ||
'Msg_text' => 'Operation failed', | ||
), | ||
); | ||
|
||
$this->assertQuery( | ||
"CHECK TABLE $table_name;" | ||
); | ||
|
||
$this->assertEquals( | ||
$expected_result, | ||
$this->engine->get_query_results() | ||
); | ||
} | ||
|
||
public function testOptimizeTable() { | ||
|
||
/* a good table */ | ||
$table_name = 'wp_options'; | ||
|
||
$this->assertQuery( | ||
"OPTIMIZE TABLE $table_name;" | ||
); | ||
|
||
$actual = $this->engine->get_query_results(); | ||
|
||
array_map( | ||
function ( $row ) { | ||
$this->assertIsObject( $row ); | ||
$row = (array) $row; | ||
$this->assertIsString( $row['Table'] ); | ||
$this->assertIsString( $row['Op'] ); | ||
$this->assertIsString( $row['Msg_type'] ); | ||
$this->assertIsString( $row['Msg_text'] ); | ||
}, | ||
$actual | ||
); | ||
|
||
$ok = array_filter( | ||
$actual, | ||
function ( $row ) { | ||
$row = (array) $row; | ||
|
||
return strtolower( $row['Msg_type'] ) === 'status' && strtolower( $row['Msg_text'] ) === 'ok'; | ||
} | ||
); | ||
$this->assertIsArray( $ok ); | ||
$this->assertGreaterThan( 0, count( $ok ) ); | ||
} | ||
|
||
public function testRepairTable() { | ||
|
||
/* a good table */ | ||
$table_name = 'wp_options'; | ||
|
||
$this->assertQuery( | ||
"REPAIR TABLE $table_name;" | ||
); | ||
|
||
$actual = $this->engine->get_query_results(); | ||
|
||
array_map( | ||
function ( $r ) { | ||
$this->assertIsObject( $r ); | ||
$row = $r; | ||
$row = (array) $row; | ||
$this->assertIsString( $row['Table'] ); | ||
$this->assertIsString( $row['Op'] ); | ||
$this->assertIsString( $row['Msg_type'] ); | ||
$this->assertIsString( $row['Msg_text'] ); | ||
}, | ||
$actual | ||
); | ||
|
||
$ok = array_filter( | ||
$actual, | ||
function ( $row ) { | ||
return strtolower( $row->Msg_type ) === 'status' && strtolower( $row->Msg_text ) === 'ok'; | ||
} | ||
); | ||
$this->assertIsArray( $ok ); | ||
$this->assertGreaterThan( 0, count( $ok ) ); | ||
} | ||
|
||
// this tests for successful rejection of a bad query | ||
|
||
public function testShowTableStatus() { | ||
|
||
$this->assertQuery( | ||
"INSERT INTO wp_comments ( comment_author, comment_content ) VALUES ( 'PhpUnit', 'Testing' )" | ||
); | ||
|
||
$this->assertQuery( | ||
"INSERT INTO wp_comments ( comment_author, comment_content ) VALUES ( 'PhpUnit0', 'Testing0' ), ( 'PhpUnit1', 'Testing1' ), ( 'PhpUnit2', 'Testing2' )" | ||
); | ||
|
||
$this->assertTableEmpty( 'wp_comments', false ); | ||
|
||
$this->assertQuery( | ||
'SHOW TABLE STATUS FROM wp;' | ||
); | ||
|
||
$actual = $this->engine->get_query_results(); | ||
|
||
$this->assertIsArray( $actual ); | ||
$this->assertGreaterThanOrEqual( | ||
1, | ||
count( $actual ) | ||
); | ||
$this->assertIsObject( $actual[0] ); | ||
|
||
$rows = array_values( | ||
array_filter( | ||
$actual, | ||
function ( $row ) { | ||
$this->assertIsObject( $row ); | ||
$this->assertIsString( $row->Name ); | ||
$this->assertIsNumeric( $row->Rows ); | ||
|
||
return str_ends_with( $row->Name, 'comments' ); | ||
} | ||
) | ||
); | ||
$this->assertEquals( 'wp_comments', $rows[0]->Name ); | ||
$this->assertEquals( 4, $rows[0]->Rows ); | ||
} | ||
|
||
private function assertTableEmpty( $table_name, $empty_var ) { | ||
|
||
$this->assertQuery( | ||
"SELECT COUNT(*) num FROM $table_name" | ||
); | ||
|
||
$actual = $this->engine->get_query_results(); | ||
if ( $empty_var ) { | ||
$this->assertEquals( 0, $actual[0]->num, "$table_name is not empty" ); | ||
} else { | ||
$this->assertGreaterThan( 0, $actual[0]->num, "$table_name is empty" ); | ||
} | ||
} | ||
|
||
public function testTruncateTable() { | ||
|
||
$this->assertQuery( | ||
"INSERT INTO wp_comments ( comment_author, comment_content ) VALUES ( 'PhpUnit', 'Testing' )" | ||
); | ||
|
||
$this->assertQuery( | ||
"INSERT INTO wp_comments ( comment_author, comment_content ) VALUES ( 'PhpUnit0', 'Testing0' ), ( 'PhpUnit1', 'Testing1' ), ( 'PhpUnit2', 'Testing2' )" | ||
); | ||
|
||
$this->assertTableEmpty( 'wp_comments', false ); | ||
|
||
$this->assertQuery( | ||
'TRUNCATE TABLE wp_comments;' | ||
); | ||
$actual = $this->engine->get_query_results(); | ||
$this->assertEquals( | ||
true, | ||
$actual | ||
); | ||
$this->assertTableEmpty( 'wp_comments', true ); | ||
} | ||
|
||
public function testBogusQuery() { | ||
$this->expectExceptionMessage( 'no such table: bogus' ); | ||
$this->assertQuery( | ||
'SELECT 1, BOGUS(1) FROM bogus;', | ||
); | ||
} | ||
} |
Oops, something went wrong.