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
41 changes: 41 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: PHPUnit

on: [ pull_request ]

jobs:
tests:
name: unit tests
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
php-version:
- "8.3"
- "8.4"
- "8.5"

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: pdo xdebug
coverage: xdebug
env:
fail-fast: true
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Composer install dependencies
uses: ramsey/composer-install@v3
with:
dependency-versions: "highest"
composer-options: "--optimize-autoloader"

- name: PHPUnit
run: vendor/bin/phpunit --configuration phpunit.xml.dist --coverage-clover ./coverage.xml
env:
XDEBUG_MODE: coverage
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# phpstorm project files
.idea
.zed
.claude

# Mac DS_Store Files
.DS_Store
Expand All @@ -18,4 +19,5 @@ phpunit.phar

# local
*.local.php
*.env
.auth.json
50 changes: 7 additions & 43 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,49 +65,6 @@ fix: phpcbf rector ## run fix tools

check: phpcs psalm phpstan ## run analysis tools

infection:
docker build \
--build-arg PHP_VERSION=$(PHP_VERSION) \
--build-arg USER=$(USER) \
--build-arg WORKDIR=/app \
--target tests \
-t app_cli .docker/php/cli
- docker run --init -it --rm \
--add-host=host.docker.internal:host-gateway \
-u $(USER) \
-v "$$(pwd):/app" \
-w /app \
app_cli ./vendor/bin/infection
docker image rm -f app_cli

tests:
docker build \
--build-arg PHP_VERSION=$(PHP_VERSION) \
--build-arg USER=$(USER) \
--build-arg WORKDIR=/app \
--target tests \
-t app_cli .docker/php/cli
- docker run --init -it --rm \
--add-host=host.docker.internal:host-gateway \
-u $(USER) \
-v "$$(pwd):/app" \
-w /app \
app_cli ./vendor/bin/phpunit \
--configuration phpunit.xml.dist \
--coverage-xml=/app/runtime/coverage/coverage-xml \
--coverage-clover=/app/runtime/coverage/clover.xml \
--log-junit=/app/runtime/coverage/phpunit.junit.xml
- docker run --init -it --rm \
--add-host=host.docker.internal:host-gateway \
-u $(USER) \
-v "$$(pwd):/app" \
-w /app \
app_cli ./vendor/bin/infection \
--coverage=/app/runtime/coverage \
--threads=max \
--skip-initial-tests
docker image rm -f app_cli

## Application

cli:
Expand Down Expand Up @@ -143,3 +100,10 @@ _volume_remove:
docker volume rm -f \
migrator_pg_data \
migrator_mysql_data

## AI
-include .claude/Makefile

