Skip to content

Commit af3a5ee

Browse files
mhotanclaude
andcommitted
Add unit tests for flyte proxy app helpers
Cover the security-relevant header stripping (inbound Authorization and hop-by-hop headers are never forwarded; Content-Encoding is preserved for byte-verbatim streaming), the JWT identity display (email preferred, sub fallback, unknown without credentials), and the generic HTTP-MCP config block. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 340ebbd commit af3a5ee

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

tests/cli/test_proxy.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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

Comments
 (0)