"""Tests para hoppscotch_run_request. Deterministas: monkeypatchean requests.request (ejecucion HTTP) y requests.post (mutation createUserHistory). Verifican la resolucion de placedores `<<>>`/`{{}}` para EJECUTAR, que el History recibe la request SIN resolver, y los caminos de record_history=False y de error de transporte. """ import json import sys from datetime import timedelta import infra.hoppscotch_run_request # noqa: F401 # El __init__ rebinds el nombre a la funcion; recuperamos el submodulo real. mod = sys.modules["infra.hoppscotch_run_request"] class _FakeResponse: """Respuesta minima para requests.request (ejecucion).""" def __init__(self, status_code=200, text="OK", headers=None, elapsed_ms=12): self.status_code = status_code self.text = text self.headers = headers or {"Content-Type": "text/plain"} self.elapsed = timedelta(milliseconds=elapsed_ms) class _FakeGraphQLResponse: """Respuesta minima para requests.post (createUserHistory).""" def __init__(self, status_code=200, json_data=None): self.status_code = status_code self._json = json_data def json(self): if self._json is None: raise ValueError("no json") return self._json def _patch_history_ok(monkeypatch, captured): def fake_post(url, **kwargs): captured["history_url"] = url captured["history_kwargs"] = kwargs return _FakeGraphQLResponse( 200, {"data": {"createUserHistory": {"id": "hist-42"}}} ) monkeypatch.setattr(mod.requests, "post", fake_post) def test_ejecuta_resolviendo_variables_angle(monkeypatch): captured = {} def fake_request(method, url, **kwargs): captured["method"] = method captured["url"] = url captured["kwargs"] = kwargs return _FakeResponse(200, text="pong") monkeypatch.setattr(mod.requests, "request", fake_request) _patch_history_ok(monkeypatch, captured) result = mod.hoppscotch_run_request( "GET", "<>/x", variables={"baseURL": "https://h"}, access_token="A", ) # La request ejecutada lleva la url resuelta. assert captured["url"] == "https://h/x" assert result["status"] == "ok" assert result["status_code"] == 200 assert result["response_body"] == "pong" assert result["duration_ms"] == 12 def test_ejecuta_resolviendo_variables_brace(monkeypatch): captured = {} def fake_request(method, url, **kwargs): captured["url"] = url return _FakeResponse(200) monkeypatch.setattr(mod.requests, "request", fake_request) _patch_history_ok(monkeypatch, captured) mod.hoppscotch_run_request( "GET", "{{baseURL}}/x", variables={"baseURL": "https://h"}, access_token="A", ) # {{var}} resuelve igual que <>. assert captured["url"] == "https://h/x" def test_record_history_registra_request_sin_resolver(monkeypatch): captured = {} def fake_request(method, url, **kwargs): return _FakeResponse(200, text="body", headers={"X-Test": "1"}) monkeypatch.setattr(mod.requests, "request", fake_request) _patch_history_ok(monkeypatch, captured) result = mod.hoppscotch_run_request( "GET", "<>/api/status", title="Status", variables={"baseURL": "https://h"}, access_token="ACCESS-JWT", record_history=True, ) assert result["recorded"] is True assert result["history_id"] == "hist-42" # El POST de History fue al endpoint GraphQL con la cookie access_token. assert captured["history_url"].endswith("/graphql") assert captured["history_kwargs"]["cookies"] == {"access_token": "ACCESS-JWT"} payload = captured["history_kwargs"]["json"] assert "createUserHistory" in payload["query"] variables = payload["variables"] assert variables["t"] == "REST" # reqData es el json string de un HoppRESTRequest v:"2" con la url SIN resolver. req = json.loads(variables["d"]) assert req["v"] == "2" assert req["method"] == "GET" assert req["endpoint"] == "<>/api/status" assert req["name"] == "Status" # resMetadata minimo: statusCode + duration. res_meta = json.loads(variables["m"]) assert res_meta == {"statusCode": 200, "duration": 12} def test_record_history_false_no_llama_create_user_history(monkeypatch): calls = {"post": 0} def fake_request(method, url, **kwargs): return _FakeResponse(200) def fake_post(url, **kwargs): calls["post"] += 1 return _FakeGraphQLResponse(200, {"data": {"createUserHistory": {"id": "x"}}}) monkeypatch.setattr(mod.requests, "request", fake_request) monkeypatch.setattr(mod.requests, "post", fake_post) result = mod.hoppscotch_run_request( "GET", "https://h/x", access_token="A", record_history=False, ) assert result["recorded"] is False assert result["history_id"] is None assert calls["post"] == 0 def test_request_exception_status_error(monkeypatch): def fake_request(method, url, **kwargs): raise mod.requests.RequestException("boom") # Si llegara a postear seria un fallo del test: no debe. def fake_post(url, **kwargs): raise AssertionError("no debe registrar history si la ejecucion fallo") monkeypatch.setattr(mod.requests, "request", fake_request) monkeypatch.setattr(mod.requests, "post", fake_post) result = mod.hoppscotch_run_request( "GET", "https://h/x", access_token="A" ) assert result["status"] == "error" assert result["recorded"] is False assert "boom" in result["error"] def test_variable_faltante_conserva_literal(monkeypatch): captured = {} def fake_request(method, url, **kwargs): captured["url"] = url return _FakeResponse(200) monkeypatch.setattr(mod.requests, "request", fake_request) _patch_history_ok(monkeypatch, captured) mod.hoppscotch_run_request( "GET", "<>/x", variables={"otra": "y"}, access_token="A", ) # baseURL no esta en variables -> el literal se conserva. assert captured["url"] == "<>/x"