Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions tests/Unit/Archive/FactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Tests\Unit\Archive;

use Spiral\RoadRunner\Console\Archive\ArchiveInterface;
use Spiral\RoadRunner\Console\Archive\Factory;
use Spiral\RoadRunner\Console\Archive\TarPharArchive;
use Spiral\RoadRunner\Console\Archive\ZipPharArchive;
use Spiral\RoadRunner\Console\Repository\GitHub\GitHubAsset;
use Spiral\RoadRunner\Console\Tests\Unit\Stub\TempDirectory;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Testo\Assert;
use Testo\Data\DataSet;
use Testo\Expect;
use Testo\Lifecycle\AfterTest;
use Testo\Lifecycle\BeforeTest;
use Testo\Test;

#[Test]
final class FactoryTest
{
private string $dir;

#[BeforeTest]
public function createDirectory(): void
{
$this->dir = TempDirectory::create();
}

#[AfterTest]
public function removeDirectory(): void
{
TempDirectory::remove($this->dir);
}

#[DataSet(['rr.zip', 'rr.zip', ZipPharArchive::class])]
#[DataSet(['rr.zip', 'RR.ZIP', ZipPharArchive::class], 'upper-case extension')]
#[DataSet(['rr.tar.gz', 'rr.tar.gz', TarPharArchive::class])]
public function createPicksArchiveByExtension(string $source, string $name, string $class): void
{
$path = TempDirectory::archive($this->dir, $source, ['rr' => 'binary']);
\rename($path, $this->dir . '/' . $name);

$archive = (new Factory())->create(new \SplFileInfo($this->dir . '/' . $name));

Assert::instanceOf($archive, $class);
}

public function createRejectsUnknownExtension(): never
{
\file_put_contents($this->dir . '/rr.rar', 'data');

Expect::exception(\InvalidArgumentException::class)
->withMessageContaining('Can not open the archive "rr.rar"');

(new Factory())->create(new \SplFileInfo($this->dir . '/rr.rar'));
}

public function createReportsErrorsOfMatchingFormats(): never
{
Expect::exception(\InvalidArgumentException::class)
->withMessageContaining('Archive "missing.zip" is not a file');

(new Factory())->create(new \SplFileInfo($this->dir . '/missing.zip'));
}

public function extendedMatcherTakesPriority(): void
{
$path = TempDirectory::archive($this->dir, 'rr.zip', ['rr' => 'binary']);
$custom = new class implements ArchiveInterface {
public function extract(iterable $mappings): \Generator
{
yield from [];
}
};

$archive = (new Factory())
->extend(static fn(\SplFileInfo $file): ArchiveInterface => $custom)
->create(new \SplFileInfo($path));

Assert::same($archive, $custom);
}

public function extendedMatcherMayDecline(): void
{
$path = TempDirectory::archive($this->dir, 'rr.zip', ['rr' => 'binary']);

$archive = (new Factory())
->extend(static fn(\SplFileInfo $file): ?ArchiveInterface => null)
->create(new \SplFileInfo($path));

Assert::instanceOf($archive, ZipPharArchive::class);
}

public function fromAssetDownloadsIntoTempDirectory(): void
{
$source = TempDirectory::archive($this->dir, 'source.zip', ['rr' => 'binary']);
$temp = $this->dir . '/temp';
\mkdir($temp);
$asset = new GitHubAsset(
new MockHttpClient(new MockResponse((string) \file_get_contents($source))),
'roadrunner-2024.1.0-linux-amd64.zip',
'https://example.com/rr.zip',
);

$archive = (new Factory())->fromAsset($asset, null, $temp);

Assert::instanceOf($archive, ZipPharArchive::class);
Assert::same(\file_get_contents($temp . '/roadrunner-2024.1.0-linux-amd64.zip'), \file_get_contents($source));
}

public function fromAssetRejectsMissingTempDirectory(): never
{
Expect::exception(\LogicException::class)
->withMessageContaining('is not writeable');

(new Factory())->fromAsset(
new GitHubAsset(new MockHttpClient(), 'rr.zip', 'https://example.com/rr.zip'),
null,
$this->dir . '/missing',
);
}

public function fromAssetRethrowsDownloadErrors(): never
{
$asset = new GitHubAsset(
new MockHttpClient(new MockResponse('', ['error' => 'connection refused'])),
'rr.zip',
'https://example.com/rr.zip',
);

Expect::exception(\RuntimeException::class)
->withMessageContaining('connection refused');

(new Factory())->fromAsset($asset, null, $this->dir);
}
}
94 changes: 94 additions & 0 deletions tests/Unit/Archive/PharAwareArchiveTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Tests\Unit\Archive;

use Spiral\RoadRunner\Console\Archive\PharArchive;
use Spiral\RoadRunner\Console\Archive\TarPharArchive;
use Spiral\RoadRunner\Console\Archive\ZipPharArchive;
use Spiral\RoadRunner\Console\Tests\Unit\Stub\TempDirectory;
use Testo\Assert;
use Testo\Data\DataSet;
use Testo\Expect;
use Testo\Lifecycle\AfterTest;
use Testo\Lifecycle\BeforeTest;
use Testo\Skip;
use Testo\Test;

