- Symfony 7.4 LTS, PHP 8.4, FrankenPHP, SQLite (3-Layer src/UI|Logic|Data) - Auth: manueller OidcClient (Authorization-Code) im UI-Layer, kein Security-Bundle/league; Session-Gate via SessionAuthSubscriber - API-Guard via statischem X-Api-Token (ApiTokenSubscriber); ExceptionSubscriber - UserEntity (Auto-Provision) + Init-Migration - Docker (php:8.4-frankenphp) + compose (essen.ljh31.de, traefik-net external) - SmokeTests (7) grün: Redirect-Fälle, API-Token, Auth-Render, Logout Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
103 lines
3.2 KiB
PHP
103 lines
3.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests;
|
|
|
|
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
|
use Symfony\Component\BrowserKit\Cookie;
|
|
|
|
final class SmokeTest extends WebTestCase
|
|
{
|
|
private const AUTH_KEYS = [
|
|
'auth_sub' => 'test-sub',
|
|
'auth_name' => 'Jens',
|
|
'auth_email' => 'jens@ljh31.de',
|
|
];
|
|
|
|
private static function client(): KernelBrowser
|
|
{
|
|
$client = self::createClient();
|
|
$client->followRedirects(false);
|
|
return $client;
|
|
}
|
|
|
|
/** Client, dessen Session bereits eine eingeloggte Identität enthält. */
|
|
private static function authedClient(): KernelBrowser
|
|
{
|
|
$client = self::client();
|
|
$session = self::getContainer()->get('session.factory')->createSession();
|
|
$session->start();
|
|
foreach (self::AUTH_KEYS as $k => $v) {
|
|
$session->set($k, $v);
|
|
}
|
|
$session->save();
|
|
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
|
|
return $client;
|
|
}
|
|
|
|
public function testRootRedirectsToLoginWithoutSession(): void
|
|
{
|
|
self::client()->request('GET', '/');
|
|
|
|
self::assertResponseStatusCodeSame(302);
|
|
self::assertResponseRedirects('/login?redirect=/');
|
|
}
|
|
|
|
public function testLoginRedirectsToKeycloakAuthorizeEndpoint(): void
|
|
{
|
|
$client = self::client();
|
|
$client->request('GET', '/login');
|
|
|
|
self::assertResponseStatusCodeSame(302);
|
|
$location = (string) $client->getResponse()->headers->get('Location');
|
|
self::assertStringContainsString('/protocol/openid-connect/auth', $location);
|
|
self::assertStringContainsString('client_id=essens-test', $location);
|
|
}
|
|
|
|
public function testCallbackWithWrongStateRedirectsBackToLogin(): void
|
|
{
|
|
self::client()->request('GET', '/auth/callback?code=xyz&state=falsch-state');
|
|
|
|
self::assertResponseStatusCodeSame(302);
|
|
self::assertResponseRedirects('/login');
|
|
}
|
|
|
|
public function testApiWithoutTokenReturns401Json(): void
|
|
{
|
|
$client = self::client();
|
|
$client->request('GET', '/api/today');
|
|
|
|
self::assertResponseStatusCodeSame(401);
|
|
$body = json_decode((string) $client->getResponse()->getContent(), true);
|
|
self::assertIsArray($body);
|
|
self::assertArrayHasKey('error', $body);
|
|
}
|
|
|
|
public function testApiWithValidTokenGets404Not401(): void
|
|
{
|
|
// /api/today existiert in M0 noch nicht (M4) → Guard lässt durch, Router gibt 404.
|
|
$client = self::client();
|
|
$client->request('GET', '/api/today', server: ['HTTP_X_API_TOKEN' => 'test-api-token']);
|
|
|
|
self::assertSame(404, $client->getResponse()->getStatusCode());
|
|
}
|
|
|
|
public function testRootRendersForAuthenticatedSession(): void
|
|
{
|
|
self::authedClient()->request('GET', '/');
|
|
|
|
self::assertResponseIsSuccessful();
|
|
self::assertSelectorTextContains('h1', 'Jens');
|
|
}
|
|
|
|
public function testLogoutRedirectsToLogin(): void
|
|
{
|
|
self::authedClient()->request('GET', '/logout');
|
|
|
|
self::assertResponseStatusCodeSame(302);
|
|
self::assertResponseRedirects('/login');
|
|
}
|
|
}
|