atlas-iac/testing/tests/test_hermes_scm_broker_client_coverage.py

161 lines
4.7 KiB
Python
Raw Normal View History

"""Behavioral coverage for the credential-free SCM broker client and I/O."""
from __future__ import annotations
import io
import json
import pytest
from testing.tests.test_hermes_scm_broker_support import Response, _load
def test_client_default_opener_uses_deadline_bounded_exchange(monkeypatch):
client = _load("scm_broker_client")
sentinel = object()
monkeypatch.setattr(
client.deadline_http,
"open_bounded",
lambda request, *, maximum, timeout: (request, maximum, timeout, sentinel),
)
assert client._open("request", 4) == (
"request",
client.MAX_RESPONSE_BYTES,
4,
sentinel,
)
@pytest.mark.parametrize(
("endpoint", "payload", "match"),
[
("/v1/admin", {}, "outside"),
("/v1/metadata", {"path": "x" * (64 * 1024)}, "safe size"),
],
)
def test_client_rejects_unknown_or_oversized_operations(endpoint, payload, match):
client = _load("scm_broker_client")
with pytest.raises(client.PolicyError, match=match):
client.request(endpoint, payload, opener=lambda *_a, **_k: None)
class _GetCodeResponse(Response):
def __init__(self, body: bytes, *, content_type="application/json", code=200):
super().__init__(body, content_type=content_type)
del self.status
self._code = code
def getcode(self):
return self._code
def test_client_accepts_getcode_fallback_and_create_payload():
client = _load("scm_broker_client")
seen = []
def opener(request, timeout):
seen.append((request, timeout))
return _GetCodeResponse(b"{}")
result = client.create_draft(
"cassandra",
base="main",
head="feature/coverage",
head_sha="a" * 40,
title="WIP: Coverage",
body="Review evidence",
opener=opener,
)
assert result == b"{}"
assert json.loads(seen[0][0].data)["repo"] == "cassandra"
@pytest.mark.parametrize(
("response", "match"),
[
(_GetCodeResponse(b"{}", code=201), "HTTP status"),
(_GetCodeResponse(b"{}", content_type="text/plain"), "response type"),
(
Response(b"x" * (2 * 1024 * 1024 + 1), content_type="application/json"),
"safe size",
),
(Response(b"not-json", content_type="application/json"), "Expecting value"),
],
)
def test_client_rejects_bad_status_type_size_and_json(response, match):
client = _load("scm_broker_client")
expected = (
json.JSONDecodeError if match == "Expecting value" else client.PolicyError
)
with pytest.raises(expected, match=match):
client.request(
"/v1/metadata",
{"path": "/api/v1/repos/titan/cassandra"},
opener=lambda *_a, **_k: response,
)
class _Socket:
def __init__(self):
self.timeouts = []
def settimeout(self, value):
self.timeouts.append(value)
class _Stream:
def __init__(self, body: bytes, socket_shape: str = "direct"):
self.body = io.BytesIO(body)
self.socket = _Socket()
if socket_shape == "nested":
self.fp = type(
"FP", (), {"raw": type("Raw", (), {"_sock": self.socket})()}
)()
elif socket_shape == "direct":
self.fp = type("FP", (), {"raw": self.socket})()
else:
self.fp = None
def read(self, size):
return self.body.read(size)
def test_response_spool_handles_direct_and_absent_socket_shapes():
module = _load("scm_broker_io")
for shape in ("direct", "absent"):
stream = _Stream(b"safe", shape)
spool, length = module.spool_response(
stream,
16,
(b"forbidden",),
memory_limit=8,
chunk_size=2,
deadline_seconds=5,
)
try:
assert length == 4 and spool.read() == b"safe"
finally:
spool.close()
@pytest.mark.parametrize("failure", ["deadline", "size", "credential", "read"])
def test_response_spool_closes_and_rejects_each_failure(monkeypatch, failure):
module = _load("scm_broker_io")
stream = _Stream(b"safe-forbidden-value")
kwargs = {
"maximum": 128,
"forbidden": (b"forbidden",),
"memory_limit": 8,
"chunk_size": 5,
"deadline_seconds": 5,
}
if failure == "deadline":
ticks = iter((10.0, 16.0))
monkeypatch.setattr(module.time, "monotonic", lambda: next(ticks))
elif failure == "size":
kwargs["maximum"] = 2
elif failure == "read":
stream.read = lambda _size: (_ for _ in ()).throw(OSError("read failed"))
expected = module.PolicyError if failure != "read" else OSError
with pytest.raises(expected):
module.spool_response(stream, **kwargs)