Files
2026-04-20 20:37:49 +09:00

205 lines
6.7 KiB
PHP

<?php
namespace Tests\Unit\Extension\Vendor;
use App\Extension\Vendor\EnvironmentDetector;
use App\Extension\Vendor\Exceptions\VendorInstallException;
use App\Extension\Vendor\VendorBundleInstaller;
use App\Extension\Vendor\VendorBundler;
use App\Extension\Vendor\VendorInstallContext;
use App\Extension\Vendor\VendorInstallResult;
use App\Extension\Vendor\VendorIntegrityChecker;
use App\Extension\Vendor\VendorMode;
use App\Extension\Vendor\VendorResolver;
use Illuminate\Support\Facades\File;
use Tests\TestCase;
class VendorResolverTest extends TestCase
{
private string $testDir;
protected function setUp(): void
{
parent::setUp();
$this->testDir = storage_path('app/test-vendor-resolver-'.uniqid());
File::ensureDirectoryExists($this->testDir);
}
protected function tearDown(): void
{
if (File::isDirectory($this->testDir)) {
File::deleteDirectory($this->testDir);
}
parent::tearDown();
}
private function makeResolver(bool $canExecuteComposer): VendorResolver
{
$env = new class($canExecuteComposer) extends EnvironmentDetector
{
public function __construct(private bool $canExec) {}
public function canExecuteComposer(?string $hint = null): bool
{
return $this->canExec;
}
};
$checker = new VendorIntegrityChecker;
return new VendorResolver(
environmentDetector: $env,
integrityChecker: $checker,
bundleInstaller: new VendorBundleInstaller($checker),
);
}
private function createValidBundle(): void
{
$zipPath = $this->testDir.'/vendor-bundle.zip';
$zip = new \ZipArchive;
$zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$zip->addFromString('vendor/autoload.php', '<?php // test');
$zip->close();
File::put($this->testDir.'/vendor-bundle.json', json_encode([
'schema_version' => VendorBundler::SCHEMA_VERSION,
'zip_sha256' => hash_file('sha256', $zipPath),
'package_count' => 1,
]));
}
private function makeContext(VendorMode $requested = VendorMode::Auto, ?VendorMode $previous = null, string $operation = 'install'): VendorInstallContext
{
return new VendorInstallContext(
target: 'module',
identifier: 'test-mod',
sourceDir: $this->testDir,
targetDir: $this->testDir,
requestedMode: $requested,
previousMode: $previous,
operation: $operation,
);
}
public function test_resolves_explicit_composer_mode(): void
{
$resolver = $this->makeResolver(canExecuteComposer: true);
$ctx = $this->makeContext(requested: VendorMode::Composer);
$this->assertSame(VendorMode::Composer, $resolver->resolveMode($ctx));
}
public function test_resolves_explicit_bundled_mode(): void
{
$resolver = $this->makeResolver(canExecuteComposer: true);
$ctx = $this->makeContext(requested: VendorMode::Bundled);
$this->assertSame(VendorMode::Bundled, $resolver->resolveMode($ctx));
}
public function test_auto_resolves_to_composer_when_available(): void
{
$resolver = $this->makeResolver(canExecuteComposer: true);
$ctx = $this->makeContext();
$this->assertSame(VendorMode::Composer, $resolver->resolveMode($ctx));
}
public function test_auto_resolves_to_bundled_when_composer_unavailable_but_bundle_exists(): void
{
$this->createValidBundle();
$resolver = $this->makeResolver(canExecuteComposer: false);
$ctx = $this->makeContext();
$this->assertSame(VendorMode::Bundled, $resolver->resolveMode($ctx));
}
public function test_auto_throws_when_neither_composer_nor_bundle_available(): void
{
$resolver = $this->makeResolver(canExecuteComposer: false);
$ctx = $this->makeContext();
$this->expectException(VendorInstallException::class);
$resolver->resolveMode($ctx);
}
public function test_update_inherits_previous_mode(): void
{
$resolver = $this->makeResolver(canExecuteComposer: true);
$ctx = $this->makeContext(
requested: VendorMode::Auto,
previous: VendorMode::Bundled,
operation: 'update',
);
$this->assertSame(VendorMode::Bundled, $resolver->resolveMode($ctx));
}
public function test_update_does_not_inherit_auto_previous_mode(): void
{
$resolver = $this->makeResolver(canExecuteComposer: true);
$ctx = $this->makeContext(
requested: VendorMode::Auto,
previous: VendorMode::Auto,
operation: 'update',
);
// auto → auto 해석 → composer 가능하므로 composer 반환
$this->assertSame(VendorMode::Composer, $resolver->resolveMode($ctx));
}
public function test_install_throws_when_composer_mode_but_executor_missing(): void
{
$resolver = $this->makeResolver(canExecuteComposer: true);
$ctx = $this->makeContext(requested: VendorMode::Composer);
$this->expectException(VendorInstallException::class);
$resolver->install($ctx, composerExecutor: null);
}
public function test_install_delegates_to_composer_executor_callback(): void
{
$resolver = $this->makeResolver(canExecuteComposer: true);
$ctx = $this->makeContext(requested: VendorMode::Composer);
$called = false;
$executor = function (VendorInstallContext $c) use (&$called) {
$called = true;
return new VendorInstallResult(
mode: VendorMode::Composer,
strategy: 'composer',
packageCount: 10,
);
};
$result = $resolver->install($ctx, $executor);
$this->assertTrue($called);
$this->assertSame(VendorMode::Composer, $result->mode);
$this->assertSame(10, $result->packageCount);
}
public function test_install_executes_bundle_strategy_end_to_end(): void
{
$this->createValidBundle();
$resolver = $this->makeResolver(canExecuteComposer: false);
$ctx = $this->makeContext(requested: VendorMode::Bundled);
$result = $resolver->install($ctx);
$this->assertSame(VendorMode::Bundled, $result->mode);
$this->assertFileExists($this->testDir.'/vendor/autoload.php');
}
public function test_install_fails_when_bundled_mode_but_no_zip(): void
{
$resolver = $this->makeResolver(canExecuteComposer: true);
$ctx = $this->makeContext(requested: VendorMode::Bundled);
$this->expectException(VendorInstallException::class);
$resolver->install($ctx);
}
}