Files
XC_VM/tests/Unit/CreatedChannelSourceTest.php
T
obscuremindandClaude Opus 4.8 017f9b0bcb fix(stream): two 2.6.6-p5 regressions — native-remux fallback and mode-2 created channels
p5 bundled four feature PRs; two of them broke real panels.

1. On-demand streams fail to open when idle (web player times out), while a
   stream already running plays fine. PR #380 made the unsupervised
   (fanout-off / PHP-monitor) launcher run the native remuxer for the DEFAULT
   fanout_source_backend='auto' with no ffmpeg fallback (the fallback block was
   later removed in refactor). A cold on-demand source the remuxer can't serve
   (fMP4/encrypted/not-yet-connected) then produces no HLS and never opens; an
   already-live stream skips the start path. Gate the unsupervised native
   launch on `fanout_source_backend === 'native'` (explicit opt-in) so `auto`
   stays on ffmpeg off-fanout, exactly as in 2.6.6. The daemon (fanout on,
   supervised) still supplies auto's own ffmpeg fallback and is unchanged.

2. Created channels stay at 0% on a cluster mode-2 (api) LB. PR #384's new
   vod:<id> resolution (StreamProcess::resolveVodSource) and created-channel
   EPG (CreatedChannelEpg::regenerate → reservedSourceId) queried MySQL
   directly, which a mode-2 node refuses (LbDatabaseAccessException). Read the
   VOD's container/status through StreamSource (replica + runtime store, as the
   rest of the build already does), and no-op the EPG regen on a mode-2 node
   (ReplicaBoot::apiMode) — MAIN's daily EPG cron regenerates every type-3
   channel from the cc_info/stream_started the node syncs back.

Full Unit suite green (5319 tests). The mode-2 and native-remux paths still
want the live-node acceptance that was skipped for #380/#384.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AneTzAHPPkYWPQjwyZ2td9
2026-10-13 16:47:57 +00:00

50 lines
1.7 KiB
PHP

<?php
use PHPUnit\Framework\TestCase;
use XcVm\Domain\Stream\StreamProcess;
use XcVm\Infrastructure\Database\DatabaseFactory;
final class CreatedChannelSourceTest extends TestCase {
private TestDb $rDb;
protected function setUp(): void {
if (!defined('SERVER_ID')) {
define('SERVER_ID', 1);
}
$this->rDb = new TestDb();
$this->rDb->exec('CREATE TABLE `streams` (`id` INT PRIMARY KEY, `type` INT, `target_container` VARCHAR(16), `direct_source` INT DEFAULT 0)');
$this->rDb->exec('CREATE TABLE `streams_servers` (`stream_id` INT, `server_id` INT, `stream_status` INT, `to_analyze` INT)');
$this->rDb->query("INSERT INTO `streams` VALUES (7, 2, 'mp4', 0)");
$this->rDb->query('INSERT INTO `streams_servers` VALUES (7, 1, 0, 0)');
DatabaseFactory::set($this->rDb);
}
protected function tearDown(): void {
DatabaseFactory::reset();
}
private function resolve(string $e, int $s = 1): ?string {
$m = new \ReflectionMethod(StreamProcess::class, 'resolveVodSource');
$m->setAccessible(true);
return $m->invoke(null, $e, $s);
}
public function testVodEntryResolvesToEncodedFile(): void {
$this->assertSame(VOD_PATH . '7.mp4', $this->resolve('vod:7'));
}
public function testVodEntryMissingOnServerReturnsNull(): void {
$this->assertNull($this->resolve('vod:7', 2));
}
public function testVodEntryBrokenReturnsNull(): void {
$this->rDb->query('UPDATE `streams_servers` SET `stream_status` = 1 WHERE `stream_id` = 7');
$this->assertNull($this->resolve('vod:7'));
}
public function testVodEntryStillAnalysingReturnsNull(): void {
$this->rDb->query('UPDATE `streams_servers` SET `to_analyze` = 1 WHERE `stream_id` = 7');
$this->assertNull($this->resolve('vod:7'));
}
}