v0.5.166: /export→/v2/export редирект, экспорт только админам
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
tests/ui-sso.py — E2E браузерный тест SSO через Playwright.
|
||||
|
||||
Проверяет полный поток:
|
||||
1. Редирект на Keycloak
|
||||
2. Вход с логином/паролем
|
||||
3. Возврат в приложение
|
||||
4. CRUD через UI
|
||||
5. Logout
|
||||
|
||||
Запуск:
|
||||
pip install playwright && playwright install chromium
|
||||
KC_LOGIN=tazet@narod.ru KC_PASS=*** python tests/ui-sso.py
|
||||
|
||||
Если Keycloak запрашивает OTP/MFA код — тест запросит его через stdin.
|
||||
"""
|
||||
|
||||
import os, sys, time
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
APP_URL = os.environ.get("APP_URL", "https://whitelist.nodejsk8s.services.ngcloud.ru")
|
||||
KC_LOGIN = os.environ.get("KC_LOGIN")
|
||||
KC_PASS = os.environ.get("KC_PASS")
|
||||
|
||||
if not KC_LOGIN or not KC_PASS:
|
||||
print("❌ Задай KC_LOGIN и KC_PASS")
|
||||
print(" KC_LOGIN=tazet@narod.ru KC_PASS=*** python tests/ui-sso.py")
|
||||
sys.exit(1)
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
def ok(name, condition, details=""):
|
||||
global passed, failed
|
||||
if condition:
|
||||
passed += 1
|
||||
print(f" ✅ {name}")
|
||||
else:
|
||||
failed += 1
|
||||
print(f" ❌ {name} — {details}")
|
||||
|
||||
def run():
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=False)
|
||||
page = browser.new_page()
|
||||
|
||||
print(f"\n🧪 SSO E2E браузерный тест")
|
||||
print(f" App: {APP_URL}")
|
||||
print(f" User: {KC_LOGIN}\n")
|
||||
|
||||
# ── 1. Заходим на / → редирект на Keycloak ──
|
||||
page.goto(f"{APP_URL}/")
|
||||
page.wait_for_timeout(2000)
|
||||
ok("1. Редирект на Keycloak", "keycloak.nubes.ru" in page.url)
|
||||
|
||||
# ── 2. Логин в Keycloak ──
|
||||
if page.locator("input[name=username], #username").count() > 0:
|
||||
page.fill("input[name=username], #username", KC_LOGIN)
|
||||
page.fill("input[name=password], #password", KC_PASS)
|
||||
page.click("button[type=submit], input[type=submit], button[name=login]")
|
||||
page.wait_for_timeout(3000)
|
||||
|
||||
# Проверяем OTP/MFA
|
||||
if page.locator("input[name=otp], input[name=totp]").count() > 0:
|
||||
otp = input("🔐 Введи код OTP/MFA: ").strip()
|
||||
page.fill("input[name=otp], input[name=totp]", otp)
|
||||
page.click("button[type=submit], input[type=submit]")
|
||||
page.wait_for_timeout(3000)
|
||||
|
||||
ok("2. Вход в Keycloak", APP_URL in page.url or "ipwhitelist" in page.url.lower() or "WhiteList" in page.title())
|
||||
else:
|
||||
ok("2. Вход в Keycloak", False, "no login form")
|
||||
|
||||
# ── 3. Главная страница ──
|
||||
page.wait_for_timeout(2000)
|
||||
ok("3. Главная страница загружена", "Белые списки" in page.content() or "WhiteList" in page.title() or page.url.startswith(APP_URL))
|
||||
|
||||
# ── 4. Добавляем запись ──
|
||||
ts = int(time.time())
|
||||
cidr = f"10.{ts % 200}.{ts % 100}.{ts % 50}"
|
||||
page.fill("input[name=value]", cidr)
|
||||
page.fill("textarea[name=comment], input[name=comment]", f"test-{ts}")
|
||||
page.click("button[type=submit]:has-text('Добавить'), button:has-text('Добавить')")
|
||||
page.wait_for_timeout(2000)
|
||||
ok("4. Запись добавлена", cidr in page.content())
|
||||
|
||||
# ── 5. Удаляем запись ──
|
||||
delete_btn = page.locator(f"tr:has-text('{cidr}') button:has-text('Удалить'), tr:has-text('{cidr}') a:has-text('Удалить')").first
|
||||
if delete_btn.count() > 0:
|
||||
delete_btn.click()
|
||||
page.wait_for_timeout(1000)
|
||||
page.click("button:has-text('OK'), button:has-text('Да'), button:has-text('Удалить')")
|
||||
page.wait_for_timeout(2000)
|
||||
ok("5. Запись удалена", cidr not in page.content())
|
||||
else:
|
||||
ok("5. Запись удалена", False, "delete button not found")
|
||||
|
||||
# ── 6. Export ──
|
||||
page.goto(f"{APP_URL}/export")
|
||||
page.wait_for_timeout(1000)
|
||||
ok("6. Export доступен", "text/plain" in page.content() or page.url.endswith("/export"))
|
||||
|
||||
# ── 7. Logout ──
|
||||
page.goto(f"{APP_URL}/logout")
|
||||
page.wait_for_timeout(3000)
|
||||
ok("7. Logout → /login", "/login" in page.url or "keycloak" in page.url.lower())
|
||||
|
||||
# ── Итог ──
|
||||
print(f"\n═══ ИТОГ ═══")
|
||||
print(f" ✅ {passed} ❌ {failed} из {passed+failed}")
|
||||
|
||||
browser.close()
|
||||
return failed == 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
ok = run()
|
||||
sys.exit(0 if ok else 1)
|
||||
Reference in New Issue
Block a user