Gate testing API for staging E2E
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-03 15:00:33 +01:00
parent 030a00ba46
commit 7ea34b3b20
17 changed files with 94 additions and 22 deletions

View File

@@ -13,7 +13,7 @@ class TestCheckoutController extends Controller
{
public function latest(Request $request): JsonResponse
{
abort_unless(app()->environment(['local', 'testing']), 404);
abort_unless(config('e2e.testing_enabled'), 404);
$validated = $request->validate([
'email' => ['nullable', 'string', 'email'],
@@ -66,7 +66,7 @@ class TestCheckoutController extends Controller
CheckoutWebhookService $webhooks,
CheckoutSession $session
): JsonResponse {
abort_unless(app()->environment(['local', 'testing']), 404);
abort_unless(config('e2e.testing_enabled'), 404);
$validated = $request->validate([
'event_type' => ['nullable', 'string'],

View File

@@ -16,7 +16,7 @@ class TestCouponController extends Controller
{
public function store(Request $request): JsonResponse
{
abort_unless(app()->environment(['local', 'testing']), 404);
abort_unless(config('e2e.testing_enabled'), 404);
$payload = $request->input('coupons');
$definitions = collect(is_array($payload) ? $payload : [])

View File

@@ -15,7 +15,7 @@ class TestEventController extends Controller
{
public function joinToken(Request $request, EventJoinTokenService $tokens): JsonResponse
{
abort_unless(app()->environment(['local', 'testing']), 404);
abort_unless(config('e2e.testing_enabled'), 404);
$validated = $request->validate([
'event_id' => ['nullable', 'integer'],

View File

@@ -20,7 +20,7 @@ class TestGuestEventController extends Controller
{
public function store(Request $request, EventJoinTokenService $joinTokens): JsonResponse
{
abort_unless(app()->environment(['local', 'testing']), 404);
abort_unless(config('e2e.testing_enabled'), 404);
$validated = $request->validate([
'slug' => ['nullable', 'string', 'max:100'],

View File

@@ -10,7 +10,7 @@ class TestMailboxController extends Controller
{
public function index(): JsonResponse
{
abort_unless(app()->environment(['local', 'testing']), 404);
abort_unless(config('e2e.testing_enabled'), 404);
return response()->json([
'data' => Mailbox::all(),
@@ -19,7 +19,7 @@ class TestMailboxController extends Controller
public function destroy(): JsonResponse
{
abort_unless(app()->environment(['local', 'testing']), 404);
abort_unless(config('e2e.testing_enabled'), 404);
Mailbox::flush();

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureE2ETestingAccess
{
public function handle(Request $request, Closure $next): Response
{
if (! config('e2e.testing_enabled')) {
abort(404);
}
$token = config('e2e.testing_token');
if ($token && $request->header('X-Testing-Token') !== $token) {
abort(404);
}
return $next($request);
}
}

View File

@@ -141,7 +141,7 @@ class AppServiceProvider extends ServiceProvider
[DispatchGuestNotificationPush::class, 'handle']
);
if ($this->app->environment(['local', 'testing'])) {
if (config('e2e.testing_enabled')) {
EventFacade::listen(
MessageSent::class,
[Mailbox::class, 'record']

View File

@@ -14,7 +14,7 @@ class Mailbox
public static function record(MessageSent $event): void
{
if (! app()->environment(['local', 'testing'])) {
if (! config('e2e.testing_enabled')) {
return;
}

6
config/e2e.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
return [
'testing_enabled' => env('E2E_TESTING_ENABLED', in_array(env('APP_ENV'), ['local', 'testing'], true)),
'testing_token' => env('E2E_TESTING_TOKEN'),
];

View File

@@ -10,6 +10,9 @@ export default defineConfig({
reporter: 'html',
use: {
baseURL: process.env.E2E_BASE_URL ?? 'https://test-y0k0.fotospiel.app',
extraHTTPHeaders: process.env.E2E_TESTING_TOKEN
? { 'X-Testing-Token': process.env.E2E_TESTING_TOKEN }
: undefined,
trace: 'on-first-retry',
headless: true,
screenshot: 'only-on-failure',

View File

@@ -353,6 +353,6 @@ Route::prefix('v1')->name('api.v1.')->group(function () {
});
if (app()->environment(['local', 'testing'])) {
if (config('e2e.testing_enabled')) {
require __DIR__.'/testing.php';
}

View File

@@ -2,12 +2,13 @@
use App\Http\Controllers\Testing\TestCheckoutController;
use App\Http\Controllers\Testing\TestCouponController;
use App\Http\Controllers\Testing\TestGuestEventController;
use App\Http\Controllers\Testing\TestEventController;
use App\Http\Controllers\Testing\TestGuestEventController;
use App\Http\Controllers\Testing\TestMailboxController;
use App\Http\Middleware\EnsureE2ETestingAccess;
use Illuminate\Support\Facades\Route;
Route::prefix('_testing')->group(function () {
Route::prefix('_testing')->middleware(EnsureE2ETestingAccess::class)->group(function () {
Route::get('/mailbox', [TestMailboxController::class, 'index'])->name('testing.mailbox.index');
Route::delete('/mailbox', [TestMailboxController::class, 'destroy'])->name('testing.mailbox.destroy');

View File

@@ -0,0 +1,36 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
class TestingApiAccessTest extends TestCase
{
public function test_testing_routes_are_blocked_when_disabled(): void
{
config([
'e2e.testing_enabled' => false,
'e2e.testing_token' => 'secret-token',
]);
$this->withHeader('X-Testing-Token', 'secret-token')
->getJson('/api/_testing/mailbox')
->assertNotFound();
}
public function test_testing_routes_require_token_when_enabled(): void
{
config([
'e2e.testing_enabled' => true,
'e2e.testing_token' => 'secret-token',
]);
$this->getJson('/api/_testing/mailbox')
->assertNotFound();
$this->withHeader('X-Testing-Token', 'secret-token')
->getJson('/api/_testing/mailbox')
->assertOk()
->assertJsonStructure(['data']);
}
}

View File

@@ -17,8 +17,8 @@ test.describe('Marketing auth flows', () => {
await page.getByLabel(/Telefon/i).fill('+49123456789');
await page.getByLabel(/Adresse/i).fill('Teststr. 1, 12345 Berlin');
await page.getByLabel(/Username/i).fill(username);
await page.getByLabel(/^Passwort$/i).fill(password);
await page.getByLabel(/Passwort bestätigen/i).fill(password);
await page.fill('input[name="password"]', password);
await page.fill('input[name="password_confirmation"]', password);
await page.locator('#privacy_consent').check();
await page.getByRole('button', { name: /^Registrieren$/i }).click();

View File

@@ -6,8 +6,8 @@ test.describe('Marketing hero CTA smoke', () => {
await page.goto('/');
const cta = page.getByRole('link', {
name: /Pakete entdecken|Jetzt loslegen|Discover Packages|Get started now/i,
const cta = page.locator('#hero').getByRole('link', {
name: /Pakete ansehen|Pakete entdecken|Discover Packages/i,
});
await expect(cta).toBeVisible();
@@ -22,12 +22,12 @@ test.describe('Marketing hero CTA smoke', () => {
await page.goto('/packages');
const cta = page.getByRole('link', {
name: /Pakete entdecken|Lieblingspaket sichern|Discover Packages|Explore top packages/i,
name: /Pakete entdecken|Discover Packages/i,
});
await expect(cta).toBeVisible();
await cta.click();
await expect(page.locator('#endcustomer')).toBeVisible();
await expect(page.locator('#packages-showcase')).toBeVisible();
});
});

View File

@@ -18,7 +18,9 @@ test.describe('Marketing frontend smoke', () => {
}
await expect(page.getByRole('heading', { level: 1, name: /Dein Event|Fotospiel/i })).toBeVisible();
const heroCta = page.getByRole('link', { name: /paket|packages|starten|ausprobieren/i }).first();
const heroCta = page.locator('#hero').getByRole('link', {
name: /Pakete ansehen|Pakete entdecken|Discover Packages/i,
});
await expect(heroCta).toBeVisible();
await heroCta.click();

View File

@@ -66,9 +66,9 @@ test.describe('Standard package checkout with Paddle completion', () => {
await page.goto('/de/packages');
const standardDetailsButton = page
.getByRole('heading', { name: /^Standard$/ })
.locator('..')
.getByRole('button', { name: /Details/i })
.locator('[data-slot="card"]')
.filter({ hasText: /Standard/i })
.getByRole('button', { name: /Details ansehen|Details anzeigen|View details/i })
.first();
await expect(standardDetailsButton).toBeVisible();