Coverage for src/meshadmin/cli/tests/test_host.py: 100%
65 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-07 19:26 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-07 19:26 +0200
1import httpx
2import pytest
3from typer.testing import CliRunner
5from meshadmin.cli.main import app
7runner = CliRunner()
10@pytest.fixture
11def mock_host_access_token(mocker):
12 return mocker.patch(
13 "meshadmin.cli.commands.host.get_access_token", return_value="fake-token"
14 )
17@pytest.fixture
18def mock_host_context_config(mocker):
19 return mocker.patch(
20 "meshadmin.cli.commands.host.get_context_config",
21 return_value={"endpoint": "http://testserver"},
22 )
25@pytest.fixture
26def mock_download(mocker):
27 return mocker.patch("meshadmin.cli.commands.host.download")
30@pytest.fixture
31def mock_host_get_config_from_mesh(mocker):
32 return mocker.patch(
33 "meshadmin.cli.commands.host.get_config_from_mesh",
34 return_value=(
35 "pki: ca: test-ca-cert cert: test-host-cert key: host.key lighthouse: am_lighthouse: false hosts: - 10.0.0.1 static_host_map: '10.0.0.1': ['lighthouse.example.com:4242']",
36 5,
37 "false",
38 ),
39 )
42@pytest.fixture
43def test_context(temp_config_dir):
44 result = runner.invoke(
45 app,
46 [
47 "--config-path",
48 str(temp_config_dir),
49 "context",
50 "create",
51 "test-context-two",
52 "--endpoint",
53 "http://localhost:8001",
54 ],
55 )
56 return result
59@pytest.fixture
60def mock_enroll_response(mocker):
61 return mocker.patch(
62 "httpx.post",
63 return_value=httpx.Response(
64 status_code=200,
65 request=httpx.Request("POST", "http://testserver/api/v1/enroll"),
66 ),
67 )
70def test_host_enrollment(
71 mock_enroll_response,
72 temp_config_dir,
73 sample_context,
74 mock_download,
75 mock_host_get_config_from_mesh,
76):
77 result = runner.invoke(
78 app,
79 [
80 "--config-path",
81 str(temp_config_dir),
82 "host",
83 "enroll",
84 "test-enrollment-key",
85 "--preferred-hostname",
86 "test-host",
87 "--public-ip",
88 "192.168.1.100",
89 ],
90 )
91 assert result.exit_code == 0
92 network_dir = temp_config_dir / "networks" / "test-context"
93 auth_key_path = temp_config_dir / "auth.key"
94 public_key_path = network_dir / "host.pub"
95 private_key_path = network_dir / "host.key"
96 config_path = network_dir / "config.yaml"
97 assert network_dir.exists()
98 assert auth_key_path.exists()
99 assert public_key_path.exists()
100 assert private_key_path.exists()
101 assert config_path.exists()
102 mock_download.assert_called()
103 mock_host_get_config_from_mesh.assert_called()
104 assert "enrollment finished" in result.stdout
105 result = runner.invoke(
106 app,
107 [
108 "--config-path",
109 str(temp_config_dir),
110 "host",
111 "enroll",
112 "test-enrollment-key",
113 ],
114 )
115 assert result.exit_code == 0
116 assert "private and public nebula key already exists" in result.stdout
119def test_host_enrollment_shared_auth_key(
120 mock_enroll_response,
121 temp_config_dir,
122 sample_context,
123 test_context,
124 mock_download,
125 mock_host_get_config_from_mesh,
126):
127 result = runner.invoke(
128 app,
129 [
130 "--config-path",
131 str(temp_config_dir),
132 "--context",
133 "test-context",
134 "host",
135 "enroll",
136 "test-key",
137 ],
138 )
139 assert result.exit_code == 0
140 assert "enrollment finished" in result.stdout
141 auth_key_path = temp_config_dir / "auth.key"
142 original_auth_key = auth_key_path.read_text()
143 result = runner.invoke(
144 app,
145 [
146 "--config-path",
147 str(temp_config_dir),
148 "--context",
149 "test-context-two",
150 "host",
151 "enroll",
152 "test-key",
153 ],
154 )
155 assert result.exit_code == 0
156 assert "enrollment finished" in result.stdout
157 assert auth_key_path.read_text() == original_auth_key
160def test_delete_host_success(mocker, mock_host_access_token, mock_host_context_config):
161 mock_response = httpx.Response(
162 status_code=200,
163 json={"message": "Host test-host deleted"},
164 request=httpx.Request("DELETE", "http://testserver/api/v1/hosts/test-host"),
165 )
166 mock_delete = mocker.patch("httpx.delete", return_value=mock_response)
167 result = runner.invoke(app, ["host", "delete", "test-host"])
168 assert result.exit_code == 0
169 mock_delete.assert_called_once_with(
170 "http://testserver/api/v1/hosts/test-host",
171 headers={"Authorization": "Bearer fake-token"},
172 )
173 assert "deleted" in result.stdout.lower()
176def test_delete_host_auth_failure(mock_host_access_token, mock_host_context_config):
177 mock_host_access_token.side_effect = Exception("Auth failed")
178 result = runner.invoke(app, ["host", "delete", "test-host"])
179 assert result.exit_code == 1
180 assert "failed to get access token" in result.stdout