0d32038150
Adds tests/e2e/ with three layers of E2E coverage: - API layer (tests/e2e/api/): unauthenticated access, admin endpoints, peer endpoints, access control enforcement — 24 tests - Playwright UI (tests/e2e/ui/): login flows, admin navigation, peer dashboard/services, role-based ACL, password change — 60+ tests - WireGuard connectivity (tests/e2e/wg/): tunnel up/down, DNS resolution through VPN, service ACL enforcement via iptables, full-tunnel routing Shared helpers: PicAPIClient, WGInterface, playwright_login, cleanup. Makefile targets: test-e2e-api, test-e2e-ui, test-e2e-wg, test-e2e. Adds scripts/reset_admin_password.py for test bootstrap. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
76 lines
2.3 KiB
Python
76 lines
2.3 KiB
Python
"""
|
|
Admin navigation tests.
|
|
|
|
Scenario 6: admin can reach every route defined in App.jsx adminNavigation
|
|
without being redirected to /login.
|
|
|
|
Routes under test (from App.jsx adminNavigation):
|
|
/ Dashboard
|
|
/peers Peers
|
|
/network Network Services
|
|
/wireguard WireGuard
|
|
/email Email
|
|
/calendar Calendar
|
|
/files Files
|
|
/routing Routing
|
|
/vault Vault
|
|
/containers Container Dashboard
|
|
/cell-network Cell Network
|
|
/logs Logs
|
|
/settings Settings
|
|
/account Account Settings
|
|
"""
|
|
import pytest
|
|
|
|
pytestmark = pytest.mark.ui
|
|
|
|
ADMIN_ROUTES = [
|
|
('/', 'Dashboard'),
|
|
('/peers', 'Peers'),
|
|
('/network', 'Network Services'),
|
|
('/wireguard', 'WireGuard'),
|
|
('/email', 'Email'),
|
|
('/calendar', 'Calendar'),
|
|
('/files', 'Files'),
|
|
('/routing', 'Routing'),
|
|
('/vault', 'Vault'),
|
|
('/containers', 'Containers'),
|
|
('/cell-network', 'Cell Network'),
|
|
('/logs', 'Logs'),
|
|
('/settings', 'Settings'),
|
|
('/account', 'Account'),
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize('route,label', ADMIN_ROUTES)
|
|
def test_admin_can_reach_route(admin_page, webui_base, route, label):
|
|
"""Admin navigating to each app route should not be sent to /login."""
|
|
page = admin_page
|
|
page.goto(f"{webui_base}{route}")
|
|
page.wait_for_load_state('networkidle')
|
|
assert '/login' not in page.url, (
|
|
f"Admin was redirected to /login when navigating to {route} ({label})"
|
|
)
|
|
|
|
|
|
def test_admin_sidebar_shows_admin_links(admin_page, webui_base):
|
|
"""The desktop sidebar must show admin-only links: Peers, Settings, WireGuard."""
|
|
page = admin_page
|
|
page.goto(f"{webui_base}/")
|
|
page.wait_for_load_state('networkidle')
|
|
# These link names come from the adminNavigation array in App.jsx.
|
|
for link_name in ('Peers', 'Settings', 'WireGuard'):
|
|
assert page.get_by_role('link', name=link_name).is_visible(), (
|
|
f"Admin sidebar link '{link_name}' not visible"
|
|
)
|
|
|
|
|
|
def test_admin_sidebar_does_not_show_my_services(admin_page, webui_base):
|
|
"""Admin sidebar should NOT contain the peer-only 'My Services' link."""
|
|
page = admin_page
|
|
page.goto(f"{webui_base}/")
|
|
page.wait_for_load_state('networkidle')
|
|
assert not page.get_by_role('link', name='My Services').is_visible(), (
|
|
"Admin sidebar should not show the peer-only 'My Services' link"
|
|
)
|