Files
KF-OnlineJudge/backend/tests/test_availability.py
T

89 lines
3.0 KiB
Python

"""Contract/admission-route tests; Root file/atomicity and drain acceptance are VM-only."""
import asyncio
import json
from pathlib import Path
from types import SimpleNamespace
import pytest
from jsonschema import Draft202012Validator
from kf_oj.api.availability import AvailabilityMiddleware, needs_open
from kf_oj.contracts.availability import AvailabilityRecord
@pytest.mark.parametrize("state", ["open", "draining", "quiescent"])
def test_availability_is_exactly_bound_to_a_known_version_and_state(state):
path = (
Path(__file__).resolve().parents[2]
/ "tests/fixtures/contracts/delivery-availability.valid.json"
)
value = json.loads(path.read_text())
value["state"] = state
assert AvailabilityRecord.model_validate(value).state == state
schema = path.parents[3] / "contracts/schemas/delivery-availability.v1.schema.json"
Draft202012Validator(json.loads(schema.read_text())).validate(value)
for key, replacement in (
("schema_version", True),
("release_id", "../release"),
("state", "always_allow"),
):
with pytest.raises(ValueError):
AvailabilityRecord.model_validate({**value, key: replacement})
@pytest.mark.parametrize(
"method,path,blocked",
[
("POST", "/api/v1/submissions", True),
("POST", "/api/v1/admin/problem-imports", True),
("POST", "/api/v1/admin/problems/publish", True),
("POST", "/api/v1/auth/login", True),
("GET", "/api/v1/internal/evaluations/artifacts", False),
("POST", "/api/v1/auth/logout", False),
("POST", "/api/v1/submissions/11111111-1111-4111-8111-111111111111/cancel", False),
(
"PUT",
"/api/v1/admin/problem-imports/11111111-1111-4111-8111-111111111111/archive",
False,
),
],
)
def test_drain_blocks_new_mutations_without_cutting_accepted_reads_and_cancel(
method, path, blocked
):
assert needs_open(method, path) is blocked
def test_missing_admission_record_rejects_before_body_or_domain_execution(monkeypatch):
# Transport policy only: this stub does not verify Root ownership or Linux file safety.
calls = []
messages = []
def missing(*_):
raise FileNotFoundError("record missing")
monkeypatch.setattr("kf_oj.api.availability.current", missing)
async def receive():
calls.append("body")
return {"type": "http.request", "body": b""}
async def domain(*_):
calls.append("domain")
async def send(message):
messages.append(message)
settings = SimpleNamespace(
environment="production", delivery_release_id=None, delivery_configuration_id=None
)
asyncio.run(
AvailabilityMiddleware(domain, settings=settings)(
{"type": "http", "method": "POST", "path": "/api/v1/submissions"}, receive, send
)
)
assert calls == []
assert messages[0]["status"] == 503
assert (b"cache-control", b"no-store") in messages[0]["headers"]