Spaces:
Sleeping
Sleeping
File size: 5,558 Bytes
165a274 90adb95 165a274 90adb95 165a274 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | """Authentication, protected-endpoint, and per-user isolation tests."""
def test_register_returns_token_and_no_password_hash(anon_client):
res = anon_client.post(
"/auth/register", json={"email": "a@b.com", "password": "password123"}
)
assert res.status_code == 201
body = res.json()
assert body["access_token"]
assert body["user"]["email"] == "a@b.com"
assert "password_hash" not in body["user"]
def test_register_duplicate_returns_409(anon_client):
payload = {"email": "dup@b.com", "password": "password123"}
assert anon_client.post("/auth/register", json=payload).status_code == 201
assert anon_client.post("/auth/register", json=payload).status_code == 409
def test_register_rejects_bad_email(anon_client):
res = anon_client.post(
"/auth/register", json={"email": "not-an-email", "password": "password123"}
)
assert res.status_code == 422
def test_register_rejects_short_password(anon_client):
res = anon_client.post(
"/auth/register", json={"email": "c@b.com", "password": "short"}
)
assert res.status_code == 422
def test_login_good_bad_and_missing(anon_client):
anon_client.post(
"/auth/register", json={"email": "login@b.com", "password": "password123"}
)
ok = anon_client.post(
"/auth/login", json={"email": "login@b.com", "password": "password123"}
)
assert ok.status_code == 200 and ok.json()["access_token"]
wrong = anon_client.post(
"/auth/login", json={"email": "login@b.com", "password": "wrongpassword"}
)
assert wrong.status_code == 401
missing = anon_client.post(
"/auth/login", json={"email": "nobody@b.com", "password": "password123"}
)
assert missing.status_code == 401
def test_me_returns_current_user(client):
res = client.get("/auth/me")
assert res.status_code == 200
assert res.json()["email"] == "user@example.com"
assert "password_hash" not in res.json()
def test_protected_endpoints_require_a_token(anon_client):
assert anon_client.get("/documents").status_code == 401
assert anon_client.post(
"/ask", json={"document_id": 1, "question": "hello?"}
).status_code == 401
assert anon_client.get("/auth/me").status_code == 401
def test_invalid_token_is_rejected(anon_client):
anon_client.headers.update({"Authorization": "Bearer not.a.real.jwt"})
assert anon_client.get("/documents").status_code == 401
def test_only_health_is_public(anon_client):
assert anon_client.get("/health").status_code == 200
# demo/seed and evaluate now require auth (resource-amplification guard).
assert anon_client.post("/demo/seed").status_code == 401
assert anon_client.post("/evaluate").status_code == 401
def test_health_exposes_google_client_id(anon_client):
body = anon_client.get("/health").json()
assert "google_client_id" in body
assert body["google_client_id"] is None # unset in tests -> Google off
def test_google_login_disabled_returns_503(anon_client):
assert anon_client.post("/auth/google", json={"credential": "x"}).status_code == 503
def test_google_login_creates_user(anon_client, monkeypatch):
from app import config
monkeypatch.setattr(config, "GOOGLE_ENABLED", True)
monkeypatch.setattr(config, "GOOGLE_CLIENT_ID", "test-client-id")
import google.oauth2.id_token as gid
monkeypatch.setattr(
gid,
"verify_oauth2_token",
lambda *a, **k: {
"iss": "accounts.google.com",
"email": "google.user@example.com",
"email_verified": True,
},
)
res = anon_client.post("/auth/google", json={"credential": "valid-id-token"})
assert res.status_code == 200
body = res.json()
assert body["access_token"]
assert body["user"]["email"] == "google.user@example.com"
def test_history_is_isolated_per_user(client, second_client):
client.post("/demo/seed")
docs = client.get("/documents").json()
did = next(d["id"] for d in docs if d["filename"] == "sample_invoice.txt")
client.post("/ask", json={"document_id": did, "question": "What is the total amount due?"})
second_client.post("/ask", json={"document_id": did, "question": "Bob private question?"})
a_hist = client.get("/history").json()
assert any("total amount due" in h["question"].lower() for h in a_hist)
assert all("bob private question" not in h["question"].lower() for h in a_hist)
b_hist = second_client.get("/history").json()
assert any("bob private question" in h["question"].lower() for h in b_hist)
assert all("total amount due" not in h["question"].lower() for h in b_hist)
def test_documents_are_isolated_per_user(client, second_client):
content = b"Invoice INV-9. Total Amount Due $2,413.98. Payment terms net 30."
doc = client.post(
"/documents/upload", files={"file": ("inv.txt", content, "text/plain")}
).json()
did = doc["id"]
# Owner sees and can use the document.
assert any(d["id"] == did for d in client.get("/documents").json())
assert client.get(f"/documents/{did}").status_code == 200
# A different user cannot see it and gets 404 on every direct access.
assert all(d["id"] != did for d in second_client.get("/documents").json())
assert second_client.get(f"/documents/{did}").status_code == 404
assert second_client.post(
"/ask", json={"document_id": did, "question": "total due?"}
).status_code == 404
assert second_client.delete(f"/documents/{did}").status_code == 404
|