优化 CLI 命令,增加获取所有节点和管道属性的功能
This commit is contained in:
@@ -97,14 +97,58 @@ def test_auth_stdin_can_be_reused_with_runtime_context_cache(monkeypatch):
|
||||
assert len(observed_runtime_ids) == 1
|
||||
|
||||
|
||||
def test_network_get_all_junction_properties_uses_network_context(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
def fake_request_json(ctx, **kwargs):
|
||||
captured["access_token"] = ctx.auth.access_token
|
||||
captured["params"] = kwargs["params"]
|
||||
return [{"id": "J1"}], 5
|
||||
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||
|
||||
result = runner.invoke(app, ["network", "get-all-junction-properties"])
|
||||
payload = json.loads(result.stdout)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert payload["ok"] is True
|
||||
assert payload["data"] == [{"id": "J1"}]
|
||||
assert captured == {"access_token": "abc", "params": {"network": "tjwater"}}
|
||||
|
||||
|
||||
def test_network_get_all_pipe_properties_uses_network_context(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
def fake_request_json(ctx, **kwargs):
|
||||
captured["access_token"] = ctx.auth.access_token
|
||||
captured["params"] = kwargs["params"]
|
||||
return [{"id": "P1"}], 5
|
||||
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||
|
||||
result = runner.invoke(app, ["network", "get-all-pipe-properties"])
|
||||
payload = json.loads(result.stdout)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert payload["ok"] is True
|
||||
assert payload["data"] == [{"id": "P1"}]
|
||||
assert captured == {"access_token": "abc", "params": {"network": "tjwater"}}
|
||||
|
||||
|
||||
def test_help_outputs_json_lists_commands():
|
||||
result = runner.invoke(app, ["help"])
|
||||
payload = json.loads(result.stdout)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert payload["schema_version"] == "tjwater-cli/v1"
|
||||
assert any(command["command"] == "project" for command in payload["commands"])
|
||||
assert any(command["command"] == "analysis" for command in payload["commands"])
|
||||
assert all(command["command"] != "project" for command in payload["commands"])
|
||||
assert payload["menu_level"] == 1
|
||||
assert all(command["command"] != "project list" for command in payload["commands"])
|
||||
|
||||
@@ -137,7 +181,7 @@ def test_nested_group_help_lists_examples():
|
||||
assert payload["summary"] == "漏损分析相关命令。"
|
||||
commands = {command["command"]: command for command in payload["commands"]}
|
||||
assert commands["analysis leakage identify"]["summary"] == "执行漏损识别"
|
||||
assert commands["analysis leakage identify"]["example"] == "tjwater-cli analysis leakage identify --start-time 2025-01-02T03:04:05+08:00 --end-time 2025-01-02T04:04:05+08:00"
|
||||
assert commands["analysis leakage identify"]["example"] == "tjwater-cli analysis leakage identify --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --scheme leak_case_01"
|
||||
|
||||
|
||||
def test_analysis_help_uses_group_summaries_for_nested_groups():
|
||||
@@ -150,8 +194,8 @@ def test_analysis_help_uses_group_summaries_for_nested_groups():
|
||||
assert commands["analysis burst-detection"]["summary"] == "爆管检测相关命令。"
|
||||
assert "analysis burst-location" not in commands
|
||||
assert "analysis risk" not in commands
|
||||
assert commands["analysis burst"]["example"] == "tjwater-cli analysis burst --start-time 2025-01-02T03:04:05+08:00 --duration 30 --burst-file ./burst.json --scheme burst_case_01"
|
||||
assert commands["analysis valve"]["example"] == "tjwater-cli analysis valve --mode close --start-time 2025-01-02T03:04:05+08:00 --valve V1 --duration 900"
|
||||
assert commands["analysis burst"]["example"] == "tjwater-cli analysis burst --start-time 2025-01-02T03:04:05+08:00 --duration 900 --burst-file ./burst.json --scheme burst_case_01"
|
||||
assert commands["analysis valve"]["example"] == "tjwater-cli analysis valve --mode close --start-time 2025-01-02T03:04:05+08:00 --valve V1 --valve V2 --duration 900 --scheme valve_case_01"
|
||||
|
||||
|
||||
def test_bare_analysis_uses_typer_help_with_descriptions():
|
||||
@@ -178,15 +222,6 @@ def test_leaf_help_outputs_json():
|
||||
assert "simulation run" in payload["examples"][0]
|
||||
|
||||
|
||||
def test_project_help_uses_legal_kind_example():
|
||||
result = runner.invoke(app, ["project", "help"])
|
||||
payload = json.loads(result.stdout)
|
||||
commands = {command["command"]: command for command in payload["commands"]}
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "project data" in commands["project data"]["example"]
|
||||
|
||||
|
||||
def test_root_help_flag_uses_typer_style_with_examples():
|
||||
result = runner.invoke(app, ["--help"], prog_name="tjwater-cli")
|
||||
|
||||
@@ -244,6 +279,214 @@ def test_analysis_burst_returns_next_step_to_fetch_scheme(monkeypatch, tmp_path:
|
||||
assert "tjwater-cli data scheme list" in result.stdout
|
||||
|
||||
|
||||
def test_analysis_contaminant_sends_required_scheme_name(monkeypatch):
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
||||
captured = {}
|
||||
|
||||
def fake_request(**kwargs):
|
||||
captured.update(kwargs)
|
||||
return DummyResponse(text="success", headers={"content-type": "text/plain"})
|
||||
|
||||
monkeypatch.setattr(core.requests, "request", fake_request)
|
||||
|
||||
result = runner.invoke(
|
||||
app,
|
||||
[
|
||||
"analysis",
|
||||
"contaminant",
|
||||
"--start-time",
|
||||
"2025-01-02T03:04:05+08:00",
|
||||
"--duration",
|
||||
"900",
|
||||
"--source-node",
|
||||
"N1",
|
||||
"--concentration",
|
||||
"10.0",
|
||||
"--scheme",
|
||||
"contam_case_01",
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert captured["params"] == {
|
||||
"network": "demo",
|
||||
"start_time": "2025-01-02T03:04:05+08:00",
|
||||
"source": "N1",
|
||||
"concentration": 10.0,
|
||||
"duration": 900,
|
||||
"scheme_name": "contam_case_01",
|
||||
}
|
||||
|
||||
|
||||
def test_analysis_flushing_sends_required_scheme_name(monkeypatch, tmp_path: Path):
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
||||
captured = {}
|
||||
valve_path = tmp_path / "valve.json"
|
||||
valve_path.write_text('[{"valve":"V1","opening":0.5}]', encoding="utf-8")
|
||||
|
||||
def fake_request(**kwargs):
|
||||
captured.update(kwargs)
|
||||
return DummyResponse(text="success", headers={"content-type": "text/plain"})
|
||||
|
||||
monkeypatch.setattr(core.requests, "request", fake_request)
|
||||
|
||||
result = runner.invoke(
|
||||
app,
|
||||
[
|
||||
"analysis",
|
||||
"flushing",
|
||||
"--start-time",
|
||||
"2025-01-02T03:04:05+08:00",
|
||||
"--valve-setting-file",
|
||||
str(valve_path),
|
||||
"--drainage-node",
|
||||
"N1",
|
||||
"--flow",
|
||||
"100.0",
|
||||
"--duration",
|
||||
"900",
|
||||
"--scheme",
|
||||
"flush_case_01",
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert captured["params"] == {
|
||||
"network": "demo",
|
||||
"start_time": "2025-01-02T03:04:05+08:00",
|
||||
"valves": ["V1"],
|
||||
"valves_k": [0.5],
|
||||
"drainage_node_ID": "N1",
|
||||
"flush_flow": 100.0,
|
||||
"duration": 900,
|
||||
"scheme_name": "flush_case_01",
|
||||
}
|
||||
|
||||
|
||||
def test_analysis_valve_close_sends_required_scheme_name(monkeypatch):
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
||||
captured = {}
|
||||
|
||||
def fake_request(**kwargs):
|
||||
captured.update(kwargs)
|
||||
return DummyResponse(text="success", headers={"content-type": "text/plain"})
|
||||
|
||||
monkeypatch.setattr(core.requests, "request", fake_request)
|
||||
|
||||
result = runner.invoke(
|
||||
app,
|
||||
[
|
||||
"analysis",
|
||||
"valve",
|
||||
"--mode",
|
||||
"close",
|
||||
"--start-time",
|
||||
"2025-01-02T03:04:05+08:00",
|
||||
"--valve",
|
||||
"V1",
|
||||
"--duration",
|
||||
"900",
|
||||
"--scheme",
|
||||
"valve_case_01",
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert captured["params"] == {
|
||||
"network": "demo",
|
||||
"start_time": "2025-01-02T03:04:05+08:00",
|
||||
"valves": ["V1"],
|
||||
"duration": 900,
|
||||
"scheme_name": "valve_case_01",
|
||||
}
|
||||
|
||||
|
||||
def test_analysis_contaminant_requires_scheme(monkeypatch, capsys):
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
||||
|
||||
exit_code = main(
|
||||
[
|
||||
"analysis",
|
||||
"contaminant",
|
||||
"--start-time",
|
||||
"2025-01-02T03:04:05+08:00",
|
||||
"--duration",
|
||||
"900",
|
||||
"--source-node",
|
||||
"N1",
|
||||
"--concentration",
|
||||
"10.0",
|
||||
],
|
||||
)
|
||||
|
||||
stdout = capsys.readouterr().out
|
||||
|
||||
assert exit_code == 2
|
||||
assert '"code": "SCHEME_REQUIRED"' in stdout
|
||||
|
||||
|
||||
def test_analysis_flushing_requires_scheme(monkeypatch, tmp_path: Path, capsys):
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
||||
valve_path = tmp_path / "valve.json"
|
||||
valve_path.write_text('[{"valve":"V1","opening":0.5}]', encoding="utf-8")
|
||||
|
||||
exit_code = main(
|
||||
[
|
||||
"analysis",
|
||||
"flushing",
|
||||
"--start-time",
|
||||
"2025-01-02T03:04:05+08:00",
|
||||
"--valve-setting-file",
|
||||
str(valve_path),
|
||||
"--drainage-node",
|
||||
"N1",
|
||||
"--flow",
|
||||
"100.0",
|
||||
],
|
||||
)
|
||||
|
||||
stdout = capsys.readouterr().out
|
||||
|
||||
assert exit_code == 2
|
||||
assert '"code": "SCHEME_REQUIRED"' in stdout
|
||||
|
||||
|
||||
def test_analysis_valve_close_requires_scheme(monkeypatch, capsys):
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
||||
|
||||
exit_code = main(
|
||||
[
|
||||
"analysis",
|
||||
"valve",
|
||||
"--mode",
|
||||
"close",
|
||||
"--start-time",
|
||||
"2025-01-02T03:04:05+08:00",
|
||||
"--valve",
|
||||
"V1",
|
||||
"--duration",
|
||||
"900",
|
||||
],
|
||||
)
|
||||
|
||||
stdout = capsys.readouterr().out
|
||||
|
||||
assert exit_code == 2
|
||||
assert '"code": "SCHEME_REQUIRED"' in stdout
|
||||
|
||||
|
||||
def test_main_missing_option_error_includes_usage_and_next_step(capsys):
|
||||
exit_code = main(["simulation", "run"])
|
||||
stdout = capsys.readouterr().out
|
||||
@@ -265,27 +508,6 @@ def test_main_bare_analysis_returns_typer_help_without_json_error(capsys):
|
||||
assert '"ok": false' not in stdout
|
||||
|
||||
|
||||
def test_project_list_uses_auth_stdin(monkeypatch):
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
monkeypatch.setenv("TJWATER_PROJECT_ID", "pid")
|
||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
||||
captured = {}
|
||||
|
||||
def fake_request(**kwargs):
|
||||
captured.update(kwargs)
|
||||
return DummyResponse(json_data=[{"project_id": "pid", "name": "Demo"}])
|
||||
|
||||
monkeypatch.setattr(core.requests, "request", fake_request)
|
||||
|
||||
result = runner.invoke(app, ["project", "list"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert '"ok": true' in result.stdout
|
||||
assert captured["headers"]["Authorization"] == "Bearer abc"
|
||||
assert captured["url"] == "http://server/api/v1/meta/projects"
|
||||
|
||||
|
||||
def test_simulation_run_translates_rfc3339(monkeypatch):
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
@@ -320,33 +542,9 @@ def test_simulation_run_translates_rfc3339(monkeypatch):
|
||||
assert "tjwater-cli data timeseries realtime nodes" in result.stdout
|
||||
|
||||
|
||||
def test_project_export_inp_downloads_file(monkeypatch, tmp_path: Path):
|
||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
||||
output = tmp_path / "demo.inp"
|
||||
calls = []
|
||||
def test_removed_project_command_returns_not_found(capsys):
|
||||
exit_code = main(["project", "list"])
|
||||
stdout = capsys.readouterr().out
|
||||
|
||||
def fake_request(**kwargs):
|
||||
calls.append(kwargs["url"])
|
||||
if kwargs["url"].endswith("/dumpinp/"):
|
||||
return DummyResponse(json_data=True)
|
||||
return DummyResponse(
|
||||
headers={"content-type": "application/octet-stream"},
|
||||
content=b"inp-content",
|
||||
text="inp-content",
|
||||
)
|
||||
|
||||
monkeypatch.setattr(core.requests, "request", fake_request)
|
||||
|
||||
result = runner.invoke(
|
||||
app,
|
||||
["project", "export-inp", "--output", str(output)],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert output.read_bytes() == b"inp-content"
|
||||
assert calls == [
|
||||
"http://server/api/v1/dumpinp/",
|
||||
"http://server/api/v1/downloadinp/",
|
||||
]
|
||||
assert exit_code == 2
|
||||
assert '"code": "COMMAND_NOT_FOUND"' in stdout or "No such command: project" in stdout
|
||||
|
||||
Reference in New Issue
Block a user