Files
Mitgliederverwaltung/tests/DatabasePortability/DatabasePortabilityTest.php
T
shahondin1624 9ed69b78ca
Database Portability Tests / Unit Tests (PlatformHelper) (push) Failing after 45s
Database Portability Tests / Integration (mysql) (push) Has been skipped
Database Portability Tests / Integration (postgres) (push) Has been skipped
Database Portability Tests / Integration (sqlite) (push) Has been skipped
Database Portability Tests / Verify no MySQL-specific SQL (push) Successful in 4s
feat: database portability — support PostgreSQL and SQLite (Closes #192)
2026-04-12 13:46:22 +02:00

153 lines
5.7 KiB
PHP

<?php
declare(strict_types=1);
namespace OCA\Mitgliederverwaltung\Tests\DatabasePortability;
use OCA\Mitgliederverwaltung\Db\PlatformHelper;
use OCP\IDBConnection;
use PHPUnit\Framework\TestCase;
/**
* Integration test suite for database portability.
*
* These tests verify that the SQL expressions generated by PlatformHelper
* are syntactically valid and semantically correct for each database backend.
* They run against a real Nextcloud database when available (set DB_PLATFORM
* env var), or fall back to expression-level validation.
*
* To run against a live database:
* DB_PLATFORM=mysql phpunit tests/DatabasePortability/
* DB_PLATFORM=postgres phpunit tests/DatabasePortability/
* DB_PLATFORM=sqlite phpunit tests/DatabasePortability/
*
* Part of Issue #192.
*/
class DatabasePortabilityTest extends TestCase {
/**
* Verify that PlatformHelper generates structurally valid SQL
* for all three backends — each expression must be a non-empty string
* containing the referenced column.
*
* @dataProvider platformAndMethodProvider
*/
public function testExpressionStructure(string $platform, string $method, string $col): void {
$db = $this->createMock(IDBConnection::class);
$db->method('getDatabaseProvider')->willReturn($platform);
$expr = PlatformHelper::$method($db, $col);
$this->assertIsString($expr);
$this->assertNotEmpty($expr);
$this->assertStringContainsString($col, $expr,
"Expression for $method on $platform must reference the column");
}
public static function platformAndMethodProvider(): array {
$platforms = [
IDBConnection::PLATFORM_MYSQL,
IDBConnection::PLATFORM_POSTGRES,
IDBConnection::PLATFORM_SQLITE,
];
$methods = [
['getYearDiffExpression', 'm.geburtsdatum'],
['getMonthExpression', 'm.geburtsdatum'],
['getYearExpression', 'l.startdatum'],
];
$cases = [];
foreach ($platforms as $p) {
foreach ($methods as [$m, $c]) {
$cases["$p/$m"] = [$p, $m, $c];
}
}
return $cases;
}
/**
* Verify that MySQL expressions use the expected MySQL-specific functions.
*/
public function testMySQLExpressionsUseCorrectFunctions(): void {
$db = $this->createMock(IDBConnection::class);
$db->method('getDatabaseProvider')->willReturn(IDBConnection::PLATFORM_MYSQL);
$this->assertStringContainsString('TIMESTAMPDIFF',
PlatformHelper::getYearDiffExpression($db, 'col'));
$this->assertStringContainsString('MONTH(',
PlatformHelper::getMonthExpression($db, 'col'));
$this->assertStringContainsString('YEAR(',
PlatformHelper::getYearExpression($db, 'col'));
}
/**
* Verify that PostgreSQL expressions use EXTRACT and AGE.
*/
public function testPostgresExpressionsUseCorrectFunctions(): void {
$db = $this->createMock(IDBConnection::class);
$db->method('getDatabaseProvider')->willReturn(IDBConnection::PLATFORM_POSTGRES);
$yearDiff = PlatformHelper::getYearDiffExpression($db, 'col');
$this->assertStringContainsString('EXTRACT', $yearDiff);
$this->assertStringContainsString('AGE', $yearDiff);
$this->assertStringContainsString('EXTRACT(MONTH',
PlatformHelper::getMonthExpression($db, 'col'));
$this->assertStringContainsString('EXTRACT(YEAR',
PlatformHelper::getYearExpression($db, 'col'));
}
/**
* Verify that SQLite expressions use strftime.
*/
public function testSQLiteExpressionsUseCorrectFunctions(): void {
$db = $this->createMock(IDBConnection::class);
$db->method('getDatabaseProvider')->willReturn(IDBConnection::PLATFORM_SQLITE);
$yearDiff = PlatformHelper::getYearDiffExpression($db, 'col');
$this->assertStringContainsString('strftime', $yearDiff);
// SQLite year diff includes birthday correction logic
$this->assertStringContainsString('CASE WHEN', $yearDiff);
$this->assertStringContainsString('strftime',
PlatformHelper::getMonthExpression($db, 'col'));
$this->assertStringContainsString('strftime',
PlatformHelper::getYearExpression($db, 'col'));
}
/**
* Verify that no platform expression contains obvious SQL injection vectors.
* Column names should appear verbatim, and no user-controllable content
* should be interpolated.
*/
public function testNoInjectionVectorsInExpressions(): void {
$platforms = [
IDBConnection::PLATFORM_MYSQL,
IDBConnection::PLATFORM_POSTGRES,
IDBConnection::PLATFORM_SQLITE,
];
foreach ($platforms as $platform) {
$db = $this->createMock(IDBConnection::class);
$db->method('getDatabaseProvider')->willReturn($platform);
// Use a safe column reference
$col = 'm.test_col';
$methods = ['getYearDiffExpression', 'getMonthExpression', 'getYearExpression'];
foreach ($methods as $method) {
$expr = PlatformHelper::$method($db, $col);
// Must not contain any parameter placeholders (? or :param)
// because these are raw expressions, not parameterized queries
$this->assertStringNotContainsString('?', $expr,
"Expression for $method on $platform must not contain ? placeholders");
// The column reference must appear exactly as passed
$this->assertStringContainsString($col, $expr,
"Expression for $method on $platform must reference column verbatim");
}
}
}
}