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
232 changes: 232 additions & 0 deletions tests/Unit/Command/AbstractCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
<?php

declare(strict_types=1);

namespace KaririCode\Devkit\Tests\Unit\Command;

use KaririCode\Devkit\Command\AbstractCommand;
use KaririCode\Devkit\Core\Devkit;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

#[CoversClass(AbstractCommand::class)]
final class AbstractCommandTest extends TestCase
{
private AbstractCommand $command;

protected function setUp(): void
{
$this->command = new class () extends AbstractCommand {
public function name(): string
{
return 'test';
}

public function description(): string
{
return 'Test command';
}

public function execute(Devkit $devkit, array $arguments): int
{
return 0;
}

/** @param list<string> $args */
public function callHasFlag(array $args, string ...$flags): bool
{
return $this->hasFlag($args, ...$flags);
}

/** @param list<string> $args */
public function callOption(array $args, string $key, ?string $default = null): ?string
{
return $this->option($args, $key, $default);
}

/** @param list<string> $args @return list<string> */
public function callPositional(array $args): array
{
return $this->positional($args);
}

/**
* @param list<string> $args
* @param list<string> $consume
* @return list<string>
*/
public function callPassthrough(array $args, array $consume = []): array
{
return $this->passthrough($args, $consume);
}

public function callInfo(string $msg): void
{
$this->info($msg);
}

public function callWarning(string $msg): void
{
$this->warning($msg);
}

public function callError(string $msg): void
{
$this->error($msg);
}

public function callLine(string $msg = ''): void
{
$this->line($msg);
}

public function callBanner(string $title): void
{
$this->banner($title);
}

public function callSection(string $title): void
{
$this->section($title);
}
};
}

// ── Identity ───────────────────────────────────────────────────

#[Test]
public function nameReturnsCommandName(): void
{
$this->assertSame('test', $this->command->name());
}

#[Test]
public function descriptionReturnsDescription(): void
{
$this->assertSame('Test command', $this->command->description());
}

// ── Argument helpers ───────────────────────────────────────────

#[Test]
public function hasFlagReturnsTrueWhenFlagPresent(): void
{
$this->assertTrue($this->command->callHasFlag(['--verbose', '--check'], '--verbose'));
}

#[Test]
public function hasFlagReturnsFalseWhenFlagAbsent(): void
{
$this->assertFalse($this->command->callHasFlag(['--check'], '--verbose'));
}

#[Test]
public function hasFlagMatchesAnyOfMultipleFlags(): void
{
$this->assertTrue($this->command->callHasFlag(['--dry-run'], '--check', '--dry-run'));
}

#[Test]
public function hasFlagReturnsFalseWithEmptyArgs(): void
{
$this->assertFalse($this->command->callHasFlag([], '--any'));
}

#[Test]
public function optionExtractsValueFromArguments(): void
{
$result = $this->command->callOption(['--level=9', '--other=x'], 'level');
$this->assertSame('9', $result);
}

#[Test]
public function optionReturnsDefaultWhenNotFound(): void
{
$result = $this->command->callOption(['--other=x'], 'level', 'default');
$this->assertSame('default', $result);
}

#[Test]
public function optionReturnsNullWhenNotFoundAndNoDefault(): void
{
$this->assertNull($this->command->callOption([], 'level'));
}

#[Test]
public function positionalFiltersOutFlags(): void
{
$result = $this->command->callPositional(['file.php', '--verbose', 'other.php', '--dry-run']);
$this->assertSame(['file.php', 'other.php'], $result);
}

#[Test]
public function positionalReturnsEmptyForAllFlags(): void
{
$result = $this->command->callPositional(['--a', '--b', '--c']);
$this->assertSame([], $result);
}

#[Test]
public function passthroughRemovesConsumedFlags(): void
{
$result = $this->command->callPassthrough(
['--verbose', '--coverage', '--check'],
['--coverage'],
);
$this->assertSame(['--verbose', '--check'], $result);
}

#[Test]
public function passthroughWithNoConsumedFlagsReturnsAll(): void
{
$args = ['--verbose', '--check'];
$result = $this->command->callPassthrough($args);
$this->assertSame($args, $result);
}

// ── Output helpers — fwrite(STDOUT/STDERR) cannot be captured by ob_start ─
// We verify these methods exist and do not throw exceptions.

#[Test]
public function infoDoesNotThrow(): void
{
$this->expectNotToPerformAssertions();
$this->command->callInfo('hello');
}

#[Test]
public function warningDoesNotThrow(): void
{
$this->expectNotToPerformAssertions();
$this->command->callWarning('caution');
}

#[Test]
public function errorDoesNotThrow(): void
{
$this->expectNotToPerformAssertions();
$this->command->callError('something failed');
}

#[Test]
public function lineDoesNotThrow(): void
{
$this->expectNotToPerformAssertions();
$this->command->callLine('some output');
}

#[Test]
public function bannerDoesNotThrow(): void
{
$this->expectNotToPerformAssertions();
$this->command->callBanner('My Banner');
}

#[Test]
public function sectionDoesNotThrow(): void
{
$this->expectNotToPerformAssertions();
$this->command->callSection('My Section');
}
}
97 changes: 97 additions & 0 deletions tests/Unit/Configuration/CsFixerConfigGeneratorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);

