From 9865c8a0856d1a9ad6e2223af0f7c38024157ad9 Mon Sep 17 00:00:00 2001 From: Joker Date: Thu, 3 Sep 2026 17:44:20 +0000 Subject: [PATCH] fix(e2e): prod-smoke creds via env + docs 404 esperado en production (card 64763175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Auth tests (Login API, Get user info, Full login flow UI) pasan a E2E_AUTH_USERNAME/E2E_AUTH_PASSWORD con test.skip si ausentes: los creds hardcodeados eran de la era railway y no existen en la BD real de prod (usuario 'joker' con password propio; verificado 2026-09-03 login 401 con ambos casings, sin emisión de token). - Docs test afirma la postura de seguridad cuando E2E_DOCS_DISABLED=1 (espera 404): docs_url=None si is_production (PR #147 / card 2972521c) hace imposible /api/v1/docs accesible en prod POR DISEÑO. - e2e-prod-smoke job: E2E_DOCS_DISABLED=1 + wiring de secrets E2E_AUTH_USERNAME/PASSWORD (hasta configurarlos: skip honesto, no rojo). Comentario del workflow corregido: "la BD real tiene el usuario Joker" era falso (run 33776208828). - Fix B (user e2e dedicado en prod DB + secrets + redeploy prod) gated aparte por Joker. --- .github/workflows/e2e.yml | 9 +++++++- dashboard/frontend/e2e/app.spec.ts | 36 +++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 15833b8..aeb8389 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -78,7 +78,11 @@ jobs: # Card 4eb58505: smoke de prod en pushes a main — el stack real self-hosted # (Coolify en jokerserver: frontend :3010, backend :8010). El runner # coolify-deploy comparte host con el stack, por eso 127.0.0.1. Aquí sí - # corren los tests de API (E2E_API_URL): la BD real tiene el usuario Joker. + # corren los tests de API (E2E_API_URL). + # Card 64763175: la BD real NO tiene el usuario Joker con el password del + # repo (user real: 'joker', password propio) y docs está off en production + # por diseño (2972521c). Auth via secrets E2E_AUTH_* (hasta configurarlos, + # los auth tests se saltan — skip honesto, no rojo) y docs espera 404. # Los PRs se saltan este job (el preview E2E vive en pr-deploy-coolify.yml). e2e-prod-smoke: name: E2E Prod Smoke (self-hosted) @@ -88,6 +92,9 @@ jobs: env: E2E_BASE_URL: http://127.0.0.1:3010 E2E_API_URL: http://127.0.0.1:8010 + E2E_DOCS_DISABLED: '1' + E2E_AUTH_USERNAME: ${{ secrets.E2E_AUTH_USERNAME || '' }} + E2E_AUTH_PASSWORD: ${{ secrets.E2E_AUTH_PASSWORD || '' }} steps: - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/dashboard/frontend/e2e/app.spec.ts b/dashboard/frontend/e2e/app.spec.ts index e74ad24..29f8a83 100644 --- a/dashboard/frontend/e2e/app.spec.ts +++ b/dashboard/frontend/e2e/app.spec.ts @@ -8,8 +8,19 @@ import { test, expect } from '@playwright/test'; // skipped otherwise: fresh preview backends boot with an empty database // (create_all, no seed), so login-dependent checks only run against the // real prod stack in the `e2e-prod-smoke` job. +// Card 64763175: los credenciales hardcodeados de la era railway no existen +// en la BD real de prod (usuario 'joker' con password propio — verificado +// 2026-09-03: login 401 con ambos casings) y /api/v1/docs está deshabilitado +// en production POR DISEÑO (main.py: docs_url=None si is_production — +// PR #147 / card 2972521c). Los auth tests pasan a E2E_AUTH_USERNAME / +// E2E_AUTH_PASSWORD (skip si ausentes) y el test de docs afirma la postura +// de seguridad (404) cuando E2E_DOCS_DISABLED=1. const BACKEND_URL = process.env.E2E_API_URL ?? ''; const hasApi = BACKEND_URL !== ''; +const AUTH_USERNAME = process.env.E2E_AUTH_USERNAME ?? ''; +const AUTH_PASSWORD = process.env.E2E_AUTH_PASSWORD ?? ''; +const hasAuth = AUTH_USERNAME !== '' && AUTH_PASSWORD !== ''; +const docsDisabled = process.env.E2E_DOCS_DISABLED === '1'; test.describe('QA-FRAMEWORK E2E Tests', () => { @@ -24,14 +35,20 @@ test.describe('QA-FRAMEWORK E2E Tests', () => { test('Backend API docs accessible', async ({ request }) => { test.skip(!hasApi, 'E2E_API_URL not set — requires a deployed backend'); const response = await request.get(`${BACKEND_URL}/api/v1/docs`); - expect(response.ok()).toBeTruthy(); + if (docsDisabled) { + // Card 2972521c (PR #147): docs_url=None si is_production — afirmar la + // postura de seguridad en production en vez de exigir docs accesibles. + expect(response.status()).toBe(404); + } else { + expect(response.ok()).toBeTruthy(); + } }); test('Login API works', async ({ request }) => { - test.skip(!hasApi, 'E2E_API_URL not set — requires a deployed backend'); + test.skip(!hasApi || !hasAuth, 'requires E2E_API_URL and E2E_AUTH_USERNAME/E2E_AUTH_PASSWORD (card 64763175: creds de la era railway no válidos en prod)'); const response = await request.post(`${BACKEND_URL}/api/v1/auth/login`, { headers: { 'Content-Type': 'application/json' }, - data: { username: 'Joker', password: 'Joker123!' } + data: { username: AUTH_USERNAME, password: AUTH_PASSWORD } }); expect(response.ok()).toBeTruthy(); const data = await response.json(); @@ -40,11 +57,11 @@ test.describe('QA-FRAMEWORK E2E Tests', () => { }); test('Get user info with token', async ({ request }) => { - test.skip(!hasApi, 'E2E_API_URL not set — requires a deployed backend'); + test.skip(!hasApi || !hasAuth, 'requires E2E_API_URL and E2E_AUTH_USERNAME/E2E_AUTH_PASSWORD (card 64763175)'); // First login const loginResponse = await request.post(`${BACKEND_URL}/api/v1/auth/login`, { headers: { 'Content-Type': 'application/json' }, - data: { username: 'Joker', password: 'Joker123!' } + data: { username: AUTH_USERNAME, password: AUTH_PASSWORD } }); const loginData = await loginResponse.json(); const token = loginData.access_token; @@ -55,7 +72,9 @@ test.describe('QA-FRAMEWORK E2E Tests', () => { }); expect(meResponse.ok()).toBeTruthy(); const userData = await meResponse.json(); - expect(userData.username).toBe('Joker'); + // Login puede ser case-insensitive mientras /me devuelve el canonical: + // comparar sin casing evita falsos rojos por 'Joker' vs 'joker'. + expect(userData.username.toLowerCase()).toBe(AUTH_USERNAME.toLowerCase()); }); test('Frontend loads', async ({ page }) => { @@ -73,13 +92,14 @@ test.describe('QA-FRAMEWORK E2E Tests', () => { }); test('Full login flow', async ({ page }) => { + test.skip(!hasAuth, 'requires E2E_AUTH_USERNAME/E2E_AUTH_PASSWORD — BD sin seed en previews y creds de era railway no válidos en prod (card 64763175)'); await page.goto('/login'); await page.waitForLoadState('networkidle'); // Fill login form using placeholder or type const inputs = page.locator('input'); - await inputs.nth(0).fill('Joker'); - await inputs.nth(1).fill('Joker123!'); + await inputs.nth(0).fill(AUTH_USERNAME); + await inputs.nth(1).fill(AUTH_PASSWORD); // Submit await page.click('button:has-text("Login")');