#[Test]
final class PharAwareArchiveTest
{
private string $dir;

#[BeforeTest]
public function createDirectory(): void
{
$this->dir = TempDirectory::create();
}

#[AfterTest]
public function removeDirectory(): void
{
TempDirectory::remove($this->dir);
}

#[DataSet(['rr.zip', ZipPharArchive::class])]
#[DataSet(['rr.tar.gz', TarPharArchive::class])]
#[DataSet(['rr.tar', PharArchive::class])]
public function extractCopiesMappedFiles(string $name, string $class): void
{
$path = TempDirectory::archive($this->dir, $name, [
'roadrunner-2024.1.0/rr' => 'binary',
'roadrunner-2024.1.0/README.md' => 'readme',
]);
$archive = new $class(new \SplFileInfo($path));

$extracted = [];
foreach ($archive->extract(['rr' => $this->dir . '/rr', 'rr.exe' => $this->dir . '/rr.exe']) as $from => $to) {
$extracted[$from->getFilename()] = $to->getPathname();
}

Assert::same($extracted, ['rr' => $this->dir . '/rr']);
Assert::same(\file_get_contents($this->dir . '/rr'), 'binary');
Assert::false(\is_file($this->dir . '/README.md'));
}

public function extractSkipsFileWhenConsumerDeclines(): void
{
$path = TempDirectory::archive($this->dir, 'rr.zip', ['roadrunner/rr' => 'binary']);
$extractor = (new ZipPharArchive(new \SplFileInfo($path)))->extract(['rr' => $this->dir . '/rr']);

Assert::true($extractor->valid());
$extractor->send(false);

Assert::false($extractor->valid());
Assert::false(\is_file($this->dir . '/rr'));
}

#[Skip('Bug: extract() reopens $this->archive, whose pathname points at the first archive entry rather than the archive itself, so a binary in the archive root cannot be extracted')]
public function extractFindsBinaryInArchiveRoot(): void
{
$path = TempDirectory::archive($this->dir, 'rr.zip', ['LICENSE' => 'license', 'rr' => 'binary']);

\iterator_to_array((new ZipPharArchive(new \SplFileInfo($path)))->extract(['rr' => $this->dir . '/rr']));

Assert::same(\file_get_contents($this->dir . '/rr'), 'binary');
}

public function rejectsMissingFile(): never
{
Expect::exception(\InvalidArgumentException::class)
->withMessage('Archive "missing.zip" is not a file');

new ZipPharArchive(new \SplFileInfo($this->dir . '/missing.zip'));
}

public function rejectsDirectory(): never
{
Expect::exception(\InvalidArgumentException::class)
->withMessageContaining('is not a file');

new ZipPharArchive(new \SplFileInfo($this->dir));
}
}
97 changes: 97 additions & 0 deletions tests/Unit/Command/EnvironmentOptionsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console\Tests\Unit\Command;

use Spiral\RoadRunner\Console\Command\ArchitectureOption;
use Spiral\RoadRunner\Console\Command\OperatingSystemOption;
use Spiral\RoadRunner\Console\Command\StabilityOption;
use Spiral\RoadRunner\Console\Tests\Unit\Stub\OptionHost;
use Testo\Assert;
use Testo\Test;

/**
* Options for the target environment: they accept any value and warn about unknown ones.
*/
#[Test]
final class EnvironmentOptionsTest
{
public function stabilityDefaultsToStable(): void
{
$host = new OptionHost();
$option = new StabilityOption($host->command);
$input = $host->input();

Assert::same($option->getName(), 'stability');
Assert::same($host->command->getDefinition()->getOption('stability')->getShortcut(), 's');
Assert::same($option->get($input, $host->io($input)), 'stable');
Assert::same($host->display(), '');
}

public function stabilityAcceptsKnownValue(): void
{
$host = new OptionHost();
$option = new StabilityOption($host->command);
$input = $host->input(['--stability' => 'beta']);

Assert::same($option->get($input, $host->io($input)), 'beta');
Assert::same($host->display(), '');
}

public function stabilityWarnsAboutUnknownValue(): void
{
$host = new OptionHost();
$option = new StabilityOption($host->command);
$input = $host->input(['--stability' => 'nightly']);

Assert::same($option->get($input, $host->io($input)), 'nightly');
Assert::string($host->display())
->ignoringWhitespace(lineBreaks: true)
->contains('Possibly invalid stability (--stability=nightly) option (available: stable, RC, beta, alpha, dev)');
}

public function operatingSystemWarnsAboutUnknownValue(): void
{
$host = new OptionHost();
$option = new OperatingSystemOption($host->command);
$input = $host->input(['--os' => 'macos']);

Assert::same($option->get($input, $host->io($input)), 'macos');
Assert::string($host->display())
->ignoringWhitespace(lineBreaks: true)
->contains('Possibly invalid operating system (--os=macos) option');
}

public function operatingSystemAcceptsKnownValue(): void
{
$host = new OptionHost();
$option = new OperatingSystemOption($host->command, 'system', 'y');
$input = $host->input(['--system' => 'darwin']);

Assert::same($option->getName(), 'system');
Assert::same($option->get($input, $host->io($input)), 'darwin');
Assert::same($host->display(), '');
}

public function architectureWarnsAboutUnknownValue(): void
{
$host = new OptionHost();
$option = new ArchitectureOption($host->command);
$input = $host->input(['--arch' => 'x86_64']);

Assert::same($option->get($input, $host->io($input)), 'x86_64');
Assert::string($host->display())
->ignoringWhitespace(lineBreaks: true)
->contains('Possibly invalid architecture (--arch=x86_64) option (available: amd64, arm64)');
}

public function architectureAcceptsShortcut(): void
{
$host = new OptionHost();
$option = new ArchitectureOption($host->command);
$input = $host->input(['-a' => 'arm64']);

Assert::same($option->get($input, $host->io($input)), 'arm64');
}
}
Loading
Loading