hap-cli 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hap_cli/README.md +194 -0
- hap_cli/README_CN.md +601 -0
- hap_cli/__init__.py +3 -0
- hap_cli/commands/__init__.py +1 -0
- hap_cli/commands/ai_cmd.py +224 -0
- hap_cli/commands/app_cmd.py +308 -0
- hap_cli/commands/calendar_cmd.py +138 -0
- hap_cli/commands/chat_cmd.py +101 -0
- hap_cli/commands/config_cmd.py +169 -0
- hap_cli/commands/contact_cmd.py +125 -0
- hap_cli/commands/department_cmd.py +168 -0
- hap_cli/commands/group_cmd.py +128 -0
- hap_cli/commands/instance_cmd.py +310 -0
- hap_cli/commands/node_cmd.py +538 -0
- hap_cli/commands/optionset_cmd.py +99 -0
- hap_cli/commands/page_cmd.py +102 -0
- hap_cli/commands/plugin_cmd.py +133 -0
- hap_cli/commands/post_cmd.py +155 -0
- hap_cli/commands/record_cmd.py +228 -0
- hap_cli/commands/role_cmd.py +221 -0
- hap_cli/commands/workflow_cmd.py +284 -0
- hap_cli/commands/worksheet_cmd.py +342 -0
- hap_cli/context.py +43 -0
- hap_cli/core/__init__.py +1 -0
- hap_cli/core/ai.py +133 -0
- hap_cli/core/app.py +307 -0
- hap_cli/core/auth.py +219 -0
- hap_cli/core/calendar_mod.py +114 -0
- hap_cli/core/chat.py +73 -0
- hap_cli/core/contact.py +85 -0
- hap_cli/core/department.py +131 -0
- hap_cli/core/flow_node.py +1001 -0
- hap_cli/core/group.py +99 -0
- hap_cli/core/instance.py +572 -0
- hap_cli/core/optionset.py +112 -0
- hap_cli/core/page.py +138 -0
- hap_cli/core/plugin.py +87 -0
- hap_cli/core/post.py +118 -0
- hap_cli/core/record.py +268 -0
- hap_cli/core/role.py +227 -0
- hap_cli/core/session.py +348 -0
- hap_cli/core/workflow.py +556 -0
- hap_cli/core/worksheet.py +403 -0
- hap_cli/hap_cli.py +105 -0
- hap_cli/skills/SKILL.md +383 -0
- hap_cli/skills/__init__.py +0 -0
- hap_cli/tests/__init__.py +1 -0
- hap_cli/tests/test_core.py +1824 -0
- hap_cli/tests/test_full_e2e.py +136 -0
- hap_cli/tests/test_integration.py +805 -0
- hap_cli/utils/__init__.py +1 -0
- hap_cli/utils/formatting.py +111 -0
- hap_cli/utils/options.py +10 -0
- hap_cli-0.5.0.dist-info/METADATA +223 -0
- hap_cli-0.5.0.dist-info/RECORD +58 -0
- hap_cli-0.5.0.dist-info/WHEEL +5 -0
- hap_cli-0.5.0.dist-info/entry_points.txt +2 -0
- hap_cli-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1824 @@
|
|
|
1
|
+
"""Unit tests for hap-cli harness core modules."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from unittest.mock import MagicMock, patch
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
from click.testing import CliRunner
|
|
11
|
+
|
|
12
|
+
from hap_cli.core.session import (
|
|
13
|
+
Session,
|
|
14
|
+
SessionError,
|
|
15
|
+
APIError,
|
|
16
|
+
load_config,
|
|
17
|
+
save_config,
|
|
18
|
+
CONFIG_DIR,
|
|
19
|
+
CONFIG_FILE,
|
|
20
|
+
)
|
|
21
|
+
from hap_cli.core import app as app_mod
|
|
22
|
+
from hap_cli.core import worksheet as ws_mod
|
|
23
|
+
from hap_cli.core import record as rec_mod
|
|
24
|
+
from hap_cli.core import workflow as wf_mod
|
|
25
|
+
from hap_cli.core import flow_node as node_mod
|
|
26
|
+
from hap_cli.core import instance as inst_mod
|
|
27
|
+
from hap_cli.core import role as role_mod
|
|
28
|
+
from hap_cli.utils.formatting import (
|
|
29
|
+
output_json,
|
|
30
|
+
output_table,
|
|
31
|
+
output_kv,
|
|
32
|
+
format_record_row,
|
|
33
|
+
)
|
|
34
|
+
from hap_cli.hap_cli import cli
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ── Fixtures ─────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@pytest.fixture
|
|
41
|
+
def tmp_config(tmp_path, monkeypatch):
|
|
42
|
+
"""Use a temporary config directory."""
|
|
43
|
+
config_dir = tmp_path / ".hap-cli"
|
|
44
|
+
config_file = config_dir / "config.json"
|
|
45
|
+
monkeypatch.setattr("hap_cli.core.session.CONFIG_DIR", config_dir)
|
|
46
|
+
monkeypatch.setattr("hap_cli.core.session.CONFIG_FILE", config_file)
|
|
47
|
+
return config_dir, config_file
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.fixture
|
|
51
|
+
def mock_session():
|
|
52
|
+
"""Create a configured mock session."""
|
|
53
|
+
session = Session(server_url="https://test.mingdao.com", auth_token="test_token_123")
|
|
54
|
+
return session
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _mock_response(data, state=1):
|
|
58
|
+
"""Create a mock requests.Response."""
|
|
59
|
+
resp = MagicMock()
|
|
60
|
+
resp.status_code = 200
|
|
61
|
+
resp.json.return_value = {"data": data, "state": state}
|
|
62
|
+
resp.raise_for_status = MagicMock()
|
|
63
|
+
return resp
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# ── Session Tests ────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class TestSession:
|
|
70
|
+
def test_load_empty_config(self, tmp_config):
|
|
71
|
+
session = Session.load()
|
|
72
|
+
assert session.server_url == ""
|
|
73
|
+
assert session.auth_token == ""
|
|
74
|
+
assert not session.is_configured()
|
|
75
|
+
|
|
76
|
+
def test_save_and_load_config(self, tmp_config):
|
|
77
|
+
config_dir, config_file = tmp_config
|
|
78
|
+
session = Session(server_url="https://test.com", auth_token="tok123")
|
|
79
|
+
session.default_app_id = "app1"
|
|
80
|
+
session.default_project_id = "proj1"
|
|
81
|
+
session.save()
|
|
82
|
+
|
|
83
|
+
assert config_file.exists()
|
|
84
|
+
loaded = Session.load()
|
|
85
|
+
assert loaded.server_url == "https://test.com"
|
|
86
|
+
assert loaded.auth_token == "tok123"
|
|
87
|
+
assert loaded.default_app_id == "app1"
|
|
88
|
+
assert loaded.default_project_id == "proj1"
|
|
89
|
+
|
|
90
|
+
def test_is_configured(self):
|
|
91
|
+
assert not Session().is_configured()
|
|
92
|
+
assert not Session(server_url="https://x.com").is_configured()
|
|
93
|
+
assert not Session(auth_token="tok").is_configured()
|
|
94
|
+
assert Session(server_url="https://x.com", auth_token="tok").is_configured()
|
|
95
|
+
|
|
96
|
+
@patch("hap_cli.core.session.requests.post")
|
|
97
|
+
def test_api_call_success(self, mock_post, mock_session):
|
|
98
|
+
mock_post.return_value = _mock_response({"id": "123", "name": "Test"})
|
|
99
|
+
result = mock_session.api_call("Worksheet", "GetWorksheetInfo", {"worksheetId": "ws1"})
|
|
100
|
+
assert result == {"id": "123", "name": "Test"}
|
|
101
|
+
|
|
102
|
+
call_args = mock_post.call_args
|
|
103
|
+
assert "api/Worksheet/GetWorksheetInfo" in call_args[0][0]
|
|
104
|
+
assert call_args[1]["json"] == {"worksheetId": "ws1"}
|
|
105
|
+
assert "md_pss_id test_token_123" in call_args[1]["headers"]["Authorization"]
|
|
106
|
+
|
|
107
|
+
@patch("hap_cli.core.session.requests.post")
|
|
108
|
+
def test_api_call_error_state(self, mock_post, mock_session):
|
|
109
|
+
resp = MagicMock()
|
|
110
|
+
resp.status_code = 200
|
|
111
|
+
resp.json.return_value = {"state": 0, "exception": "Not found"}
|
|
112
|
+
resp.raise_for_status = MagicMock()
|
|
113
|
+
mock_post.return_value = resp
|
|
114
|
+
|
|
115
|
+
with pytest.raises(APIError, match="Not found"):
|
|
116
|
+
mock_session.api_call("Worksheet", "GetWorksheetInfo", {})
|
|
117
|
+
|
|
118
|
+
def test_api_call_not_configured(self):
|
|
119
|
+
session = Session()
|
|
120
|
+
with pytest.raises(SessionError, match="not configured"):
|
|
121
|
+
session.api_call("Worksheet", "GetWorksheetInfo", {})
|
|
122
|
+
|
|
123
|
+
@patch("hap_cli.core.session.requests.post")
|
|
124
|
+
def test_api_call_strips_trailing_slash(self, mock_post, mock_session):
|
|
125
|
+
mock_session.server_url = "https://test.com/"
|
|
126
|
+
mock_post.return_value = _mock_response({})
|
|
127
|
+
mock_session.api_call("Test", "Action", {})
|
|
128
|
+
url = mock_post.call_args[0][0]
|
|
129
|
+
assert "//api" not in url
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ── App Tests ────────────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class TestApp:
|
|
136
|
+
@patch("hap_cli.core.session.requests.post")
|
|
137
|
+
def test_get_app_info(self, mock_post, mock_session):
|
|
138
|
+
mock_post.return_value = _mock_response({"appId": "a1", "name": "TestApp"})
|
|
139
|
+
result = app_mod.get_app_info(mock_session, "a1")
|
|
140
|
+
assert result["appId"] == "a1"
|
|
141
|
+
|
|
142
|
+
@patch("hap_cli.core.session.requests.post")
|
|
143
|
+
def test_get_app_worksheets_list(self, mock_post, mock_session):
|
|
144
|
+
mock_post.return_value = _mock_response([
|
|
145
|
+
{"worksheetId": "ws1", "name": "Sheet1"},
|
|
146
|
+
{"worksheetId": "ws2", "name": "Sheet2"},
|
|
147
|
+
])
|
|
148
|
+
result = app_mod.get_app_worksheets(mock_session, "a1")
|
|
149
|
+
assert len(result) == 2
|
|
150
|
+
assert result[0]["worksheetId"] == "ws1"
|
|
151
|
+
|
|
152
|
+
@patch("hap_cli.core.session.requests.post")
|
|
153
|
+
def test_get_apps_for_project_list(self, mock_post, mock_session):
|
|
154
|
+
mock_post.return_value = _mock_response([
|
|
155
|
+
{"appId": "a1", "name": "App1"},
|
|
156
|
+
])
|
|
157
|
+
result = app_mod.get_apps_for_project(mock_session, "proj1")
|
|
158
|
+
assert len(result) == 1
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# ── Worksheet Tests ──────────────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class TestWorksheet:
|
|
165
|
+
@patch("hap_cli.core.session.requests.post")
|
|
166
|
+
def test_get_worksheet_info(self, mock_post, mock_session):
|
|
167
|
+
mock_post.return_value = _mock_response({"worksheetId": "ws1", "name": "Test"})
|
|
168
|
+
result = ws_mod.get_worksheet_info(mock_session, "ws1")
|
|
169
|
+
assert result["name"] == "Test"
|
|
170
|
+
|
|
171
|
+
@patch("hap_cli.core.session.requests.post")
|
|
172
|
+
def test_get_worksheet_controls(self, mock_post, mock_session):
|
|
173
|
+
mock_post.return_value = _mock_response([
|
|
174
|
+
{"controlId": "c1", "controlName": "Name", "type": 2},
|
|
175
|
+
])
|
|
176
|
+
result = ws_mod.get_worksheet_controls(mock_session, "ws1")
|
|
177
|
+
assert len(result) == 1
|
|
178
|
+
assert result[0]["controlName"] == "Name"
|
|
179
|
+
|
|
180
|
+
@patch("hap_cli.core.session.requests.post")
|
|
181
|
+
def test_get_worksheet_views(self, mock_post, mock_session):
|
|
182
|
+
mock_post.return_value = _mock_response([
|
|
183
|
+
{"viewId": "v1", "name": "Default"},
|
|
184
|
+
])
|
|
185
|
+
result = ws_mod.get_worksheet_views(mock_session, "ws1")
|
|
186
|
+
assert len(result) == 1
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# ── Record Tests ─────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class TestRecord:
|
|
193
|
+
@patch("hap_cli.core.session.requests.post")
|
|
194
|
+
def test_get_records(self, mock_post, mock_session):
|
|
195
|
+
mock_post.return_value = _mock_response({
|
|
196
|
+
"data": [{"rowid": "r1"}, {"rowid": "r2"}],
|
|
197
|
+
"count": 2,
|
|
198
|
+
})
|
|
199
|
+
result = rec_mod.get_records(mock_session, "ws1", page_size=10)
|
|
200
|
+
assert result["count"] == 2
|
|
201
|
+
assert len(result["data"]) == 2
|
|
202
|
+
|
|
203
|
+
@patch("hap_cli.core.session.requests.post")
|
|
204
|
+
def test_get_record(self, mock_post, mock_session):
|
|
205
|
+
mock_post.return_value = _mock_response({"rowid": "r1", "c1": "val1"})
|
|
206
|
+
result = rec_mod.get_record(mock_session, "ws1", "r1")
|
|
207
|
+
assert result["rowid"] == "r1"
|
|
208
|
+
|
|
209
|
+
@patch("hap_cli.core.session.requests.post")
|
|
210
|
+
def test_create_record(self, mock_post, mock_session):
|
|
211
|
+
mock_post.return_value = _mock_response({"rowid": "r_new"})
|
|
212
|
+
result = rec_mod.create_record(
|
|
213
|
+
mock_session, "ws1",
|
|
214
|
+
[{"controlId": "c1", "value": "hello"}],
|
|
215
|
+
)
|
|
216
|
+
assert result["rowid"] == "r_new"
|
|
217
|
+
body = mock_post.call_args[1]["json"]
|
|
218
|
+
assert body["receiveControls"] == [{"controlId": "c1", "value": "hello"}]
|
|
219
|
+
|
|
220
|
+
@patch("hap_cli.core.session.requests.post")
|
|
221
|
+
def test_update_record(self, mock_post, mock_session):
|
|
222
|
+
mock_post.return_value = _mock_response(True)
|
|
223
|
+
result = rec_mod.update_record(
|
|
224
|
+
mock_session, "ws1", "r1",
|
|
225
|
+
[{"controlId": "c1", "value": "updated"}],
|
|
226
|
+
)
|
|
227
|
+
body = mock_post.call_args[1]["json"]
|
|
228
|
+
assert body["rowId"] == "r1"
|
|
229
|
+
assert body["newOldControl"] == [{"controlId": "c1", "value": "updated"}]
|
|
230
|
+
|
|
231
|
+
@patch("hap_cli.core.session.requests.post")
|
|
232
|
+
def test_delete_records(self, mock_post, mock_session):
|
|
233
|
+
mock_post.return_value = _mock_response({"isSuccess": True})
|
|
234
|
+
result = rec_mod.delete_records(mock_session, "ws1", ["r1", "r2"])
|
|
235
|
+
body = mock_post.call_args[1]["json"]
|
|
236
|
+
assert body["rowIds"] == ["r1", "r2"]
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# ── Workflow Tests ───────────────────────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class TestWorkflow:
|
|
243
|
+
@patch("hap_cli.core.session.requests.post")
|
|
244
|
+
def test_get_process_list(self, mock_post, mock_session):
|
|
245
|
+
mock_post.return_value = _mock_response({
|
|
246
|
+
"list": [{"id": "wf1", "name": "Flow1"}],
|
|
247
|
+
"total": 1,
|
|
248
|
+
})
|
|
249
|
+
result = wf_mod.get_process_list(mock_session, "a1")
|
|
250
|
+
assert result["count"] == 1
|
|
251
|
+
|
|
252
|
+
@patch("hap_cli.core.session.requests.post")
|
|
253
|
+
def test_create_process(self, mock_post, mock_session):
|
|
254
|
+
mock_post.return_value = _mock_response({"processId": "wf_new"})
|
|
255
|
+
result = wf_mod.create_process(mock_session, "comp1", "My Flow", relation_id="app1")
|
|
256
|
+
body = mock_post.call_args[1]["json"]
|
|
257
|
+
assert body["companyId"] == "comp1"
|
|
258
|
+
assert body["name"] == "My Flow"
|
|
259
|
+
assert body["relationId"] == "app1"
|
|
260
|
+
assert result["processId"] == "wf_new"
|
|
261
|
+
|
|
262
|
+
@patch("hap_cli.core.session.requests.post")
|
|
263
|
+
def test_delete_process(self, mock_post, mock_session):
|
|
264
|
+
mock_post.return_value = _mock_response(True)
|
|
265
|
+
wf_mod.delete_process(mock_session, "wf1")
|
|
266
|
+
body = mock_post.call_args[1]["json"]
|
|
267
|
+
assert body["processId"] == "wf1"
|
|
268
|
+
|
|
269
|
+
@patch("hap_cli.core.session.requests.post")
|
|
270
|
+
def test_update_process(self, mock_post, mock_session):
|
|
271
|
+
mock_post.return_value = _mock_response(True)
|
|
272
|
+
wf_mod.update_process(mock_session, "wf1", name="Renamed")
|
|
273
|
+
body = mock_post.call_args[1]["json"]
|
|
274
|
+
assert body["processId"] == "wf1"
|
|
275
|
+
assert body["name"] == "Renamed"
|
|
276
|
+
|
|
277
|
+
@patch("hap_cli.core.session.requests.post")
|
|
278
|
+
def test_copy_process(self, mock_post, mock_session):
|
|
279
|
+
mock_post.return_value = _mock_response({"processId": "wf_copy"})
|
|
280
|
+
result = wf_mod.copy_process(mock_session, "wf1", "Copy of Flow")
|
|
281
|
+
body = mock_post.call_args[1]["json"]
|
|
282
|
+
assert body["processId"] == "wf1"
|
|
283
|
+
assert body["name"] == "Copy of Flow"
|
|
284
|
+
|
|
285
|
+
@patch("hap_cli.core.session.requests.post")
|
|
286
|
+
def test_publish(self, mock_post, mock_session):
|
|
287
|
+
mock_post.return_value = _mock_response(True)
|
|
288
|
+
wf_mod.publish(mock_session, "wf1", is_publish=True)
|
|
289
|
+
body = mock_post.call_args[1]["json"]
|
|
290
|
+
assert body["processId"] == "wf1"
|
|
291
|
+
assert body["isPublish"] is True
|
|
292
|
+
|
|
293
|
+
@patch("hap_cli.core.session.requests.post")
|
|
294
|
+
def test_publish_disable(self, mock_post, mock_session):
|
|
295
|
+
mock_post.return_value = _mock_response(True)
|
|
296
|
+
wf_mod.publish(mock_session, "wf1", is_publish=False)
|
|
297
|
+
body = mock_post.call_args[1]["json"]
|
|
298
|
+
assert body["isPublish"] is False
|
|
299
|
+
|
|
300
|
+
@patch("hap_cli.core.session.requests.post")
|
|
301
|
+
def test_rollback_version(self, mock_post, mock_session):
|
|
302
|
+
mock_post.return_value = _mock_response(True)
|
|
303
|
+
wf_mod.rollback_version(mock_session, "wf1")
|
|
304
|
+
body = mock_post.call_args[1]["json"]
|
|
305
|
+
assert body["processId"] == "wf1"
|
|
306
|
+
|
|
307
|
+
@patch("hap_cli.core.session.requests.post")
|
|
308
|
+
def test_get_process_config(self, mock_post, mock_session):
|
|
309
|
+
mock_post.return_value = _mock_response({"executeType": 1, "allowRevoke": True})
|
|
310
|
+
result = wf_mod.get_process_config(mock_session, "wf1")
|
|
311
|
+
assert result["allowRevoke"] is True
|
|
312
|
+
|
|
313
|
+
@patch("hap_cli.core.session.requests.post")
|
|
314
|
+
def test_save_process_config(self, mock_post, mock_session):
|
|
315
|
+
mock_post.return_value = _mock_response(True)
|
|
316
|
+
wf_mod.save_process_config(mock_session, "wf1", {"allowRevoke": False, "executeType": 2})
|
|
317
|
+
body = mock_post.call_args[1]["json"]
|
|
318
|
+
assert body["processId"] == "wf1"
|
|
319
|
+
assert body["allowRevoke"] is False
|
|
320
|
+
|
|
321
|
+
@patch("hap_cli.core.session.requests.post")
|
|
322
|
+
def test_start_process_by_id(self, mock_post, mock_session):
|
|
323
|
+
mock_post.return_value = _mock_response({"instanceId": "inst1"})
|
|
324
|
+
result = wf_mod.start_process_by_id(mock_session, "wf1", source_id="r1")
|
|
325
|
+
body = mock_post.call_args[1]["json"]
|
|
326
|
+
assert body["processId"] == "wf1"
|
|
327
|
+
assert body["sourceId"] == "r1"
|
|
328
|
+
|
|
329
|
+
@patch("hap_cli.core.session.requests.post")
|
|
330
|
+
def test_get_process_by_id(self, mock_post, mock_session):
|
|
331
|
+
mock_post.return_value = _mock_response({"id": "wf1", "name": "Flow"})
|
|
332
|
+
result = wf_mod.get_process_by_id(mock_session, "wf1")
|
|
333
|
+
assert result["name"] == "Flow"
|
|
334
|
+
|
|
335
|
+
@patch("hap_cli.core.session.requests.post")
|
|
336
|
+
def test_get_version_history(self, mock_post, mock_session):
|
|
337
|
+
mock_post.return_value = _mock_response([{"version": 1}, {"version": 2}])
|
|
338
|
+
result = wf_mod.get_version_history(mock_session, "wf1")
|
|
339
|
+
assert isinstance(result, list)
|
|
340
|
+
|
|
341
|
+
@patch("hap_cli.core.session.requests.post")
|
|
342
|
+
def test_move_process(self, mock_post, mock_session):
|
|
343
|
+
mock_post.return_value = _mock_response(True)
|
|
344
|
+
wf_mod.move_process(mock_session, "wf1", "app2")
|
|
345
|
+
body = mock_post.call_args[1]["json"]
|
|
346
|
+
assert body["relationId"] == "app2"
|
|
347
|
+
|
|
348
|
+
@patch("hap_cli.core.session.requests.post")
|
|
349
|
+
def test_get_group_list(self, mock_post, mock_session):
|
|
350
|
+
mock_post.return_value = _mock_response([{"id": "g1", "name": "Group1"}])
|
|
351
|
+
result = wf_mod.get_group_list(mock_session, "app1")
|
|
352
|
+
assert len(result) == 1
|
|
353
|
+
|
|
354
|
+
@patch("hap_cli.core.session.requests.post")
|
|
355
|
+
def test_create_group(self, mock_post, mock_session):
|
|
356
|
+
mock_post.return_value = _mock_response({"id": "g_new"})
|
|
357
|
+
result = wf_mod.create_group(mock_session, "app1", "New Group")
|
|
358
|
+
body = mock_post.call_args[1]["json"]
|
|
359
|
+
assert body["name"] == "New Group"
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
# ── FlowNode Tests ──────────────────────────────────────────────────────
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class TestFlowNode:
|
|
366
|
+
@patch("hap_cli.core.session.requests.post")
|
|
367
|
+
def test_add_node(self, mock_post, mock_session):
|
|
368
|
+
mock_post.return_value = _mock_response({"nodeId": "n_new"})
|
|
369
|
+
result = node_mod.add_node(mock_session, "wf1", 4, name="Approval")
|
|
370
|
+
body = mock_post.call_args[1]["json"]
|
|
371
|
+
assert body["processId"] == "wf1"
|
|
372
|
+
assert body["flowNodeType"] == 4
|
|
373
|
+
assert body["name"] == "Approval"
|
|
374
|
+
|
|
375
|
+
@patch("hap_cli.core.session.requests.post")
|
|
376
|
+
def test_delete_node(self, mock_post, mock_session):
|
|
377
|
+
mock_post.return_value = _mock_response(True)
|
|
378
|
+
node_mod.delete_node(mock_session, "wf1", "n1")
|
|
379
|
+
body = mock_post.call_args[1]["json"]
|
|
380
|
+
assert body["nodeId"] == "n1"
|
|
381
|
+
|
|
382
|
+
@patch("hap_cli.core.session.requests.post")
|
|
383
|
+
def test_get_nodes(self, mock_post, mock_session):
|
|
384
|
+
mock_post.return_value = _mock_response({"startId": "n0", "nodes": []})
|
|
385
|
+
result = node_mod.get_nodes(mock_session, "wf1")
|
|
386
|
+
assert "startId" in result
|
|
387
|
+
|
|
388
|
+
@patch("hap_cli.core.session.requests.post")
|
|
389
|
+
def test_get_node_detail(self, mock_post, mock_session):
|
|
390
|
+
mock_post.return_value = _mock_response({"nodeId": "n1", "flowNodeType": 4})
|
|
391
|
+
result = node_mod.get_node_detail(mock_session, "wf1", "n1")
|
|
392
|
+
assert result["flowNodeType"] == 4
|
|
393
|
+
|
|
394
|
+
@patch("hap_cli.core.session.requests.post")
|
|
395
|
+
def test_update_node_name(self, mock_post, mock_session):
|
|
396
|
+
mock_post.return_value = _mock_response(True)
|
|
397
|
+
node_mod.update_node_name(mock_session, "wf1", "n1", "New Name")
|
|
398
|
+
body = mock_post.call_args[1]["json"]
|
|
399
|
+
assert body["name"] == "New Name"
|
|
400
|
+
|
|
401
|
+
@patch("hap_cli.core.session.requests.post")
|
|
402
|
+
def test_update_node_desc(self, mock_post, mock_session):
|
|
403
|
+
mock_post.return_value = _mock_response(True)
|
|
404
|
+
node_mod.update_node_desc(mock_session, "wf1", "n1", desc="A desc", alias="Alias1")
|
|
405
|
+
body = mock_post.call_args[1]["json"]
|
|
406
|
+
assert body["desc"] == "A desc"
|
|
407
|
+
assert body["alias"] == "Alias1"
|
|
408
|
+
|
|
409
|
+
@patch("hap_cli.core.session.requests.post")
|
|
410
|
+
def test_save_node(self, mock_post, mock_session):
|
|
411
|
+
mock_post.return_value = _mock_response(True)
|
|
412
|
+
config = {"accounts": [{"accountId": "u1"}], "condition": "auto"}
|
|
413
|
+
node_mod.save_node(mock_session, "wf1", "n1", 4, config, name="Approval")
|
|
414
|
+
body = mock_post.call_args[1]["json"]
|
|
415
|
+
assert body["processId"] == "wf1"
|
|
416
|
+
assert body["flowNodeType"] == 4
|
|
417
|
+
assert body["accounts"] == [{"accountId": "u1"}]
|
|
418
|
+
assert body["name"] == "Approval"
|
|
419
|
+
|
|
420
|
+
@patch("hap_cli.core.session.requests.post")
|
|
421
|
+
def test_test_code(self, mock_post, mock_session):
|
|
422
|
+
mock_post.return_value = _mock_response({"output": "Hello"})
|
|
423
|
+
result = node_mod.test_code(
|
|
424
|
+
mock_session, "wf1", "n1", "return 'Hello'",
|
|
425
|
+
input_data=[{"name": "x", "value": "1", "type": "number"}],
|
|
426
|
+
)
|
|
427
|
+
body = mock_post.call_args[1]["json"]
|
|
428
|
+
assert body["code"] == "return 'Hello'"
|
|
429
|
+
assert body["inputDatas"][0]["name"] == "x"
|
|
430
|
+
|
|
431
|
+
@patch("hap_cli.core.session.requests.post")
|
|
432
|
+
def test_test_webhook(self, mock_post, mock_session):
|
|
433
|
+
mock_post.return_value = _mock_response({"status": 200, "body": "{}"})
|
|
434
|
+
result = node_mod.test_webhook(
|
|
435
|
+
mock_session, "wf1", "n1", "https://example.com/api",
|
|
436
|
+
method="GET", headers=[{"name": "X-Key", "value": "abc"}],
|
|
437
|
+
)
|
|
438
|
+
body = mock_post.call_args[1]["json"]
|
|
439
|
+
assert body["url"] == "https://example.com/api"
|
|
440
|
+
assert body["method"] == "GET"
|
|
441
|
+
|
|
442
|
+
@patch("hap_cli.core.session.requests.post")
|
|
443
|
+
def test_test_aigc(self, mock_post, mock_session):
|
|
444
|
+
mock_post.return_value = _mock_response({"text": "AI response"})
|
|
445
|
+
result = node_mod.test_aigc(
|
|
446
|
+
mock_session, "wf1", "n1", "Summarize this",
|
|
447
|
+
model="gpt-4", system_message="You are helpful", temperature=0.5,
|
|
448
|
+
)
|
|
449
|
+
body = mock_post.call_args[1]["json"]
|
|
450
|
+
assert body["prompt"] == "Summarize this"
|
|
451
|
+
assert body["model"] == "gpt-4"
|
|
452
|
+
assert body["temperature"] == 0.5
|
|
453
|
+
|
|
454
|
+
@patch("hap_cli.core.session.requests.post")
|
|
455
|
+
def test_json_to_controls(self, mock_post, mock_session):
|
|
456
|
+
mock_post.return_value = _mock_response([{"controlId": "auto1"}])
|
|
457
|
+
result = node_mod.json_to_controls(mock_session, '{"name": "test"}')
|
|
458
|
+
body = mock_post.call_args[1]["json"]
|
|
459
|
+
assert body["json"] == '{"name": "test"}'
|
|
460
|
+
|
|
461
|
+
@patch("hap_cli.core.session.requests.post")
|
|
462
|
+
def test_get_code_template_list(self, mock_post, mock_session):
|
|
463
|
+
mock_post.return_value = _mock_response([{"id": "t1", "name": "Template1"}])
|
|
464
|
+
result = node_mod.get_code_template_list(mock_session, keyword="test")
|
|
465
|
+
body = mock_post.call_args[1]["json"]
|
|
466
|
+
assert body["keyword"] == "test"
|
|
467
|
+
|
|
468
|
+
@patch("hap_cli.core.session.requests.post")
|
|
469
|
+
def test_create_code_template(self, mock_post, mock_session):
|
|
470
|
+
mock_post.return_value = _mock_response({"id": "t_new"})
|
|
471
|
+
result = node_mod.create_code_template(mock_session, "My Tmpl", "console.log('hi')")
|
|
472
|
+
body = mock_post.call_args[1]["json"]
|
|
473
|
+
assert body["name"] == "My Tmpl"
|
|
474
|
+
assert body["code"] == "console.log('hi')"
|
|
475
|
+
|
|
476
|
+
@patch("hap_cli.core.session.requests.post")
|
|
477
|
+
def test_get_sub_process_list(self, mock_post, mock_session):
|
|
478
|
+
mock_post.return_value = _mock_response([{"id": "sub1"}])
|
|
479
|
+
result = node_mod.get_sub_process_list(mock_session, "wf1")
|
|
480
|
+
body = mock_post.call_args[1]["json"]
|
|
481
|
+
assert body["processId"] == "wf1"
|
|
482
|
+
|
|
483
|
+
@patch("hap_cli.core.session.requests.post")
|
|
484
|
+
def test_add_node_with_action_id(self, mock_post, mock_session):
|
|
485
|
+
mock_post.return_value = _mock_response({"nodeId": "n_act"})
|
|
486
|
+
node_mod.add_node(mock_session, "wf1", 6, name="Add Record", action_id="1")
|
|
487
|
+
body = mock_post.call_args[1]["json"]
|
|
488
|
+
assert body["flowNodeType"] == 6
|
|
489
|
+
assert body["actionId"] == "1"
|
|
490
|
+
|
|
491
|
+
@patch("hap_cli.core.session.requests.post")
|
|
492
|
+
def test_save_action_node_add_record(self, mock_post, mock_session):
|
|
493
|
+
mock_post.return_value = _mock_response(True)
|
|
494
|
+
fields = [{"fieldId": "c001", "type": 2, "fieldValue": "hello"}]
|
|
495
|
+
node_mod.save_action_node(
|
|
496
|
+
mock_session, "wf1", "n1", "1", "ws_abc",
|
|
497
|
+
fields=fields, name="Add Record",
|
|
498
|
+
)
|
|
499
|
+
body = mock_post.call_args[1]["json"]
|
|
500
|
+
assert body["flowNodeType"] == 6
|
|
501
|
+
assert body["actionId"] == "1"
|
|
502
|
+
assert body["appId"] == "ws_abc"
|
|
503
|
+
assert body["appType"] == 1
|
|
504
|
+
assert body["fields"][0]["fieldId"] == "c001"
|
|
505
|
+
assert body["name"] == "Add Record"
|
|
506
|
+
|
|
507
|
+
@patch("hap_cli.core.session.requests.post")
|
|
508
|
+
def test_save_action_node_edit_record(self, mock_post, mock_session):
|
|
509
|
+
mock_post.return_value = _mock_response(True)
|
|
510
|
+
node_mod.save_action_node(
|
|
511
|
+
mock_session, "wf1", "n1", "2", "ws_abc",
|
|
512
|
+
select_node_id="src_node",
|
|
513
|
+
fields=[{"fieldId": "c001", "type": 2, "fieldValue": "updated"}],
|
|
514
|
+
)
|
|
515
|
+
body = mock_post.call_args[1]["json"]
|
|
516
|
+
assert body["actionId"] == "2"
|
|
517
|
+
assert body["selectNodeId"] == "src_node"
|
|
518
|
+
|
|
519
|
+
@patch("hap_cli.core.session.requests.post")
|
|
520
|
+
def test_save_action_node_delete_record(self, mock_post, mock_session):
|
|
521
|
+
mock_post.return_value = _mock_response(True)
|
|
522
|
+
node_mod.save_action_node(
|
|
523
|
+
mock_session, "wf1", "n1", "3", "ws_abc",
|
|
524
|
+
select_node_id="src_node",
|
|
525
|
+
)
|
|
526
|
+
body = mock_post.call_args[1]["json"]
|
|
527
|
+
assert body["actionId"] == "3"
|
|
528
|
+
|
|
529
|
+
@patch("hap_cli.core.session.requests.post")
|
|
530
|
+
def test_save_search_node_worksheet_find(self, mock_post, mock_session):
|
|
531
|
+
mock_post.return_value = _mock_response(True)
|
|
532
|
+
cond = [{"fieldId": "c001", "conditionId": "1", "value": ["test"]}]
|
|
533
|
+
node_mod.save_search_node(
|
|
534
|
+
mock_session, "wf1", "n1", "406",
|
|
535
|
+
app_id="ws_abc", operate_condition=cond,
|
|
536
|
+
)
|
|
537
|
+
body = mock_post.call_args[1]["json"]
|
|
538
|
+
assert body["flowNodeType"] == 7
|
|
539
|
+
assert body["actionId"] == "406"
|
|
540
|
+
assert body["appId"] == "ws_abc"
|
|
541
|
+
assert body["operateCondition"][0]["fieldId"] == "c001"
|
|
542
|
+
|
|
543
|
+
@patch("hap_cli.core.session.requests.post")
|
|
544
|
+
def test_save_search_node_find_and_update(self, mock_post, mock_session):
|
|
545
|
+
mock_post.return_value = _mock_response(True)
|
|
546
|
+
node_mod.save_search_node(
|
|
547
|
+
mock_session, "wf1", "n1", "421",
|
|
548
|
+
app_id="ws_abc",
|
|
549
|
+
fields=[{"fieldId": "c002", "type": 2, "fieldValue": "new"}],
|
|
550
|
+
)
|
|
551
|
+
body = mock_post.call_args[1]["json"]
|
|
552
|
+
assert body["actionId"] == "421"
|
|
553
|
+
assert body["fields"][0]["fieldValue"] == "new"
|
|
554
|
+
|
|
555
|
+
@patch("hap_cli.core.session.requests.post")
|
|
556
|
+
def test_save_search_node_execute_type(self, mock_post, mock_session):
|
|
557
|
+
mock_post.return_value = _mock_response(True)
|
|
558
|
+
node_mod.save_search_node(
|
|
559
|
+
mock_session, "wf1", "n1", "406",
|
|
560
|
+
app_id="ws_abc", execute_type=2, random=True,
|
|
561
|
+
)
|
|
562
|
+
body = mock_post.call_args[1]["json"]
|
|
563
|
+
assert body["executeType"] == 2
|
|
564
|
+
assert body["random"] is True
|
|
565
|
+
|
|
566
|
+
@patch("hap_cli.core.session.requests.post")
|
|
567
|
+
def test_save_get_more_record_from_worksheet(self, mock_post, mock_session):
|
|
568
|
+
mock_post.return_value = _mock_response(True)
|
|
569
|
+
node_mod.save_get_more_record_node(
|
|
570
|
+
mock_session, "wf1", "n1", "400",
|
|
571
|
+
app_id="ws_abc",
|
|
572
|
+
sorts=[{"fieldId": "c001", "isAsc": True}],
|
|
573
|
+
)
|
|
574
|
+
body = mock_post.call_args[1]["json"]
|
|
575
|
+
assert body["flowNodeType"] == 13
|
|
576
|
+
assert body["actionId"] == "400"
|
|
577
|
+
assert body["appId"] == "ws_abc"
|
|
578
|
+
assert body["sorts"][0]["isAsc"] is True
|
|
579
|
+
|
|
580
|
+
@patch("hap_cli.core.session.requests.post")
|
|
581
|
+
def test_save_get_more_record_batch_update(self, mock_post, mock_session):
|
|
582
|
+
mock_post.return_value = _mock_response(True)
|
|
583
|
+
node_mod.save_get_more_record_node(
|
|
584
|
+
mock_session, "wf1", "n1", "412",
|
|
585
|
+
app_id="ws_abc",
|
|
586
|
+
fields=[{"fieldId": "c001", "type": 2, "fieldValue": "batch"}],
|
|
587
|
+
)
|
|
588
|
+
body = mock_post.call_args[1]["json"]
|
|
589
|
+
assert body["actionId"] == "412"
|
|
590
|
+
assert body["fields"][0]["fieldValue"] == "batch"
|
|
591
|
+
|
|
592
|
+
@patch("hap_cli.core.session.requests.post")
|
|
593
|
+
def test_save_get_more_record_batch_delete(self, mock_post, mock_session):
|
|
594
|
+
mock_post.return_value = _mock_response(True)
|
|
595
|
+
node_mod.save_get_more_record_node(
|
|
596
|
+
mock_session, "wf1", "n1", "413",
|
|
597
|
+
app_id="ws_abc",
|
|
598
|
+
)
|
|
599
|
+
body = mock_post.call_args[1]["json"]
|
|
600
|
+
assert body["actionId"] == "413"
|
|
601
|
+
|
|
602
|
+
@patch("hap_cli.core.session.requests.post")
|
|
603
|
+
def test_save_get_more_record_with_limit(self, mock_post, mock_session):
|
|
604
|
+
mock_post.return_value = _mock_response(True)
|
|
605
|
+
node_mod.save_get_more_record_node(
|
|
606
|
+
mock_session, "wf1", "n1", "400",
|
|
607
|
+
app_id="ws_abc",
|
|
608
|
+
number_field_value={"fieldValue": 100},
|
|
609
|
+
)
|
|
610
|
+
body = mock_post.call_args[1]["json"]
|
|
611
|
+
assert body["numberFieldValue"]["fieldValue"] == 100
|
|
612
|
+
|
|
613
|
+
@patch("hap_cli.core.session.requests.post")
|
|
614
|
+
def test_get_app_template_controls(self, mock_post, mock_session):
|
|
615
|
+
mock_post.return_value = _mock_response([{"controlId": "c001", "controlName": "Name"}])
|
|
616
|
+
result = node_mod.get_app_template_controls(mock_session, "wf1", "n1", "ws_abc")
|
|
617
|
+
body = mock_post.call_args[1]["json"]
|
|
618
|
+
assert body["appId"] == "ws_abc"
|
|
619
|
+
assert body["appType"] == 1
|
|
620
|
+
assert result[0]["controlId"] == "c001"
|
|
621
|
+
|
|
622
|
+
@patch("hap_cli.core.session.requests.post")
|
|
623
|
+
def test_node_type_constants(self, mock_post, mock_session):
|
|
624
|
+
assert node_mod.NODE_TYPE["ACTION"] == 6
|
|
625
|
+
assert node_mod.NODE_TYPE["SEARCH"] == 7
|
|
626
|
+
assert node_mod.NODE_TYPE["GET_MORE_RECORD"] == 13
|
|
627
|
+
assert node_mod.NODE_TYPE["AIGC"] == 31
|
|
628
|
+
assert node_mod.NODE_TYPE["LOOP"] == 29
|
|
629
|
+
|
|
630
|
+
def test_action_id_constants(self, mock_session):
|
|
631
|
+
assert node_mod.ACTION_ID["ADD_RECORD"] == "1"
|
|
632
|
+
assert node_mod.ACTION_ID["EDIT_RECORD"] == "2"
|
|
633
|
+
assert node_mod.ACTION_ID["DELETE_RECORD"] == "3"
|
|
634
|
+
assert node_mod.ACTION_ID["WORKSHEET_FIND"] == "406"
|
|
635
|
+
assert node_mod.ACTION_ID["BATCH_UPDATE"] == "412"
|
|
636
|
+
assert node_mod.ACTION_ID["BATCH_DELETE"] == "413"
|
|
637
|
+
assert node_mod.ACTION_ID["FROM_WORKSHEET"] == "400"
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
# ── Instance Tests ──────────────────────────────────────────────────────
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
class TestInstance:
|
|
644
|
+
@patch("hap_cli.core.session.requests.post")
|
|
645
|
+
def test_get_todo_count(self, mock_post, mock_session):
|
|
646
|
+
mock_post.return_value = _mock_response({"waitingApproval": 3, "waitingFill": 1})
|
|
647
|
+
result = inst_mod.get_todo_count(mock_session)
|
|
648
|
+
assert result["waitingApproval"] == 3
|
|
649
|
+
|
|
650
|
+
@patch("hap_cli.core.session.requests.post")
|
|
651
|
+
def test_get_todo_list(self, mock_post, mock_session):
|
|
652
|
+
mock_post.return_value = _mock_response({
|
|
653
|
+
"list": [{"id": "inst1", "title": "Task1"}],
|
|
654
|
+
"total": 1,
|
|
655
|
+
})
|
|
656
|
+
result = inst_mod.get_todo_list(mock_session, type_=4)
|
|
657
|
+
assert result["count"] == 1
|
|
658
|
+
|
|
659
|
+
@patch("hap_cli.core.session.requests.post")
|
|
660
|
+
def test_get_instance_detail(self, mock_post, mock_session):
|
|
661
|
+
mock_post.return_value = _mock_response({"id": "inst1", "status": 1})
|
|
662
|
+
result = inst_mod.get_instance_detail(mock_session, "inst1")
|
|
663
|
+
assert result["status"] == 1
|
|
664
|
+
|
|
665
|
+
@patch("hap_cli.core.session.requests.post")
|
|
666
|
+
def test_approve(self, mock_post, mock_session):
|
|
667
|
+
mock_post.return_value = _mock_response(True)
|
|
668
|
+
inst_mod.approve(mock_session, "inst1", work_id="w1", opinion="LGTM")
|
|
669
|
+
body = mock_post.call_args[1]["json"]
|
|
670
|
+
assert body["id"] == "inst1"
|
|
671
|
+
assert body["workId"] == "w1"
|
|
672
|
+
assert body["opinion"] == "LGTM"
|
|
673
|
+
|
|
674
|
+
@patch("hap_cli.core.session.requests.post")
|
|
675
|
+
def test_reject(self, mock_post, mock_session):
|
|
676
|
+
mock_post.return_value = _mock_response(True)
|
|
677
|
+
inst_mod.reject(mock_session, "inst1", opinion="Not ready", back_node_id="n0")
|
|
678
|
+
body = mock_post.call_args[1]["json"]
|
|
679
|
+
assert body["opinion"] == "Not ready"
|
|
680
|
+
assert body["backNodeId"] == "n0"
|
|
681
|
+
|
|
682
|
+
@patch("hap_cli.core.session.requests.post")
|
|
683
|
+
def test_forward(self, mock_post, mock_session):
|
|
684
|
+
mock_post.return_value = _mock_response(True)
|
|
685
|
+
inst_mod.forward(mock_session, "inst1", "user2", opinion="Please review")
|
|
686
|
+
body = mock_post.call_args[1]["json"]
|
|
687
|
+
assert body["forwardAccountId"] == "user2"
|
|
688
|
+
|
|
689
|
+
@patch("hap_cli.core.session.requests.post")
|
|
690
|
+
def test_sign_task(self, mock_post, mock_session):
|
|
691
|
+
mock_post.return_value = _mock_response(True)
|
|
692
|
+
inst_mod.sign_task(mock_session, "inst1", "user3", before=False)
|
|
693
|
+
body = mock_post.call_args[1]["json"]
|
|
694
|
+
assert body["forwardAccountId"] == "user3"
|
|
695
|
+
assert body["before"] is False
|
|
696
|
+
|
|
697
|
+
@patch("hap_cli.core.session.requests.post")
|
|
698
|
+
def test_submit(self, mock_post, mock_session):
|
|
699
|
+
mock_post.return_value = _mock_response(True)
|
|
700
|
+
inst_mod.submit(mock_session, "inst1", work_id="w1")
|
|
701
|
+
body = mock_post.call_args[1]["json"]
|
|
702
|
+
assert body["id"] == "inst1"
|
|
703
|
+
|
|
704
|
+
@patch("hap_cli.core.session.requests.post")
|
|
705
|
+
def test_revoke(self, mock_post, mock_session):
|
|
706
|
+
mock_post.return_value = _mock_response(True)
|
|
707
|
+
inst_mod.revoke(mock_session, "inst1")
|
|
708
|
+
body = mock_post.call_args[1]["json"]
|
|
709
|
+
assert body["id"] == "inst1"
|
|
710
|
+
|
|
711
|
+
@patch("hap_cli.core.session.requests.post")
|
|
712
|
+
def test_urge(self, mock_post, mock_session):
|
|
713
|
+
mock_post.return_value = _mock_response(True)
|
|
714
|
+
inst_mod.urge(mock_session, "inst1", work_id="w1")
|
|
715
|
+
body = mock_post.call_args[1]["json"]
|
|
716
|
+
assert body["operationType"] == 18
|
|
717
|
+
|
|
718
|
+
@patch("hap_cli.core.session.requests.post")
|
|
719
|
+
def test_end_instance(self, mock_post, mock_session):
|
|
720
|
+
mock_post.return_value = _mock_response(True)
|
|
721
|
+
inst_mod.end_instance(mock_session, "inst1")
|
|
722
|
+
body = mock_post.call_args[1]["json"]
|
|
723
|
+
assert body["instanceId"] == "inst1"
|
|
724
|
+
|
|
725
|
+
@patch("hap_cli.core.session.requests.post")
|
|
726
|
+
def test_reset_instance(self, mock_post, mock_session):
|
|
727
|
+
mock_post.return_value = _mock_response(True)
|
|
728
|
+
inst_mod.reset_instance(mock_session, "inst1")
|
|
729
|
+
body = mock_post.call_args[1]["json"]
|
|
730
|
+
assert body["instanceId"] == "inst1"
|
|
731
|
+
|
|
732
|
+
@patch("hap_cli.core.session.requests.post")
|
|
733
|
+
def test_get_history_list(self, mock_post, mock_session):
|
|
734
|
+
mock_post.return_value = _mock_response({
|
|
735
|
+
"list": [{"instanceId": "inst1", "status": 2}],
|
|
736
|
+
"total": 1,
|
|
737
|
+
})
|
|
738
|
+
result = inst_mod.get_history_list(mock_session, process_id="wf1", status=2)
|
|
739
|
+
assert result["count"] == 1
|
|
740
|
+
|
|
741
|
+
@patch("hap_cli.core.session.requests.post")
|
|
742
|
+
def test_get_history_detail(self, mock_post, mock_session):
|
|
743
|
+
mock_post.return_value = _mock_response({"instanceId": "inst1", "nodes": []})
|
|
744
|
+
result = inst_mod.get_history_detail(mock_session, "inst1")
|
|
745
|
+
assert result["instanceId"] == "inst1"
|
|
746
|
+
|
|
747
|
+
@patch("hap_cli.core.session.requests.post")
|
|
748
|
+
def test_get_operation_history(self, mock_post, mock_session):
|
|
749
|
+
mock_post.return_value = _mock_response([{"action": "pass", "time": "2024-01-01"}])
|
|
750
|
+
result = inst_mod.get_operation_history(mock_session, "inst1")
|
|
751
|
+
assert len(result) == 1
|
|
752
|
+
|
|
753
|
+
@patch("hap_cli.core.session.requests.post")
|
|
754
|
+
def test_batch_operation(self, mock_post, mock_session):
|
|
755
|
+
mock_post.return_value = _mock_response({"success": 5})
|
|
756
|
+
result = inst_mod.batch_operation(
|
|
757
|
+
mock_session, 4, process_id="wf1", selects=["s1", "s2"],
|
|
758
|
+
)
|
|
759
|
+
body = mock_post.call_args[1]["json"]
|
|
760
|
+
assert body["batchOperationType"] == 4
|
|
761
|
+
assert body["selects"] == ["s1", "s2"]
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
# ── Role Tests ───────────────────────────────────────────────────────────
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
class TestRole:
|
|
768
|
+
@patch("hap_cli.core.session.requests.post")
|
|
769
|
+
def test_get_roles(self, mock_post, mock_session):
|
|
770
|
+
mock_post.return_value = _mock_response([
|
|
771
|
+
{"roleId": "role1", "name": "Admin"},
|
|
772
|
+
])
|
|
773
|
+
result = role_mod.get_roles(mock_session, "a1")
|
|
774
|
+
assert len(result) == 1
|
|
775
|
+
|
|
776
|
+
@patch("hap_cli.core.session.requests.post")
|
|
777
|
+
def test_create_role(self, mock_post, mock_session):
|
|
778
|
+
mock_post.return_value = _mock_response({"roleId": "role_new"})
|
|
779
|
+
result = role_mod.create_role(mock_session, "a1", "Viewer", "Read-only", 10)
|
|
780
|
+
body = mock_post.call_args[1]["json"]
|
|
781
|
+
assert body["name"] == "Viewer"
|
|
782
|
+
assert body["permissionWay"] == 10
|
|
783
|
+
|
|
784
|
+
@patch("hap_cli.core.session.requests.post")
|
|
785
|
+
def test_delete_role(self, mock_post, mock_session):
|
|
786
|
+
mock_post.return_value = _mock_response(True)
|
|
787
|
+
role_mod.delete_role(mock_session, "a1", "role1", "role2")
|
|
788
|
+
body = mock_post.call_args[1]["json"]
|
|
789
|
+
assert body["roleId"] == "role1"
|
|
790
|
+
assert body["resultRoleId"] == "role2"
|
|
791
|
+
|
|
792
|
+
@patch("hap_cli.core.session.requests.post")
|
|
793
|
+
def test_add_role_members(self, mock_post, mock_session):
|
|
794
|
+
mock_post.return_value = _mock_response(True)
|
|
795
|
+
role_mod.add_role_members(mock_session, "a1", "role1", user_ids=["u1", "u2"])
|
|
796
|
+
body = mock_post.call_args[1]["json"]
|
|
797
|
+
assert body["userIds"] == ["u1", "u2"]
|
|
798
|
+
|
|
799
|
+
@patch("hap_cli.core.session.requests.post")
|
|
800
|
+
def test_remove_role_members(self, mock_post, mock_session):
|
|
801
|
+
mock_post.return_value = _mock_response(True)
|
|
802
|
+
role_mod.remove_role_members(mock_session, "a1", "role1", department_ids=["d1"])
|
|
803
|
+
body = mock_post.call_args[1]["json"]
|
|
804
|
+
assert body["departmentIds"] == ["d1"]
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
# ── Formatting Tests ─────────────────────────────────────────────────────
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
class TestFormatting:
|
|
811
|
+
def test_output_json(self, capsys):
|
|
812
|
+
output_json({"key": "value", "num": 42})
|
|
813
|
+
captured = capsys.readouterr()
|
|
814
|
+
data = json.loads(captured.out)
|
|
815
|
+
assert data["key"] == "value"
|
|
816
|
+
assert data["num"] == 42
|
|
817
|
+
|
|
818
|
+
def test_output_table(self, capsys):
|
|
819
|
+
rows = [
|
|
820
|
+
{"id": "1", "name": "Alice"},
|
|
821
|
+
{"id": "2", "name": "Bob"},
|
|
822
|
+
]
|
|
823
|
+
output_table(rows, ["id", "name"], ["ID", "Name"])
|
|
824
|
+
captured = capsys.readouterr()
|
|
825
|
+
assert "Alice" in captured.out
|
|
826
|
+
assert "Bob" in captured.out
|
|
827
|
+
assert "ID" in captured.out
|
|
828
|
+
|
|
829
|
+
def test_output_table_empty(self, capsys):
|
|
830
|
+
output_table([], ["id"], ["ID"])
|
|
831
|
+
captured = capsys.readouterr()
|
|
832
|
+
assert "no data" in captured.out
|
|
833
|
+
|
|
834
|
+
def test_format_record_row_basic(self):
|
|
835
|
+
record = {"rowid": "r1", "c1": "hello", "c2": "world"}
|
|
836
|
+
result = format_record_row(record)
|
|
837
|
+
assert result["rowId"] == "r1"
|
|
838
|
+
assert result["c1"] == "hello"
|
|
839
|
+
|
|
840
|
+
def test_format_record_row_with_controls(self):
|
|
841
|
+
record = {"rowid": "r1", "c1": "hello"}
|
|
842
|
+
controls = [{"controlId": "c1", "controlName": "Name"}]
|
|
843
|
+
result = format_record_row(record, controls)
|
|
844
|
+
assert "Name" in result
|
|
845
|
+
assert result["Name"] == "hello"
|
|
846
|
+
|
|
847
|
+
def test_format_record_row_json_array(self):
|
|
848
|
+
record = {"rowid": "r1", "c1": json.dumps([{"name": "A"}, {"name": "B"}])}
|
|
849
|
+
result = format_record_row(record)
|
|
850
|
+
assert result["c1"] == "A, B"
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
# ── Auth Tests ───────────────────────────────────────────────────────────
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
class TestAuth:
|
|
857
|
+
def test_resolve_server_mingdao(self):
|
|
858
|
+
from hap_cli.core.auth import resolve_server
|
|
859
|
+
api, webui = resolve_server("mingdao")
|
|
860
|
+
assert api == "https://www.mingdao.com/api"
|
|
861
|
+
assert webui == "https://www.mingdao.com"
|
|
862
|
+
|
|
863
|
+
def test_resolve_server_nocoly(self):
|
|
864
|
+
from hap_cli.core.auth import resolve_server
|
|
865
|
+
api, webui = resolve_server("nocoly")
|
|
866
|
+
assert api == "https://www.nocoly.com/wwwapi"
|
|
867
|
+
assert webui == "https://www.nocoly.com"
|
|
868
|
+
|
|
869
|
+
def test_resolve_server_self_hosted(self):
|
|
870
|
+
from hap_cli.core.auth import resolve_server
|
|
871
|
+
api, webui = resolve_server("https://hap.example.com")
|
|
872
|
+
assert api == "https://hap.example.com/wwwapi"
|
|
873
|
+
assert webui == "https://hap.example.com"
|
|
874
|
+
|
|
875
|
+
def test_resolve_server_trailing_slash(self):
|
|
876
|
+
from hap_cli.core.auth import resolve_server
|
|
877
|
+
api, webui = resolve_server("https://hap.example.com/")
|
|
878
|
+
assert api == "https://hap.example.com/wwwapi"
|
|
879
|
+
assert webui == "https://hap.example.com"
|
|
880
|
+
|
|
881
|
+
def test_build_auth_url(self):
|
|
882
|
+
from hap_cli.core.auth import build_auth_url
|
|
883
|
+
import base64
|
|
884
|
+
url = build_auth_url("https://www.mingdao.com", 5100)
|
|
885
|
+
assert url.startswith("https://www.mingdao.com/cliauth?p=")
|
|
886
|
+
# Decode and verify callback info
|
|
887
|
+
encoded = url.split("?p=")[1]
|
|
888
|
+
decoded = json.loads(base64.b64decode(encoded).decode())
|
|
889
|
+
assert decoded == {"url": "http://localhost:5100"}
|
|
890
|
+
|
|
891
|
+
def test_get_available_port(self):
|
|
892
|
+
from hap_cli.core.auth import get_available_port
|
|
893
|
+
port = get_available_port()
|
|
894
|
+
assert 5100 <= port < 5200
|
|
895
|
+
|
|
896
|
+
@patch("hap_cli.core.auth.requests.post")
|
|
897
|
+
def test_get_user_info_success(self, mock_post):
|
|
898
|
+
from hap_cli.core.auth import get_user_info
|
|
899
|
+
resp = MagicMock()
|
|
900
|
+
resp.json.return_value = {
|
|
901
|
+
"state": 1,
|
|
902
|
+
"data": {
|
|
903
|
+
"md.global": {
|
|
904
|
+
"Account": {
|
|
905
|
+
"accountId": "uid1",
|
|
906
|
+
"fullname": "Test User",
|
|
907
|
+
"email": "test@example.com",
|
|
908
|
+
"avatar": "avatar.png",
|
|
909
|
+
"lang": "zh-Hans",
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
},
|
|
913
|
+
}
|
|
914
|
+
resp.raise_for_status = MagicMock()
|
|
915
|
+
mock_post.return_value = resp
|
|
916
|
+
|
|
917
|
+
info = get_user_info("https://www.mingdao.com/api", "tok123")
|
|
918
|
+
assert info["id"] == "uid1"
|
|
919
|
+
assert info["name"] == "Test User"
|
|
920
|
+
assert info["email"] == "test@example.com"
|
|
921
|
+
assert info["lang"] == "zh-Hans"
|
|
922
|
+
|
|
923
|
+
# Verify correct API call
|
|
924
|
+
call_args = mock_post.call_args
|
|
925
|
+
assert "/Global/GetGlobalMeta" in call_args[0][0]
|
|
926
|
+
assert call_args[1]["headers"]["Authorization"] == "md_pss_id tok123"
|
|
927
|
+
|
|
928
|
+
@patch("hap_cli.core.auth.requests.post")
|
|
929
|
+
def test_get_user_info_invalid_token(self, mock_post):
|
|
930
|
+
from hap_cli.core.auth import get_user_info
|
|
931
|
+
resp = MagicMock()
|
|
932
|
+
resp.json.return_value = {"state": 0, "exception": "Invalid token"}
|
|
933
|
+
resp.raise_for_status = MagicMock()
|
|
934
|
+
mock_post.return_value = resp
|
|
935
|
+
|
|
936
|
+
with pytest.raises(ValueError, match="Invalid token"):
|
|
937
|
+
get_user_info("https://www.mingdao.com/api", "bad_token")
|
|
938
|
+
|
|
939
|
+
def test_config_login_help(self):
|
|
940
|
+
runner = CliRunner()
|
|
941
|
+
result = runner.invoke(cli, ["config", "login", "--help"])
|
|
942
|
+
assert result.exit_code == 0
|
|
943
|
+
assert "Login via browser" in result.output
|
|
944
|
+
assert "mingdao" in result.output
|
|
945
|
+
|
|
946
|
+
def test_config_logout(self, tmp_config):
|
|
947
|
+
runner = CliRunner()
|
|
948
|
+
runner.invoke(cli, [
|
|
949
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
950
|
+
])
|
|
951
|
+
result = runner.invoke(cli, ["config", "logout"])
|
|
952
|
+
assert result.exit_code == 0
|
|
953
|
+
assert "Logged out" in result.output
|
|
954
|
+
|
|
955
|
+
# Verify token is cleared
|
|
956
|
+
result = runner.invoke(cli, ["--json", "config", "show"])
|
|
957
|
+
data = json.loads(result.output)
|
|
958
|
+
assert data["auth_token"] == ""
|
|
959
|
+
assert data["configured"] is False
|
|
960
|
+
|
|
961
|
+
@patch("hap_cli.core.auth.get_user_info")
|
|
962
|
+
def test_config_whoami(self, mock_user_info, tmp_config):
|
|
963
|
+
runner = CliRunner()
|
|
964
|
+
runner.invoke(cli, [
|
|
965
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
966
|
+
])
|
|
967
|
+
mock_user_info.return_value = {
|
|
968
|
+
"id": "uid1", "name": "Test", "email": "t@e.com",
|
|
969
|
+
"avatar": "", "lang": "en",
|
|
970
|
+
}
|
|
971
|
+
result = runner.invoke(cli, ["--json", "config", "whoami"])
|
|
972
|
+
assert result.exit_code == 0
|
|
973
|
+
data = json.loads(result.output)
|
|
974
|
+
assert data["name"] == "Test"
|
|
975
|
+
assert data["email"] == "t@e.com"
|
|
976
|
+
|
|
977
|
+
def test_config_whoami_not_logged_in(self, tmp_config):
|
|
978
|
+
runner = CliRunner()
|
|
979
|
+
result = runner.invoke(cli, ["config", "whoami"])
|
|
980
|
+
assert result.exit_code != 0
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
# ── CLI Tests ────────────────────────────────────────────────────────────
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
class TestCLI:
|
|
987
|
+
def test_help(self):
|
|
988
|
+
runner = CliRunner()
|
|
989
|
+
result = runner.invoke(cli, ["--help"])
|
|
990
|
+
assert result.exit_code == 0
|
|
991
|
+
assert "MingDAO HAP" in result.output
|
|
992
|
+
assert "app" in result.output
|
|
993
|
+
assert "record" in result.output
|
|
994
|
+
assert "workflow" in result.output
|
|
995
|
+
|
|
996
|
+
def test_config_set_and_show(self, tmp_config):
|
|
997
|
+
runner = CliRunner()
|
|
998
|
+
result = runner.invoke(cli, [
|
|
999
|
+
"config", "set",
|
|
1000
|
+
"--server", "https://test.com",
|
|
1001
|
+
"--token", "tok123",
|
|
1002
|
+
])
|
|
1003
|
+
assert result.exit_code == 0
|
|
1004
|
+
|
|
1005
|
+
result = runner.invoke(cli, ["config", "show"])
|
|
1006
|
+
assert result.exit_code == 0
|
|
1007
|
+
assert "test.com" in result.output
|
|
1008
|
+
|
|
1009
|
+
def test_config_show_json(self, tmp_config):
|
|
1010
|
+
runner = CliRunner()
|
|
1011
|
+
runner.invoke(cli, [
|
|
1012
|
+
"config", "set",
|
|
1013
|
+
"--server", "https://test.com",
|
|
1014
|
+
"--token", "tok123",
|
|
1015
|
+
])
|
|
1016
|
+
result = runner.invoke(cli, ["--json", "config", "show"])
|
|
1017
|
+
assert result.exit_code == 0
|
|
1018
|
+
data = json.loads(result.output)
|
|
1019
|
+
assert data["server_url"] == "https://test.com"
|
|
1020
|
+
assert data["configured"] is True
|
|
1021
|
+
|
|
1022
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1023
|
+
def test_record_list(self, mock_post, tmp_config):
|
|
1024
|
+
# First configure
|
|
1025
|
+
runner = CliRunner()
|
|
1026
|
+
runner.invoke(cli, [
|
|
1027
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
1028
|
+
])
|
|
1029
|
+
mock_post.return_value = _mock_response({
|
|
1030
|
+
"data": [{"rowid": "r1", "c1": "val"}],
|
|
1031
|
+
"count": 1,
|
|
1032
|
+
})
|
|
1033
|
+
result = runner.invoke(cli, ["--json", "record", "list", "ws1"])
|
|
1034
|
+
assert result.exit_code == 0
|
|
1035
|
+
data = json.loads(result.output)
|
|
1036
|
+
assert data["count"] == 1
|
|
1037
|
+
|
|
1038
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1039
|
+
def test_worksheet_fields_json(self, mock_post, tmp_config):
|
|
1040
|
+
runner = CliRunner()
|
|
1041
|
+
runner.invoke(cli, [
|
|
1042
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
1043
|
+
])
|
|
1044
|
+
mock_post.return_value = _mock_response([
|
|
1045
|
+
{"controlId": "c1", "controlName": "Title", "type": 2},
|
|
1046
|
+
])
|
|
1047
|
+
result = runner.invoke(cli, ["--json", "worksheet", "fields", "ws1"])
|
|
1048
|
+
assert result.exit_code == 0
|
|
1049
|
+
data = json.loads(result.output)
|
|
1050
|
+
assert len(data) == 1
|
|
1051
|
+
assert data[0]["controlName"] == "Title"
|
|
1052
|
+
|
|
1053
|
+
def test_missing_app_id_error(self, tmp_config):
|
|
1054
|
+
runner = CliRunner()
|
|
1055
|
+
runner.invoke(cli, [
|
|
1056
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
1057
|
+
])
|
|
1058
|
+
result = runner.invoke(cli, ["app", "worksheets"])
|
|
1059
|
+
assert result.exit_code != 0
|
|
1060
|
+
|
|
1061
|
+
def test_help_includes_new_groups(self):
|
|
1062
|
+
runner = CliRunner()
|
|
1063
|
+
result = runner.invoke(cli, ["--help"])
|
|
1064
|
+
assert "node" in result.output
|
|
1065
|
+
assert "instance" in result.output
|
|
1066
|
+
assert "workflow" in result.output
|
|
1067
|
+
|
|
1068
|
+
def test_workflow_help(self):
|
|
1069
|
+
runner = CliRunner()
|
|
1070
|
+
result = runner.invoke(cli, ["workflow", "--help"])
|
|
1071
|
+
assert result.exit_code == 0
|
|
1072
|
+
assert "create" in result.output
|
|
1073
|
+
assert "publish" in result.output
|
|
1074
|
+
assert "rollback" in result.output
|
|
1075
|
+
assert "trigger" in result.output
|
|
1076
|
+
assert "config-get" in result.output
|
|
1077
|
+
|
|
1078
|
+
def test_node_help(self):
|
|
1079
|
+
runner = CliRunner()
|
|
1080
|
+
result = runner.invoke(cli, ["node", "--help"])
|
|
1081
|
+
assert result.exit_code == 0
|
|
1082
|
+
assert "add" in result.output
|
|
1083
|
+
assert "save" in result.output
|
|
1084
|
+
assert "test-code" in result.output
|
|
1085
|
+
assert "test-webhook" in result.output
|
|
1086
|
+
assert "test-ai" in result.output
|
|
1087
|
+
assert "save-action" in result.output
|
|
1088
|
+
assert "save-search" in result.output
|
|
1089
|
+
assert "save-get-more" in result.output
|
|
1090
|
+
assert "controls" in result.output
|
|
1091
|
+
assert "types" in result.output
|
|
1092
|
+
|
|
1093
|
+
def test_node_types_command(self):
|
|
1094
|
+
runner = CliRunner()
|
|
1095
|
+
result = runner.invoke(cli, ["node", "types"])
|
|
1096
|
+
assert result.exit_code == 0
|
|
1097
|
+
assert "ACTION" in result.output
|
|
1098
|
+
assert "SEARCH" in result.output
|
|
1099
|
+
assert "GET_MORE_RECORD" in result.output
|
|
1100
|
+
|
|
1101
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1102
|
+
def test_node_save_action_cli(self, mock_post, tmp_config):
|
|
1103
|
+
runner = CliRunner()
|
|
1104
|
+
runner.invoke(cli, [
|
|
1105
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
1106
|
+
])
|
|
1107
|
+
mock_post.return_value = _mock_response(True)
|
|
1108
|
+
result = runner.invoke(cli, [
|
|
1109
|
+
"node", "save-action", "wf1", "n1",
|
|
1110
|
+
"-a", "1", "--app-id", "ws_abc",
|
|
1111
|
+
"-f", '[{"fieldId":"c001","type":2,"fieldValue":"hello"}]',
|
|
1112
|
+
])
|
|
1113
|
+
assert result.exit_code == 0
|
|
1114
|
+
assert "saved" in result.output.lower()
|
|
1115
|
+
|
|
1116
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1117
|
+
def test_node_save_search_cli(self, mock_post, tmp_config):
|
|
1118
|
+
runner = CliRunner()
|
|
1119
|
+
runner.invoke(cli, [
|
|
1120
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
1121
|
+
])
|
|
1122
|
+
mock_post.return_value = _mock_response(True)
|
|
1123
|
+
result = runner.invoke(cli, [
|
|
1124
|
+
"node", "save-search", "wf1", "n1",
|
|
1125
|
+
"-a", "406", "--app-id", "ws_abc",
|
|
1126
|
+
])
|
|
1127
|
+
assert result.exit_code == 0
|
|
1128
|
+
|
|
1129
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1130
|
+
def test_node_save_get_more_cli(self, mock_post, tmp_config):
|
|
1131
|
+
runner = CliRunner()
|
|
1132
|
+
runner.invoke(cli, [
|
|
1133
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
1134
|
+
])
|
|
1135
|
+
mock_post.return_value = _mock_response(True)
|
|
1136
|
+
result = runner.invoke(cli, [
|
|
1137
|
+
"node", "save-get-more", "wf1", "n1",
|
|
1138
|
+
"-a", "400", "--app-id", "ws_abc",
|
|
1139
|
+
])
|
|
1140
|
+
assert result.exit_code == 0
|
|
1141
|
+
|
|
1142
|
+
def test_instance_help(self):
|
|
1143
|
+
runner = CliRunner()
|
|
1144
|
+
result = runner.invoke(cli, ["instance", "--help"])
|
|
1145
|
+
assert result.exit_code == 0
|
|
1146
|
+
assert "approve" in result.output
|
|
1147
|
+
assert "reject" in result.output
|
|
1148
|
+
assert "forward" in result.output
|
|
1149
|
+
assert "todo" in result.output
|
|
1150
|
+
assert "batch" in result.output
|
|
1151
|
+
|
|
1152
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1153
|
+
def test_workflow_create_json(self, mock_post, tmp_config):
|
|
1154
|
+
runner = CliRunner()
|
|
1155
|
+
runner.invoke(cli, [
|
|
1156
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
1157
|
+
])
|
|
1158
|
+
mock_post.return_value = _mock_response({"processId": "wf_new"})
|
|
1159
|
+
result = runner.invoke(cli, [
|
|
1160
|
+
"--json", "workflow", "create",
|
|
1161
|
+
"--company-id", "comp1", "--name", "Test Flow", "--app-id", "app1",
|
|
1162
|
+
])
|
|
1163
|
+
assert result.exit_code == 0
|
|
1164
|
+
data = json.loads(result.output)
|
|
1165
|
+
assert data["processId"] == "wf_new"
|
|
1166
|
+
|
|
1167
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1168
|
+
def test_node_list_json(self, mock_post, tmp_config):
|
|
1169
|
+
runner = CliRunner()
|
|
1170
|
+
runner.invoke(cli, [
|
|
1171
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
1172
|
+
])
|
|
1173
|
+
mock_post.return_value = _mock_response({"startId": "n0", "nodes": [{"id": "n1"}]})
|
|
1174
|
+
result = runner.invoke(cli, ["--json", "node", "list", "wf1"])
|
|
1175
|
+
assert result.exit_code == 0
|
|
1176
|
+
data = json.loads(result.output)
|
|
1177
|
+
assert "startId" in data
|
|
1178
|
+
|
|
1179
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1180
|
+
def test_instance_todo_json(self, mock_post, tmp_config):
|
|
1181
|
+
runner = CliRunner()
|
|
1182
|
+
runner.invoke(cli, [
|
|
1183
|
+
"config", "set", "--server", "https://t.com", "--token", "tok",
|
|
1184
|
+
])
|
|
1185
|
+
mock_post.return_value = _mock_response({
|
|
1186
|
+
"list": [{"id": "i1", "title": "Task"}], "total": 1,
|
|
1187
|
+
})
|
|
1188
|
+
result = runner.invoke(cli, ["--json", "instance", "todo"])
|
|
1189
|
+
assert result.exit_code == 0
|
|
1190
|
+
data = json.loads(result.output)
|
|
1191
|
+
assert data["count"] == 1
|
|
1192
|
+
|
|
1193
|
+
|
|
1194
|
+
# ── Optionset Tests ───────────────────────────────────────────────────────
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
class TestOptionset:
|
|
1198
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1199
|
+
def test_get_collections(self, mock_post, mock_session):
|
|
1200
|
+
mock_post.return_value = _mock_response([{"id": "os1", "name": "Status"}])
|
|
1201
|
+
from hap_cli.core import optionset as os_mod
|
|
1202
|
+
result = os_mod.get_collections(mock_session, "app1")
|
|
1203
|
+
assert result == [{"id": "os1", "name": "Status"}]
|
|
1204
|
+
args = mock_post.call_args
|
|
1205
|
+
assert "Worksheet/GetCollectionsByAppId" in args[0][0]
|
|
1206
|
+
assert args[1]["json"]["appId"] == "app1"
|
|
1207
|
+
|
|
1208
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1209
|
+
def test_get_collection(self, mock_post, mock_session):
|
|
1210
|
+
mock_post.return_value = _mock_response({"id": "os1", "options": []})
|
|
1211
|
+
from hap_cli.core import optionset as os_mod
|
|
1212
|
+
result = os_mod.get_collection(mock_session, "os1")
|
|
1213
|
+
assert result["id"] == "os1"
|
|
1214
|
+
args = mock_post.call_args
|
|
1215
|
+
assert "Worksheet/GetCollectionByCollectId" in args[0][0]
|
|
1216
|
+
assert args[1]["json"]["collectId"] == "os1"
|
|
1217
|
+
|
|
1218
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1219
|
+
def test_save_collection(self, mock_post, mock_session):
|
|
1220
|
+
mock_post.return_value = _mock_response({"id": "os1"})
|
|
1221
|
+
from hap_cli.core import optionset as os_mod
|
|
1222
|
+
result = os_mod.save_collection(mock_session, "app1", "Priority", [])
|
|
1223
|
+
assert result["id"] == "os1"
|
|
1224
|
+
args = mock_post.call_args
|
|
1225
|
+
assert "Worksheet/SaveOptionsCollection" in args[0][0]
|
|
1226
|
+
|
|
1227
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1228
|
+
def test_delete_collection(self, mock_post, mock_session):
|
|
1229
|
+
mock_post.return_value = _mock_response(True)
|
|
1230
|
+
from hap_cli.core import optionset as os_mod
|
|
1231
|
+
result = os_mod.delete_collection(mock_session, "os1")
|
|
1232
|
+
assert result is True
|
|
1233
|
+
args = mock_post.call_args
|
|
1234
|
+
assert "Worksheet/DeleteOptionsCollection" in args[0][0]
|
|
1235
|
+
assert args[1]["json"]["collectId"] == "os1"
|
|
1236
|
+
|
|
1237
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1238
|
+
def test_move_collection(self, mock_post, mock_session):
|
|
1239
|
+
mock_post.return_value = _mock_response(True)
|
|
1240
|
+
from hap_cli.core import optionset as os_mod
|
|
1241
|
+
result = os_mod.move_collection(mock_session, "os1", "app2")
|
|
1242
|
+
assert result is True
|
|
1243
|
+
args = mock_post.call_args
|
|
1244
|
+
assert "Worksheet/UpdateOptionsCollectionAppId" in args[0][0]
|
|
1245
|
+
assert args[1]["json"]["collectId"] == "os1"
|
|
1246
|
+
assert args[1]["json"]["appId"] == "app2"
|
|
1247
|
+
|
|
1248
|
+
|
|
1249
|
+
# ── Page Tests ────────────────────────────────────────────────────────────
|
|
1250
|
+
|
|
1251
|
+
|
|
1252
|
+
class TestPage:
|
|
1253
|
+
def test_component_types(self):
|
|
1254
|
+
from hap_cli.core.page import PAGE_COMPONENT_TYPES
|
|
1255
|
+
assert "analysis" in PAGE_COMPONENT_TYPES
|
|
1256
|
+
assert "button" in PAGE_COMPONENT_TYPES
|
|
1257
|
+
assert len(PAGE_COMPONENT_TYPES) >= 6
|
|
1258
|
+
|
|
1259
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1260
|
+
def test_copy_page(self, mock_post, mock_session):
|
|
1261
|
+
mock_post.return_value = _mock_response({"pageId": "pg2"})
|
|
1262
|
+
from hap_cli.core import page as page_mod
|
|
1263
|
+
result = page_mod.copy_page(mock_session, "app1", "pg1", "Copy of Page")
|
|
1264
|
+
assert result["pageId"] == "pg2"
|
|
1265
|
+
args = mock_post.call_args
|
|
1266
|
+
assert "AppManagement/CopyCustomPage" in args[0][0]
|
|
1267
|
+
assert args[1]["json"]["pageId"] == "pg1"
|
|
1268
|
+
|
|
1269
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1270
|
+
def test_add_authorize(self, mock_post, mock_session):
|
|
1271
|
+
mock_post.return_value = _mock_response({"id": "auth1"})
|
|
1272
|
+
from hap_cli.core import page as page_mod
|
|
1273
|
+
result = page_mod.add_authorize(mock_session, "app1", "pg1", "role1")
|
|
1274
|
+
assert result["id"] == "auth1"
|
|
1275
|
+
args = mock_post.call_args
|
|
1276
|
+
assert "AppManagement/AddAuthorize" in args[0][0]
|
|
1277
|
+
|
|
1278
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1279
|
+
def test_get_authorizes(self, mock_post, mock_session):
|
|
1280
|
+
mock_post.return_value = _mock_response([{"id": "auth1"}])
|
|
1281
|
+
from hap_cli.core import page as page_mod
|
|
1282
|
+
result = page_mod.get_authorizes(mock_session, "app1", "pg1")
|
|
1283
|
+
assert result == [{"id": "auth1"}]
|
|
1284
|
+
args = mock_post.call_args
|
|
1285
|
+
assert "AppManagement/GetAuthorizes" in args[0][0]
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
# ── Contact Tests ─────────────────────────────────────────────────────────
|
|
1289
|
+
|
|
1290
|
+
|
|
1291
|
+
class TestContact:
|
|
1292
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1293
|
+
def test_search_contacts(self, mock_post, mock_session):
|
|
1294
|
+
mock_post.return_value = _mock_response([{"accountId": "u1", "fullname": "Alice"}])
|
|
1295
|
+
from hap_cli.core import contact as contact_mod
|
|
1296
|
+
result = contact_mod.search_contacts(mock_session, "Alice", project_id="proj1")
|
|
1297
|
+
assert result == [{"accountId": "u1", "fullname": "Alice"}]
|
|
1298
|
+
args = mock_post.call_args
|
|
1299
|
+
assert "AddressBook/SearchAddressbookAndDepartment" in args[0][0]
|
|
1300
|
+
assert args[1]["json"]["keywords"] == "Alice"
|
|
1301
|
+
|
|
1302
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1303
|
+
def test_get_account_detail(self, mock_post, mock_session):
|
|
1304
|
+
mock_post.return_value = _mock_response({"accountId": "u1", "fullname": "Alice"})
|
|
1305
|
+
from hap_cli.core import contact as contact_mod
|
|
1306
|
+
result = contact_mod.get_account_detail(mock_session, "u1")
|
|
1307
|
+
assert result["accountId"] == "u1"
|
|
1308
|
+
args = mock_post.call_args
|
|
1309
|
+
assert "AddressBook/GetAccountDetail" in args[0][0]
|
|
1310
|
+
|
|
1311
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1312
|
+
def test_get_friends(self, mock_post, mock_session):
|
|
1313
|
+
mock_post.return_value = _mock_response([{"accountId": "u2"}])
|
|
1314
|
+
from hap_cli.core import contact as contact_mod
|
|
1315
|
+
result = contact_mod.get_friends(mock_session)
|
|
1316
|
+
assert result == [{"accountId": "u2"}]
|
|
1317
|
+
args = mock_post.call_args
|
|
1318
|
+
assert "AddressBook/GetAllAddressbook" in args[0][0]
|
|
1319
|
+
|
|
1320
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1321
|
+
def test_add_friend(self, mock_post, mock_session):
|
|
1322
|
+
mock_post.return_value = _mock_response(True)
|
|
1323
|
+
from hap_cli.core import contact as contact_mod
|
|
1324
|
+
result = contact_mod.add_friend(mock_session, "u2")
|
|
1325
|
+
assert result is True
|
|
1326
|
+
args = mock_post.call_args
|
|
1327
|
+
assert "AddressBook/AddFriend" in args[0][0]
|
|
1328
|
+
assert args[1]["json"]["accountId"] == "u2"
|
|
1329
|
+
|
|
1330
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1331
|
+
def test_accept_friend(self, mock_post, mock_session):
|
|
1332
|
+
mock_post.return_value = _mock_response(True)
|
|
1333
|
+
from hap_cli.core import contact as contact_mod
|
|
1334
|
+
result = contact_mod.accept_friend(mock_session, "u2")
|
|
1335
|
+
assert result is True
|
|
1336
|
+
args = mock_post.call_args
|
|
1337
|
+
assert "AddressBook/EditAgreeFriend" in args[0][0]
|
|
1338
|
+
|
|
1339
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1340
|
+
def test_reject_friend(self, mock_post, mock_session):
|
|
1341
|
+
mock_post.return_value = _mock_response(True)
|
|
1342
|
+
from hap_cli.core import contact as contact_mod
|
|
1343
|
+
result = contact_mod.reject_friend(mock_session, "u2")
|
|
1344
|
+
assert result is True
|
|
1345
|
+
args = mock_post.call_args
|
|
1346
|
+
assert "AddressBook/EditRefuseFriend" in args[0][0]
|
|
1347
|
+
|
|
1348
|
+
|
|
1349
|
+
# ── Department Tests ──────────────────────────────────────────────────────
|
|
1350
|
+
|
|
1351
|
+
|
|
1352
|
+
class TestDepartment:
|
|
1353
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1354
|
+
def test_list_departments(self, mock_post, mock_session):
|
|
1355
|
+
mock_post.return_value = _mock_response({"list": [{"departmentId": "d1"}], "total": 1})
|
|
1356
|
+
from hap_cli.core import department as dept_mod
|
|
1357
|
+
result = dept_mod.list_departments(mock_session, "proj1", "d0")
|
|
1358
|
+
assert result["total"] == 1
|
|
1359
|
+
args = mock_post.call_args
|
|
1360
|
+
assert "Department/PagedSubDepartments" in args[0][0]
|
|
1361
|
+
assert args[1]["json"]["projectId"] == "proj1"
|
|
1362
|
+
|
|
1363
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1364
|
+
def test_get_department_info(self, mock_post, mock_session):
|
|
1365
|
+
mock_post.return_value = _mock_response({"departmentId": "d1", "departmentName": "HR"})
|
|
1366
|
+
from hap_cli.core import department as dept_mod
|
|
1367
|
+
result = dept_mod.get_department_info(mock_session, "d1")
|
|
1368
|
+
assert result["departmentId"] == "d1"
|
|
1369
|
+
args = mock_post.call_args
|
|
1370
|
+
assert "Department/GetDepartmentInfo" in args[0][0]
|
|
1371
|
+
|
|
1372
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1373
|
+
def test_create_department(self, mock_post, mock_session):
|
|
1374
|
+
mock_post.return_value = _mock_response({"departmentId": "d2"})
|
|
1375
|
+
from hap_cli.core import department as dept_mod
|
|
1376
|
+
result = dept_mod.create_department(mock_session, "proj1", "Engineering", parent_id="d0")
|
|
1377
|
+
assert result["departmentId"] == "d2"
|
|
1378
|
+
args = mock_post.call_args
|
|
1379
|
+
assert "Department/AddDepartment" in args[0][0]
|
|
1380
|
+
assert args[1]["json"]["departmentName"] == "Engineering"
|
|
1381
|
+
|
|
1382
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1383
|
+
def test_delete_departments(self, mock_post, mock_session):
|
|
1384
|
+
mock_post.return_value = _mock_response(True)
|
|
1385
|
+
from hap_cli.core import department as dept_mod
|
|
1386
|
+
result = dept_mod.delete_departments(mock_session, "proj1", ["d1", "d2"])
|
|
1387
|
+
assert result is True
|
|
1388
|
+
args = mock_post.call_args
|
|
1389
|
+
assert "Department/DeleteDepartments" in args[0][0]
|
|
1390
|
+
assert "d1" in args[1]["json"]["departmentIds"]
|
|
1391
|
+
|
|
1392
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1393
|
+
def test_get_department_users(self, mock_post, mock_session):
|
|
1394
|
+
mock_post.return_value = _mock_response([{"accountId": "u1"}])
|
|
1395
|
+
from hap_cli.core import department as dept_mod
|
|
1396
|
+
result = dept_mod.get_department_users(mock_session, "proj1", "d1")
|
|
1397
|
+
assert result == [{"accountId": "u1"}]
|
|
1398
|
+
args = mock_post.call_args
|
|
1399
|
+
assert "Department/GetProjectDepartmentUsers" in args[0][0]
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
# ── Post Tests ────────────────────────────────────────────────────────────
|
|
1403
|
+
|
|
1404
|
+
|
|
1405
|
+
class TestPost:
|
|
1406
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1407
|
+
def test_get_post_list(self, mock_post, mock_session):
|
|
1408
|
+
mock_post.return_value = _mock_response({"list": [{"postId": "p1"}], "total": 1})
|
|
1409
|
+
from hap_cli.core import post as post_mod
|
|
1410
|
+
result = post_mod.get_post_list(mock_session, "proj1")
|
|
1411
|
+
assert result["total"] == 1
|
|
1412
|
+
args = mock_post.call_args
|
|
1413
|
+
assert "Post/GetPostList" in args[0][0]
|
|
1414
|
+
assert args[1]["json"]["projectId"] == "proj1"
|
|
1415
|
+
|
|
1416
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1417
|
+
def test_get_post_detail(self, mock_post, mock_session):
|
|
1418
|
+
mock_post.return_value = _mock_response({"postId": "p1", "message": "Hello"})
|
|
1419
|
+
from hap_cli.core import post as post_mod
|
|
1420
|
+
result = post_mod.get_post_detail(mock_session, "p1")
|
|
1421
|
+
assert result["postId"] == "p1"
|
|
1422
|
+
args = mock_post.call_args
|
|
1423
|
+
assert "Post/GetPostDetail" in args[0][0]
|
|
1424
|
+
|
|
1425
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1426
|
+
def test_add_post(self, mock_post, mock_session):
|
|
1427
|
+
mock_post.return_value = _mock_response({"postId": "p2"})
|
|
1428
|
+
from hap_cli.core import post as post_mod
|
|
1429
|
+
result = post_mod.add_post(mock_session, "proj1", 1, "Hello World")
|
|
1430
|
+
assert result["postId"] == "p2"
|
|
1431
|
+
args = mock_post.call_args
|
|
1432
|
+
assert "Post/AddPost" in args[0][0]
|
|
1433
|
+
assert args[1]["json"]["postMsg"] == "Hello World"
|
|
1434
|
+
|
|
1435
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1436
|
+
def test_add_comment(self, mock_post, mock_session):
|
|
1437
|
+
mock_post.return_value = _mock_response({"commentId": "c1"})
|
|
1438
|
+
from hap_cli.core import post as post_mod
|
|
1439
|
+
result = post_mod.add_comment(mock_session, "p1", "Great post!")
|
|
1440
|
+
assert result["commentId"] == "c1"
|
|
1441
|
+
args = mock_post.call_args
|
|
1442
|
+
assert "Post/AddPostComment" in args[0][0]
|
|
1443
|
+
assert args[1]["json"]["message"] == "Great post!"
|
|
1444
|
+
|
|
1445
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1446
|
+
def test_like_post(self, mock_post, mock_session):
|
|
1447
|
+
mock_post.return_value = _mock_response(True)
|
|
1448
|
+
from hap_cli.core import post as post_mod
|
|
1449
|
+
result = post_mod.like_post(mock_session, "p1")
|
|
1450
|
+
assert result is True
|
|
1451
|
+
args = mock_post.call_args
|
|
1452
|
+
assert "Post/Like" in args[0][0]
|
|
1453
|
+
|
|
1454
|
+
|
|
1455
|
+
# ── Calendar Tests ────────────────────────────────────────────────────────
|
|
1456
|
+
|
|
1457
|
+
|
|
1458
|
+
class TestCalendar:
|
|
1459
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1460
|
+
def test_get_calendars(self, mock_post, mock_session):
|
|
1461
|
+
mock_post.return_value = _mock_response([{"calendarId": "cal1"}])
|
|
1462
|
+
from hap_cli.core import calendar_mod
|
|
1463
|
+
result = calendar_mod.get_calendars(mock_session, "2026-01-01", "2026-12-31")
|
|
1464
|
+
assert result == [{"calendarId": "cal1"}]
|
|
1465
|
+
args = mock_post.call_args
|
|
1466
|
+
assert "Calendar/GetCalendars" in args[0][0]
|
|
1467
|
+
|
|
1468
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1469
|
+
def test_insert_calendar(self, mock_post, mock_session):
|
|
1470
|
+
mock_post.return_value = _mock_response({"calendarId": "cal2"})
|
|
1471
|
+
from hap_cli.core import calendar_mod
|
|
1472
|
+
result = calendar_mod.insert_calendar(mock_session, "Team Meetings", "2026-01-01", "2026-01-02")
|
|
1473
|
+
assert result["calendarId"] == "cal2"
|
|
1474
|
+
args = mock_post.call_args
|
|
1475
|
+
assert "Calendar/InsertCalendar" in args[0][0]
|
|
1476
|
+
assert args[1]["json"]["name"] == "Team Meetings"
|
|
1477
|
+
|
|
1478
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1479
|
+
def test_delete_calendar(self, mock_post, mock_session):
|
|
1480
|
+
mock_post.return_value = _mock_response(True)
|
|
1481
|
+
from hap_cli.core import calendar_mod
|
|
1482
|
+
result = calendar_mod.delete_calendar(mock_session, "cal1")
|
|
1483
|
+
assert result is True
|
|
1484
|
+
args = mock_post.call_args
|
|
1485
|
+
assert "Calendar/DeleteCalendar" in args[0][0]
|
|
1486
|
+
assert args[1]["json"]["calendarId"] == "cal1"
|
|
1487
|
+
|
|
1488
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1489
|
+
def test_get_categories(self, mock_post, mock_session):
|
|
1490
|
+
mock_post.return_value = _mock_response([{"categoryId": "cat1"}])
|
|
1491
|
+
from hap_cli.core import calendar_mod
|
|
1492
|
+
result = calendar_mod.get_categories(mock_session)
|
|
1493
|
+
assert result == [{"categoryId": "cat1"}]
|
|
1494
|
+
args = mock_post.call_args
|
|
1495
|
+
assert "Calendar/GetUserAllCalCategories" in args[0][0]
|
|
1496
|
+
|
|
1497
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1498
|
+
def test_add_members(self, mock_post, mock_session):
|
|
1499
|
+
mock_post.return_value = _mock_response(True)
|
|
1500
|
+
from hap_cli.core import calendar_mod
|
|
1501
|
+
result = calendar_mod.add_members(mock_session, "cal1", ["u1", "u2"])
|
|
1502
|
+
assert result is True
|
|
1503
|
+
args = mock_post.call_args
|
|
1504
|
+
assert "Calendar/AddMembers" in args[0][0]
|
|
1505
|
+
assert "u1" in args[1]["json"]["memberIds"]
|
|
1506
|
+
|
|
1507
|
+
|
|
1508
|
+
# ── Chat Tests ────────────────────────────────────────────────────────────
|
|
1509
|
+
|
|
1510
|
+
|
|
1511
|
+
class TestChat:
|
|
1512
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1513
|
+
def test_get_chat_list(self, mock_post, mock_session):
|
|
1514
|
+
mock_post.return_value = _mock_response([{"chatId": "ch1"}])
|
|
1515
|
+
from hap_cli.core import chat as chat_mod
|
|
1516
|
+
result = chat_mod.get_chat_list(mock_session)
|
|
1517
|
+
assert result == [{"chatId": "ch1"}]
|
|
1518
|
+
args = mock_post.call_args
|
|
1519
|
+
assert "Chat/GetChatList" in args[0][0]
|
|
1520
|
+
|
|
1521
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1522
|
+
def test_send_message(self, mock_post, mock_session):
|
|
1523
|
+
mock_post.return_value = _mock_response({"msgId": "m1"})
|
|
1524
|
+
from hap_cli.core import chat as chat_mod
|
|
1525
|
+
result = chat_mod.send_message(mock_session, ["u1", "u2"], "Hello!")
|
|
1526
|
+
assert result["msgId"] == "m1"
|
|
1527
|
+
args = mock_post.call_args
|
|
1528
|
+
assert "Message/SendMessageToAccountIds" in args[0][0]
|
|
1529
|
+
assert args[1]["json"]["message"] == "Hello!"
|
|
1530
|
+
assert "u1" in args[1]["json"]["accountIds"]
|
|
1531
|
+
|
|
1532
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1533
|
+
def test_get_group_info(self, mock_post, mock_session):
|
|
1534
|
+
mock_post.return_value = _mock_response({"groupId": "g1", "name": "Dev Team"})
|
|
1535
|
+
from hap_cli.core import chat as chat_mod
|
|
1536
|
+
result = chat_mod.get_group_info(mock_session, "g1")
|
|
1537
|
+
assert result["groupId"] == "g1"
|
|
1538
|
+
args = mock_post.call_args
|
|
1539
|
+
assert "Chat/GetGroupInfo" in args[0][0]
|
|
1540
|
+
assert args[1]["json"]["groupId"] == "g1"
|
|
1541
|
+
|
|
1542
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1543
|
+
def test_send_card(self, mock_post, mock_session):
|
|
1544
|
+
mock_post.return_value = _mock_response({"msgId": "m2"})
|
|
1545
|
+
from hap_cli.core import chat as chat_mod
|
|
1546
|
+
result = chat_mod.send_card(mock_session, "g1", {"title": "Alert"})
|
|
1547
|
+
assert result["msgId"] == "m2"
|
|
1548
|
+
args = mock_post.call_args
|
|
1549
|
+
assert "Chat/SendCardToChat" in args[0][0]
|
|
1550
|
+
|
|
1551
|
+
|
|
1552
|
+
# ── Group Tests ───────────────────────────────────────────────────────────
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
class TestGroup:
|
|
1556
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1557
|
+
def test_get_groups(self, mock_post, mock_session):
|
|
1558
|
+
mock_post.return_value = _mock_response({"list": [{"groupId": "gr1"}], "total": 1})
|
|
1559
|
+
from hap_cli.core import group as group_mod
|
|
1560
|
+
result = group_mod.get_groups(mock_session, "proj1")
|
|
1561
|
+
assert result["total"] == 1
|
|
1562
|
+
args = mock_post.call_args
|
|
1563
|
+
assert "Group/GetGroups" in args[0][0]
|
|
1564
|
+
assert args[1]["json"]["projectId"] == "proj1"
|
|
1565
|
+
|
|
1566
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1567
|
+
def test_get_group_info(self, mock_post, mock_session):
|
|
1568
|
+
mock_post.return_value = _mock_response({"groupId": "gr1", "name": "Frontend"})
|
|
1569
|
+
from hap_cli.core import group as group_mod
|
|
1570
|
+
result = group_mod.get_group_info(mock_session, "gr1")
|
|
1571
|
+
assert result["groupId"] == "gr1"
|
|
1572
|
+
args = mock_post.call_args
|
|
1573
|
+
assert "Group/GetGroupInfo" in args[0][0]
|
|
1574
|
+
|
|
1575
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1576
|
+
def test_add_group(self, mock_post, mock_session):
|
|
1577
|
+
mock_post.return_value = _mock_response({"groupId": "gr2"})
|
|
1578
|
+
from hap_cli.core import group as group_mod
|
|
1579
|
+
result = group_mod.add_group(mock_session, "proj1", "Backend", about="Backend team")
|
|
1580
|
+
assert result["groupId"] == "gr2"
|
|
1581
|
+
args = mock_post.call_args
|
|
1582
|
+
assert "Group/AddGroup" in args[0][0]
|
|
1583
|
+
assert args[1]["json"]["groupName"] == "Backend"
|
|
1584
|
+
|
|
1585
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1586
|
+
def test_remove_group(self, mock_post, mock_session):
|
|
1587
|
+
mock_post.return_value = _mock_response(True)
|
|
1588
|
+
from hap_cli.core import group as group_mod
|
|
1589
|
+
result = group_mod.remove_group(mock_session, "gr1")
|
|
1590
|
+
assert result is True
|
|
1591
|
+
args = mock_post.call_args
|
|
1592
|
+
assert "Group/RemoveGroup" in args[0][0]
|
|
1593
|
+
assert args[1]["json"]["groupId"] == "gr1"
|
|
1594
|
+
|
|
1595
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1596
|
+
def test_remove_user(self, mock_post, mock_session):
|
|
1597
|
+
mock_post.return_value = _mock_response(True)
|
|
1598
|
+
from hap_cli.core import group as group_mod
|
|
1599
|
+
result = group_mod.remove_user(mock_session, "gr1", "u1")
|
|
1600
|
+
assert result is True
|
|
1601
|
+
args = mock_post.call_args
|
|
1602
|
+
assert "Group/RemoveUser" in args[0][0]
|
|
1603
|
+
assert args[1]["json"]["accountId"] == "u1"
|
|
1604
|
+
|
|
1605
|
+
|
|
1606
|
+
# ── AI Tests ──────────────────────────────────────────────────────────────
|
|
1607
|
+
|
|
1608
|
+
|
|
1609
|
+
class TestAI:
|
|
1610
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1611
|
+
def test_get_assistants(self, mock_post, mock_session):
|
|
1612
|
+
mock_post.return_value = _mock_response({"list": [{"assistantId": "a1"}], "total": 1})
|
|
1613
|
+
from hap_cli.core import ai as ai_mod
|
|
1614
|
+
result = ai_mod.get_assistants(mock_session, "proj1")
|
|
1615
|
+
assert result["total"] == 1
|
|
1616
|
+
args = mock_post.call_args
|
|
1617
|
+
assert "Assistant/GetList" in args[0][0]
|
|
1618
|
+
assert args[1]["json"]["projectId"] == "proj1"
|
|
1619
|
+
|
|
1620
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1621
|
+
def test_get_assistant(self, mock_post, mock_session):
|
|
1622
|
+
mock_post.return_value = _mock_response({"assistantId": "a1", "name": "Bot"})
|
|
1623
|
+
from hap_cli.core import ai as ai_mod
|
|
1624
|
+
result = ai_mod.get_assistant(mock_session, "a1")
|
|
1625
|
+
assert result["assistantId"] == "a1"
|
|
1626
|
+
args = mock_post.call_args
|
|
1627
|
+
assert "Assistant/Get" in args[0][0]
|
|
1628
|
+
assert args[1]["json"]["assistantId"] == "a1"
|
|
1629
|
+
|
|
1630
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1631
|
+
def test_upsert_assistant(self, mock_post, mock_session):
|
|
1632
|
+
mock_post.return_value = _mock_response({"assistantId": "a2"})
|
|
1633
|
+
from hap_cli.core import ai as ai_mod
|
|
1634
|
+
config = {"name": "NewBot", "projectId": "proj1"}
|
|
1635
|
+
result = ai_mod.upsert_assistant(mock_session, config)
|
|
1636
|
+
assert result["assistantId"] == "a2"
|
|
1637
|
+
args = mock_post.call_args
|
|
1638
|
+
assert "Assistant/Upsert" in args[0][0]
|
|
1639
|
+
|
|
1640
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1641
|
+
def test_delete_assistant(self, mock_post, mock_session):
|
|
1642
|
+
mock_post.return_value = _mock_response(True)
|
|
1643
|
+
from hap_cli.core import ai as ai_mod
|
|
1644
|
+
result = ai_mod.delete_assistant(mock_session, "a1")
|
|
1645
|
+
assert result is True
|
|
1646
|
+
args = mock_post.call_args
|
|
1647
|
+
assert "Assistant/Delete" in args[0][0]
|
|
1648
|
+
assert args[1]["json"]["assistantId"] == "a1"
|
|
1649
|
+
|
|
1650
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1651
|
+
def test_set_assistant_status(self, mock_post, mock_session):
|
|
1652
|
+
mock_post.return_value = _mock_response(True)
|
|
1653
|
+
from hap_cli.core import ai as ai_mod
|
|
1654
|
+
result = ai_mod.set_assistant_status(mock_session, "a1", 1)
|
|
1655
|
+
assert result is True
|
|
1656
|
+
args = mock_post.call_args
|
|
1657
|
+
assert "Assistant/SetStatus" in args[0][0]
|
|
1658
|
+
assert args[1]["json"]["status"] == 1
|
|
1659
|
+
|
|
1660
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1661
|
+
def test_get_knowledge_bases(self, mock_post, mock_session):
|
|
1662
|
+
mock_post.return_value = _mock_response([{"knowledgeBaseId": "kb1"}])
|
|
1663
|
+
from hap_cli.core import ai as ai_mod
|
|
1664
|
+
result = ai_mod.get_knowledge_bases(mock_session, "proj1")
|
|
1665
|
+
assert result == [{"knowledgeBaseId": "kb1"}]
|
|
1666
|
+
args = mock_post.call_args
|
|
1667
|
+
assert "Assistant/GetListKnowledgeBase" in args[0][0]
|
|
1668
|
+
|
|
1669
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1670
|
+
def test_upsert_knowledge_base(self, mock_post, mock_session):
|
|
1671
|
+
mock_post.return_value = _mock_response({"knowledgeBaseId": "kb2"})
|
|
1672
|
+
from hap_cli.core import ai as ai_mod
|
|
1673
|
+
config = {"name": "Docs KB", "projectId": "proj1"}
|
|
1674
|
+
result = ai_mod.upsert_knowledge_base(mock_session, config)
|
|
1675
|
+
assert result["knowledgeBaseId"] == "kb2"
|
|
1676
|
+
args = mock_post.call_args
|
|
1677
|
+
assert "Assistant/UpsertKnowledgeBase" in args[0][0]
|
|
1678
|
+
|
|
1679
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1680
|
+
def test_get_chatbot_config(self, mock_post, mock_session):
|
|
1681
|
+
mock_post.return_value = _mock_response({"processId": "wf1", "enabled": True})
|
|
1682
|
+
from hap_cli.core import ai as ai_mod
|
|
1683
|
+
result = ai_mod.get_chatbot_config(mock_session, "wf1")
|
|
1684
|
+
assert result["processId"] == "wf1"
|
|
1685
|
+
args = mock_post.call_args
|
|
1686
|
+
assert "workflow/getChatbotConfig" in args[0][0]
|
|
1687
|
+
|
|
1688
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1689
|
+
def test_get_ai_service_status(self, mock_post, mock_session):
|
|
1690
|
+
mock_post.return_value = _mock_response({"enabled": True})
|
|
1691
|
+
from hap_cli.core import ai as ai_mod
|
|
1692
|
+
result = ai_mod.get_ai_service_status(mock_session, "proj1")
|
|
1693
|
+
assert result["enabled"] is True
|
|
1694
|
+
args = mock_post.call_args
|
|
1695
|
+
assert "AIService/GetAIServiceStatus" in args[0][0]
|
|
1696
|
+
|
|
1697
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1698
|
+
def test_get_models(self, mock_post, mock_session):
|
|
1699
|
+
mock_post.return_value = _mock_response([{"modelId": "gpt4"}])
|
|
1700
|
+
from hap_cli.core import ai as ai_mod
|
|
1701
|
+
result = ai_mod.get_models(mock_session, "proj1")
|
|
1702
|
+
assert result == [{"modelId": "gpt4"}]
|
|
1703
|
+
args = mock_post.call_args
|
|
1704
|
+
assert "AIService/GetDeveloperWithModes" in args[0][0]
|
|
1705
|
+
|
|
1706
|
+
|
|
1707
|
+
# ── Plugin Tests ──────────────────────────────────────────────────────────
|
|
1708
|
+
|
|
1709
|
+
|
|
1710
|
+
class TestPlugin:
|
|
1711
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1712
|
+
def test_get_plugins(self, mock_post, mock_session):
|
|
1713
|
+
mock_post.return_value = _mock_response({"list": [{"id": "pl1"}], "total": 1})
|
|
1714
|
+
from hap_cli.core import plugin as plugin_mod
|
|
1715
|
+
result = plugin_mod.get_plugins(mock_session, "proj1")
|
|
1716
|
+
assert result["total"] == 1
|
|
1717
|
+
args = mock_post.call_args
|
|
1718
|
+
assert "Plugin/getList" in args[0][0]
|
|
1719
|
+
assert args[1]["json"]["projectId"] == "proj1"
|
|
1720
|
+
|
|
1721
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1722
|
+
def test_get_plugin_detail(self, mock_post, mock_session):
|
|
1723
|
+
mock_post.return_value = _mock_response({"id": "pl1", "name": "MyPlugin"})
|
|
1724
|
+
from hap_cli.core import plugin as plugin_mod
|
|
1725
|
+
result = plugin_mod.get_plugin_detail(mock_session, "pl1")
|
|
1726
|
+
assert result["id"] == "pl1"
|
|
1727
|
+
args = mock_post.call_args
|
|
1728
|
+
assert "Plugin/getDetail" in args[0][0]
|
|
1729
|
+
assert args[1]["json"]["id"] == "pl1"
|
|
1730
|
+
|
|
1731
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1732
|
+
def test_create_plugin(self, mock_post, mock_session):
|
|
1733
|
+
mock_post.return_value = _mock_response({"id": "pl2"})
|
|
1734
|
+
from hap_cli.core import plugin as plugin_mod
|
|
1735
|
+
result = plugin_mod.create_plugin(mock_session, "proj1", "NewPlugin", description="Desc")
|
|
1736
|
+
assert result["id"] == "pl2"
|
|
1737
|
+
args = mock_post.call_args
|
|
1738
|
+
assert "Plugin/create" in args[0][0]
|
|
1739
|
+
assert args[1]["json"]["name"] == "NewPlugin"
|
|
1740
|
+
assert args[1]["json"]["description"] == "Desc"
|
|
1741
|
+
|
|
1742
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1743
|
+
def test_edit_plugin(self, mock_post, mock_session):
|
|
1744
|
+
mock_post.return_value = _mock_response(True)
|
|
1745
|
+
from hap_cli.core import plugin as plugin_mod
|
|
1746
|
+
result = plugin_mod.edit_plugin(mock_session, "pl1", name="Updated", description="NewDesc")
|
|
1747
|
+
assert result is True
|
|
1748
|
+
args = mock_post.call_args
|
|
1749
|
+
assert "Plugin/edit" in args[0][0]
|
|
1750
|
+
assert args[1]["json"]["name"] == "Updated"
|
|
1751
|
+
|
|
1752
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1753
|
+
def test_remove_plugin(self, mock_post, mock_session):
|
|
1754
|
+
mock_post.return_value = _mock_response(True)
|
|
1755
|
+
from hap_cli.core import plugin as plugin_mod
|
|
1756
|
+
result = plugin_mod.remove_plugin(mock_session, "pl1")
|
|
1757
|
+
assert result is True
|
|
1758
|
+
args = mock_post.call_args
|
|
1759
|
+
assert "Plugin/remove" in args[0][0]
|
|
1760
|
+
assert args[1]["json"]["id"] == "pl1"
|
|
1761
|
+
|
|
1762
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1763
|
+
def test_release_plugin(self, mock_post, mock_session):
|
|
1764
|
+
mock_post.return_value = _mock_response({"versionId": "v2"})
|
|
1765
|
+
from hap_cli.core import plugin as plugin_mod
|
|
1766
|
+
result = plugin_mod.release_plugin(mock_session, "pl1")
|
|
1767
|
+
assert result["versionId"] == "v2"
|
|
1768
|
+
args = mock_post.call_args
|
|
1769
|
+
assert "Plugin/release" in args[0][0]
|
|
1770
|
+
assert args[1]["json"]["id"] == "pl1"
|
|
1771
|
+
|
|
1772
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1773
|
+
def test_rollback_plugin(self, mock_post, mock_session):
|
|
1774
|
+
mock_post.return_value = _mock_response(True)
|
|
1775
|
+
from hap_cli.core import plugin as plugin_mod
|
|
1776
|
+
result = plugin_mod.rollback_plugin(mock_session, "pl1", "v1")
|
|
1777
|
+
assert result is True
|
|
1778
|
+
args = mock_post.call_args
|
|
1779
|
+
assert "Plugin/rollback" in args[0][0]
|
|
1780
|
+
assert args[1]["json"]["versionId"] == "v1"
|
|
1781
|
+
|
|
1782
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1783
|
+
def test_get_release_history(self, mock_post, mock_session):
|
|
1784
|
+
mock_post.return_value = _mock_response([{"versionId": "v1"}, {"versionId": "v2"}])
|
|
1785
|
+
from hap_cli.core import plugin as plugin_mod
|
|
1786
|
+
result = plugin_mod.get_release_history(mock_session, "pl1")
|
|
1787
|
+
assert len(result) == 2
|
|
1788
|
+
args = mock_post.call_args
|
|
1789
|
+
assert "Plugin/getReleaseHistory" in args[0][0]
|
|
1790
|
+
|
|
1791
|
+
|
|
1792
|
+
class TestConfigOrgs:
|
|
1793
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1794
|
+
def test_orgs_list(self, mock_post, tmp_config):
|
|
1795
|
+
runner = CliRunner()
|
|
1796
|
+
runner.invoke(cli, ["config", "set", "--server", "https://t.com", "--token", "tok"])
|
|
1797
|
+
mock_post.return_value = _mock_response([
|
|
1798
|
+
{"projectId": "p1", "companyName": "Acme Corp", "roleName": "Admin"},
|
|
1799
|
+
{"projectId": "p2", "companyName": "Beta Inc", "roleName": "Member"},
|
|
1800
|
+
])
|
|
1801
|
+
result = runner.invoke(cli, ["config", "orgs"])
|
|
1802
|
+
assert result.exit_code == 0
|
|
1803
|
+
assert "p1" in result.output
|
|
1804
|
+
assert "Acme Corp" in result.output
|
|
1805
|
+
|
|
1806
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1807
|
+
def test_orgs_json(self, mock_post, tmp_config):
|
|
1808
|
+
runner = CliRunner()
|
|
1809
|
+
runner.invoke(cli, ["config", "set", "--server", "https://t.com", "--token", "tok"])
|
|
1810
|
+
mock_post.return_value = _mock_response([
|
|
1811
|
+
{"projectId": "p1", "companyName": "Acme Corp"},
|
|
1812
|
+
])
|
|
1813
|
+
result = runner.invoke(cli, ["--json", "config", "orgs"])
|
|
1814
|
+
assert result.exit_code == 0
|
|
1815
|
+
import json as _json
|
|
1816
|
+
data = _json.loads(result.output)
|
|
1817
|
+
assert data[0]["projectId"] == "p1"
|
|
1818
|
+
|
|
1819
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1820
|
+
def test_orgs_api_call(self, mock_post, mock_session):
|
|
1821
|
+
mock_post.return_value = _mock_response([{"projectId": "p1"}])
|
|
1822
|
+
mock_session.api_call("Account", "GetProjectList", {})
|
|
1823
|
+
args = mock_post.call_args
|
|
1824
|
+
assert "Account/GetProjectList" in args[0][0]
|