Files
fn_registry/python/functions/infra/hoppscotch_set_environment_test.py
T
egutierrez eb8dbf66a1 feat(infra): auto-commit con 88 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-11 00:16:46 +02:00

227 lines
7.0 KiB
Python

"""Tests para hoppscotch_set_environment.
Deterministas: monkeypatchean requests.post (capa de red) y pass_get_secret.
Verifican crear vs actualizar (idempotencia por nombre), resolucion de secretos
`pass:` (fuerza secret=True, key en resolved_secrets, valor crudo fuera del
output), y abortar sin llamar la mutation si pass falla.
Hay un test e2e real marcado skip por defecto (self-host vivo).
"""
import json
import sys
import pytest
import infra.hoppscotch_set_environment # noqa: F401
# El __init__ rebinds el nombre a la funcion; recuperamos el submodulo real.
mod = sys.modules["infra.hoppscotch_set_environment"]
class _FakeResponse:
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 _make_post(call_log, list_envs, mutation_result):
"""Construye un fake requests.post que distingue listado de mutation.
El listado lleva 'teamEnvironments' en la query; cualquier otra es mutation.
Cada llamada se registra en call_log para inspeccion.
"""
def fake_post(url, **kwargs):
payload = kwargs["json"]
query = payload["query"]
call_log.append({"url": url, "kwargs": kwargs, "query": query})
if "teamEnvironments" in query:
return _FakeResponse(
200, {"data": {"team": {"teamEnvironments": list_envs}}}
)
# mutation (create o update)
field = (
"updateTeamEnvironment"
if "updateTeamEnvironment" in query
else "createTeamEnvironment"
)
return _FakeResponse(200, {"data": {field: mutation_result}})
return fake_post
def test_crea_cuando_no_existe(monkeypatch):
calls = []
monkeypatch.setattr(
mod.requests,
"post",
_make_post(calls, list_envs=[], mutation_result={"id": "env-1", "name": "test_env"}),
)
result = mod.hoppscotch_set_environment(
"team-1",
"test_env",
[{"key": "foo", "value": "bar", "secret": False}],
access_token="ACCESS-JWT",
)
assert result["status"] == "ok"
assert result["id"] == "env-1"
assert result["action"] == "created"
assert result["resolved_secrets"] == []
# Segunda llamada = mutation createTeamEnvironment con las variables.
mutation = calls[1]
assert "createTeamEnvironment" in mutation["query"]
assert mutation["kwargs"]["cookies"] == {"access_token": "ACCESS-JWT"}
gql_vars = mutation["kwargs"]["json"]["variables"]
assert gql_vars["n"] == "test_env"
assert gql_vars["t"] == "team-1"
sent_vars = json.loads(gql_vars["v"])
assert sent_vars == [{"key": "foo", "value": "bar", "secret": False}]
def test_actualiza_cuando_existe(monkeypatch):
calls = []
monkeypatch.setattr(
mod.requests,
"post",
_make_post(
calls,
list_envs=[{"id": "env-existing", "name": "test_env"}],
mutation_result={"id": "env-existing", "name": "test_env"},
),
)
result = mod.hoppscotch_set_environment(
"team-1",
"test_env",
[{"key": "foo", "value": "bar"}],
access_token="A",
)
assert result["status"] == "ok"
assert result["id"] == "env-existing"
assert result["action"] == "updated"
mutation = calls[1]
assert "updateTeamEnvironment" in mutation["query"]
gql_vars = mutation["kwargs"]["json"]["variables"]
assert gql_vars["id"] == "env-existing"
def test_resuelve_secreto_desde_pass(monkeypatch):
calls = []
monkeypatch.setattr(
mod.requests,
"post",
_make_post(calls, list_envs=[], mutation_result={"id": "env-2", "name": "e"}),
)
def fake_pass(path, **kwargs):
assert path == "apis/lpd"
return {"status": "ok", "value": "TOP-SECRET-VALUE"}
monkeypatch.setattr(mod, "pass_get_secret", fake_pass)
result = mod.hoppscotch_set_environment(
"team-1",
"e",
[
{"key": "plain", "value": "visible", "secret": False},
{"key": "apikey", "value": "pass:apis/lpd", "secret": False},
],
access_token="A",
)
assert result["status"] == "ok"
# El key resuelto aparece, pero NUNCA el valor crudo.
assert result["resolved_secrets"] == ["apikey"]
assert "TOP-SECRET-VALUE" not in json.dumps(result)
# La variable resuelta viaja con el valor real y secret=True forzado.
mutation = calls[1]
sent_vars = json.loads(mutation["kwargs"]["json"]["variables"]["v"])
by_key = {v["key"]: v for v in sent_vars}
assert by_key["apikey"]["value"] == "TOP-SECRET-VALUE"
assert by_key["apikey"]["secret"] is True
assert by_key["plain"]["value"] == "visible"
assert by_key["plain"]["secret"] is False
def test_error_pass_no_llama_mutation(monkeypatch):
calls = []
def fake_post(url, **kwargs):
calls.append(kwargs["json"]["query"])
return _FakeResponse(200, {"data": {"team": {"teamEnvironments": []}}})
monkeypatch.setattr(mod.requests, "post", fake_post)
def fake_pass(path, **kwargs):
return {"status": "error", "error": "pass not installed"}
monkeypatch.setattr(mod, "pass_get_secret", fake_pass)
result = mod.hoppscotch_set_environment(
"team-1",
"e",
[{"key": "apikey", "value": "pass:apis/lpd"}],
access_token="A",
)
assert result["status"] == "error"
assert "apikey" in result["error"]
# No se hizo ninguna llamada de red (ni listado ni mutation): aborta antes.
assert calls == []
@pytest.mark.skip(reason="e2e real contra self-host vivo")
def test_e2e_create_then_update_live():
"""End-to-end real contra el Hoppscotch self-host vivo.
login -> set_environment("test_env") -> created -> set_environment de nuevo
-> updated. Limpia el env al final con deleteTeamEnvironment.
"""
sys.path.insert(0, "python/functions")
from infra.hoppscotch_login import hoppscotch_login
import requests
team_id = "cmq8kn0v500030xls1nvminjy"
token = hoppscotch_login("admin@example.com")["access_token"]
first = mod.hoppscotch_set_environment(
team_id,
"test_env",
[{"key": "foo", "value": "bar", "secret": False}],
access_token=token,
)
assert first["status"] == "ok"
assert first["action"] == "created"
env_id = first["id"]
second = mod.hoppscotch_set_environment(
team_id,
"test_env",
[{"key": "foo", "value": "baz", "secret": False}],
access_token=token,
)
assert second["status"] == "ok"
assert second["action"] == "updated"
assert second["id"] == env_id
# Cleanup: borra el env de prueba.
del_q = "mutation($id:ID!){ deleteTeamEnvironment(id:$id) }"
requests.post(
"http://localhost:3170/graphql",
json={"query": del_q, "variables": {"id": env_id}},
cookies={"access_token": token},
timeout=15.0,
)