This commit is contained in:
2026-05-04 13:12:57 +02:00
parent e41831447f
commit 48d57c31e1
7 changed files with 216 additions and 132 deletions

View File

@@ -2,14 +2,86 @@
Resource ${CURDIR}/../../resources/common.resource
Resource ${CURDIR}/../../resources/auth.resource
# Use unique X-Forwarded-For to bypass per-IP rate limit across test runs.
# Rate limit: 10 imports / IP / hour. Overridden at runtime via header.
Suite Setup Login As Admin
*** Test Cases ***
Blocklist Import Page Opens
New Browser chromium headless=${TRUE}
Login As Admin
Manual Blocklist Import Completes Without Error
[Documentation] Verifies the full import pipeline:
... UI button click → async backend task → HTTP fetch → DB write → UI log entry.
...
... - Uses local mock server when external network is unavailable.
... - Rate limit bypassed via X-Forwarded-For header.
... - Import "completes successfully" is distinct from "bans were added".
[Teardown] Cleanup Mock Server
# Pre-condition: ensure at least one source is configured.
Ensure Blocklist Source Exists
# Determine if external network is reachable.
${no_internet}= Evaluate __import__("socket").gethostbyname("one.one.one.one") is None modules=socket
IF ${no_internet}
Start Local Mock Server
END
# Navigate to blocklists page and locate the import button.
Go To ${FRONTEND_URL}/blocklists
Wait For Elements State css=[data-testid="blocklist-import-button"],button visible timeout=15s
${content}= Get Page Source
Should Not Be Empty ${content}
# Record current log entry count before triggering import.
${headers}= Create Dictionary X-Forwarded-For 10.0.0.99
${resp_before}= GET ${BACKEND_URL}/api/v1/blocklists/log headers=${headers} expected_status=200
${log_count_before}= Get Length ${resp_before.json()}[entries]
Close Browser
# Trigger the import via the manual import button.
Click css=[data-testid="blocklist-import-button"],button
# Wait for import to finish: button re-enabled or success toast appears.
Wait For Elements State css=[data-testid="blocklist-import-button"],button enabled timeout=45s
# Assert no error banner in the UI.
Get Text css=body not contains error
# Verify the log has a new entry (import ran, regardless of bans added).
${resp_after}= GET ${BACKEND_URL}/api/v1/blocklists/log headers=${headers} expected_status=200
${log_count_after}= Get Length ${resp_after.json()}[entries]
Should Be True ${log_count_after} > ${log_count_before}
Log Import completed. Log entries: ${log_count_before} → ${log_count_after}
*** Keywords ***
Ensure Blocklist Source Exists
[Documentation] Guarantee at least one blocklist source exists.
... If GET /api/v1/blocklists returns an empty list, a minimal local-file
... source is added so the import test has a target.
${headers}= Create Dictionary X-Forwarded-For 10.0.0.99
${resp}= GET ${BACKEND_URL}/api/v1/blocklists headers=${headers} expected_status=200
${sources}= Set Variable ${resp.json()}[sources]
IF ${sources} == ${NONE} or ${len(sources)} == 0
# No sources configured — add a minimal entry pointing to the mock server.
# The mock server serves test.txt from the e2e directory.
${payload}= Create Dictionary
... name=Local Mock Source
... url=http://localhost:8765/test.txt
... enabled=true
POST ${BACKEND_URL}/api/v1/blocklists json=${payload} headers=${headers} expected_status=201
Log Created local mock blocklist source.
ELSE
Log Blocklist source already exists — using first available.
END
Start Local Mock Server
[Documentation] Start a minimal Python HTTP server on port 8765 to serve a test blocklist file.
... The test.txt file contains one IP per line in plain-text format (fail2ban plain list).
${mock_file}= Set Variable ${CURDIR}/../../test_blocklist.txt
${file_exists}= OperatingSystem.File Should Exist ${mock_file}
Start Process python -m http.server 8765 --directory ${CURDIR}/../../ alias=mockserver
... stdout=PIPE stderr=STDOUT
Sleep 2s
Log Local mock HTTP server started on port 8765.
Cleanup Mock Server
[Documentation] Stop the mock HTTP server started by Start Local Mock Server.
Terminate Process mockserver

