feature-api: Übername des Slim-Skeleton

This commit is contained in:
2022-04-30 15:30:26 +02:00
parent f48208ab42
commit 6ed248e925
45 changed files with 1493 additions and 0 deletions
View File
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
@@ -0,0 +1,10 @@
<?php
namespace Helper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
class Unit extends \Codeception\Module
{
}
@@ -0,0 +1,26 @@
<?php
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method void pause()
*
* @SuppressWarnings(PHPMD)
*/
class UnitTester extends \Codeception\Actor
{
use _generated\UnitTesterActions;
/**
* Define custom actions here
*/
}
@@ -0,0 +1,2 @@
*
!.gitignore
+10
View File
@@ -0,0 +1,10 @@
# Codeception Test Suite Configuration
#
# Suite for unit or integration tests.
actor: UnitTester
modules:
enabled:
- Asserts
- \Helper\Unit
step_decorators: ~
@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
namespace TorstenHettstedt\OptLogin\Tests\unit\Application\Actions;
use DateTimeImmutable;
use DateTimeInterface;
use Exception;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Log\LoggerInterface;
use TorstenHettstedt\OptLogin\Application\Actions\Action;
use TorstenHettstedt\OptLogin\Application\Actions\ActionPayload;
use TorstenHettstedt\OptLogin\Tests\unit\TestCase;
class ActionTest extends TestCase
{
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function testActionSetsHttpCodeInRespond()
{
$app = $this->getAppInstance();
$container = $app->getContainer();
$logger = $container->get(LoggerInterface::class);
$testAction = new class ($logger) extends Action {
public function __construct(
LoggerInterface $loggerInterface
) {
parent::__construct($loggerInterface);
}
public function action(): Response
{
return $this->respond(
new ActionPayload(
202,
[
'willBeDoneAt' => (new DateTimeImmutable())->format(DateTimeInterface::ATOM)
]
)
);
}
};
$app->get('/test-action-response-code', $testAction);
$request = $this->createRequest('GET', '/test-action-response-code');
$response = $app->handle($request);
$this->assertEquals(202, $response->getStatusCode());
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function testActionSetsHttpCodeRespondData()
{
$app = $this->getAppInstance();
$container = $app->getContainer();
$logger = $container->get(LoggerInterface::class);
$testAction = new class ($logger) extends Action {
public function __construct(
LoggerInterface $loggerInterface
) {
parent::__construct($loggerInterface);
}
public function action(): Response
{
return $this->respondWithData(
[
'willBeDoneAt' => (new DateTimeImmutable())->format(DateTimeInterface::ATOM)
],
202
);
}
};
$app->get('/test-action-response-code', $testAction);
$request = $this->createRequest('GET', '/test-action-response-code');
$response = $app->handle($request);
$this->assertEquals(202, $response->getStatusCode());
}
}
@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace TorstenHettstedt\OptLogin\Tests\unit\Application\Actions\User;
use Codeception\Stub\Expected;
use DI\Container;
use Exception;
use TorstenHettstedt\OptLogin\Application\Actions\ActionPayload;
use TorstenHettstedt\OptLogin\Domain\User\User;
use TorstenHettstedt\OptLogin\Domain\User\UserRepository;
use TorstenHettstedt\OptLogin\Tests\unit\TestCase;
class ListUserActionTest extends TestCase
{
/**
* @throws Exception
*/
public function testAction()
{
$app = $this->getAppInstance();
/** @var Container $container */
$container = $app->getContainer();
$user = new User(1, 'bill.gates', 'Bill', 'Gates');
$userRepository = $this->makeEmpty(UserRepository::class, [
'findAll' => Expected::once([$user])
]);
$container->set(UserRepository::class, $userRepository);
$request = $this->createRequest('GET', '/users');
$response = $app->handle($request);
$payload = (string) $response->getBody();
$expectedPayload = new ActionPayload(200, [$user]);
$serializedPayload = json_encode($expectedPayload, JSON_PRETTY_PRINT);
$this->assertEquals($serializedPayload, $payload);
}
}
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace TorstenHettstedt\OptLogin\Tests\unit\Application\Actions\User;
use Codeception\Stub\Expected;
use DI\Container;
use Exception;
use Slim\Middleware\ErrorMiddleware;
use TorstenHettstedt\OptLogin\Application\Actions\ActionError;
use TorstenHettstedt\OptLogin\Application\Actions\ActionPayload;
use TorstenHettstedt\OptLogin\Application\Handlers\HttpErrorHandler;
use TorstenHettstedt\OptLogin\Domain\User\User;
use TorstenHettstedt\OptLogin\Domain\User\UserNotFoundException;
use TorstenHettstedt\OptLogin\Domain\User\UserRepository;
use TorstenHettstedt\OptLogin\Tests\unit\TestCase;
class ViewUserActionTest extends TestCase
{
/**
* @throws Exception
*/
public function testAction()
{
$app = $this->getAppInstance();
/** @var Container $container */
$container = $app->getContainer();
$user = new User(1, 'bill.gates', 'Bill', 'Gates');
$userRepository = $this->makeEmpty(UserRepository::class, [
'findUserOfId' => Expected::once($user)
]);
$container->set(UserRepository::class, $userRepository);
$request = $this->createRequest('GET', '/users/1');
$response = $app->handle($request);
$payload = (string) $response->getBody();
$expectedPayload = new ActionPayload(200, $user);
$serializedPayload = json_encode($expectedPayload, JSON_PRETTY_PRINT);
$this->assertEquals($serializedPayload, $payload);
}
/**
* @throws Exception
*/
public function testActionThrowsUserNotFoundException()
{
$app = $this->getAppInstance();
$callableResolver = $app->getCallableResolver();
$responseFactory = $app->getResponseFactory();
$errorHandler = new HttpErrorHandler($callableResolver, $responseFactory);
$errorMiddleware = new ErrorMiddleware($callableResolver, $responseFactory, true, false, false);
$errorMiddleware->setDefaultErrorHandler($errorHandler);
$app->add($errorMiddleware);
/** @var Container $container */
$container = $app->getContainer();
$userRepository = $this->makeEmpty(UserRepository::class, [
'findUserOfId' => Expected::once(function() {throw new UserNotFoundException();})
]);
$container->set(UserRepository::class, $userRepository);
$request = $this->createRequest('GET', '/users/1');
$response = $app->handle($request);
$payload = (string) $response->getBody();
$expectedError = new ActionError(ActionError::RESOURCE_NOT_FOUND, 'The user you requested does not exist.');
$expectedPayload = new ActionPayload(404, null, $expectedError);
$serializedPayload = json_encode($expectedPayload, JSON_PRETTY_PRINT);
$this->assertEquals($serializedPayload, $payload);
}
}
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace TorstenHettstedt\OptLogin\Tests\unit\Domain\User;
use TorstenHettstedt\OptLogin\Domain\User\User;
use TorstenHettstedt\OptLogin\Tests\unit\TestCase;
class UserTest extends TestCase
{
public function userProvider(): array
{
return [
[1, 'bill.gates', 'Bill', 'Gates'],
[2, 'steve.jobs', 'Steve', 'Jobs'],
[3, 'mark.zuckerberg', 'Mark', 'Zuckerberg'],
[4, 'evan.spiegel', 'Evan', 'Spiegel'],
[5, 'jack.dorsey', 'Jack', 'Dorsey'],
];
}
/**
* @dataProvider userProvider
* @param int $id
* @param string $username
* @param string $firstName
* @param string $lastName
*/
public function testGetters(int $id, string $username, string $firstName, string $lastName)
{
$user = new User($id, $username, $firstName, $lastName);
$this->assertEquals($id, $user->getId());
$this->assertEquals($username, $user->getUsername());
$this->assertEquals($firstName, $user->getFirstName());
$this->assertEquals($lastName, $user->getLastName());
}
/**
* @dataProvider userProvider
* @param int $id
* @param string $username
* @param string $firstName
* @param string $lastName
*/
public function testJsonSerialize(int $id, string $username, string $firstName, string $lastName)
{
$user = new User($id, $username, $firstName, $lastName);
$expectedPayload = json_encode([
'id' => $id,
'username' => $username,
'firstName' => $firstName,
'lastName' => $lastName,
]);
$this->assertEquals($expectedPayload, json_encode($user));
}
}
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace TorstenHettstedt\OptLogin\Tests\unit\Infrastructure\Persistence\User;
use TorstenHettstedt\OptLogin\Domain\User\User;
use TorstenHettstedt\OptLogin\Domain\User\UserNotFoundException;
use TorstenHettstedt\OptLogin\Infrastructure\Persistence\User\InMemoryUserRepository;
use TorstenHettstedt\OptLogin\Tests\unit\TestCase;
class InMemoryUserRepositoryTest extends TestCase
{
public function testFindAll()
{
$user = new User(1, 'bill.gates', 'Bill', 'Gates');
$userRepository = new InMemoryUserRepository([1 => $user]);
$this->assertEquals([$user], $userRepository->findAll());
}
public function testFindAllUsersByDefault()
{
$users = [
1 => new User(1, 'bill.gates', 'Bill', 'Gates'),
2 => new User(2, 'steve.jobs', 'Steve', 'Jobs'),
3 => new User(3, 'mark.zuckerberg', 'Mark', 'Zuckerberg'),
4 => new User(4, 'evan.spiegel', 'Evan', 'Spiegel'),
5 => new User(5, 'jack.dorsey', 'Jack', 'Dorsey'),
];
$userRepository = new InMemoryUserRepository();
$this->assertEquals(array_values($users), $userRepository->findAll());
}
/**
* @throws UserNotFoundException
*/
public function testFindUserOfId()
{
$user = new User(1, 'bill.gates', 'Bill', 'Gates');
$userRepository = new InMemoryUserRepository([1 => $user]);
$this->assertEquals($user, $userRepository->findUserOfId(1));
}
public function testFindUserOfIdThrowsNotFoundException()
{
$userRepository = new InMemoryUserRepository([]);
$this->expectException(UserNotFoundException::class);
$userRepository->findUserOfId(1);
}
}
+89
View File
@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace TorstenHettstedt\OptLogin\Tests\unit;
use Codeception\Test\Unit;
use DI\ContainerBuilder;
use Exception;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\App;
use Slim\Factory\AppFactory;
use Slim\Psr7\Factory\StreamFactory;
use Slim\Psr7\Headers;
use Slim\Psr7\Request as SlimRequest;
use Slim\Psr7\Uri;
class TestCase extends Unit
{
/**
* @return App
* @throws Exception
*/
protected function getAppInstance(): App
{
// Instantiate PHP-DI ContainerBuilder
$containerBuilder = new ContainerBuilder();
$app_folder = __DIR__ . '/../../app';
// Container intentionally not compiled for tests.
// Set up settings
$settings = require $app_folder . '/settings.php';
$settings($containerBuilder);
// Set up dependencies
$dependencies = require $app_folder . '/dependencies.php';
$dependencies($containerBuilder);
// Set up repositories
$repositories = require $app_folder . '/repositories.php';
$repositories($containerBuilder);
// Build PHP-DI Container instance
$container = $containerBuilder->build();
// Instantiate the app
AppFactory::setContainer($container);
$app = AppFactory::create();
// Register middleware
$middleware = require $app_folder . '/middleware.php';
$middleware($app);
// Register routes
$routes = require $app_folder . '/routes.php';
$routes($app);
return $app;
}
/**
* @param string $method
* @param string $path
* @param array $headers
* @param array $cookies
* @param array $serverParams
* @return Request
*/
protected function createRequest(
string $method,
string $path,
array $headers = ['HTTP_ACCEPT' => 'application/json'],
array $cookies = [],
array $serverParams = []
): Request {
$uri = new Uri('', '', 80, $path);
$handle = fopen('php://temp', 'w+');
$stream = (new StreamFactory())->createStreamFromResource($handle);
$h = new Headers();
foreach ($headers as $name => $value) {
$h->addHeader($name, $value);
}
return new SlimRequest($method, $uri, $h, $cookies, $serverParams, $stream);
}
}