Skip to content

Commit 454b0e4

Browse files
test(cli): add failing tests for env list hosts
1 parent a218dea commit 454b0e4

File tree

1 file changed

+257
-0
lines changed

1 file changed

+257
-0
lines changed

tests/integration/cli/test_cli_reporter_integration.py

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,3 +965,260 @@ def get_strategy_side_effect(host_type):
965965
# claude-desktop should appear before cursor (alphabetically)
966966
assert claude_pos < cursor_pos, \
967967
"Hosts should be sorted alphabetically (claude-desktop before cursor)"
968+
969+
970+
class TestEnvListHostsCommand:
971+
"""Integration tests for env list hosts command.
972+
973+
Reference: R10 §3.3 (10-namespace_consistency_specification_v2.md)
974+
975+
These tests verify that handle_env_list_hosts:
976+
1. Reads from environment data (Hatch-managed packages only)
977+
2. Shows environment/host/server deployments with columns: Environment → Host → Server → Version
978+
3. Supports --env and --server filters (regex patterns)
979+
4. First column (Environment) sorted alphabetically
980+
"""
981+
982+
def test_env_list_hosts_uniform_output(self):
983+
"""Command should produce uniform table output with Environment → Host → Server → Version columns.
984+
985+
Reference: R10 §3.3 - Column order matches command structure
986+
"""
987+
from hatch.cli.cli_env import handle_env_list_hosts
988+
989+
mock_env_manager = MagicMock()
990+
mock_env_manager.list_environments.return_value = [
991+
{"name": "default", "is_current": True},
992+
{"name": "dev", "is_current": False},
993+
]
994+
mock_env_manager.get_environment_data.side_effect = lambda env_name: {
995+
"default": {
996+
"packages": [
997+
{
998+
"name": "weather-server",
999+
"version": "1.0.0",
1000+
"configured_hosts": {
1001+
"claude-desktop": {"configured_at": "2026-01-30"},
1002+
"cursor": {"configured_at": "2026-01-30"},
1003+
}
1004+
}
1005+
]
1006+
},
1007+
"dev": {
1008+
"packages": [
1009+
{
1010+
"name": "test-server",
1011+
"version": "0.1.0",
1012+
"configured_hosts": {
1013+
"claude-desktop": {"configured_at": "2026-01-30"},
1014+
}
1015+
}
1016+
]
1017+
},
1018+
}.get(env_name, {"packages": []})
1019+
1020+
args = Namespace(
1021+
env_manager=mock_env_manager,
1022+
env=None,
1023+
server=None,
1024+
json=False,
1025+
)
1026+
1027+
captured_output = io.StringIO()
1028+
with patch('sys.stdout', captured_output):
1029+
result = handle_env_list_hosts(args)
1030+
1031+
output = captured_output.getvalue()
1032+
1033+
# Verify column headers present
1034+
assert "Environment" in output, "Environment column should be present"
1035+
assert "Host" in output, "Host column should be present"
1036+
assert "Server" in output, "Server column should be present"
1037+
assert "Version" in output, "Version column should be present"
1038+
1039+
# Verify data appears
1040+
assert "default" in output, "default environment should appear"
1041+
assert "dev" in output, "dev environment should appear"
1042+
assert "weather-server" in output, "weather-server should appear"
1043+
assert "test-server" in output, "test-server should appear"
1044+
1045+
def test_env_list_hosts_env_filter_exact(self):
1046+
"""--env flag with exact name should filter to matching environment only.
1047+
1048+
Reference: R10 §3.3 - --env <pattern> filter
1049+
"""
1050+
from hatch.cli.cli_env import handle_env_list_hosts
1051+
1052+
mock_env_manager = MagicMock()
1053+
mock_env_manager.list_environments.return_value = [
1054+
{"name": "default"},
1055+
{"name": "dev"},
1056+
]
1057+
mock_env_manager.get_environment_data.side_effect = lambda env_name: {
1058+
"default": {
1059+
"packages": [
1060+
{"name": "server-a", "version": "1.0.0", "configured_hosts": {"claude-desktop": {}}}
1061+
]
1062+
},
1063+
"dev": {
1064+
"packages": [
1065+
{"name": "server-b", "version": "0.1.0", "configured_hosts": {"claude-desktop": {}}}
1066+
]
1067+
},
1068+
}.get(env_name, {"packages": []})
1069+
1070+
args = Namespace(
1071+
env_manager=mock_env_manager,
1072+
env="default", # Exact match filter
1073+
server=None,
1074+
json=False,
1075+
)
1076+
1077+
captured_output = io.StringIO()
1078+
with patch('sys.stdout', captured_output):
1079+
result = handle_env_list_hosts(args)
1080+
1081+
output = captured_output.getvalue()
1082+
1083+
# Matching environment should appear
1084+
assert "server-a" in output, "server-a from default should appear"
1085+
1086+
# Non-matching environment should NOT appear
1087+
assert "server-b" not in output, "server-b from dev should NOT appear"
1088+
1089+
def test_env_list_hosts_env_filter_pattern(self):
1090+
"""--env flag with regex pattern should filter matching environments.
1091+
1092+
Reference: R10 §3.3 - --env accepts regex patterns
1093+
"""
1094+
from hatch.cli.cli_env import handle_env_list_hosts
1095+
1096+
mock_env_manager = MagicMock()
1097+
mock_env_manager.list_environments.return_value = [
1098+
{"name": "default"},
1099+
{"name": "dev"},
1100+
{"name": "dev-staging"},
1101+
]
1102+
mock_env_manager.get_environment_data.side_effect = lambda env_name: {
1103+
"default": {
1104+
"packages": [
1105+
{"name": "server-a", "version": "1.0.0", "configured_hosts": {"claude-desktop": {}}}
1106+
]
1107+
},
1108+
"dev": {
1109+
"packages": [
1110+
{"name": "server-b", "version": "0.1.0", "configured_hosts": {"claude-desktop": {}}}
1111+
]
1112+
},
1113+
"dev-staging": {
1114+
"packages": [
1115+
{"name": "server-c", "version": "0.2.0", "configured_hosts": {"claude-desktop": {}}}
1116+
]
1117+
},
1118+
}.get(env_name, {"packages": []})
1119+
1120+
args = Namespace(
1121+
env_manager=mock_env_manager,
1122+
env="dev.*", # Regex pattern
1123+
server=None,
1124+
json=False,
1125+
)
1126+
1127+
captured_output = io.StringIO()
1128+
with patch('sys.stdout', captured_output):
1129+
result = handle_env_list_hosts(args)
1130+
1131+
output = captured_output.getvalue()
1132+
1133+
# Matching environments should appear
1134+
assert "server-b" in output, "server-b from dev should appear"
1135+
assert "server-c" in output, "server-c from dev-staging should appear"
1136+
1137+
# Non-matching environment should NOT appear
1138+
assert "server-a" not in output, "server-a from default should NOT appear"
1139+
1140+
def test_env_list_hosts_server_filter(self):
1141+
"""--server flag should filter by server name regex.
1142+
1143+
Reference: R10 §3.3 - --server <pattern> filter
1144+
"""
1145+
from hatch.cli.cli_env import handle_env_list_hosts
1146+
1147+
mock_env_manager = MagicMock()
1148+
mock_env_manager.list_environments.return_value = [{"name": "default"}]
1149+
mock_env_manager.get_environment_data.return_value = {
1150+
"packages": [
1151+
{"name": "weather-server", "version": "1.0.0", "configured_hosts": {"claude-desktop": {}}},
1152+
{"name": "fetch-server", "version": "2.0.0", "configured_hosts": {"claude-desktop": {}}},
1153+
{"name": "custom-tool", "version": "0.5.0", "configured_hosts": {"claude-desktop": {}}},
1154+
]
1155+
}
1156+
1157+
args = Namespace(
1158+
env_manager=mock_env_manager,
1159+
env=None,
1160+
server=".*-server", # Regex pattern
1161+
json=False,
1162+
)
1163+
1164+
captured_output = io.StringIO()
1165+
with patch('sys.stdout', captured_output):
1166+
result = handle_env_list_hosts(args)
1167+
1168+
output = captured_output.getvalue()
1169+
1170+
# Matching servers should appear
1171+
assert "weather-server" in output, "weather-server should match pattern"
1172+
assert "fetch-server" in output, "fetch-server should match pattern"
1173+
1174+
# Non-matching server should NOT appear
1175+
assert "custom-tool" not in output, "custom-tool should NOT match pattern"
1176+
1177+
def test_env_list_hosts_combined_filters(self):
1178+
"""Combined --env and --server filters should work with AND logic.
1179+
1180+
Reference: R10 §1.5 - Combined filters
1181+
"""
1182+
from hatch.cli.cli_env import handle_env_list_hosts
1183+
1184+
mock_env_manager = MagicMock()
1185+
mock_env_manager.list_environments.return_value = [
1186+
{"name": "default"},
1187+
{"name": "dev"},
1188+
]
1189+
mock_env_manager.get_environment_data.side_effect = lambda env_name: {
1190+
"default": {
1191+
"packages": [
1192+
{"name": "weather-server", "version": "1.0.0", "configured_hosts": {"claude-desktop": {}}},
1193+
{"name": "fetch-server", "version": "2.0.0", "configured_hosts": {"claude-desktop": {}}},
1194+
]
1195+
},
1196+
"dev": {
1197+
"packages": [
1198+
{"name": "weather-server", "version": "0.9.0", "configured_hosts": {"claude-desktop": {}}},
1199+
]
1200+
},
1201+
}.get(env_name, {"packages": []})
1202+
1203+
args = Namespace(
1204+
env_manager=mock_env_manager,
1205+
env="default",
1206+
server="weather.*",
1207+
json=False,
1208+
)
1209+
1210+
captured_output = io.StringIO()
1211+
with patch('sys.stdout', captured_output):
1212+
result = handle_env_list_hosts(args)
1213+
1214+
output = captured_output.getvalue()
1215+
1216+
# Only weather-server from default should appear
1217+
assert "weather-server" in output, "weather-server from default should appear"
1218+
assert "1.0.0" in output, "Version 1.0.0 should appear"
1219+
1220+
# fetch-server should NOT appear (doesn't match server filter)
1221+
assert "fetch-server" not in output, "fetch-server should NOT appear"
1222+
1223+
# dev environment should NOT appear (doesn't match env filter)
1224+
assert "0.9.0" not in output, "Version 0.9.0 from dev should NOT appear"

0 commit comments

Comments
 (0)