View File

@@ -2,14 +2,75 @@
Resource ${CURDIR}/../../resources/common.resource
Resource ${CURDIR}/../../resources/auth.resource
Suite Setup Login As Admin
*** Test Cases ***
Config Edit Page Opens
New Browser chromium headless=${TRUE}
Login As Admin
Config Field Edit Persists After Reload
[Documentation] Verifies auto-save round-trip: UI edit → debounced PATCH → reload rehydration.
...
... - Waits for "Saved" indicator rather than fixed Sleep (debounce may delay PATCH).
... - Restores original value in teardown so subsequent tests are not affected.
... - Runs last in suite ordering to avoid destabilising fail2ban health for other tests.
[Teardown] Restore Original Ban Time
# Step 1 — navigate to config page and activate the Jails tab.
Go To ${FRONTEND_URL}/config
Wait For Elements State css=[role="tablist"] visible timeout=15s
Click role=tab name=Jails
${content}= Get Page Source
Should Not Be Empty ${content}
# Step 2 — wait for jail list to render, then select the first jail.
Wait For Elements State css=[role="listbox"] visible timeout=15s
${jail_items}= Get Elements css=[role="option"]
${count}= Get Length ${jail_items}
IF ${count} == 0
Fatal Error No jails found in config list — cannot run test.
END
Click css=[role="option"]:first-child
Close Browser
# Step 3 — read current ban_time value for teardown.
Wait For Elements State css=input[aria-label="Ban Time"] visible timeout=10s
${original}= Get Value css=input[aria-label="Ban Time"]
Set Suite Variable ${ORIGINAL_BANTIME} ${original}
Log Original bantime: ${original}
# Step 4 — edit ban_time to a safe, valid integer (7200 s = 2 h).
Fill Text css=input[aria-label="Ban Time"] 7200
# Step 5 — wait for auto-save debounce to fire PATCH and backend to respond.
# The indicator shows "Saved" (role=status, Badge with text "Saved").
Wait For Elements State css=[role="status"]:has-text("Saved") visible timeout=15s
Log Auto-save confirmed — PATCH completed.
# Step 6 — verify via API that the value was actually written to the jail config file.
${resp}= GET ${BACKEND_URL}/api/jails
Should Contain ${resp.text} 7200
# Step 7 — reload the page and confirm the new value is rehydrated from the backend.
Reload
Wait For Elements State css=[role="tablist"] visible timeout=15s
Click role=tab name=Jails
Wait For Elements State css=[role="listbox"] visible timeout=15s
Click css=[role="option"]:first-child
Wait For Elements State css=input[aria-label="Ban Time"] visible timeout=10s
${reloaded}= Get Value css=input[aria-label="Ban Time"]
Should Be Equal As Strings ${reloaded} 7200
Log Reload verification passed — value persisted.
*** Keywords ***
Restore Original Ban Time
[Documentation] Restore the jail's original ban_time so subsequent tests are unaffected.
... Runs as Test Teardown so it executes even if the test fails mid-way.
Go To ${FRONTEND_URL}/config
Wait For Elements State css=[role="tablist"] visible timeout=15s
Click role=tab name=Jails
Wait For Elements State css=[role="listbox"] visible timeout=15s
${jail_items}= Get Elements css=[role="option"]
${count}= Get Length ${jail_items}
IF ${count} > 0
Click css=[role="option"]:first-child
END
Wait For Elements State css=input[aria-label="Ban Time"] visible timeout=10s
Fill Text css=input[aria-label="Ban Time"] ${ORIGINAL_BANTIME}
Wait For Elements State css=[role="status"]:has-text("Saved") visible timeout=15s
Log Restored original ban_time: ${ORIGINAL_BANTIME}