179 lines
5.5 KiB
PHP
179 lines
5.5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Phred\Tasker\Tests;
|
||
|
|
|
||
|
|
use PHPUnit\Framework\TestCase;
|
||
|
|
use Phred\Tasker\Runner;
|
||
|
|
use Phred\Tasker\ArgvParser;
|
||
|
|
use Phred\TaskerBridges\Phred\InputAdapter;
|
||
|
|
use Phred\TaskerBridges\Phred\OutputAdapter;
|
||
|
|
use Phred\ConsoleContracts\CommandInterface;
|
||
|
|
use Phred\ConsoleContracts\InputInterface;
|
||
|
|
use Phred\ConsoleContracts\OutputInterface;
|
||
|
|
|
||
|
|
class IntegrationTest extends TestCase
|
||
|
|
{
|
||
|
|
private string $tempVendor;
|
||
|
|
private string $cwd;
|
||
|
|
|
||
|
|
protected function setUp(): void
|
||
|
|
{
|
||
|
|
$this->cwd = getcwd();
|
||
|
|
$this->tempVendor = sys_get_temp_dir() . '/tasker_integration_test_' . uniqid();
|
||
|
|
mkdir($this->tempVendor, 0777, true);
|
||
|
|
mkdir($this->tempVendor . '/composer', 0777, true);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function tearDown(): void
|
||
|
|
{
|
||
|
|
$this->removeDirectory($this->tempVendor);
|
||
|
|
chdir($this->cwd);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function removeDirectory(string $path): void
|
||
|
|
{
|
||
|
|
if (is_link($path)) {
|
||
|
|
unlink($path);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!is_dir($path)) {
|
||
|
|
if (file_exists($path)) {
|
||
|
|
unlink($path);
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$files = array_diff(scandir($path), ['.', '..']);
|
||
|
|
foreach ($files as $file) {
|
||
|
|
$this->removeDirectory("$path/$file");
|
||
|
|
}
|
||
|
|
rmdir($path);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testCommandDiscoveryViaComposer(): void
|
||
|
|
{
|
||
|
|
// Mock installed.json
|
||
|
|
$installedJson = [
|
||
|
|
'packages' => [
|
||
|
|
[
|
||
|
|
'name' => 'vendor/package',
|
||
|
|
'extra' => [
|
||
|
|
'phred-tasker' => [
|
||
|
|
'commands' => [
|
||
|
|
IntegrationMockCommand::class
|
||
|
|
]
|
||
|
|
]
|
||
|
|
]
|
||
|
|
]
|
||
|
|
]
|
||
|
|
];
|
||
|
|
file_put_contents($this->tempVendor . '/composer/installed.json', json_encode($installedJson));
|
||
|
|
|
||
|
|
// Mock root composer.json in the temp dir (we'll chdir there)
|
||
|
|
$rootComposer = [
|
||
|
|
'extra' => [
|
||
|
|
'phred-tasker' => [
|
||
|
|
'commands' => [
|
||
|
|
// Root commands can also be here
|
||
|
|
]
|
||
|
|
]
|
||
|
|
]
|
||
|
|
];
|
||
|
|
$testDir = sys_get_temp_dir() . '/tasker_root_' . uniqid();
|
||
|
|
mkdir($testDir, 0777, true);
|
||
|
|
file_put_contents($testDir . '/composer.json', json_encode($rootComposer));
|
||
|
|
|
||
|
|
// Symlink vendor to the testDir
|
||
|
|
symlink($this->tempVendor, $testDir . '/vendor');
|
||
|
|
|
||
|
|
chdir($testDir);
|
||
|
|
|
||
|
|
$runner = new Runner();
|
||
|
|
$runner->discover();
|
||
|
|
|
||
|
|
$this->assertInstanceOf(IntegrationMockCommand::class, $runner->find('integration:mock'));
|
||
|
|
|
||
|
|
$this->removeDirectory($testDir);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testFullExecutionFlow(): void
|
||
|
|
{
|
||
|
|
$runner = new Runner();
|
||
|
|
$command = new IntegrationMockCommand();
|
||
|
|
$runner->register($command);
|
||
|
|
|
||
|
|
$parser = new ArgvParser(['bin/tasker', 'integration:mock', 'val1']);
|
||
|
|
|
||
|
|
// IO Wiring
|
||
|
|
$output = new OutputAdapter(false); // no ansi for easier testing
|
||
|
|
$output->setVerbosity($parser->getVerbosity());
|
||
|
|
|
||
|
|
$cmdArgs = $command->getArguments();
|
||
|
|
$remaining = $parser->getRemainingArguments();
|
||
|
|
$mappedArgs = [];
|
||
|
|
$i = 0;
|
||
|
|
foreach ($cmdArgs as $name => $desc) {
|
||
|
|
if (isset($remaining[$i])) {
|
||
|
|
$mappedArgs[$name] = $remaining[$i];
|
||
|
|
}
|
||
|
|
$i++;
|
||
|
|
}
|
||
|
|
$input = new InputAdapter($mappedArgs, []);
|
||
|
|
|
||
|
|
ob_start();
|
||
|
|
$exitCode = $runner->run($command, $input, $output);
|
||
|
|
$content = ob_get_clean();
|
||
|
|
|
||
|
|
$this->assertEquals(0, $exitCode);
|
||
|
|
$this->assertStringContainsString('Mock executed with arg: val1', $content);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testArgvParserEdgeCases(): void
|
||
|
|
{
|
||
|
|
// Combined flags rejection is NOT implemented in current ArgvParser,
|
||
|
|
// it treats unknown things as remaining arguments.
|
||
|
|
// Let's test current behavior and see if it meets needs.
|
||
|
|
|
||
|
|
$parser = new ArgvParser(['bin/tasker', '-vn', 'cmd']);
|
||
|
|
// Current implementation:
|
||
|
|
// -vn is not recognized as -v and -n, it goes to remaining.
|
||
|
|
$this->assertEquals(1, $parser->getVerbosity());
|
||
|
|
$this->assertFalse($parser->isNoInteraction());
|
||
|
|
$this->assertContains('-vn', $parser->getRemainingArguments());
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testHelpOutput(): void
|
||
|
|
{
|
||
|
|
$runner = new Runner();
|
||
|
|
$command = $runner->find('help');
|
||
|
|
$this->assertNotNull($command);
|
||
|
|
|
||
|
|
$input = new InputAdapter(['command' => 'list'], []);
|
||
|
|
$output = new OutputAdapter(false);
|
||
|
|
|
||
|
|
ob_start();
|
||
|
|
$runner->run($command, $input, $output);
|
||
|
|
$content = ob_get_clean();
|
||
|
|
|
||
|
|
$this->assertStringContainsString('Usage:', $content);
|
||
|
|
$this->assertStringContainsString('tasker help <command_name>', $content);
|
||
|
|
$this->assertStringContainsString('tasker list', $content);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class IntegrationMockCommand implements CommandInterface
|
||
|
|
{
|
||
|
|
public function getName(): string { return 'integration:mock'; }
|
||
|
|
public function getDescription(): string { return 'Mock for integration test'; }
|
||
|
|
public function getArguments(): array { return ['arg1' => 'An argument']; }
|
||
|
|
public function getOptions(): array { return []; }
|
||
|
|
public function execute(InputInterface $input, OutputInterface $output): int
|
||
|
|
{
|
||
|
|
$output->writeln('Mock executed with arg: ' . $input->getArgument('arg1'));
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|