jetzt auch mit einheitstests
This commit is contained in:
parent
d68e7f45b3
commit
e883913d3a
10 changed files with 3952 additions and 16 deletions
|
@ -8,6 +8,11 @@
|
||||||
"App\\": "src/"
|
"App\\": "src/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.4",
|
"php": ">=8.4",
|
||||||
"symfony/uid": "^7.0",
|
"symfony/uid": "^7.0",
|
||||||
|
@ -18,11 +23,13 @@
|
||||||
"rector/rector": "^2.1",
|
"rector/rector": "^2.1",
|
||||||
"phpstan/phpstan": "^2.1",
|
"phpstan/phpstan": "^2.1",
|
||||||
"phpstan/extension-installer": "^1.4",
|
"phpstan/extension-installer": "^1.4",
|
||||||
"phpstan/phpstan-strict-rules": "^2.0"
|
"phpstan/phpstan-strict-rules": "^2.0",
|
||||||
|
"pestphp/pest": "^3.8"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"allow-plugins": {
|
"allow-plugins": {
|
||||||
"phpstan/extension-installer": true
|
"phpstan/extension-installer": true,
|
||||||
|
"pestphp/pest-plugin": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -30,6 +37,7 @@
|
||||||
"rector",
|
"rector",
|
||||||
"ecs --fix || ecs --fix"
|
"ecs --fix || ecs --fix"
|
||||||
],
|
],
|
||||||
"phpstan": "phpstan"
|
"phpstan": "phpstan",
|
||||||
|
"test": "pest --mutate --parallel"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3677
composer.lock
generated
3677
composer.lock
generated
File diff suppressed because it is too large
Load diff
9
ecs.php
9
ecs.php
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use PhpCsFixer\Fixer\Import\OrderedImportsFixer;
|
||||||
use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer;
|
use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer;
|
||||||
use Symplify\EasyCodingStandard\Config\ECSConfig;
|
use Symplify\EasyCodingStandard\Config\ECSConfig;
|
||||||
|
|
||||||
|
@ -9,11 +10,9 @@ return ECSConfig::configure()
|
||||||
->withPaths([
|
->withPaths([
|
||||||
__DIR__ . '/public',
|
__DIR__ . '/public',
|
||||||
__DIR__ . '/src',
|
__DIR__ . '/src',
|
||||||
|
__DIR__ . '/tests',
|
||||||
])
|
])
|
||||||
->withRootFiles()
|
->withRootFiles()
|
||||||
->withRules([
|
|
||||||
NoUnusedImportsFixer::class,
|
|
||||||
])
|
|
||||||
->withPhpCsFixerSets(
|
->withPhpCsFixerSets(
|
||||||
per: true,
|
per: true,
|
||||||
php84Migration: true,
|
php84Migration: true,
|
||||||
|
@ -25,4 +24,8 @@ return ECSConfig::configure()
|
||||||
strict: true,
|
strict: true,
|
||||||
cleanCode: true,
|
cleanCode: true,
|
||||||
)
|
)
|
||||||
|
->withRules([
|
||||||
|
NoUnusedImportsFixer::class,
|
||||||
|
OrderedImportsFixer::class,
|
||||||
|
])
|
||||||
;
|
;
|
||||||
|
|
18
phpunit.xml
Normal file
18
phpunit.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
colors="true"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Test Suite">
|
||||||
|
<directory suffix="Test.php">./tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory>app</directory>
|
||||||
|
<directory>src</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
</phpunit>
|
|
@ -8,6 +8,7 @@ return RectorConfig::configure()
|
||||||
->withPaths([
|
->withPaths([
|
||||||
__DIR__ . '/public',
|
__DIR__ . '/public',
|
||||||
__DIR__ . '/src',
|
__DIR__ . '/src',
|
||||||
|
__DIR__ . '/tests',
|
||||||
])
|
])
|
||||||
->withRootFiles()
|
->withRootFiles()
|
||||||
->withImportNames(removeUnusedImports: true)
|
->withImportNames(removeUnusedImports: true)
|
||||||
|
|
11
src/App.php
11
src/App.php
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Uid\Ulid;
|
use Symfony\Component\Uid\Ulid;
|
||||||
|
@ -23,7 +22,7 @@ final readonly class App
|
||||||
'/v6' => new PlaintextResponse(Uuid::v6()->toString()),
|
'/v6' => new PlaintextResponse(Uuid::v6()->toString()),
|
||||||
'/v7' => new PlaintextResponse(Uuid::v7()->toString()),
|
'/v7' => new PlaintextResponse(Uuid::v7()->toString()),
|
||||||
'/ulid' => new PlaintextResponse(Ulid::generate()),
|
'/ulid' => new PlaintextResponse(Ulid::generate()),
|
||||||
'/' => new PlaintextResponse(
|
default => new PlaintextResponse(
|
||||||
<<<TXT
|
<<<TXT
|
||||||
UUID/ULID Webservice
|
UUID/ULID Webservice
|
||||||
|
|
||||||
|
@ -31,15 +30,9 @@ final readonly class App
|
||||||
/v1 - Generate a UUID v1 (time-based)
|
/v1 - Generate a UUID v1 (time-based)
|
||||||
curl {$this->base}v1
|
curl {$this->base}v1
|
||||||
|
|
||||||
/v3 - Generate a UUID v3 (name-based, MD5 hash)
|
|
||||||
curl {$this->base}v3
|
|
||||||
|
|
||||||
/v4 - Generate a UUID v4 (random)
|
/v4 - Generate a UUID v4 (random)
|
||||||
curl {$this->base}v4
|
curl {$this->base}v4
|
||||||
|
|
||||||
/v5 - Generate a UUID v5 (name-based, SHA-1 hash)
|
|
||||||
curl {$this->base}v5
|
|
||||||
|
|
||||||
/v6 - Generate a UUID v6 (reordered time-based)
|
/v6 - Generate a UUID v6 (reordered time-based)
|
||||||
curl {$this->base}v6
|
curl {$this->base}v6
|
||||||
|
|
||||||
|
@ -52,9 +45,9 @@ final readonly class App
|
||||||
source: https://git.hannover.ccc.de/lubiana/uuid
|
source: https://git.hannover.ccc.de/lubiana/uuid
|
||||||
TXT
|
TXT
|
||||||
),
|
),
|
||||||
default => new RedirectResponse('/', 301),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(?Request $request = null): void
|
public function run(?Request $request = null): void
|
||||||
{
|
{
|
||||||
$request ??= Request::createFromGlobals();
|
$request ??= Request::createFromGlobals();
|
||||||
|
|
138
tests/Feature/AppTest.php
Normal file
138
tests/Feature/AppTest.php
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use App\App;
|
||||||
|
use Symfony\Component\Uid\UuidV1;
|
||||||
|
use Symfony\Component\Uid\UuidV4;
|
||||||
|
use Symfony\Component\Uid\UuidV7;
|
||||||
|
use Symfony\Component\Uid\UuidV6;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
use Symfony\Component\Uid\Ulid;
|
||||||
|
|
||||||
|
covers(App::class);
|
||||||
|
test('App returns correct version for endpoint', function (string $endpoint, string $class): void {
|
||||||
|
$app = new App('https://example.com/');
|
||||||
|
$request = Request::create('/' . $endpoint);
|
||||||
|
|
||||||
|
$response = $app->handle($request);
|
||||||
|
|
||||||
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
expect($response->headers->get('Content-Type'))->toBe('text/plain');
|
||||||
|
|
||||||
|
$uuid = $response->getContent();
|
||||||
|
expect(Uuid::isValid($uuid))->toBeTrue();
|
||||||
|
expect(Uuid::fromString($uuid))->toBeInstanceOf($class);
|
||||||
|
})->with([
|
||||||
|
[
|
||||||
|
'endpoint' => 'v1',
|
||||||
|
'class' => UuidV1::class,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'endpoint' => 'v4',
|
||||||
|
'class' => UuidV4::class,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'endpoint' => 'v6',
|
||||||
|
'class' => UuidV6::class,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'endpoint' => 'v7',
|
||||||
|
'class' => UuidV7::class,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
test('App returns ULID for /ulid endpoint', function (): void {
|
||||||
|
$app = new App('https://example.com/');
|
||||||
|
$request = Request::create('/ulid');
|
||||||
|
|
||||||
|
$response = $app->handle($request);
|
||||||
|
|
||||||
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
expect($response->headers->get('Content-Type'))->toBe('text/plain');
|
||||||
|
|
||||||
|
$ulid = $response->getContent();
|
||||||
|
expect(Ulid::isValid($ulid))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('App returns help text for default endpoint', function (): void {
|
||||||
|
$base = 'https://example.com/';
|
||||||
|
$app = new App($base);
|
||||||
|
$request = Request::create('/');
|
||||||
|
|
||||||
|
$response = $app->handle($request);
|
||||||
|
|
||||||
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
expect($response->headers->get('Content-Type'))->toBe('text/plain');
|
||||||
|
|
||||||
|
$content = $response->getContent();
|
||||||
|
expect($content)->toContain(
|
||||||
|
'UUID/ULID Webservice',
|
||||||
|
'Available Endpoints:',
|
||||||
|
'v1',
|
||||||
|
'v4',
|
||||||
|
'v6',
|
||||||
|
'v7',
|
||||||
|
'ulid',
|
||||||
|
$base,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('App run method processes the request with provided request', function (): void {
|
||||||
|
$app = new App('https://example.com/');
|
||||||
|
$request = Request::create('/v4');
|
||||||
|
|
||||||
|
// Use output buffering to capture the output
|
||||||
|
ob_start();
|
||||||
|
$app->run($request);
|
||||||
|
$output = ob_get_clean();
|
||||||
|
|
||||||
|
// Verify the output is a valid UUID v4
|
||||||
|
expect(Uuid::isValid($output))->toBeTrue();
|
||||||
|
expect(Uuid::fromString($output))->toBeInstanceOf(UuidV4::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('App run method processes the request with createFromGlobals when no request provided', function (): void {
|
||||||
|
// Save current server state
|
||||||
|
$originalServer = $_SERVER;
|
||||||
|
|
||||||
|
// Set up $_SERVER to simulate a request to /v4
|
||||||
|
$_SERVER['REQUEST_URI'] = '/v4';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||||
|
$_SERVER['HTTP_HOST'] = 'example.com';
|
||||||
|
|
||||||
|
$app = new App('https://example.com/');
|
||||||
|
|
||||||
|
// Use output buffering to capture the output
|
||||||
|
ob_start();
|
||||||
|
$app->run();
|
||||||
|
$output = ob_get_clean();
|
||||||
|
|
||||||
|
// Restore original server state
|
||||||
|
$_SERVER = $originalServer;
|
||||||
|
|
||||||
|
// Verify the output is a valid UUID v4
|
||||||
|
expect(Uuid::isValid($output))->toBeTrue();
|
||||||
|
expect(Uuid::fromString($output))->toBeInstanceOf(UuidV4::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('App run method sends the response for help page', function (): void {
|
||||||
|
$app = new App('https://example.com/');
|
||||||
|
$request = Request::create('/');
|
||||||
|
|
||||||
|
// Use output buffering to capture the output
|
||||||
|
ob_start();
|
||||||
|
$app->run($request);
|
||||||
|
$output = ob_get_clean();
|
||||||
|
|
||||||
|
// Verify the output contains the help text
|
||||||
|
expect($output)->toContain('UUID/ULID Webservice');
|
||||||
|
expect($output)->toContain('Available Endpoints:');
|
||||||
|
expect($output)->toContain('/v1');
|
||||||
|
expect($output)->toContain('/v4');
|
||||||
|
expect($output)->toContain('/v6');
|
||||||
|
expect($output)->toContain('/v7');
|
||||||
|
expect($output)->toContain('/ulid');
|
||||||
|
expect($output)->toContain('https://example.com/');
|
||||||
|
});
|
31
tests/Pest.php
Normal file
31
tests/Pest.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Test Case
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The closure you provide to your test functions is always bound to a specific PHPUnit test
|
||||||
|
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
|
||||||
|
| need to change it using the "pest()" function to bind a different classes or traits.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pest()->extend(TestCase::class)->in('Feature', 'Unit');
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Expectations
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| When you're writing tests, you often need to check that values meet certain conditions. The
|
||||||
|
| "expect()" function gives you access to a set of "expectations" methods that you can use
|
||||||
|
| to assert different things. Of course, you may extend the Expectation API at any time.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
expect()->extend('toBeOne', fn() => $this->toBe(1));
|
12
tests/TestCase.php
Normal file
12
tests/TestCase.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase as BaseTestCase;
|
||||||
|
|
||||||
|
abstract class TestCase extends BaseTestCase
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
57
tests/Unit/PlaintextResponseTest.php
Normal file
57
tests/Unit/PlaintextResponseTest.php
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use App\PlaintextResponse;
|
||||||
|
|
||||||
|
covers(PlaintextResponse::class);
|
||||||
|
test('it sets content-type to text/plain by default', function (): void {
|
||||||
|
$response = new PlaintextResponse('Hello, World!');
|
||||||
|
|
||||||
|
expect($response->headers->get('Content-Type'))->toBe('text/plain');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it allows custom content-type to be set', function (): void {
|
||||||
|
$response = new PlaintextResponse('Hello, World!', 200, [
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($response->headers->get('Content-Type'))->toBe('application/json');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it sets content correctly', function (): void {
|
||||||
|
$content = 'Hello, World!';
|
||||||
|
$response = new PlaintextResponse($content);
|
||||||
|
|
||||||
|
expect($response->getContent())->toBe($content);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it sets status code correctly', function (): void {
|
||||||
|
$status = 201;
|
||||||
|
$response = new PlaintextResponse('Hello, World!', $status);
|
||||||
|
|
||||||
|
expect($response->getStatusCode())->toBe($status);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it sets headers correctly', function (): void {
|
||||||
|
$headers = [
|
||||||
|
'X-Custom-Header' => 'Custom Value',
|
||||||
|
'X-Another-Header' => 'Another Value',
|
||||||
|
];
|
||||||
|
$response = new PlaintextResponse('Hello, World!', 200, $headers);
|
||||||
|
|
||||||
|
foreach ($headers as $key => $value) {
|
||||||
|
expect($response->headers->get($key))->toBe($value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it handles null content', function (): void {
|
||||||
|
$response = new PlaintextResponse(null);
|
||||||
|
|
||||||
|
expect($response->getContent())->toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it has correct defaultstatuscode', function (): void {
|
||||||
|
$response = new PlaintextResponse('Hello, World!');
|
||||||
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue