Files
XC_VM/tests/Unit/StrictQueryTest.php
T
Claude e6b6234175 refactor(cluster): enum settings, policy version, enrolment start and CLI helpers written once
- ClusterSettings::enum() reads every enum setting against ENUMS; an
  unknown stored cluster_transport is now published as the default,
  auto (recorded in ADR 0004).
- ClusterPolicy::ver(), one enrolment start for SSH and code enrolment,
  one public protocol range.
- StrictQuery takes a message; the query-or-throw copies use it.
- The cluster CLI commands share the 'Cluster API unavailable' and
  'is an LB' helpers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rdv7hXd8RWhLHoj5Ma2ZqY
2026-09-28 18:02:41 +00:00

99 lines
3.6 KiB
PHP

<?php
use PHPUnit\Framework\TestCase;
use XcVm\Core\Cluster\StreamRecords;
use XcVm\Core\Cluster\StrictQuery;
use XcVm\Core\Database\DatabaseHandler;
use XcVm\Domain\Cluster\BlocklistDelta;
use XcVm\Domain\Cluster\ReplicaBuilder;
use XcVm\Domain\Cluster\StreamReplica;
/** Every statement it was asked, answered with $rResult as Database::query does. */
class StrictQueryDb extends DatabaseHandler {
/** @var list<array{string, list<mixed>}> */
public array $rQueries = [];
public function __construct(public mixed $rResult = true) {
$this->dbh = true;
}
public function query($query, ...$args): mixed {
$this->rQueries[] = [(string) $query, $args];
return $this->rResult;
}
public function get_rows($use_id = false, $column_as_id = '', $unique_row = true, $sub_row_id = '') {
return [];
}
public function get_row() {
// The previous statement's row, as a failed read leaves it.
return ['hi' => 42, 'value' => '1:1'];
}
}
/**
* The one "run a statement, throw when it fails" of what MAIN signs (ADR
* 0004, "Never from a failed read"), and the section each caller names.
*/
final class StrictQueryTest extends TestCase {
/** The callers, each with its own injected database. */
private const CALLERS = [StreamRecords::class, StreamReplica::class, BlocklistDelta::class, ReplicaBuilder::class];
protected function tearDown(): void {
foreach (self::CALLERS as $rClass) {
(new ReflectionProperty($rClass, 'db'))->setValue(null, null);
}
}
public function testAStatementThatRunsPassesItsArgumentsAndThrowsNothing(): void {
$rDb = new StrictQueryDb();
StrictQuery::run($rDb, 'streams', 'SELECT 1 WHERE `a` = ? AND `b` = ?;', 7, 'x');
$this->assertSame([['SELECT 1 WHERE `a` = ? AND `b` = ?;', [7, 'x']]], $rDb->rQueries);
}
public function testOnlyFalseIsAFailure(): void {
foreach ([0, null, '', []] as $rResult) {
StrictQuery::run(new StrictQueryDb($rResult), 'streams', 'SELECT 1;');
}
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('replica: a read failed');
StrictQuery::run(new StrictQueryDb(false), 'replica', 'SELECT 1;');
}
public function testOrThrowThrowsItsMessageAsItIs(): void {
$rDb = new StrictQueryDb();
StrictQuery::orThrow($rDb, 'flush', 'UPDATE `t` SET `a` = ?;', 3);
$this->assertSame([['UPDATE `t` SET `a` = ?;', [3]]], $rDb->rQueries);
foreach ([0, null, ''] as $rResult) {
StrictQuery::orThrow(new StrictQueryDb($rResult), 'db', 'SELECT 1;');
}
try {
StrictQuery::orThrow(new StrictQueryDb(false), 'db', 'SELECT 1;');
$this->fail('a failed statement did not throw');
} catch (\RuntimeException $rE) {
$this->assertSame('db', $rE->getMessage());
}
}
public function testEachCallerNamesItsSection(): void {
$rCases = [
'streams (StreamRecords)' => [StreamRecords::class, static fn() => StreamRecords::held(5, null), 'streams: a read failed'],
'streams (StreamReplica)' => [StreamReplica::class, static fn() => StreamReplica::prune(), 'streams: a read failed'],
'blocklist' => [BlocklistDelta::class, static fn() => BlocklistDelta::head(), 'blocklist: a read failed'],
'replica' => [ReplicaBuilder::class, static fn() => ReplicaBuilder::serversData(), 'replica: a read failed'],
];
foreach ($rCases as $rName => [$rClass, $rCall, $rMessage]) {
$rDb = new StrictQueryDb(false);
$rClass::setDb($rDb);
try {
$rCall();
$this->fail($rName . ': a failed read did not throw');
} catch (\RuntimeException $rE) {
$this->assertSame($rMessage, $rE->getMessage(), $rName);
}
$this->assertCount(1, $rDb->rQueries, $rName . ': it read on after a failed read');
}
}
}