feat(infra): auto-commit con 88 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
"""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,
|
||||
)
|
||||
Reference in New Issue
Block a user