*** Settings *** Library Browser Library RequestsLibrary Library Process Library String Library Collections Library DateTime *** Variables *** ${FRONTEND_URL} http://localhost:5173 ${BACKEND_URL} http://localhost:8000 ${TEST_PASSWORD} Hallo123! ${XFF_HEADER} ${EMPTY} *** Keywords *** Wait For Backend Health [Documentation] Polls /api/v1/health/live until 200 or timeout. ... Uses the liveness probe because it is independent of ... fail2ban availability, unlike the combined /api/v1/health ... which returns 503 when fail2ban is offline. [Arguments] ${timeout}=120 ${interval}=5 ${deadline}= Evaluate time.time() + ${timeout} WHILE True ${now}= Evaluate time.time() IF ${now} >= ${deadline} FAIL Backend did not become healthy within ${timeout} seconds ${response}= GET ${BACKEND_URL}/api/v1/health/live expected_status=any IF ${response.status_code} == 200 BREAK Sleep ${interval} END Log Backend is healthy. Wait For Frontend [Documentation] Polls ${FRONTEND_URL} until HTTP 200 or timeout. [Arguments] ${timeout}=60 ${interval}=2 ${deadline}= Evaluate time.time() + ${timeout} WHILE True ${now}= Evaluate time.time() IF ${now} >= ${deadline} FAIL Frontend did not respond within ${timeout} seconds ${result}= Run Keyword And Return Status GET ${FRONTEND_URL} expected_status=any IF ${result} BREAK END Sleep ${interval} END Log Frontend is reachable. Set Random Xff Header [Documentation] Generates a fresh documentation-only IP for X-Forwarded-For ... to bypass per-IP rate limits. RFC5737 192.0.2.0/24. ${octet}= Evaluate random.randint(1, 254) modules=random ${ip}= Set Variable 192.0.2.${octet} Set Suite Variable ${XFF_HEADER} ${ip} RETURN ${ip} Generate Unique Ip [Documentation] Returns a fresh IP from RFC5737 203.0.113.0/24. ${a}= Evaluate random.randint(1, 254) modules=random ${b}= Evaluate random.randint(1, 254) modules=random ${ip}= Set Variable 203.0.113.${a} RETURN ${ip} Generate Unique Jail Name [Documentation] Returns a unique jail name with a timestamp suffix to avoid collisions. ${stamp}= Evaluate int(time.time()) modules=time ${name}= Set Variable test-jail-${stamp} RETURN ${name} Get First Active Jail Name [Documentation] Returns the name of the first active jail via the API. ... Requires the caller to have an authenticated session named 'bangsess'. ${headers}= Create Dictionary X-BanGUI-Request 1 IF "${XFF_HEADER}" != "" Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER} END ${resp}= GET On Session bangsess /api/v1/jails headers=${headers} expected_status=200 ${items}= Set Variable ${resp.json()}[items] ${count}= Get Length ${items} IF ${count} == 0 FAIL No active jails found via API ${first}= Get From List ${items} 0 RETURN ${first}[name] Page Should Contain [Documentation] Convenience wrapper around Browser's Get Text. ... Use a locator (default: body) and a substring; passes if substring is present. [Arguments] ${text} ${locator}=body ${found}= Run Keyword And Return Status Get Text css=${locator} contains ${text} Should Be True ${found} msg=Page text '${text}' not found in ${locator} Page Should Not Contain [Documentation] Inverse: passes if substring is absent from locator. [Arguments] ${text} ${locator}=body ${found}= Run Keyword And Return Status Get Text css=${locator} contains ${text} Should Not Be True ${found} msg=Page text '${text}' unexpectedly found in ${locator} Reset Application State [Documentation] Stub: not all deployments expose a reset endpoint. ... Logs the action and lets tests proceed with current state. Log Reset Application State called (no-op in default stack)