DFiP_Budget_planing/perf/tests/test_compare_responses.py
2026-10-28 21:30:49 +04:00

134 lines
5.9 KiB
Python

"""Самопроверка compare_responses (без стенда)."""
import gzip
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "analysis"))
import compare_responses as cr # noqa: E402
def test_strip_nulls_pg_semantics():
v = {"a": None, "b": {"c": None}, "d": [None, {"e": None, "f": 1}], "g": {}}
assert cr.strip_nulls(v) == {"b": {}, "d": [None, {"f": 1}], "g": {}}
def test_strip_response_keeps_wrapper_and_sort_order():
body = {"success": True, "message": None, "count": 1,
"result": [{"row_type": "INPUT", "depth": 3, "sort_order": None, "data": {"x": None, "y": 1}}]}
out = cr.strip_response(body)
assert out["message"] is None
assert out["result"][0]["sort_order"] is None
assert out["result"][0]["data"] == {"y": 1}
assert body["result"][0]["data"] == {"x": None, "y": 1} # исходное не изменено
def test_first_diff_path():
a = {"result": [{"data": {"q1": {"m1": 1}}}]}
b = {"result": [{"data": {"q1": {"m1": 2}}}]}
assert cr.first_diff(a, b).startswith("$.result[0].data.q1.m1")
assert cr.first_diff(a, a) is None
assert cr.first_diff({"a": [1]}, {"a": [1, 2]}).startswith("$.a")
assert cr.first_diff({"a": 1}, {"a": 1, "b": 2}).startswith("$")
def _write(d: Path, name: str, body: dict, status: int = 200):
d.mkdir(parents=True, exist_ok=True)
(d / f"{name}.json.gz").write_bytes(gzip.compress(json.dumps(body).encode()))
idx = d / "index.json"
cur = json.loads(idx.read_text()) if idx.exists() else {}
cur[name] = status
idx.write_text(json.dumps(cur))
def _body(data):
return {"success": True, "message": None, "count": 1,
"result": [{"row_type": "INPUT", "depth": 1, "sort_order": 1, "data": data}]}
def test_compare_exact_and_strip_modes(tmp_path):
b, a = tmp_path / "before", tmp_path / "after"
for name in ("sheet-FORM_2-AHR-none-1", "report-5-2026-LIMIT"):
_write(b, name, _body({"x": None, "y": 1}))
_write(a, name, _body({"y": 1}))
n, bad = cr.compare(b, a, "exact")
assert n == 0 and len(bad) == 2
n, bad = cr.compare(b, a, "strip-nulls")
assert n == 1 and len(bad) == 1 and "report-5-2026-LIMIT" in bad[0]
def test_compare_status_mismatch(tmp_path):
b, a = tmp_path / "before", tmp_path / "after"
_write(b, "svod1-MAIN", _body({"y": 1}), 200)
_write(a, "svod1-MAIN", _body({"y": 1}), 500)
n, bad = cr.compare(b, a, "exact")
assert n == 0 and len(bad) == 1
def test_compare_missing_after(tmp_path):
b, a = tmp_path / "before", tmp_path / "after"
_write(b, "svod1-MAIN", _body({}))
a.mkdir()
n, bad = cr.compare(b, a, "exact")
assert n == 0 and len(bad) == 1
def test_build_requests_minimal_manifest():
man = {"sheets": [
{"form_id": 1, "form_type": "FORM_2", "sheet": "AHR", "direction": None, "year": 2026, "url": "", "rows": 1, "owner": "rf"},
{"form_id": 2, "form_type": "FORM_1", "sheet": "AHR", "direction": "Support", "year": 2026, "url": "", "rows": 1, "owner": "ssp"},
], "project_reports": [{"project_id": 5, "year": 2026, "report_type": "LIMIT", "url": "", "rows": 1}]}
reqs = cr.build_requests(man)
by = {n: (r, p) for r, n, p in reqs}
assert by["sheet-FORM_2-AHR-none-1"] == ("rf", "/api/v1/form/1/sheet/AHR")
assert by["sheet-FORM_2-AHR-none-1-sections"] == ("rf", "/api/v1/form/1/sheet/AHR?sections=plan,q1")
assert by["sheet-FORM_1-AHR-Support-2"] == ("dfip", "/api/v1/form/2/sheet/AHR?direction=Support")
assert sum(n.startswith("svod1-") for n in by) == 5
assert sum(n.startswith("svod2-") for n in by) == 2
assert by["svod1-MAIN"] == ("dfip", "/api/v1/svod/1/MAIN/2026")
assert by["report-5-2026-LIMIT"][0] == "rf"
assert by["project-summary-5"] == ("rf", "/api/v1/projects/5/summary")
assert {"projects-plan-summary", "projects-fact-summary", "projects-detail-summary"} <= set(by)
assert not any("rollup" in n for n in by)
assert cr.is_stripped("sheet-x") and cr.is_stripped("svod2-GO") and not cr.is_stripped("report-1")
def test_strip_response_without_result():
assert cr.strip_response({"success": False}) == {"success": False}
assert cr.strip_response({"result": None}) == {"result": None}
def test_compare_non_json_bodies(tmp_path):
b, a = tmp_path / "before", tmp_path / "after"
for name, bb, ab in (("svod1-A", b"<html>502", b"<html>502"), ("svod1-B", b"<html>502", b"<html>503"),
("svod1-C", b"<html>", b'{"result": []}')):
for d, raw in ((b, bb), (a, ab)):
d.mkdir(exist_ok=True)
(d / f"{name}.json.gz").write_bytes(gzip.compress(raw))
for d in (b, a):
(d / "index.json").write_text(json.dumps({"svod1-A": 502, "svod1-B": 502, "svod1-C": 502}))
n, bad = cr.compare(b, a, "strip-nulls")
assert n == 1 and len(bad) == 2
def test_dump_survives_network_error(tmp_path):
import httpx
def handler(req: httpx.Request) -> httpx.Response:
if req.url.path == "/api/v1/auth/login":
return httpx.Response(200, json={"access_token": "t"})
if req.url.path.startswith("/api/v1/svod/1/MAIN"):
raise httpx.ConnectError("boom", request=req)
return httpx.Response(200, json={"result": []})
man = {"users": {"rf": {"username": "u", "password": "p"}, "dfip": {"username": "u", "password": "p"}},
"sheets": [{"form_id": 1, "form_type": "FORM_2", "sheet": "AHR", "direction": None, "year": 2026,
"url": "", "rows": 1, "owner": "rf"}], "project_reports": []}
with httpx.Client(base_url="http://x", transport=httpx.MockTransport(handler)) as c:
idx = cr.dump(man, c, tmp_path / "o")
assert idx["svod1-MAIN"] == "error: ConnectError"
assert idx["sheet-FORM_2-AHR-none-1"] == 200
assert json.loads((tmp_path / "o" / "index.json").read_text()) == idx
assert not (tmp_path / "o" / "svod1-MAIN.json.gz").exists()