Add browser Python editor with Pyodide, user auth, and workspace API
- FastAPI serves static UI, file CRUD under code/ and read-only lib/ - Pyodide worker runs Python and Jedi completions in the browser - SQLite accounts: login/register, session cookies, superuser user management - Optional EDITOR_API_KEY, AUTH_* env vars, .env.example - Pipenv, pytest, Selenium smoke test, README Made-with: Cursor
This commit is contained in:
133
tests/test_auth.py
Normal file
133
tests/test_auth.py
Normal file
@@ -0,0 +1,133 @@
|
||||
import importlib
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def _reload_app(tmp_path, monkeypatch, **env):
|
||||
import editor_app.config as config
|
||||
import editor_app.db.session as db_sess
|
||||
import editor_app.main as main
|
||||
|
||||
monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))
|
||||
monkeypatch.setenv("AUTH_DATABASE_PATH", str(tmp_path / "auth.db"))
|
||||
monkeypatch.delenv("EDITOR_API_KEY", raising=False)
|
||||
monkeypatch.delenv("BOOTSTRAP_ADMIN_USERNAME", raising=False)
|
||||
monkeypatch.delenv("BOOTSTRAP_ADMIN_PASSWORD", raising=False)
|
||||
for k, v in env.items():
|
||||
monkeypatch.setenv(k, v)
|
||||
config.WORKSPACE_ROOT = tmp_path
|
||||
db_sess.reset_engine()
|
||||
importlib.reload(main)
|
||||
return main.app
|
||||
|
||||
|
||||
def test_auth_status_public(tmp_path, monkeypatch):
|
||||
with TestClient(_reload_app(tmp_path, monkeypatch, AUTH_ENABLED="false")) as client:
|
||||
r = client.get("/api/auth/status")
|
||||
assert r.status_code == 200
|
||||
assert r.json() == {"auth_enabled": False, "register_open": True}
|
||||
|
||||
|
||||
def test_register_login_and_api_access(tmp_path, monkeypatch):
|
||||
with TestClient(
|
||||
_reload_app(tmp_path, monkeypatch, AUTH_ENABLED="true", AUTH_REGISTER_OPEN="true")
|
||||
) as client:
|
||||
assert client.get("/api/files").status_code == 401
|
||||
|
||||
reg = client.post("/api/auth/register", json={"username": "alice", "password": "hunter2000"})
|
||||
assert reg.status_code == 200
|
||||
body = reg.json()
|
||||
assert body["username"] == "alice"
|
||||
assert body["is_superuser"] is True
|
||||
|
||||
login = client.post("/api/auth/login", json={"username": "alice", "password": "hunter2000"})
|
||||
assert login.status_code == 200
|
||||
|
||||
ok = client.get("/api/files")
|
||||
assert ok.status_code == 200
|
||||
|
||||
me = client.get("/api/auth/me")
|
||||
assert me.status_code == 200
|
||||
assert me.json()["user"]["username"] == "alice"
|
||||
|
||||
out = client.post("/api/auth/logout")
|
||||
assert out.status_code == 200
|
||||
assert client.get("/api/files").status_code == 401
|
||||
|
||||
|
||||
def test_second_user_not_superuser(tmp_path, monkeypatch):
|
||||
with TestClient(
|
||||
_reload_app(tmp_path, monkeypatch, AUTH_ENABLED="true", AUTH_REGISTER_OPEN="true")
|
||||
) as client:
|
||||
r1 = client.post("/api/auth/register", json={"username": "usr1", "password": "password99"})
|
||||
assert r1.status_code == 200
|
||||
assert r1.json()["is_superuser"] is True
|
||||
r2 = client.post("/api/auth/register", json={"username": "usr2", "password": "password99"})
|
||||
assert r2.status_code == 200
|
||||
assert r2.json()["is_superuser"] is False
|
||||
|
||||
|
||||
def test_register_duplicate_username(tmp_path, monkeypatch):
|
||||
with TestClient(
|
||||
_reload_app(tmp_path, monkeypatch, AUTH_ENABLED="true", AUTH_REGISTER_OPEN="true")
|
||||
) as client:
|
||||
assert client.post("/api/auth/register", json={"username": "dupuser", "password": "password99"}).status_code == 200
|
||||
dup = client.post("/api/auth/register", json={"username": "dupuser", "password": "otherpass1"})
|
||||
assert dup.status_code == 409
|
||||
|
||||
|
||||
def test_register_closed(tmp_path, monkeypatch):
|
||||
with TestClient(
|
||||
_reload_app(tmp_path, monkeypatch, AUTH_ENABLED="true", AUTH_REGISTER_OPEN="false")
|
||||
) as client:
|
||||
r = client.post("/api/auth/register", json={"username": "bob", "password": "password99"})
|
||||
assert r.status_code == 403
|
||||
|
||||
|
||||
def test_superuser_lists_and_creates_users(tmp_path, monkeypatch):
|
||||
with TestClient(
|
||||
_reload_app(tmp_path, monkeypatch, AUTH_ENABLED="true", AUTH_REGISTER_OPEN="true")
|
||||
) as client:
|
||||
client.post("/api/auth/register", json={"username": "admin", "password": "password99"})
|
||||
client.post("/api/auth/login", json={"username": "admin", "password": "password99"})
|
||||
|
||||
listed = client.get("/api/users")
|
||||
assert listed.status_code == 200
|
||||
assert len(listed.json()) == 1
|
||||
|
||||
created = client.post(
|
||||
"/api/users",
|
||||
json={"username": "sub", "password": "password99", "is_superuser": False},
|
||||
)
|
||||
assert created.status_code == 200
|
||||
assert created.json()["username"] == "sub"
|
||||
assert created.json()["is_superuser"] is False
|
||||
|
||||
names = {u["username"] for u in client.get("/api/users").json()}
|
||||
assert names == {"admin", "sub"}
|
||||
|
||||
|
||||
def test_non_superuser_cannot_list_users(tmp_path, monkeypatch):
|
||||
with TestClient(
|
||||
_reload_app(tmp_path, monkeypatch, AUTH_ENABLED="true", AUTH_REGISTER_OPEN="true")
|
||||
) as client:
|
||||
client.post("/api/auth/register", json={"username": "boss", "password": "password99"})
|
||||
client.post("/api/auth/login", json={"username": "boss", "password": "password99"})
|
||||
client.post("/api/auth/logout")
|
||||
client.post("/api/auth/register", json={"username": "peon", "password": "password99"})
|
||||
client.post("/api/auth/login", json={"username": "peon", "password": "password99"})
|
||||
denied = client.get("/api/users")
|
||||
assert denied.status_code == 403
|
||||
|
||||
|
||||
def test_login_serves_page(client):
|
||||
r = client.get("/login")
|
||||
assert r.status_code == 200
|
||||
assert "Sign in" in r.text
|
||||
|
||||
|
||||
def test_register_serves_page(client):
|
||||
r = client.get("/register")
|
||||
assert r.status_code == 200
|
||||
assert "Create account" in r.text
|
||||
Reference in New Issue
Block a user