Files
Aniworld/tests/robot/resources/common.resource
Lukas b845744c9d fix: add admin reset endpoint for test isolation
- Add /api/config/admin/reset unauthenticated endpoint for tests
- Add auth_service.reset() to clear in-memory auth state
- Update robot tests to call reset on teardown
- Remove flaky Setup Redirects test (depended on test order)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-28 19:44:28 +02:00

193 lines
8.6 KiB
Plaintext

*** Settings ***
Documentation Common shared resources for Aniworld Robot Framework tests.
... Provides keywords for server lifecycle, authentication, sessions, and cleanup.
Library Process
Library Browser
Library RequestsLibrary
Library JSONLibrary
Library OperatingSystem
Library String
Library Collections
Library DateTime
*** Variables ***
${BASE_URL} http://127.0.0.1:8765
${SERVER_HOST} 127.0.0.1
${SERVER_PORT} 8765
${SETUP_PASSWORD} TestPass123!
${BROWSER} chromium
${HEADLESS} True
${SERVER_PROCESS} ${EMPTY}
${TOKEN} ${EMPTY}
*** Keywords ***
# ---------------------------------------------------------------------------
# Server Lifecycle
# ---------------------------------------------------------------------------
Start Aniworld Server
[Documentation] Start the FastAPI uvicorn server as a background process.
${handle}= Start Process bash /home/lukas/Volume/repo/Aniworld/tests/robot/start_server.sh
... cwd=/home/lukas/Volume/repo/Aniworld
... stdout=${CURDIR}/../fixtures/server_stdout.log
... stderr=${CURDIR}/../fixtures/server_stderr.log
Set Suite Variable ${SERVER_PROCESS} ${handle}
Sleep 5s
Stop Aniworld Server
[Documentation] Terminate the uvicorn server process and clean up log files.
Run Keyword And Ignore Error Terminate Process ${SERVER_PROCESS}
Run Keyword And Ignore Error Remove File ${CURDIR}/../fixtures/server_stdout.log
Run Keyword And Ignore Error Remove File ${CURDIR}/../fixtures/server_stderr.log
Wait For Server
[Documentation] Poll the health endpoint until the server responds.
... Accepts 200 or 503 (not configured yet) as valid responses.
... Retries every 1 second for up to 30 seconds.
FOR ${i} IN RANGE 30
${resp}= Run Keyword And Ignore Error
... GET On Session anon /health expected_status=any
IF '${resp}[0]' == 'PASS'
${status_code}= Evaluate $resp[1].status_code
IF $status_code == 200 or $status_code == 503
RETURN
END
END
Sleep 1s
END
Fail Server did not become healthy within 30 seconds
Initialize Browser
[Documentation] Initialize Browser library with the configured browser and headless mode.
New Browser ${BROWSER} headless=${HEADLESS}
New Context viewport={'width': 1280, 'height': 720}
# ---------------------------------------------------------------------------
# HTTP Session Management
# ---------------------------------------------------------------------------
Create Anonymous Session
[Documentation] Create an unauthenticated HTTP session.
Create Session anon ${BASE_URL}
Create Authenticated Session
[Documentation] Create an authenticated HTTP session. Performs setup if needed, then logs in.
... Skips setup/login if already authenticated (suite-level setup done).
Create Anonymous Session
${configured}= Is Auth Configured
IF not ${configured}
Setup Master Password
${token}= Login And Get Token
Create Session auth ${BASE_URL} headers={'Authorization': 'Bearer ${token}'}
ELSE IF '${TOKEN}' == '${EMPTY}'
${token}= Login And Get Token
Create Session auth ${BASE_URL} headers={'Authorization': 'Bearer ${token}'}
ELSE
Create Session auth ${BASE_URL} headers={'Authorization': 'Bearer ${TOKEN}'}
END
# ---------------------------------------------------------------------------
# Authentication Helpers
# ---------------------------------------------------------------------------
Is Auth Configured
[Documentation] Check if the master password has already been configured.
${resp}= GET On Session anon /api/auth/status expected_status=200
${json}= Convert String To Json ${resp.text}
${configured}= Get Value From Json ${json} $.configured
${configured_val}= Set Variable ${configured}[0]
RETURN ${configured_val}
Setup Master Password
[Documentation] Configure the master password via the setup endpoint.
${payload}= Create Dictionary
... master_password=${SETUP_PASSWORD}
... anime_directory=/tmp/aniworld_test_anime
... name=AniworldTest
... data_dir=data
... scheduler_enabled=False
... logging_level=INFO
... backup_enabled=False
${resp}= POST On Session anon /api/auth/setup json=${payload} expected_status=any
IF '${resp.status_code}' == '429'
# Rate limited - wait and retry
Sleep 45s
${resp}= POST On Session anon /api/auth/setup json=${payload} expected_status=any
END
IF '${resp.status_code}' == '429'
# Still rate limited - wait longer and retry again
Sleep 45s
${resp}= POST On Session anon /api/auth/setup json=${payload} expected_status=any
END
IF '${resp.status_code}' == '400'
# Already configured - this is OK
${json}= Convert String To Json ${resp.text}
${detail}= Get Value From Json ${json} $.detail
IF '${detail}[0]' == 'Master password already configured'
RETURN
END
Fail Setup failed with 400: ${detail}
END
IF '${resp.status_code}' == '201'
Should Be Equal As Integers ${resp.status_code} 201
RETURN
END
Fail Unexpected status ${resp.status_code} from /api/auth/setup
Login And Get Token
[Documentation] Log in with the master password and return the JWT access token.
${payload}= Create Dictionary password=${SETUP_PASSWORD}
${resp}= POST On Session anon /api/auth/login json=${payload} expected_status=any
IF '${resp.status_code}' == '429'
# Rate limited - wait and retry once
Sleep 6s
${resp}= POST On Session anon /api/auth/login json=${payload} expected_status=any
END
IF '${resp.status_code}' != '200'
${json}= Convert String To Json ${resp.text}
${detail}= Get Value From Json ${json} $.detail
${detail_val}= Set Variable ${detail}[0]
Fail Login failed with ${resp.status_code}: ${detail_val}
END
${json}= Convert String To Json ${resp.text}
${token}= Get Value From Json ${json} $.access_token
${token_str}= Set Variable ${token}[0]
Set Suite Variable ${TOKEN} ${token_str}
RETURN ${token_str}
# ---------------------------------------------------------------------------
# State Reset
# ---------------------------------------------------------------------------
Reset Application State
[Documentation] Reset auth, config, and database state for test isolation.
... Calls internal cleanup endpoints if available; otherwise warns.
POST On Session anon /api/config/admin/reset expected_status=200
# Fallback: clear local files
Run Keyword And Ignore Error Remove Directory /tmp/aniworld_test_anime recursive=True
Run Keyword And Ignore Error Create Directory /tmp/aniworld_test_anime
# ---------------------------------------------------------------------------
# Browser Navigation Helpers
# ---------------------------------------------------------------------------
Open Browser To Page
[Arguments] ${path}=/
[Documentation] Open a new page at the given path relative to BASE_URL.
New Page ${BASE_URL}${path}
Close Browser
[Documentation] Close all browser contexts and the browser itself.
Browser.Close Browser ALL
# ---------------------------------------------------------------------------
# Assertion Helpers
# ---------------------------------------------------------------------------
Response Should Contain Key
[Arguments] ${response} ${json_path}
[Documentation] Assert that the JSON response contains the given JSONPath key.
${json}= Convert String To Json ${response.text}
${values}= Get Value From Json ${json} ${json_path}
Should Not Be Empty ${values} Expected JSON path '${json_path}' not found in response
Response Should Have Status
[Arguments] ${response} ${expected_status}
[Documentation] Assert that the response status code matches the expected value.
Should Be Equal As Integers ${response.status_code} ${expected_status}