|
| 1 | +"""Unit tests for flyte.cli._proxy (the `flyte proxy app` command helpers).""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +import base64 |
| 6 | +import json |
| 7 | +from unittest.mock import MagicMock |
| 8 | + |
| 9 | +from flyte.cli._proxy import ( |
| 10 | + _emit_mcp_config, |
| 11 | + _filter_request_headers, |
| 12 | + _filter_response_headers, |
| 13 | + _identity, |
| 14 | +) |
| 15 | + |
| 16 | + |
| 17 | +def _jwt(claims: dict) -> str: |
| 18 | + def b64(obj: dict) -> str: |
| 19 | + return base64.urlsafe_b64encode(json.dumps(obj).encode()).rstrip(b"=").decode() |
| 20 | + |
| 21 | + return f"{b64({'alg': 'none'})}.{b64(claims)}.signature" |
| 22 | + |
| 23 | + |
| 24 | +class TestHeaderFiltering: |
| 25 | + def test_request_headers_drop_authorization_and_hop_by_hop(self): |
| 26 | + out = _filter_request_headers( |
| 27 | + { |
| 28 | + "Authorization": "Bearer inbound", |
| 29 | + "Host": "localhost:8600", |
| 30 | + "Connection": "keep-alive", |
| 31 | + "Content-Length": "10", |
| 32 | + "Accept": "*/*", |
| 33 | + "X-Custom": "keep", |
| 34 | + } |
| 35 | + ) |
| 36 | + lowered = {k.lower() for k in out} |
| 37 | + assert "authorization" not in lowered # inbound auth must never be forwarded |
| 38 | + assert "host" not in lowered |
| 39 | + assert "connection" not in lowered |
| 40 | + assert "content-length" not in lowered |
| 41 | + assert out["Accept"] == "*/*" |
| 42 | + assert out["X-Custom"] == "keep" |
| 43 | + |
| 44 | + def test_response_headers_drop_hop_by_hop_keep_others(self): |
| 45 | + out = _filter_response_headers( |
| 46 | + {"Transfer-Encoding": "chunked", "Content-Type": "application/json", "Content-Encoding": "gzip"} |
| 47 | + ) |
| 48 | + lowered = {k.lower() for k in out} |
| 49 | + assert "transfer-encoding" not in lowered |
| 50 | + assert out["Content-Type"] == "application/json" |
| 51 | + assert out["Content-Encoding"] == "gzip" # preserved for byte-verbatim streaming |
| 52 | + |
| 53 | + |
| 54 | +class TestIdentity: |
| 55 | + def test_prefers_email_claim(self): |
| 56 | + auth = MagicMock() |
| 57 | + auth.get_credentials.return_value = MagicMock(access_token=_jwt({"email": "me@union.ai", "sub": "abc"})) |
| 58 | + assert _identity(auth) == "me@union.ai" |
| 59 | + |
| 60 | + def test_falls_back_to_sub(self): |
| 61 | + auth = MagicMock() |
| 62 | + auth.get_credentials.return_value = MagicMock(access_token=_jwt({"sub": "subject-123"})) |
| 63 | + assert _identity(auth) == "subject-123" |
| 64 | + |
| 65 | + def test_unknown_when_no_credentials(self): |
| 66 | + auth = MagicMock() |
| 67 | + auth.get_credentials.return_value = None |
| 68 | + assert _identity(auth) == "<unknown>" |
| 69 | + |
| 70 | + |
| 71 | +class TestEmitMcpConfig: |
| 72 | + def test_emits_generic_http_block(self, capsys): |
| 73 | + _emit_mcp_config("grafana", "http://127.0.0.1:8600") |
| 74 | + block = json.loads(capsys.readouterr().out) |
| 75 | + assert block == {"mcpServers": {"grafana": {"type": "http", "url": "http://127.0.0.1:8600"}}} |
0 commit comments