mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-09-26 04:02:51 +02:00
The two methods were ~90% duplicate, differing only in how they obtain
settings/cache/bouquets and in the divergent GeoIP + signal backends
(GeoIPService/file-signals vs GeoIP/Redis) that stay inline. Extract the
identical blocks into private helpers, called by both:
loadUserRow - credential resolution (token/cache file or DB),
resolving $rUserID by reference so the cached
re-verification keeps its exact behaviour
verifyCachedCredentials - cached access-token / username+password re-check
decodeUserFields - JSON line fields -> arrays
resolveOutputFormats - allowed output-format keys
aggregateBouquetIds - bouquet -> channel/series/vod/live/radio id lists
resolveCategoryIds - bouquet -> category ids
Also de-obfuscate the remaining `if (cond) {} else { body }` blocks in both
methods. Net -74 lines despite adding the shared helpers (~240 lines of
duplication removed). Behaviour preserved; the file-vs-Redis signal divergence
is intentionally left as-is (separate decision).
Tests: UserRepositoryTest covers the pure helpers (aggregate/category/decode/
verify), 12 tests total. Validated live: player_api/testxc returns auth=1,
Active, allowed_output_formats=[m3u8,ts,rtmp]; on-demand /live start still works.
Verified: php -l, phpunit (430 tests, 962 assertions), make gates.
115 lines
4.9 KiB
PHP
115 lines
4.9 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use XcVm\Domain\User\UserRepository;
|
|
|
|
/**
|
|
* Unit tests for UserRepository::ispChanged() — the ISP-persist gate extracted
|
|
* from getStreamingUserInfo()/getUserInfo(). It guards the block that writes a
|
|
* looked-up ISP back to the line. A GeoIP lookup miss (empty con_isp_name) must
|
|
* NOT persist: the old code did, which read the undefined isp_asn key (the
|
|
* "Undefined array key isp_asn" warning) and stored a null isp_desc/as_number.
|
|
*/
|
|
final class UserRepositoryTest extends TestCase {
|
|
|
|
/** Invoke a private static UserRepository method via reflection. */
|
|
private function call(string $rMethod, ...$rArgs) {
|
|
$rM = new ReflectionMethod(UserRepository::class, $rMethod);
|
|
$rM->setAccessible(true);
|
|
return $rM->invoke(null, ...$rArgs);
|
|
}
|
|
|
|
public function testPersistsWhenIspDetectedAndChanged(): void {
|
|
$this->assertTrue(UserRepository::ispChanged('Comcast', 0, 'Verizon'));
|
|
}
|
|
|
|
public function testFirstDetectionAgainstNullDescPersists(): void {
|
|
// No ISP stored yet -> a detected ISP counts as changed.
|
|
$this->assertTrue(UserRepository::ispChanged('Comcast', 0, null));
|
|
}
|
|
|
|
// ── the bug: a GeoIP miss must not reach the persist branch ──
|
|
|
|
public function testGeoipMissDoesNotPersist(): void {
|
|
$this->assertFalse(UserRepository::ispChanged(null, 0, 'Verizon'), 'null con_isp_name');
|
|
$this->assertFalse(UserRepository::ispChanged('', 0, 'Verizon'), 'empty con_isp_name');
|
|
}
|
|
|
|
// ── no write when nothing actually changed ──
|
|
|
|
public function testUnchangedIspDoesNotPersist(): void {
|
|
$this->assertFalse(UserRepository::ispChanged('Comcast', 0, 'Comcast'));
|
|
$this->assertFalse(UserRepository::ispChanged('Comcast', 0, 'comcast'), 'case-insensitive');
|
|
$this->assertFalse(UserRepository::ispChanged('COMCAST', 0, 'comcast'));
|
|
}
|
|
|
|
// ── a line already flagged in violation is left alone ──
|
|
|
|
public function testViolationDoesNotPersist(): void {
|
|
$this->assertFalse(UserRepository::ispChanged('Comcast', 1, 'Verizon'));
|
|
}
|
|
|
|
// ── aggregateBouquetIds — locks the streams→channel_ids / channels→live_ids mapping ──
|
|
|
|
public function testAggregateBouquetIdsMapsAndDedupes(): void {
|
|
$rBouquets = array(
|
|
1 => array('streams' => array(10, 11), 'series' => array(20), 'channels' => array(30), 'movies' => array(40), 'radios' => array(50)),
|
|
2 => array('streams' => array(11, 12)), // 11 shared across bouquets
|
|
);
|
|
$rOut = $this->call('aggregateBouquetIds', array(1, 2), $rBouquets);
|
|
$this->assertSame(array(10, 11, 12), array_values($rOut['channel_ids']), 'streams -> channel_ids, unique');
|
|
$this->assertSame(array(20), array_values($rOut['series_ids']));
|
|
$this->assertSame(array(30), array_values($rOut['live_ids']), 'channels -> live_ids');
|
|
$this->assertSame(array(40), array_values($rOut['vod_ids']), 'movies -> vod_ids');
|
|
$this->assertSame(array(50), array_values($rOut['radio_ids']));
|
|
}
|
|
|
|
public function testAggregateBouquetIdsIgnoresMissingKeys(): void {
|
|
$rOut = $this->call('aggregateBouquetIds', array(1), array(1 => array('streams' => array(7))));
|
|
$this->assertSame(array(7), array_values($rOut['channel_ids']));
|
|
$this->assertSame(array(), $rOut['series_ids']);
|
|
$this->assertSame(array(), $rOut['radio_ids']);
|
|
}
|
|
|
|
// ── resolveCategoryIds ──
|
|
|
|
public function testResolveCategoryIdsUniqueAcrossBouquets(): void {
|
|
$rMap = array(1 => array(100, 101), 2 => array(101, 102));
|
|
$this->assertSame(array(100, 101, 102), $this->call('resolveCategoryIds', array(1, 2), $rMap));
|
|
}
|
|
|
|
// ── decodeUserFields ──
|
|
|
|
public function testDecodeUserFieldsNormalises(): void {
|
|
$rOut = $this->call('decodeUserFields', array(
|
|
'allowed_ips' => '["1.2.3.4"," 5.6.7.8 "]',
|
|
'allowed_ua' => 'null',
|
|
'bouquet' => '[1,2,3]',
|
|
'allowed_outputs' => '["1","2"]',
|
|
));
|
|
$this->assertSame(array(1, 2, 3), $rOut['bouquet']);
|
|
$this->assertSame(array('1.2.3.4', '5.6.7.8'), array_values($rOut['allowed_ips']), 'trimmed');
|
|
$this->assertSame(array(), $rOut['allowed_ua'], 'non-array json -> []');
|
|
$this->assertSame(array(1, 2), $rOut['allowed_outputs'], 'intval-mapped');
|
|
}
|
|
|
|
// ── verifyCachedCredentials ──
|
|
|
|
public function testVerifyTokenLookup(): void {
|
|
$rToken = str_repeat('a', 32);
|
|
$this->assertTrue($this->call('verifyCachedCredentials', array('access_token' => $rToken), null, $rToken, null));
|
|
$this->assertFalse($this->call('verifyCachedCredentials', array('access_token' => 'other'), null, $rToken, null));
|
|
}
|
|
|
|
public function testVerifyCredentialLookup(): void {
|
|
$rRow = array('username' => 'u', 'password' => 'p');
|
|
$this->assertTrue($this->call('verifyCachedCredentials', $rRow, null, 'u', 'p'));
|
|
$this->assertFalse($this->call('verifyCachedCredentials', $rRow, null, 'u', 'wrong'));
|
|
}
|
|
|
|
public function testVerifyIdLookupSkipsCheck(): void {
|
|
// An id-based lookup carries no username/password to re-verify.
|
|
$this->assertTrue($this->call('verifyCachedCredentials', array(), 5, null, null));
|
|
}
|
|
}
|