test(e2e): split suite by feature area with shared resources
Restructure 5 existing .robot files into 10 numbered files, one per feature area in Docs/Features.md. Each file is independently runnable. Add api.resource + data.resource for CSRF/XFF-aware wrappers and RFC5737 IP generators. Coverage: 110 new tests across login, dashboard, map, jails, config, history, blocklists, layout. Uses existing data-testid/aria-label/role selectors only — no frontend changes. Tests bypass per-IP rate limits via X-Forwarded-For header rotation. Hard rule preserved: failures are findings, never app-code fixes.
This commit is contained in:
@@ -2,20 +2,94 @@
|
||||
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 until 200 or timeout.
|
||||
[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 expected_status=200
|
||||
${response}= GET ${BACKEND_URL}/api/v1/health expected_status=any
|
||||
IF ${response.status} == 200 BREAK
|
||||
Sleep ${interval}
|
||||
END
|
||||
Log Backend is healthy.
|
||||
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)
|
||||
Reference in New Issue
Block a user