Files
XC_VM/tests/Unit/ConsoleDiscoveryTest.php
T
Divarion-D fd35f00c4d fix(views): import migrated classes at top of 5 admin view templates
enigma, episode, episodes, process_monitor and stream_view referenced migrated
classes (UserRepository, BouquetService, StreamRepository, RequestManager,
SettingsManager, ...) by short name with either no `use` or a `use` placed
*below* the first usage. PHP imports outside the top scope are positional, so the
short name resolved to a now-nonexistent global class — a runtime fatal on those
admin pages (php -l passes; not covered by PHPStan/PHPUnit). The migration's
automated `use` insertion missed them due to the interleaved HTML / short-tag
(<? , <?=) structure, and the later php-cs-fixer pass stripped some as 'unused'
because it could not see usage inside short-tag blocks.

- Consolidate every needed `use XcVm\...;` into a single top-of-file PHP block.
- Exclude Public/Views and Modules/*/views from php-cs-fixer (no_unused_imports
  is unreliable on short-tag templates); their import correctness is enforced by
  the new check_procedural_use gate instead.

Verified: php -l (short_open_tag=1) clean; PHPStan no errors; PHPUnit green.
2026-06-25 20:57:37 +03:00

69 lines
2.0 KiB
PHP

<?php
use PHPUnit\Framework\TestCase;
use XcVm\Cli\CommandInterface;
/**
* Guard for Фаза 2: console.php discovers commands by FQCN built from the scan
* directory's PSR-4 namespace (no basename==classname, no manual require, no
* global 'CommandInterface' string). This test mirrors that discovery without
* booting the full CLI (which needs the ioncube XC_VM class) and asserts the
* command surface stays stable.
*/
final class ConsoleDiscoveryTest extends TestCase {
private const DIRS = [
'Cli/Commands' => 'XcVm\\Cli\\Commands\\',
'Cli/CronJobs' => 'XcVm\\Cli\\CronJobs\\',
];
/** @return array{0:string[],1:string[]} [resolved FQCNs, concrete commands implementing the interface] */
private function discover(): array {
$resolved = [];
$commands = [];
foreach (self::DIRS as $dir => $ns) {
$path = MAIN_HOME . $dir;
$this->assertDirectoryExists($path);
foreach (glob($path . '/*.php') as $file) {
$fqcn = $ns . basename($file, '.php');
if (!class_exists($fqcn)) {
continue;
}
$resolved[] = $fqcn;
$ref = new ReflectionClass($fqcn);
if (!$ref->isAbstract() && $ref->implementsInterface(CommandInterface::class)) {
$commands[] = $fqcn;
}
}
}
return [$resolved, $commands];
}
public function testEveryCliFileResolvesByFqcn(): void {
$files = 0;
foreach (self::DIRS as $dir => $ns) {
$files += count(glob(MAIN_HOME . $dir . '/*.php'));
}
[$resolved] = $this->discover();
$this->assertSame(
$files,
count($resolved),
'Every Cli/Commands + Cli/CronJobs file must resolve to a class by FQCN.'
);
}
public function testDiscoversAStableSetOfCommands(): void {
[, $commands] = $this->discover();
// The concrete command surface should not silently shrink.
$this->assertGreaterThanOrEqual(
40,
count($commands),
'Far fewer commands than expected — discovery likely broke.'
);
foreach ($commands as $fqcn) {
$this->assertTrue(
(new ReflectionClass($fqcn))->implementsInterface(CommandInterface::class)
);
}
}
}