namespace KaririCode\Devkit\Tests\Unit\Configuration;

use KaririCode\Devkit\Configuration\CsFixerConfigGenerator;
use KaririCode\Devkit\Core\ProjectContext;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

#[CoversClass(CsFixerConfigGenerator::class)]
final class CsFixerConfigGeneratorTest extends TestCase
{
private CsFixerConfigGenerator $generator;

protected function setUp(): void
{
$this->generator = new CsFixerConfigGenerator();
}

private function makeContext(array $rules = ['@PSR12' => true]): ProjectContext
{
return new ProjectContext(
projectRoot: '/project',
projectName: 'test/project',
namespace: 'Test\\Project',
phpVersion: '8.4',
phpstanLevel: 9,
psalmLevel: 3,
sourceDirs: ['/project/src'],
testDirs: ['/project/tests'],
excludeDirs: [],
testSuites: [],
coverageExclude: [],
csFixerRules: $rules,
rectorSets: [],
toolVersions: [],
);
}

#[Test]
public function toolNameReturnsCsFixer(): void
{
$this->assertSame('cs-fixer', $this->generator->toolName());
}

#[Test]
public function outputPathReturnsPhpCsFixerPhp(): void
{
$this->assertSame('php-cs-fixer.php', $this->generator->outputPath());
}

#[Test]
public function generateContainsPhpDeclaration(): void
{
$output = $this->generator->generate($this->makeContext());
$this->assertStringContainsString('<?php', $output);
}

#[Test]
public function generateContainsSourceDir(): void
{
$output = $this->generator->generate($this->makeContext());
$this->assertStringContainsString("->in(__DIR__ . '/../src')", $output);
}

#[Test]
public function generateContainsTestDir(): void
{
$output = $this->generator->generate($this->makeContext());
$this->assertStringContainsString("->in(__DIR__ . '/../tests')", $output);
}

#[Test]
public function generateContainsRulesFromContext(): void
{
$output = $this->generator->generate($this->makeContext(['@PSR12' => true, 'array_syntax' => ['syntax' => 'short']]));
$this->assertStringContainsString('@PSR12', $output);
$this->assertStringContainsString('array_syntax', $output);
}

#[Test]
public function generateContainsCacheFile(): void
{
$output = $this->generator->generate($this->makeContext());
$this->assertStringContainsString('.php-cs-fixer.cache', $output);
}

#[Test]
public function generateContainsRiskyAllowed(): void
{
$output = $this->generator->generate($this->makeContext());
$this->assertStringContainsString('setRiskyAllowed(true)', $output);
}
}
Loading
Loading