maestroai / tests /e2e /test_api_keys_flow.py
gabrielpamplonapg
feat: MaestroAI v1.0 — instrument identification SaaS
d4d0bc7
Raw
History Blame Contribute Delete
5.08 kB
"""E2E tests for API-key management on the Developer tab."""
from __future__ import annotations
import pytest
from playwright.sync_api import Page, expect
pytestmark = pytest.mark.e2e
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _register_user(page: Page, base_url: str, email: str,
password: str = "E2E_Test_P@55") -> None:
page.goto(base_url, timeout=10000)
expect(page.locator("#navbar")).to_be_visible(timeout=5000)
page.locator("#nav-auth-logged-out button", has_text="Get Started").first.click()
page.wait_for_selector("#register-modal.open", timeout=5000)
page.fill("#register-email", email)
page.fill("#register-password", password)
with page.expect_response("**/api/auth/register", timeout=15000) as resp_info:
page.click("#register-submit")
assert resp_info.value.status == 201, f"Register failed: {resp_info.value.text()}"
page.wait_for_function(
"() => typeof state !== 'undefined' && !!state.user",
timeout=10000,
)
# Force-close any onboarding overlay that might intercept clicks.
page.wait_for_timeout(600)
page.evaluate(
"() => { const m = document.getElementById('onboarding-modal');"
" if (m) { m.classList.remove('open'); m.style.display = 'none'; } }"
)
def _open_developer_tab(page: Page) -> None:
"""Navigate to the Developer tab from the user dropdown (API Keys link)."""
tab_btn = page.locator("#tab-developer-btn")
expect(tab_btn).to_be_visible(timeout=5000)
tab_btn.click()
expect(page.locator("#tab-developer")).to_be_visible()
def _accept_dialogs(page: Page) -> None:
"""Stub window.confirm to always return true — avoids native dialog timing issues."""
page.evaluate("() => { window.confirm = () => true; }")
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
def test_create_api_key(page: Page, base_url: str, unique_email: str) -> None:
"""Create an API key via the Developer tab and verify it's listed."""
_register_user(page, base_url, unique_email)
_open_developer_tab(page)
# Click "+ Create Key" and fill in the label.
page.locator("#tab-developer >> text=+ Create Key").click()
page.wait_for_selector("#create-key-modal.open", timeout=5000)
page.fill("#key-label", "E2E Test Key")
page.click("#key-submit")
# Newly created key modal appears with the plaintext key value.
page.wait_for_selector("#new-key-modal.open", timeout=10000)
key_value = page.locator("#new-key-value").inner_text().strip()
assert key_value.startswith("msk_"), f"Expected key prefix msk_, got: {key_value!r}"
# Close the modal and verify the key now appears in the list.
page.locator("#new-key-modal >> text=Done").click()
key_rows = page.locator(".api-key-row")
expect(key_rows.first).to_be_visible(timeout=5000)
# At least one row should show our label.
assert "E2E Test Key" in page.locator("#api-keys-list").inner_text()
def test_revoke_api_key(page: Page, base_url: str, unique_email: str) -> None:
"""Create a key, revoke it, and verify the row flips to revoked state."""
_register_user(page, base_url, unique_email)
_open_developer_tab(page)
_accept_dialogs(page)
# Create a key first.
page.locator("#tab-developer >> text=+ Create Key").click()
page.wait_for_selector("#create-key-modal.open", timeout=5000)
page.fill("#key-label", "Revoke Me")
page.click("#key-submit")
page.wait_for_selector("#new-key-modal.open", timeout=10000)
page.locator("#new-key-modal >> text=Done").click()
# Find the row and click "Revoke".
rows = page.locator(".api-key-row")
expect(rows.first).to_be_visible(timeout=5000)
initial_count = rows.count()
assert initial_count >= 1
# Get the first active key ID from the DOM, then invoke revokeApiKey() directly.
key_id = page.evaluate("""
() => {
const btn = document.querySelector(
'.api-key-row button[onclick^="revokeApiKey"]'
);
if (!btn) return null;
const m = btn.getAttribute('onclick').match(/revokeApiKey\((\d+)\)/);
return m ? parseInt(m[1]) : null;
}
""")
assert key_id is not None, "No revokable API key button found in DOM"
page.evaluate(f"() => revokeApiKey({key_id})")
# Wait for the API call and list re-render.
page.wait_for_timeout(3000)
list_text = page.locator("#api-keys-list").inner_text()
dev_empty_visible = page.locator("#dev-empty").is_visible()
# Success conditions:
# 1. The list is now empty (dev-empty showing), OR
# 2. The row is still visible but marked as "Revoked".
assert dev_empty_visible or "revok" in list_text.lower(), (
f"Key still appears active after revocation. Current list: {list_text!r}"
)