Skip to content

Commit 8c79eb3

Browse files
committed
test: raise coverage for registry, cache, quoting, explain, filters, windows
chore: add LB tests (round-robin, weighted); all tests/examples green
1 parent fb77062 commit 8c79eb3

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed

tests/SharedCoverageTest.php

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3183,6 +3183,96 @@ public function testQueryCaching(): void
31833183
$this->assertEquals($countBefore, $cache->count()); // No new cache entry
31843184
}
31853185

3186+
public function testDialectRegistryAndCacheManagerCoverage(): void
3187+
{
3188+
// DialectRegistry
3189+
$drivers = \tommyknocker\pdodb\connection\DialectRegistry::getSupportedDrivers();
3190+
$this->assertNotEmpty($drivers);
3191+
$this->assertTrue(\tommyknocker\pdodb\connection\DialectRegistry::isSupported('sqlite'));
3192+
$dialect = \tommyknocker\pdodb\connection\DialectRegistry::resolve('sqlite');
3193+
$this->assertEquals('sqlite', $dialect->getDriverName());
3194+
3195+
// CacheManager basic ops
3196+
$cache = new ArrayCache();
3197+
$cm = new \tommyknocker\pdodb\cache\CacheManager($cache, ['enabled' => true, 'default_ttl' => 60, 'prefix' => 'p']);
3198+
$key = $cm->generateKey('SELECT 1', ['a' => 1], 'sqlite');
3199+
$this->assertIsString($key);
3200+
$this->assertFalse($cm->has($key));
3201+
$this->assertTrue($cm->set($key, 'val'));
3202+
$this->assertTrue($cm->has($key));
3203+
$this->assertEquals('val', $cm->get($key));
3204+
$this->assertTrue($cm->delete($key));
3205+
$this->assertTrue($cm->clear());
3206+
}
3207+
3208+
public function testIdentifierQuotingAndUnsafeDetection(): void
3209+
{
3210+
$db = new \tommyknocker\pdodb\PdoDb('sqlite', ['path' => ':memory:']);
3211+
$db->rawQuery('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INT)');
3212+
3213+
// Quoting of qualified identifier
3214+
$db->find()->from('users')->select(['users.name'])->get();
3215+
$this->assertStringContainsString('"users"."name"', $db->lastQuery);
3216+
3217+
// Pass-through expression
3218+
$db->find()->from('users')->select(['SUM(age)'])->get();
3219+
$this->assertStringContainsString('SUM(age)', $db->lastQuery);
3220+
3221+
// Unsafe token should throw
3222+
$this->expectException(\InvalidArgumentException::class);
3223+
$db->find()->from('users')->select(['name; DROP TABLE'])->get();
3224+
}
3225+
3226+
public function testExplainDescribeAndIndexes(): void
3227+
{
3228+
$db = new \tommyknocker\pdodb\PdoDb('sqlite', ['path' => ':memory:']);
3229+
$db->rawQuery('CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT, age INT)');
3230+
$db->rawQuery('CREATE INDEX idx_t_name ON t(name)');
3231+
3232+
$report = $db->find()->from('t')->where('name', 'Alice')->explain();
3233+
$this->assertIsArray($report);
3234+
3235+
$reportAnalyze = $db->find()->from('t')->where('name', 'Alice')->explainAnalyze();
3236+
$this->assertIsArray($reportAnalyze);
3237+
3238+
$desc = $db->find()->from('t')->describe();
3239+
$this->assertIsArray($desc);
3240+
3241+
$idx = $db->find()->from('t')->indexes();
3242+
$this->assertIsArray($idx);
3243+
3244+
$keys = $db->find()->from('t')->keys();
3245+
$this->assertIsArray($keys);
3246+
3247+
$constraints = $db->find()->from('t')->constraints();
3248+
$this->assertIsArray($constraints);
3249+
}
3250+
3251+
public function testFilterValueAndWindowFunctionResolution(): void
3252+
{
3253+
$db = new \tommyknocker\pdodb\PdoDb('sqlite', ['path' => ':memory:']);
3254+
$db->rawQuery('CREATE TABLE s (id INTEGER PRIMARY KEY, grp INT, val INT)');
3255+
$db->find()->table('s')->insertMulti([
3256+
['grp' => 1, 'val' => 10],
3257+
['grp' => 1, 'val' => 20],
3258+
['grp' => 2, 'val' => 30],
3259+
]);
3260+
3261+
// FilterValue COUNT(*) where val > 10 -> in SQLite fallback to CASE WHEN form
3262+
$fv = new \tommyknocker\pdodb\helpers\values\FilterValue('COUNT(*)');
3263+
$fv->filter('val', 10, '>');
3264+
$db->find()->from('s')->select(['cnt' => $fv])->get();
3265+
$this->assertTrue(
3266+
str_contains($db->lastQuery, ' FILTER (WHERE')
3267+
|| str_contains($db->lastQuery, 'COUNT(CASE WHEN')
3268+
);
3269+
3270+
// Window function example: ROW_NUMBER() OVER (PARTITION BY grp ORDER BY val)
3271+
$wf = new \tommyknocker\pdodb\helpers\values\WindowFunctionValue('ROW_NUMBER');
3272+
$db->find()->from('s')->select(['rn' => $wf])->get();
3273+
$this->assertStringContainsString('ROW_NUMBER', $db->lastQuery);
3274+
}
3275+
31863276
/**
31873277
* Ensure wildcard selections work uniformly across input forms.
31883278
* - select(['*']) behaves like select('*')
@@ -3228,6 +3318,75 @@ public function testSelectWildcardForms(): void
32283318
$this->assertEquals($rows2[0]['amount'], $rows3[0]['amount']);
32293319
}
32303320

3321+
public function testRoundRobinAndWeightedLoadBalancers(): void
3322+
{
3323+
// Minimal ConnectionInterface stub
3324+
$makeConn = function (string $driver): \tommyknocker\pdodb\connection\ConnectionInterface {
3325+
return new class($driver) implements \tommyknocker\pdodb\connection\ConnectionInterface {
3326+
public function __construct(private string $driver) {}
3327+
public function getPdo(): \PDO { return new \PDO('sqlite::memory:'); }
3328+
public function getDriverName(): string { return $this->driver; }
3329+
public function getDialect(): \tommyknocker\pdodb\dialects\DialectInterface { throw new \RuntimeException('not used'); }
3330+
public function resetState(): void {}
3331+
public function prepare(string $sql, array $params = []): static { return $this; }
3332+
public function execute(array $params = []): \PDOStatement { throw new \RuntimeException('not used'); }
3333+
public function query(string $sql): \PDOStatement|false { return false; }
3334+
public function quote(mixed $value): string|false { return (string)$value; }
3335+
public function transaction(): bool { return true; }
3336+
public function commit(): bool { return true; }
3337+
public function rollBack(): bool { return true; }
3338+
public function inTransaction(): bool { return false; }
3339+
public function getLastInsertId(?string $name = null): false|string { return '0'; }
3340+
public function getLastQuery(): ?string { return null; }
3341+
public function getLastError(): ?string { return null; }
3342+
public function getLastErrno(): int { return 0; }
3343+
public function getExecuteState(): ?bool { return true; }
3344+
public function setAttribute(int $attribute, mixed $value): bool { return true; }
3345+
public function getAttribute(int $attribute): mixed { return null; }
3346+
};
3347+
};
3348+
3349+
$connections = [
3350+
'a' => $makeConn('sqlite'),
3351+
'b' => $makeConn('sqlite'),
3352+
'c' => $makeConn('sqlite'),
3353+
];
3354+
3355+
// RoundRobin
3356+
$rr = new \tommyknocker\pdodb\connection\loadbalancer\RoundRobinLoadBalancer();
3357+
$first = $rr->select($connections);
3358+
$second = $rr->select($connections);
3359+
$this->assertContains($first, array_values($connections));
3360+
$this->assertContains($second, array_values($connections));
3361+
$this->assertNotSame($first, $second);
3362+
$rr->markFailed('b');
3363+
$third = $rr->select($connections);
3364+
$this->assertContains($third, array_values($connections));
3365+
$rr->reset();
3366+
$fourth = $rr->select($connections);
3367+
$this->assertContains($fourth, array_values($connections));
3368+
3369+
// Weighted: ensure failed nodes excluded and selection returns healthy names
3370+
$wlb = new \tommyknocker\pdodb\connection\loadbalancer\WeightedLoadBalancer();
3371+
$wlb->setWeights(['a' => 1, 'b' => 5, 'c' => 1]);
3372+
$wlb->markFailed('b');
3373+
$seen = [];
3374+
for ($i = 0; $i < 20; $i++) {
3375+
$picked = $wlb->select($connections);
3376+
$this->assertContains($picked, [$connections['a'], $connections['c']]);
3377+
$seen[spl_object_hash($picked)] = true;
3378+
}
3379+
// Both a and c appeared at least once
3380+
$this->assertCount(2, $seen);
3381+
// After reset failure, 'b' can be selected
3382+
$wlb->markHealthy('b');
3383+
$foundB = false;
3384+
for ($i = 0; $i < 50; $i++) {
3385+
if ($wlb->select($connections) === $connections['b']) { $foundB = true; break; }
3386+
}
3387+
$this->assertTrue($foundB);
3388+
}
3389+
32313390
/**
32323391
* Test caching without cache manager (should work normally).
32333392
*/

0 commit comments

Comments
 (0)