mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-09-25 20:01:56 +02:00
Signals — the file-backed queue of deferred DB writes drained by the cache-handler daemon — were produced two different ways: five call sites went through RedisManager::setSignal() while getStreamingUserInfo inlined the same file_put_contents(). Worse, the writer lived on RedisManager yet wrote a *file*, not Redis — a misleading home that made the two paths look like different transports (they were byte-identical). Introduce XcVm\Infrastructure\Signal\SignalQueue as the single owner of the on-disk contract (cache_<md5(key)> holding [key, data]): - push() - the one producer API; all 7 call sites use it now (UserRepository x4, auth.php x2, BruteforceGuard x1) - pending() - the drain API; CacheHandlerCommand reads through it instead of its own glob + json_decode - pathFor() / PREFIX - path helpers RedisManager::setSignal() stays as a thin @deprecated alias delegating to SignalQueue::push() for any out-of-tree callers. Behaviour unchanged (same files, same format, same consumer switch). Adds SignalQueueTest (format / idempotency / drain / malformed). Validated live: player_api/testxc still authenticates (auth=1) with SignalQueue autoloaded. Verified: php -l, phpunit (435 tests, 973 assertions), make gates.
68 lines
2.3 KiB
PHP
68 lines
2.3 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use XcVm\Infrastructure\Signal\SignalQueue;
|
|
|
|
/**
|
|
* Unit tests for SignalQueue — the file-backed deferred-work queue that
|
|
* producers (auth / ISP / forced_country / flood) push to and the cache-handler
|
|
* daemon drains. Locks the on-disk contract (cache_<md5(key)> holding
|
|
* [key, data]) that the consumer's switch depends on.
|
|
*/
|
|
final class SignalQueueTest extends TestCase {
|
|
|
|
public static function setUpBeforeClass(): void {
|
|
if (!defined('SIGNALS_TMP_PATH')) {
|
|
define('SIGNALS_TMP_PATH', sys_get_temp_dir() . '/xcvm-sig-test/');
|
|
}
|
|
if (!is_dir(SIGNALS_TMP_PATH)) {
|
|
mkdir(SIGNALS_TMP_PATH, 0777, true);
|
|
}
|
|
}
|
|
|
|
protected function setUp(): void {
|
|
foreach (glob(SIGNALS_TMP_PATH . 'cache_*') ?: array() as $rFile) {
|
|
unlink($rFile);
|
|
}
|
|
}
|
|
|
|
public function testPushWritesCanonicalFileFormat(): void {
|
|
SignalQueue::push('isp/42', json_encode(array('Comcast', 7922)));
|
|
$rPath = SIGNALS_TMP_PATH . 'cache_' . md5('isp/42');
|
|
$this->assertFileExists($rPath);
|
|
$this->assertSame(array('isp/42', json_encode(array('Comcast', 7922))), json_decode(file_get_contents($rPath), true));
|
|
}
|
|
|
|
public function testPathForMatchesPushLocation(): void {
|
|
$this->assertSame(SIGNALS_TMP_PATH . 'cache_' . md5('forced_country/5'), SignalQueue::pathFor('forced_country/5'));
|
|
}
|
|
|
|
public function testPushIsIdempotentPerKey(): void {
|
|
SignalQueue::push('expiring/9', 100);
|
|
SignalQueue::push('expiring/9', 200); // same key overwrites the pending record
|
|
$rPending = SignalQueue::pending();
|
|
$this->assertCount(1, $rPending);
|
|
$this->assertSame(200, $rPending[0][2]);
|
|
}
|
|
|
|
public function testPendingReturnsFileKeyDataTuples(): void {
|
|
SignalQueue::push('forced_country/1', 'DE');
|
|
SignalQueue::push('isp/2', json_encode(array('ISP', 1)));
|
|
$rByKey = array();
|
|
foreach (SignalQueue::pending() as list($rFile, $rKey, $rData)) {
|
|
$this->assertFileExists($rFile);
|
|
$rByKey[$rKey] = $rData;
|
|
}
|
|
$this->assertSame('DE', $rByKey['forced_country/1']);
|
|
$this->assertSame(json_encode(array('ISP', 1)), $rByKey['isp/2']);
|
|
}
|
|
|
|
public function testPendingSkipsMalformedRecords(): void {
|
|
file_put_contents(SIGNALS_TMP_PATH . 'cache_bad', 'not-json');
|
|
SignalQueue::push('isp/3', 1);
|
|
$rPending = SignalQueue::pending();
|
|
$this->assertCount(1, $rPending);
|
|
$this->assertSame('isp/3', $rPending[0][1]);
|
|
}
|
|
}
|