Build reusable bot framework
This commit is contained in:
592
tests/unit/test_api_routes.py
Normal file
592
tests/unit/test_api_routes.py
Normal file
@@ -0,0 +1,592 @@
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, call
|
||||
|
||||
import flask
|
||||
import pytest
|
||||
|
||||
from api import main as apiMain
|
||||
from api import security
|
||||
|
||||
|
||||
def _bearer(token="token"):
|
||||
return {"Authorization": f"Bearer {token}"}
|
||||
|
||||
|
||||
def _userPrincipal(userUUID="user-1", authentication="jwt"):
|
||||
return {
|
||||
"type": "user",
|
||||
"authentication": authentication,
|
||||
"user_uuid": userUUID,
|
||||
"scopes": [],
|
||||
}
|
||||
|
||||
|
||||
def _servicePrincipal(*scopes):
|
||||
return {
|
||||
"type": "service",
|
||||
"authentication": "api_key",
|
||||
"service_name": "test-service",
|
||||
"scopes": list(scopes),
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def securityClient():
|
||||
app = flask.Flask("security-test")
|
||||
app.config["TESTING"] = True
|
||||
|
||||
@app.route("/json", methods=["POST"])
|
||||
def parseJson():
|
||||
return flask.jsonify({"parsed": security.jsonObject()})
|
||||
|
||||
@app.route("/user")
|
||||
@security.requireUser()
|
||||
def userRoute():
|
||||
return flask.jsonify(
|
||||
{
|
||||
"user_uuid": flask.g.user_uuid,
|
||||
"principal_type": flask.g.principal["type"],
|
||||
}
|
||||
)
|
||||
|
||||
@app.route("/login-user")
|
||||
@security.requireUser(requireLogin=True)
|
||||
def loginUserRoute():
|
||||
return flask.jsonify({"ok": True})
|
||||
|
||||
@app.route("/service")
|
||||
@security.requireService("jobs:claim")
|
||||
def serviceRoute():
|
||||
return flask.jsonify({"service": flask.g.principal["service_name"]})
|
||||
|
||||
return app.test_client()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def apiClient(monkeypatch):
|
||||
registry = SimpleNamespace(route_registrars=[])
|
||||
monkeypatch.setattr(apiMain, "discover_modules", MagicMock(return_value=registry))
|
||||
app = apiMain.createApp()
|
||||
app.config.update(TESTING=True, SERVICE_KEY_READY=True)
|
||||
return app.test_client()
|
||||
|
||||
|
||||
def test_json_object_accepts_only_json_objects(securityClient):
|
||||
assert securityClient.post("/json", json={"value": 1}).get_json() == {
|
||||
"parsed": {"value": 1}
|
||||
}
|
||||
assert securityClient.post("/json", json=[1, 2]).get_json() == {"parsed": None}
|
||||
assert securityClient.post(
|
||||
"/json",
|
||||
data="not-json",
|
||||
content_type="application/json",
|
||||
).get_json() == {"parsed": None}
|
||||
|
||||
|
||||
def test_require_user_sets_context_and_forwards_login_requirement(
|
||||
monkeypatch, securityClient
|
||||
):
|
||||
principal = _userPrincipal()
|
||||
authenticate = MagicMock(return_value=principal)
|
||||
isUser = MagicMock(return_value=True)
|
||||
monkeypatch.setattr(security.auth, "authenticateBearerToken", authenticate)
|
||||
monkeypatch.setattr(security.auth, "isUserPrincipal", isUser)
|
||||
|
||||
response = securityClient.get("/user", headers=_bearer("user-token"))
|
||||
loginResponse = securityClient.get(
|
||||
"/login-user",
|
||||
headers=_bearer("user-token"),
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.get_json() == {
|
||||
"user_uuid": "user-1",
|
||||
"principal_type": "user",
|
||||
}
|
||||
assert loginResponse.status_code == 200
|
||||
assert authenticate.call_args_list == [
|
||||
call("Bearer user-token", allowService=False),
|
||||
call("Bearer user-token", allowService=False),
|
||||
]
|
||||
assert isUser.call_args_list == [
|
||||
call(principal, requireLogin=False),
|
||||
call(principal, requireLogin=True),
|
||||
]
|
||||
|
||||
|
||||
def test_require_user_rejects_invalid_principal(monkeypatch, securityClient):
|
||||
monkeypatch.setattr(
|
||||
security.auth,
|
||||
"authenticateBearerToken",
|
||||
MagicMock(return_value=None),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
security.auth,
|
||||
"isUserPrincipal",
|
||||
MagicMock(return_value=False),
|
||||
)
|
||||
|
||||
response = securityClient.get("/user")
|
||||
|
||||
assert response.status_code == 401
|
||||
assert response.get_json() == {"error": "unauthorized"}
|
||||
|
||||
|
||||
def test_require_service_enforces_scope_and_sets_principal(
|
||||
monkeypatch, securityClient
|
||||
):
|
||||
principal = _servicePrincipal("jobs:claim")
|
||||
authenticate = MagicMock(side_effect=[principal, None])
|
||||
hasScope = MagicMock(side_effect=[True, False])
|
||||
monkeypatch.setattr(security.auth, "authenticateBearerToken", authenticate)
|
||||
monkeypatch.setattr(security.auth, "hasServiceScope", hasScope)
|
||||
|
||||
accepted = securityClient.get("/service", headers=_bearer("service-key"))
|
||||
rejected = securityClient.get("/service", headers=_bearer("wrong-key"))
|
||||
|
||||
assert accepted.status_code == 200
|
||||
assert accepted.get_json() == {"service": "test-service"}
|
||||
assert rejected.status_code == 401
|
||||
assert rejected.get_json() == {"error": "unauthorized"}
|
||||
assert authenticate.call_args_list == [
|
||||
call(
|
||||
"Bearer service-key",
|
||||
requiredScopes=["jobs:claim"],
|
||||
allowUser=False,
|
||||
),
|
||||
call(
|
||||
"Bearer wrong-key",
|
||||
requiredScopes=["jobs:claim"],
|
||||
allowUser=False,
|
||||
),
|
||||
]
|
||||
assert hasScope.call_args_list == [
|
||||
call(principal, "jobs:claim"),
|
||||
call(None, "jobs:claim"),
|
||||
]
|
||||
|
||||
|
||||
def test_registration_and_login_success_and_errors(monkeypatch, apiClient):
|
||||
registerUser = MagicMock(
|
||||
side_effect=[True, False, ValueError("invalid registration")]
|
||||
)
|
||||
getToken = MagicMock(side_effect=["login-token", False])
|
||||
monkeypatch.setattr(apiMain.users, "registerUser", registerUser)
|
||||
monkeypatch.setattr(apiMain.auth, "getLoginToken", getToken)
|
||||
|
||||
registered = apiClient.post(
|
||||
"/api/register",
|
||||
json={"username": "alice", "password": "password123", "timezone": "UTC"},
|
||||
)
|
||||
duplicate = apiClient.post(
|
||||
"/api/register",
|
||||
json={"username": "alice", "password": "password123"},
|
||||
)
|
||||
invalid = apiClient.post(
|
||||
"/api/register",
|
||||
json={"username": "", "password": "password123"},
|
||||
)
|
||||
invalidJson = apiClient.post("/api/register", json=["not", "an", "object"])
|
||||
loggedIn = apiClient.post(
|
||||
"/api/login",
|
||||
json={"username": "alice", "password": "password123"},
|
||||
)
|
||||
denied = apiClient.post(
|
||||
"/api/login",
|
||||
json={"username": "alice", "password": "wrong-password"},
|
||||
)
|
||||
|
||||
assert registered.status_code == 201
|
||||
assert registered.get_json() == {"success": True}
|
||||
assert duplicate.status_code == 409
|
||||
assert duplicate.get_json() == {"error": "username taken"}
|
||||
assert invalid.status_code == 400
|
||||
assert invalid.get_json() == {"error": "invalid registration"}
|
||||
assert invalidJson.status_code == 400
|
||||
assert loggedIn.get_json() == {"token": "login-token"}
|
||||
assert denied.status_code == 401
|
||||
assert denied.get_json() == {"error": "invalid credentials"}
|
||||
|
||||
|
||||
def test_discord_session_success_and_enrollment_errors(monkeypatch, apiClient):
|
||||
principal = _servicePrincipal("discord:session")
|
||||
monkeypatch.setattr(
|
||||
apiMain.auth,
|
||||
"authenticateBearerToken",
|
||||
MagicMock(return_value=principal),
|
||||
)
|
||||
getOrCreate = MagicMock(
|
||||
side_effect=[ValueError("invalid Discord ID"), None, {"id": "user-1"}]
|
||||
)
|
||||
monkeypatch.setattr(apiMain.identity, "getOrCreateDiscordUser", getOrCreate)
|
||||
monkeypatch.setattr(
|
||||
apiMain.auth,
|
||||
"createLoginToken",
|
||||
MagicMock(return_value="discord-token"),
|
||||
)
|
||||
|
||||
missing = apiClient.post(
|
||||
"/api/auth/discord/session",
|
||||
headers=_bearer("service-key"),
|
||||
json={},
|
||||
)
|
||||
invalid = apiClient.post(
|
||||
"/api/auth/discord/session",
|
||||
headers=_bearer("service-key"),
|
||||
json={"discord_id": "bad"},
|
||||
)
|
||||
denied = apiClient.post(
|
||||
"/api/auth/discord/session",
|
||||
headers=_bearer("service-key"),
|
||||
json={"discord_id": "456"},
|
||||
)
|
||||
accepted = apiClient.post(
|
||||
"/api/auth/discord/session",
|
||||
headers=_bearer("service-key"),
|
||||
json={"discord_id": "123", "display_name": "Alice"},
|
||||
)
|
||||
|
||||
assert missing.status_code == 400
|
||||
assert invalid.get_json() == {"error": "invalid Discord ID"}
|
||||
assert denied.status_code == 403
|
||||
assert accepted.status_code == 200
|
||||
assert accepted.get_json() == {
|
||||
"token": "discord-token",
|
||||
"user_uuid": "user-1",
|
||||
"timezone": "UTC",
|
||||
}
|
||||
apiMain.auth.createLoginToken.assert_called_once_with(
|
||||
"user-1",
|
||||
name="Alice",
|
||||
extraClaims={"provider": "discord"},
|
||||
)
|
||||
|
||||
|
||||
def test_api_key_routes_use_authenticated_owner(monkeypatch, apiClient):
|
||||
monkeypatch.setattr(
|
||||
apiMain.auth,
|
||||
"authenticateBearerToken",
|
||||
MagicMock(return_value=_userPrincipal()),
|
||||
)
|
||||
listKeys = MagicMock(return_value=[{"id": "key-1", "name": "CLI"}])
|
||||
createKey = MagicMock(
|
||||
side_effect=[
|
||||
{"id": "key-2", "name": "new", "key": "secret"},
|
||||
ValueError("invalid expiry"),
|
||||
]
|
||||
)
|
||||
revokeKey = MagicMock(side_effect=[False, True])
|
||||
monkeypatch.setattr(apiMain.apiKeys, "listUserApiKeys", listKeys)
|
||||
monkeypatch.setattr(apiMain.apiKeys, "createUserApiKey", createKey)
|
||||
monkeypatch.setattr(apiMain.apiKeys, "revokeUserApiKey", revokeKey)
|
||||
|
||||
listed = apiClient.get("/api/keys", headers=_bearer())
|
||||
created = apiClient.post(
|
||||
"/api/keys",
|
||||
headers=_bearer(),
|
||||
json={"name": "new", "expires_at": "2099-01-01T00:00:00Z"},
|
||||
)
|
||||
invalid = apiClient.post(
|
||||
"/api/keys",
|
||||
headers=_bearer(),
|
||||
json={"name": "bad"},
|
||||
)
|
||||
missing = apiClient.delete("/api/keys/missing", headers=_bearer())
|
||||
revoked = apiClient.delete("/api/keys/key-1", headers=_bearer())
|
||||
|
||||
assert listed.get_json() == {"keys": [{"id": "key-1", "name": "CLI"}]}
|
||||
assert created.status_code == 201
|
||||
assert created.get_json()["key"] == "secret"
|
||||
assert invalid.status_code == 400
|
||||
assert invalid.get_json() == {"error": "invalid expiry"}
|
||||
assert missing.status_code == 404
|
||||
assert revoked.get_json() == {"success": True}
|
||||
listKeys.assert_called_once_with("user-1")
|
||||
createKey.assert_has_calls(
|
||||
[
|
||||
call("user-1", "new", expiresAt="2099-01-01T00:00:00Z"),
|
||||
call("user-1", "bad", expiresAt=None),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_user_profile_and_username_routes_enforce_ownership(monkeypatch, apiClient):
|
||||
monkeypatch.setattr(
|
||||
apiMain.auth,
|
||||
"authenticateBearerToken",
|
||||
MagicMock(return_value=_userPrincipal()),
|
||||
)
|
||||
getUser = MagicMock(
|
||||
side_effect=[
|
||||
{
|
||||
"id": "user-1",
|
||||
"username": "alice",
|
||||
"password_hashed": b"secret-hash",
|
||||
},
|
||||
None,
|
||||
]
|
||||
)
|
||||
getUserUUID = MagicMock(side_effect=["user-1", "user-2", False])
|
||||
monkeypatch.setattr(apiMain.users, "getUser", getUser)
|
||||
monkeypatch.setattr(apiMain.users, "getUserUUID", getUserUUID)
|
||||
|
||||
foreign = apiClient.get("/api/user/user-2", headers=_bearer())
|
||||
own = apiClient.get("/api/user/user-1", headers=_bearer())
|
||||
missing = apiClient.get("/api/user/user-1", headers=_bearer())
|
||||
username = apiClient.get("/api/getUserUUID/alice", headers=_bearer())
|
||||
foreignUsername = apiClient.get("/api/getUserUUID/bob", headers=_bearer())
|
||||
missingUsername = apiClient.get("/api/getUserUUID/missing", headers=_bearer())
|
||||
|
||||
assert foreign.status_code == 403
|
||||
assert getUser.call_count == 2
|
||||
assert own.status_code == 200
|
||||
assert own.get_json() == {"id": "user-1", "username": "alice"}
|
||||
assert missing.status_code == 404
|
||||
assert username.get_json() == "user-1"
|
||||
assert foreignUsername.status_code == 403
|
||||
assert missingUsername.status_code == 404
|
||||
|
||||
|
||||
def test_user_update_and_delete_success_and_errors(monkeypatch, apiClient):
|
||||
monkeypatch.setattr(
|
||||
apiMain.auth,
|
||||
"authenticateBearerToken",
|
||||
MagicMock(return_value=_userPrincipal()),
|
||||
)
|
||||
updateUser = MagicMock(side_effect=[ValueError("bad timezone"), False, True])
|
||||
unregisterUser = MagicMock(side_effect=[False, True])
|
||||
monkeypatch.setattr(apiMain.users, "updateUser", updateUser)
|
||||
monkeypatch.setattr(apiMain.auth, "unregisterUser", unregisterUser)
|
||||
|
||||
assert apiClient.put(
|
||||
"/api/user/user-2", headers=_bearer(), json={"timezone": "UTC"}
|
||||
).status_code == 403
|
||||
assert apiClient.put(
|
||||
"/api/user/user-1", headers=_bearer(), json=[]
|
||||
).status_code == 400
|
||||
invalid = apiClient.put(
|
||||
"/api/user/user-1", headers=_bearer(), json={"timezone": "Invalid"}
|
||||
)
|
||||
empty = apiClient.put(
|
||||
"/api/user/user-1", headers=_bearer(), json={"username": "ignored"}
|
||||
)
|
||||
updated = apiClient.put(
|
||||
"/api/user/user-1", headers=_bearer(), json={"timezone": "UTC"}
|
||||
)
|
||||
missingPassword = apiClient.delete(
|
||||
"/api/user/user-1", headers=_bearer(), json={}
|
||||
)
|
||||
wrongPassword = apiClient.delete(
|
||||
"/api/user/user-1", headers=_bearer(), json={"password": "wrong"}
|
||||
)
|
||||
deleted = apiClient.delete(
|
||||
"/api/user/user-1", headers=_bearer(), json={"password": "correct"}
|
||||
)
|
||||
|
||||
assert invalid.get_json() == {"error": "bad timezone"}
|
||||
assert empty.get_json() == {"error": "no valid fields to update"}
|
||||
assert updated.get_json() == {"success": True}
|
||||
assert missingPassword.status_code == 400
|
||||
assert wrongPassword.status_code == 401
|
||||
assert deleted.get_json() == {"success": True}
|
||||
|
||||
|
||||
def test_timezone_routes_read_validate_and_update(monkeypatch, apiClient):
|
||||
monkeypatch.setattr(
|
||||
apiMain.auth,
|
||||
"authenticateBearerToken",
|
||||
MagicMock(return_value=_userPrincipal()),
|
||||
)
|
||||
normalize = MagicMock(
|
||||
side_effect=["America/Chicago", ValueError("invalid timezone")]
|
||||
)
|
||||
updateUser = MagicMock(return_value=True)
|
||||
monkeypatch.setattr(
|
||||
apiMain.users,
|
||||
"getUserTimezone",
|
||||
MagicMock(return_value="UTC"),
|
||||
)
|
||||
monkeypatch.setattr(apiMain.users, "normalizeTimezone", normalize)
|
||||
monkeypatch.setattr(apiMain.users, "updateUser", updateUser)
|
||||
|
||||
current = apiClient.get("/api/user/me/timezone", headers=_bearer())
|
||||
updated = apiClient.put(
|
||||
"/api/user/me/timezone",
|
||||
headers=_bearer(),
|
||||
json={"timezone": "America/Chicago"},
|
||||
)
|
||||
invalid = apiClient.put(
|
||||
"/api/user/me/timezone",
|
||||
headers=_bearer(),
|
||||
json={"timezone": "Invalid"},
|
||||
)
|
||||
|
||||
assert current.get_json() == {"timezone": "UTC"}
|
||||
assert updated.get_json() == {"timezone": "America/Chicago"}
|
||||
assert invalid.status_code == 400
|
||||
assert invalid.get_json() == {"error": "invalid timezone"}
|
||||
updateUser.assert_called_once_with(
|
||||
"user-1",
|
||||
{"timezone": "America/Chicago"},
|
||||
)
|
||||
|
||||
|
||||
def test_outbox_claim_filters_missing_discord_identities(monkeypatch, apiClient):
|
||||
monkeypatch.setattr(
|
||||
apiMain.auth,
|
||||
"authenticateBearerToken",
|
||||
MagicMock(return_value=_servicePrincipal("outbox:claim")),
|
||||
)
|
||||
claimMessages = MagicMock(
|
||||
side_effect=[
|
||||
ValueError("invalid limit"),
|
||||
[
|
||||
{
|
||||
"id": "message-1",
|
||||
"user_uuid": "user-1",
|
||||
"payload": {"content": "hello"},
|
||||
"attempts": 1,
|
||||
},
|
||||
{
|
||||
"id": "message-2",
|
||||
"user_uuid": "user-2",
|
||||
"payload": {},
|
||||
"attempts": 2,
|
||||
},
|
||||
],
|
||||
]
|
||||
)
|
||||
identities = MagicMock(
|
||||
side_effect=[
|
||||
[{"provider": "discord", "provider_user_id": "discord-1"}],
|
||||
[],
|
||||
]
|
||||
)
|
||||
retryMessage = MagicMock()
|
||||
monkeypatch.setattr(apiMain.outbox, "claim_messages", claimMessages)
|
||||
monkeypatch.setattr(apiMain.identity, "listUserIdentities", identities)
|
||||
monkeypatch.setattr(apiMain.outbox, "retry_message", retryMessage)
|
||||
|
||||
missingWorker = apiClient.post(
|
||||
"/api/internal/outbox/claim",
|
||||
headers=_bearer(),
|
||||
json={},
|
||||
)
|
||||
invalid = apiClient.post(
|
||||
"/api/internal/outbox/claim",
|
||||
headers=_bearer(),
|
||||
json={"worker_id": "worker-1", "limit": 0},
|
||||
)
|
||||
claimed = apiClient.post(
|
||||
"/api/internal/outbox/claim",
|
||||
headers=_bearer(),
|
||||
json={"worker_id": "worker-1", "channel": "discord_dm", "limit": 2},
|
||||
)
|
||||
|
||||
assert missingWorker.status_code == 400
|
||||
assert invalid.get_json() == {"error": "invalid limit"}
|
||||
assert claimed.get_json() == {
|
||||
"messages": [
|
||||
{
|
||||
"id": "message-1",
|
||||
"provider_user_id": "discord-1",
|
||||
"content": "hello",
|
||||
"attempts": 1,
|
||||
"worker_id": "worker-1",
|
||||
}
|
||||
]
|
||||
}
|
||||
retryMessage.assert_called_once_with(
|
||||
"message-2",
|
||||
"worker-1",
|
||||
"user has no Discord identity",
|
||||
)
|
||||
|
||||
|
||||
def test_outbox_result_enforces_lease_owner_and_records_results(
|
||||
monkeypatch, apiClient
|
||||
):
|
||||
monkeypatch.setattr(
|
||||
apiMain.auth,
|
||||
"authenticateBearerToken",
|
||||
MagicMock(return_value=_servicePrincipal("outbox:deliver")),
|
||||
)
|
||||
leased = {"id": "message-1", "status": "delivering", "leased_by": "worker-1"}
|
||||
getMessage = MagicMock(side_effect=[None, leased, leased, leased])
|
||||
deliveredRecord = {"id": "message-1", "status": "delivered"}
|
||||
retryRecord = {"id": "message-1", "status": "pending"}
|
||||
markDelivered = MagicMock(return_value=deliveredRecord)
|
||||
retryMessage = MagicMock(return_value=retryRecord)
|
||||
monkeypatch.setattr(apiMain.outbox, "get_message", getMessage)
|
||||
monkeypatch.setattr(apiMain.outbox, "mark_delivered", markDelivered)
|
||||
monkeypatch.setattr(apiMain.outbox, "retry_message", retryMessage)
|
||||
|
||||
invalid = apiClient.post(
|
||||
"/api/internal/outbox/message-1/result",
|
||||
headers=_bearer(),
|
||||
json={"status": "unknown"},
|
||||
)
|
||||
missing = apiClient.post(
|
||||
"/api/internal/outbox/message-1/result",
|
||||
headers=_bearer(),
|
||||
json={"status": "sent", "worker_id": "worker-1"},
|
||||
)
|
||||
wrongWorker = apiClient.post(
|
||||
"/api/internal/outbox/message-1/result",
|
||||
headers=_bearer(),
|
||||
json={"status": "sent", "worker_id": "worker-2"},
|
||||
)
|
||||
delivered = apiClient.post(
|
||||
"/api/internal/outbox/message-1/result",
|
||||
headers=_bearer(),
|
||||
json={
|
||||
"status": "sent",
|
||||
"worker_id": "worker-1",
|
||||
"external_message_id": "x" * 300,
|
||||
},
|
||||
)
|
||||
retried = apiClient.post(
|
||||
"/api/internal/outbox/message-1/result",
|
||||
headers=_bearer(),
|
||||
json={
|
||||
"status": "retry",
|
||||
"worker_id": "worker-1",
|
||||
"error": "temporary failure",
|
||||
},
|
||||
)
|
||||
|
||||
assert invalid.status_code == 400
|
||||
assert missing.status_code == 404
|
||||
assert wrongWorker.status_code == 409
|
||||
assert delivered.get_json()["message"] == deliveredRecord
|
||||
assert retried.get_json()["message"] == retryRecord
|
||||
markDelivered.assert_called_once_with(
|
||||
"message-1",
|
||||
"worker-1",
|
||||
external_message_id="x" * 255,
|
||||
)
|
||||
retryMessage.assert_called_once_with(
|
||||
"message-1",
|
||||
"worker-1",
|
||||
"temporary failure",
|
||||
)
|
||||
|
||||
|
||||
def test_health_and_standard_error_responses(monkeypatch, apiClient):
|
||||
execute = MagicMock(side_effect=[[{"ready": 1}], RuntimeError("offline")])
|
||||
monkeypatch.setattr(apiMain.postgres, "execute", execute)
|
||||
|
||||
live = apiClient.get("/health/live")
|
||||
ready = apiClient.get("/health/ready")
|
||||
unavailable = apiClient.get("/health")
|
||||
missing = apiClient.get("/missing")
|
||||
wrongMethod = apiClient.get("/api/register")
|
||||
|
||||
assert live.get_json() == {"status": "ok"}
|
||||
assert ready.get_json() == {"status": "ready"}
|
||||
assert unavailable.status_code == 503
|
||||
assert unavailable.get_json() == {"status": "not ready"}
|
||||
assert missing.status_code == 404
|
||||
assert missing.get_json() == {"error": "not found"}
|
||||
assert wrongMethod.status_code == 405
|
||||
assert wrongMethod.get_json() == {"error": "method not allowed"}
|
||||
Reference in New Issue
Block a user