# Спец-правило, чтобы Makefile не ругался на неизвестные команды (аргументы)
%:
@:
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
},
"require-dev": {
"buggregator/trap": "^1.15",
"infection/infection": "^0.32.4",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^12.5",
"rector/rector": "^2.0",
Expand Down
9 changes: 9 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,13 @@
<rule ref="SlevomatCodingStandard.PHP.UselessSemicolon"/>
<!-- https://github.com/slevomat/coding-standard/blob/master/doc/whitespaces.md -->
<rule ref="SlevomatCodingStandard.Whitespaces.DuplicateSpaces"/>

<!-- Test methods use snake_case names (PHPUnit #[Test] attribute convention) -->
<rule ref="PSR1.Methods.CamelCapsMethodName">
<exclude-pattern>tests/</exclude-pattern>
</rule>
<!-- Test files use aligned arrays for readability in data providers -->
<rule ref="SlevomatCodingStandard.Whitespaces.DuplicateSpaces">
<exclude-pattern>tests/</exclude-pattern>
</rule>
</ruleset>
2 changes: 2 additions & 0 deletions runtime/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
103 changes: 103 additions & 0 deletions tests/Fakes/FakeConsoleOutput.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

declare(strict_types=1);

namespace dbschemix\migrator\tests\Fakes;

use LogicException;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Hand-written fake that records writeln calls for assertion.
*/
final class FakeConsoleOutput implements ConsoleOutputInterface
{
/** @var list<string> */
public array $lines = [];

/** @param string|iterable<string> $messages */
public function writeln(string | iterable $messages, int $options = 0): void
{
if (is_iterable($messages)) {
foreach ($messages as $message) {
$this->lines[] = $message;
}
} else {
$this->lines[] = $messages;
}
}

/** @param string|iterable<string> $messages */
public function write(string | iterable $messages, bool $newline = false, int $options = 0): void
{
// Not needed for our tests
}

public function getErrorOutput(): OutputInterface
{
return $this;
}

public function setErrorOutput(OutputInterface $error): void
{
// Not needed for our tests
}

public function section(): ConsoleSectionOutput
{
throw new LogicException('Not implemented in fake');
}

public function setVerbosity(int $level): void
{
// Not needed for our tests
}

public function getVerbosity(): int
{
return self::VERBOSITY_NORMAL;
}

public function isQuiet(): bool
{
return false;
}

public function isVerbose(): bool
{
return false;
}

public function isVeryVerbose(): bool
{
return false;
}

public function isDebug(): bool
{
return false;
}

public function setDecorated(bool $decorated): void
{
// Not needed for our tests
}

public function isDecorated(): bool
{
return false;
}

public function setFormatter(OutputFormatterInterface $formatter): void
{
// Not needed for our tests
}

public function getFormatter(): OutputFormatterInterface
{
throw new LogicException('Not implemented in fake');
}
}
87 changes: 87 additions & 0 deletions tests/Fakes/FakeMigrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

namespace dbschemix\migrator\tests\Fakes;

use dbschemix\core\InputOptions;
use dbschemix\core\MigratorInterface;
use Throwable;

/**
* Hand-written fake that records calls and can be configured to throw.
*/
final class FakeMigrator implements MigratorInterface
{
public ?InputOptions $lastUpOptions = null;
public ?InputOptions $lastDownOptions = null;
public ?InputOptions $lastRedoOptions = null;
public ?InputOptions $lastVerifyOptions = null;
public ?InputOptions $lastFixtureOptions = null;
public ?InputOptions $lastCreateOptions = null;
public int $initCallCount = 0;

public ?Throwable $upException = null;
public ?Throwable $downException = null;
public ?Throwable $redoException = null;
public ?Throwable $verifyException = null;
public ?Throwable $fixtureException = null;
public ?Throwable $createException = null;
public ?Throwable $initException = null;

public function init(): void
{
$this->initCallCount++;
if ($this->initException instanceof Throwable) {
throw $this->initException;
}
}

public function create(InputOptions $args = new InputOptions()): void
{
$this->lastCreateOptions = $args;
if ($this->createException instanceof Throwable) {
throw $this->createException;
}
}

public function up(InputOptions $args = new InputOptions()): void
{
$this->lastUpOptions = $args;
if ($this->upException instanceof Throwable) {
throw $this->upException;
}
}

public function down(InputOptions $args = new InputOptions()): void
{
$this->lastDownOptions = $args;
if ($this->downException instanceof Throwable) {
throw $this->downException;
}
}

public function fixture(InputOptions $args = new InputOptions()): void
{
$this->lastFixtureOptions = $args;
if ($this->fixtureException instanceof Throwable) {
throw $this->fixtureException;
}
}

public function redo(InputOptions $args = new InputOptions()): void
{
$this->lastRedoOptions = $args;
if ($this->redoException instanceof Throwable) {
throw $this->redoException;
}
}

public function verify(InputOptions $args = new InputOptions()): void
{
$this->lastVerifyOptions = $args;
if ($this->verifyException instanceof Throwable) {
throw $this->verifyException;
}
}
}
18 changes: 18 additions & 0 deletions tests/Fakes/FakeMigratorException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace dbschemix\migrator\tests\Fakes;

use dbschemix\core\exception\MigratorException;

/**
* Concrete subclass of the abstract MigratorException for use in tests.
*/
final class FakeMigratorException extends MigratorException
{
public function __construct(string $message = 'fake migrator error')
{
parent::__construct($message);
}
}
Loading
Loading