mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-11-06 11:03:33 +01:00
The suite ran on in-memory SQLite, an engine production never uses: it hid column lengths, MySQL-only syntax and type coercion (the identity bug fixed in the previous commit passed CI that way). SQLite is gone: - TestDb connects only to MariaDB/MySQL (XCVM_TEST_DB_DSN/_USER/_PASS; root over the local socket, else 127.0.0.1:3306). Each instance gets a throwaway schema, dropped with it, in the installer's sql_mode, with an explicit connect timeout (a boot under test can zero default_socket_timeout). - InstallSchema and the tests take the production DDL as is: the SQLite reducers, the keys they re-added, the faked information_schema and the sqliteCreateFunction() stand-ins are removed; test DDL is MySQL DDL. - ReplicaBootTest, DbConnectRefusalTest and LazyDatabaseHandlerTest reach the test server instead of SQLite files; RecordingFinalizer no longer swallows an information_schema failure "for tests on SQLite". - `make test-db` starts a throwaway mariadb:11.4 (tmpfs) for a machine without a server; the CI PHPUnit job gets the same as a service. The suite also runs on a panel host with the bundled PHP and its own php.ini: tests reach the code through MAIN_HOME, not `<repo>/src`; repository checks and process-isolated tests (ionCube Loader + OPcache CLI segfault on a script read from stdin) are in the skip-on-panel group; xcvm_test_child_php() starts a child that fakes \XC_VM without the real extension; Redis-down tests use RedisManager::useConnector() instead of an eval'd \XC_VM. Stale opt-in tests fixed on the way: the real-agent provisioning test answers the agent-started check, the extension integration test sends the typed starts and reads the binary signature raw, a fake producer starts as PHP_BINARY, and the certbot test waits for the line its background command writes.
97 lines
5.5 KiB
PHP
97 lines
5.5 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use XcVm\Core\Module\SourceDriverRegistry;
|
|
use XcVm\Domain\Stream\StreamService;
|
|
use XcVm\Infrastructure\Database\DatabaseFactory;
|
|
|
|
/**
|
|
* Stream saves that would hand a module source-driver URL to something other
|
|
* than the driver's engine on the main server are refused
|
|
* (StreamService::driverSourceConflict and its form / import / mass-edit uses).
|
|
*/
|
|
final class StreamServiceDriverConflictTest extends TestCase {
|
|
private static function main(): callable {
|
|
return static fn(): array => [1];
|
|
}
|
|
|
|
protected function setUp(): void {
|
|
SourceDriverRegistry::reset();
|
|
SourceDriverRegistry::register(new TestSourceDriver(['acmedash']));
|
|
}
|
|
|
|
protected function tearDown(): void {
|
|
SourceDriverRegistry::reset();
|
|
DatabaseFactory::reset();
|
|
}
|
|
|
|
private static function call(string $rMethod, ...$rArgs) {
|
|
$m = new ReflectionMethod(StreamService::class, $rMethod);
|
|
$m->setAccessible(true);
|
|
return $m->invoke(null, ...$rArgs);
|
|
}
|
|
|
|
private static function tree(array $rNodes): array {
|
|
return array_map(static fn(array $n): array => ['id' => (string) $n[0], 'parent' => (string) $n[1]], $rNodes);
|
|
}
|
|
|
|
/** A save with no driver source never looks the servers up (the table may not even be there). */
|
|
public function testServersAreOnlyLookedUpForAFedDriverSource(): void {
|
|
$rNever = static function (): array {
|
|
throw new LogicException('looked up');
|
|
};
|
|
$this->assertNull(StreamService::driverSourceConflict(['http://a/b.ts'], [], self::tree([[5, 'source']]), $rNever));
|
|
$this->assertNull(StreamService::driverSourceConflict(['acmedash://p/c'], [], self::tree([[5, 1]]), $rNever));
|
|
}
|
|
|
|
public function testCoreSourcesAreNeverRefused(): void {
|
|
$this->assertNull(StreamService::driverSourceConflict(['http://a/b.ts'], ['direct_source' => 1, 'llod' => 2], self::tree([[5, 'source']]), self::main()));
|
|
}
|
|
|
|
public function testDriverSourceCannotBeDirect(): void {
|
|
$this->assertSame('source_driver_no_direct', StreamService::driverSourceConflict(['http://a/b.ts', 'acmedash://p/c'], ['direct_source' => 1], null, self::main()));
|
|
$this->assertSame('source_driver_no_direct', StreamService::driverSourceConflict(['acmedash://p/c'], ['direct_proxy' => 1], null, self::main()));
|
|
}
|
|
|
|
public function testDriverSourceCannotUseLlodV2(): void {
|
|
$this->assertSame('source_driver_no_llod2', StreamService::driverSourceConflict(['acmedash://p/c'], ['llod' => '2'], null, self::main()));
|
|
$this->assertNull(StreamService::driverSourceConflict(['acmedash://p/c'], ['llod' => 1], null, self::main()), 'LLOD v1 runs the producer path');
|
|
}
|
|
|
|
/** An LB fed from the source would run the source itself — without the module. */
|
|
public function testOnlyMainMayBeFedFromTheSource(): void {
|
|
$this->assertSame('source_driver_main_only', StreamService::driverSourceConflict(['acmedash://p/c'], [], self::tree([[1, 'source'], [5, 'source']]), self::main()));
|
|
$this->assertNull(StreamService::driverSourceConflict(['acmedash://p/c'], [], self::tree([[1, 'source'], [5, 1], [6, 5]]), self::main()), 'LB children of main pull the loopback');
|
|
$this->assertNull(StreamService::driverSourceConflict(['acmedash://p/c'], [], null, self::main()), 'servers unchanged');
|
|
}
|
|
|
|
public function testImportChecksEveryStreamWithItsOwnValues(): void {
|
|
$rStreams = [['stream_source' => ['http://a/b.ts']], ['stream_source' => ['acmedash://p/c'], 'direct_source' => 1]];
|
|
$this->assertSame('source_driver_no_direct', self::call('importDriverConflict', $rStreams, ['direct_source' => 0], null, self::main()));
|
|
$this->assertNull(self::call('importDriverConflict', [['stream_source' => ['acmedash://p/c']]], ['direct_source' => 0], null, self::main()));
|
|
}
|
|
|
|
public function testPostedServerTree(): void {
|
|
$rTree = json_encode([['id' => '1', 'parent' => 'source']]);
|
|
$this->assertSame([['id' => '1', 'parent' => 'source']], self::call('postedServerTree', ['server_tree_data' => $rTree], false));
|
|
$this->assertNull(self::call('postedServerTree', ['server_tree_data' => $rTree], true), 'mass edit without the servers box');
|
|
$this->assertNull(self::call('postedServerTree', ['server_tree_data' => $rTree, 'c_server_tree' => 1, 'server_type' => 'DEL'], true), 'removing servers conflicts with nothing');
|
|
$this->assertNotNull(self::call('postedServerTree', ['server_tree_data' => $rTree, 'c_server_tree' => 1, 'server_type' => 'SET'], true));
|
|
$this->assertNull(self::call('postedServerTree', [], false));
|
|
}
|
|
|
|
public function testMassEditChecksTheStreamsAsTheyWillBe(): void {
|
|
$rDb = new TestDb();
|
|
$rDb->exec('CREATE TABLE streams (id INTEGER PRIMARY KEY AUTO_INCREMENT, stream_source TEXT, direct_source INTEGER, direct_proxy INTEGER, llod INTEGER)');
|
|
$rDb->query('INSERT INTO streams VALUES (1, ?, 0, 0, 0), (2, ?, 1, 0, 0)', json_encode(['acmedash://p/c']), json_encode(['http://a/b.ts']));
|
|
DatabaseFactory::set($rDb);
|
|
|
|
$this->assertSame('source_driver_no_direct', self::call('massEditDriverConflict', [1, 2], ['direct_source' => 1], null, self::main()));
|
|
$this->assertNull(self::call('massEditDriverConflict', [2], ['direct_source' => 1], null, self::main()), 'core source');
|
|
$this->assertNull(self::call('massEditDriverConflict', [1], ['direct_source' => 0, 'direct_proxy' => 0], null, self::main()), 'the edit turns it off');
|
|
$this->assertSame('source_driver_main_only', self::call('massEditDriverConflict', [1], [], self::tree([[5, 'source']]), self::main()));
|
|
$this->assertNull(self::call('massEditDriverConflict', [1], ['stream_all' => 1], null, self::main()), 'nothing relevant changes');
|
|
$this->assertNull(self::call('massEditDriverConflict', [], ['direct_source' => 1], null, self::main()));
|
|
}
|
|
}
|