agentcache / tests /test_auth.py
Yash030's picture
Raplh 2 in process
ac98b25
Raw
History Blame Contribute Delete
5.96 kB
"""
Unit and integration tests for authentication and authorization.
"""
from flask import Flask, jsonify
from agentcache.routes.auth import require_auth, verify_token
# ------------------------------------------------------------------------------
# Unit Tests — verify_token & require_auth
# ------------------------------------------------------------------------------
def test_verify_token():
"""Verify verify_token direct behavior."""
secret = "my-secret-key"
assert verify_token("my-secret-key", secret) is True
assert verify_token("wrong-secret", secret) is False
assert verify_token("", secret) is False
def test_require_auth_unit(monkeypatch):
"""Test require_auth decorator behavior under various header & env combinations on a toy app."""
app = Flask(__name__)
@app.route("/protected")
@require_auth
def protected():
return jsonify({"status": "ok"}), 200
client = app.test_client()
# 1. No secret set -> request passes through with 200
monkeypatch.delenv("AGENTCACHE_SECRET", raising=False)
monkeypatch.delenv("AGENTMEMORY_SECRET", raising=False)
res = client.get("/protected")
assert res.status_code == 200
# Set secret for remaining tests
monkeypatch.setenv("AGENTCACHE_SECRET", "secret-token")
# 2. Missing Authorization header -> 401
res = client.get("/protected")
assert res.status_code == 401
# 3. Basic scheme (non-Bearer) -> 401
res = client.get("/protected", headers={"Authorization": "Basic secret-token"})
assert res.status_code == 401
# 4. Correct Bearer token -> 200
res = client.get("/protected", headers={"Authorization": "Bearer secret-token"})
assert res.status_code == 200
# 5. Wrong Bearer token -> 401
res = client.get("/protected", headers={"Authorization": "Bearer wrong-token"})
assert res.status_code == 401
# 6. Lowercase authorization header -> 200
res = client.get("/protected", headers={"authorization": "Bearer secret-token"})
assert res.status_code == 200
# 7. Token with leading/trailing whitespace -> 200 (.strip() confirmed)
res = client.get(
"/protected", headers={"Authorization": "Bearer secret-token "}
)
assert res.status_code == 200
# 8. Secret set via AGENTMEMORY_SECRET fallback
monkeypatch.delenv("AGENTCACHE_SECRET")
monkeypatch.setenv("AGENTMEMORY_SECRET", "fallback-secret")
res = client.get("/protected", headers={"Authorization": "Bearer fallback-secret"})
assert res.status_code == 200
res = client.get("/protected", headers={"Authorization": "Bearer wrong-secret"})
assert res.status_code == 401
# ------------------------------------------------------------------------------
# Integration Tests — protected & unprotected routes via authed_client
# ------------------------------------------------------------------------------
def test_protected_routes_require_auth(authed_client):
"""Verify all protected HTTP routes return 401 without auth and 200/201 with auth."""
client, secret = authed_client
headers = {"Authorization": f"Bearer {secret}"}
protected_endpoints = [
(
"POST",
"/agentcache/observe",
{"folderPath": "src/test", "agentId": "a1", "text": "test"},
),
("POST", "/agentcache/remember", {"content": "test memory"}),
("POST", "/agentcache/search", {"query": "test"}),
("POST", "/agentcache/timeline", {}),
("GET", "/agentcache/graph", None),
("GET", "/agentcache/audit", None),
("GET", "/agentcache/config/flags", None),
("POST", "/agentcache/migrate", {}),
]
for method, path, payload in protected_endpoints:
# Unauthenticated request -> 401
if method == "POST":
res_unauth = client.post(path, json=payload or {})
res_auth = client.post(path, json=payload or {}, headers=headers)
else:
res_unauth = client.get(path)
res_auth = client.get(path, headers=headers)
assert res_unauth.status_code == 401, (
f"{method} {path} should require auth (got {res_unauth.status_code})"
)
assert res_auth.status_code in (200, 201), (
f"{method} {path} failed with valid auth (got {res_auth.status_code})"
)
def test_unprotected_routes_accessible_without_auth(authed_client):
"""Verify unprotected routes return 200 even when AGENTCACHE_SECRET is set and no token is provided."""
client, _ = authed_client
unprotected_paths = [
"/agentcache/livez",
"/agentcache/health",
"/auth.md",
]
for path in unprotected_paths:
res = client.get(path)
assert res.status_code == 200, (
f"Unprotected route {path} failed (got {res.status_code})"
)
def test_wrong_token_on_any_blueprint_returns_401(authed_client):
"""Verify an invalid token returns 401 across all protected blueprints."""
client, _ = authed_client
bad_headers = {"Authorization": "Bearer wrong-token-value"}
protected_endpoints = [
(
"POST",
"/agentcache/observe",
{"folderPath": "src/test", "agentId": "a1", "text": "test"},
),
("POST", "/agentcache/remember", {"content": "test memory"}),
("POST", "/agentcache/search", {"query": "test"}),
("POST", "/agentcache/timeline", {}),
("GET", "/agentcache/graph", None),
("GET", "/agentcache/audit", None),
("GET", "/agentcache/config/flags", None),
("POST", "/agentcache/migrate", {}),
]
for method, path, payload in protected_endpoints:
if method == "POST":
res = client.post(path, json=payload or {}, headers=bad_headers)
else:
res = client.get(path, headers=bad_headers)
assert res.status_code == 401, f"{method} {path} accepted invalid token!"