Tasker/tests/IntegrationTest.php

179 lines
5.5 KiB
PHP
Raw Permalink Normal View History

<?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;
}
}