125 lines
4.6 KiB
Python
125 lines
4.6 KiB
Python
import csv
|
|
import os
|
|
import time
|
|
from datetime import datetime
|
|
|
|
import pytest
|
|
from selenium.webdriver.common.by import By
|
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
from selenium.webdriver.support import expected_conditions as EC
|
|
|
|
from tests.browser import create_chrome_driver
|
|
from tests.config_reader import load_config
|
|
from tests.helpers import login, logout
|
|
from tests.locators import Locator
|
|
|
|
|
|
LOGIN_ATTEMPTS = 50
|
|
MAX_CAPTCHA_RETRIES = 7
|
|
|
|
|
|
@pytest.mark.captcha
|
|
def test_captcha_login_stress():
|
|
"""Run bounded EasyOCR login attempts and retain auditable CAPTCHA evidence."""
|
|
config = load_config()
|
|
output_dir = os.path.abspath(os.path.join("reports", "captcha_login_stress"))
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
started = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
csv_path = os.path.join(output_dir, f"captcha_login_{started}.csv")
|
|
rows = []
|
|
passed = 0
|
|
failed = 0
|
|
|
|
def record(row):
|
|
rows.append(row)
|
|
|
|
for login_number in range(1, LOGIN_ATTEMPTS + 1):
|
|
login_started = datetime.now().isoformat(timespec="seconds")
|
|
captcha_rows = []
|
|
|
|
driver = create_chrome_driver()
|
|
try:
|
|
driver.get(config["url"])
|
|
|
|
login_status = "FAILED"
|
|
logout_status = "NOT_ATTEMPTED"
|
|
error_text = ""
|
|
captcha_metrics = {"captcha_attempts": []}
|
|
try:
|
|
login(
|
|
driver,
|
|
config["login"]["username"],
|
|
config["login"]["password"],
|
|
max_captcha_retries=MAX_CAPTCHA_RETRIES,
|
|
captcha_metrics=captcha_metrics,
|
|
max_login_duration_seconds=1200,
|
|
)
|
|
login_status = "PASSED"
|
|
passed += 1
|
|
except Exception as exc:
|
|
failed += 1
|
|
error_text = f"{type(exc).__name__}: {exc}"
|
|
try:
|
|
driver.save_screenshot(os.path.join(output_dir, f"login_{login_number:03d}_failure.png"))
|
|
except Exception:
|
|
pass
|
|
finally:
|
|
# Explicitly log out after a successful authenticated flow,
|
|
# then close the browser session.
|
|
if login_status == "PASSED":
|
|
try:
|
|
logout_status = "PASSED" if logout(driver) else "FAILED"
|
|
except Exception as exc:
|
|
logout_status = "FAILED"
|
|
print(f"Logout error: {type(exc).__name__}: {exc}")
|
|
|
|
try:
|
|
driver.quit()
|
|
except Exception:
|
|
pass
|
|
|
|
captcha_rows = captcha_metrics.get("captcha_attempts", []) or [{
|
|
"attempt": 0, "captcha_text": "", "variant": "",
|
|
"status": "no_captcha_record", "screenshot": "", "error": error_text,
|
|
}]
|
|
for item in captcha_rows:
|
|
record({
|
|
"login_attempt": login_number,
|
|
"started_at": login_started,
|
|
"login_status": login_status,
|
|
"logout_status": logout_status,
|
|
"captcha_attempt": item["attempt"],
|
|
"attempted_captcha": item["variant"],
|
|
"ocr_captcha": item["captcha_text"],
|
|
"screenshot_path": item["screenshot"],
|
|
"captcha_status": item["status"],
|
|
"captcha_entered_count": sum(
|
|
1 for captcha_row in captcha_rows if captcha_row["variant"]
|
|
),
|
|
"error": item["error"] or error_text,
|
|
"attempts_to_result": len(captcha_rows),
|
|
"passed_count": passed,
|
|
"failed_count": failed,
|
|
})
|
|
finally:
|
|
# Clean up the driver after all attempts
|
|
try:
|
|
driver.quit()
|
|
except Exception:
|
|
pass
|
|
|
|
fieldnames = [
|
|
"login_attempt", "started_at", "login_status", "captcha_attempt",
|
|
"logout_status",
|
|
"attempted_captcha", "ocr_captcha", "screenshot_path", "captcha_status",
|
|
"captcha_entered_count", "error", "attempts_to_result", "passed_count",
|
|
"failed_count",
|
|
]
|
|
with open(csv_path, "w", newline="", encoding="utf-8") as report:
|
|
writer = csv.DictWriter(report, fieldnames=fieldnames)
|
|
writer.writeheader()
|
|
writer.writerows(rows)
|
|
|
|
print(f"CAPTCHA stress report: {csv_path}")
|
|
print(f"Passed: {passed}; Failed: {failed}; Total: {LOGIN_ATTEMPTS}")
|