itsulu-blog-publisher/e2e/conftest.py
Nicholas Riegel 2cb2a48b98 fix: deploy E2E tests on ITSulu K8s cluster instead of external Runboat
Updated CI/CD pipeline to run E2E tests on the ITSulu K8s cluster
(itsulu-testing namespace) instead of using external Runboat service.

.gitlab-ci.yml Changes:
- Removed preview stage (no external Runboat dependency)
- Removed e2e_tests dependency on runboat_preview
- Updated e2e_tests job to run on K8s cluster:
  * Uses bitnami/kubectl image for K8s access
  * Configures kubectl with KUBE_CONFIG secret
  * Creates ephemeral E2E test job on K8s
  * initContainer: Creates test database from template
  * container: Installs Odoo, addon, Playwright, runs pytest
  * Waits for job completion and downloads report
  * Reports available in GitLab artifacts
- Consolidated performance tests into unit_tests stage (same runner)

K8s Job Manifest (inline YAML):
- Namespace: itsulu-testing
- Init container: postgres:15-alpine for DB setup
- Test container: blog-publisher Docker image
- Volumes: emptyDir for test results
- TTL: 3600s (1 hour) for log retention
- Uses existing secrets: test-db-info, gitlab-docker-creds

e2e/conftest.py Changes:
- Updated docstring: "Runs on ITSulu K8s cluster"
- Changed RUNBOAT_TIMEOUT → K8S_TIMEOUT (300s)
- Updated wait_for_odoo() to mention K8s namespace in error
- Increased timeout: 180s → 300s (Odoo init + addon install)

Required CI/CD Variables (now reduced):
- KUBE_CONFIG: base64-encoded kubeconfig for itsulu-testing namespace
  * Only one variable needed (replaces 3 Runboat variables)
  * Must have permissions to create Jobs in itsulu-testing namespace

Prerequisites (K8s cluster):
- test-db-svc: PostgreSQL service in itsulu-testing namespace
- odoo_template: Pre-seeded database
- gitlab-docker-creds: Secret for image pull
- test-runner: ServiceAccount with Job create permissions

Benefits:
- No external service dependency
- Leverages existing ITSulu K8s infrastructure
- Full isolation per test run (ephemeral jobs)
- Persistent logs via TTL
- Cost-effective (uses existing cluster)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-30 00:58:46 -04:00

120 lines
3.5 KiB
Python

"""
Playwright E2E test configuration for ITSulu Blog Publisher.
Runs on ITSulu K8s cluster (itsulu-testing namespace).
Handles cold-start polling, authentication, and fixture setup.
"""
import os
import time
import requests
import pytest
BASE_URL = os.environ.get('ODOO_BASE_URL', 'http://localhost:8069')
K8S_TIMEOUT = 300 # seconds (Odoo startup + addon install can take time)
def wait_for_odoo(url, timeout=K8S_TIMEOUT):
"""
Poll Odoo login page until it responds (K8s container cold-start handling).
K8s pod startup + Odoo initialization + addon installation can take 60-120s.
"""
deadline = time.time() + timeout
last_error = None
while time.time() < deadline:
try:
response = requests.get(f"{url}/web/login", timeout=5)
if response.status_code == 200:
return
except Exception as e:
last_error = e
pass
time.sleep(2)
raise TimeoutError(
f"Odoo at {url} did not respond after {timeout}s. "
f"Running on ITSulu K8s cluster (itsulu-testing namespace). "
f"Last error: {last_error}"
)
@pytest.fixture(scope='session')
def browser_context_args(browser_context_args):
"""
Poll Runboat instance and set base URL for all tests.
Runs once per test session before any browser is created.
"""
print(f"\n📡 Waiting for Odoo at {BASE_URL}...")
wait_for_odoo(BASE_URL)
print("✅ Odoo is ready")
return {**browser_context_args, 'base_url': BASE_URL}
@pytest.fixture(scope='session')
def auth_state(browser, browser_context_args):
"""
Log in as admin and save authentication state.
Reused across all tests to avoid repeated login (30s+ per test).
"""
print("\n🔐 Authenticating as admin...")
ctx = browser.new_context(**browser_context_args)
page = ctx.new_page()
page.set_default_timeout(30_000)
try:
page.goto('/web/login')
page.get_by_label('Email').fill('admin')
page.get_by_label('Password').fill('admin')
page.get_by_role('button', name='Log in').click()
page.wait_for_url('**/odoo/**', timeout=30_000)
state = ctx.storage_state()
print("✅ Authentication successful")
ctx.close()
return state
except Exception as e:
ctx.close()
raise RuntimeError(f"Login failed: {e}")
@pytest.fixture
def page(browser, browser_context_args, auth_state):
"""
Authenticated Playwright page fixture.
Each test gets a fresh context with saved auth state.
"""
ctx = browser.new_context(**browser_context_args, storage_state=auth_state)
pg = ctx.new_page()
pg.set_default_timeout(30_000) # Odoo JS rendering is slow
yield pg
ctx.close()
@pytest.fixture
def admin_user(page):
"""
Helper to get admin user record (via API call within page context).
Useful for setting up test data that requires user relationships.
"""
# In a real Runboat instance, we'd fetch this via the web services
# For now, return a simple dict with admin info
return {
'id': 2, # Odoo default admin user ID
'name': 'Administrator',
'email': 'admin@example.com',
'login': 'admin',
}
@pytest.fixture(scope='session')
def blog_name():
"""Pre-configured test blog name (must exist in template DB)."""
return 'ITSulu Insights'
@pytest.fixture(scope='session')
def notification_email():
"""Email address for testing notifications."""
return 'test@itsulu.com'