mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-31 19:03:43 +01:00
75 lines
2.8 KiB
PHP
75 lines
2.8 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
use PHPUnit\Framework\TestCase;
|
||
|
|
use XcVm\Core\Database\Database;
|
||
|
|
use XcVm\Core\Logging\FileLogger;
|
||
|
|
|
||
|
|
/** A Database over a connection the test opened (the panel's comes from the xcvm_core extension). */
|
||
|
|
final class AuditCoreDataQueryLogDb extends Database {
|
||
|
|
public function __construct(\PDO $rPdo) {
|
||
|
|
$this->dbh = $rPdo;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A statement that fails is written to the panel log as it was written in
|
||
|
|
* the code, with its placeholders: the values bound to it (passwords, keys,
|
||
|
|
* tokens) are not part of the log, which the panel shows and submits with its
|
||
|
|
* diagnostics. Whichever step failed, query() answers false.
|
||
|
|
*/
|
||
|
|
final class AuditCoreDataQueryLogTest extends TestCase {
|
||
|
|
private TestDb $rTestDb;
|
||
|
|
|
||
|
|
private string $rLog;
|
||
|
|
|
||
|
|
protected function setUp(): void {
|
||
|
|
$this->rTestDb = new TestDb();
|
||
|
|
$this->rTestDb->exec('CREATE TABLE `lines` (`id` INT AUTO_INCREMENT PRIMARY KEY, `username` VARCHAR(64), `password` VARCHAR(64))');
|
||
|
|
$this->rLog = sys_get_temp_dir() . '/xcvm-querylog-' . bin2hex(random_bytes(4)) . '.log';
|
||
|
|
FileLogger::setLogFile($this->rLog);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function tearDown(): void {
|
||
|
|
FileLogger::setLogFile(null);
|
||
|
|
@unlink($this->rLog);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @return list<array<string,mixed>> the entries written to the panel log */
|
||
|
|
private function logged(): array {
|
||
|
|
$rEntries = [];
|
||
|
|
foreach (file_exists($this->rLog) ? file($this->rLog, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) : [] as $rLine) {
|
||
|
|
$rEntries[] = json_decode(base64_decode($rLine), true);
|
||
|
|
}
|
||
|
|
return $rEntries;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testAFailedStatementIsLoggedWithoutTheValuesBoundToIt(): void {
|
||
|
|
$rDb = new AuditCoreDataQueryLogDb($this->rTestDb->pdo);
|
||
|
|
$rQuery = 'INSERT INTO `lines` (`username`, `password`, `no_such_column`) VALUES (?, ?, ?);';
|
||
|
|
|
||
|
|
$this->assertFalse($rDb->query($rQuery, 'a-line', 'the-line-password', 1));
|
||
|
|
$this->assertStringContainsString('no_such_column', $rDb->error());
|
||
|
|
|
||
|
|
$rEntries = $this->logged();
|
||
|
|
$this->assertCount(1, $rEntries);
|
||
|
|
$this->assertSame('pdo', $rEntries[0]['type']);
|
||
|
|
$this->assertSame($rQuery, $rEntries[0]['extra']);
|
||
|
|
$this->assertStringNotContainsString('the-line-password', json_encode($rEntries));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testAStatementTheServerRefusesToPrepareIsAFailedQueryToo(): void {
|
||
|
|
// The server prepares the statement itself, so the refusal comes before there is a statement to execute.
|
||
|
|
$rPdo = TestDb::connect($this->rTestDb->schema());
|
||
|
|
$rPdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
|
||
|
|
$rDb = new AuditCoreDataQueryLogDb($rPdo);
|
||
|
|
$rQuery = 'SELECT `id` FROM `no_such_table` WHERE `password` = ?;';
|
||
|
|
|
||
|
|
$this->assertFalse($rDb->query($rQuery, 'the-line-password'));
|
||
|
|
$this->assertStringContainsString('no_such_table', $rDb->error());
|
||
|
|
|
||
|
|
$rEntries = $this->logged();
|
||
|
|
$this->assertCount(1, $rEntries);
|
||
|
|
$this->assertSame($rQuery, $rEntries[0]['extra']);
|
||
|
|
}
|
||
|
|
}
|