Skip to content

Commit 32a9550

Browse files
committed
Add test coverage for client.py
Needed after refactoring and removing test_main_regression.py.
1 parent 1e16b00 commit 32a9550

2 files changed

Lines changed: 185 additions & 0 deletions

File tree

changelog.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
Upcoming (TBD)
2+
==============
3+
4+
Internal
5+
--------
6+
* Add test coverage for `client.py`.
7+
8+
19
1.74.0 (2026/06/06)
210
==============
311

test/pytests/test_client.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
from __future__ import annotations
2+
3+
from io import StringIO, TextIOWrapper
4+
import os
5+
from pathlib import Path
6+
from types import SimpleNamespace
7+
from typing import Any
8+
9+
import pytest
10+
11+
import mycli.client as client_module
12+
from mycli.client import MyCli
13+
14+
15+
def write_myclirc(tmp_path: Path, content: str) -> str:
16+
myclirc = tmp_path / 'myclirc'
17+
myclirc.write_text(content, encoding='utf-8')
18+
return str(myclirc)
19+
20+
21+
def patch_constructor_side_effects(monkeypatch: pytest.MonkeyPatch) -> None:
22+
monkeypatch.setattr(MyCli, 'system_config_files', [])
23+
monkeypatch.setattr(MyCli, 'pwd_config_file', os.devnull)
24+
monkeypatch.setattr(MyCli, 'initialize_logging', lambda self: None)
25+
monkeypatch.setattr(MyCli, 'register_special_commands', lambda self: None)
26+
monkeypatch.setattr(client_module, 'get_mylogin_cnf_path', lambda: None)
27+
28+
29+
def test_init_reports_invalid_ssl_mode(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
30+
patch_constructor_side_effects(monkeypatch)
31+
echo_calls: list[tuple[str, dict[str, Any]]] = []
32+
monkeypatch.setattr(MyCli, 'echo', lambda self, message, **kwargs: echo_calls.append((message, kwargs)))
33+
myclirc = write_myclirc(
34+
tmp_path,
35+
"""
36+
[main]
37+
ssl_mode = invalid
38+
""",
39+
)
40+
41+
cli = MyCli(myclirc=myclirc)
42+
43+
assert cli.ssl_mode is None
44+
assert echo_calls == [('Invalid config option provided for ssl_mode (invalid); ignoring.', {'err': True, 'fg': 'red'})]
45+
46+
47+
def test_init_uses_defaults_file_for_mysql_config_files(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
48+
patch_constructor_side_effects(monkeypatch)
49+
defaults_file = tmp_path / 'defaults.cnf'
50+
defaults_file.write_text('[client]\nuser = alice\n', encoding='utf-8')
51+
myclirc = write_myclirc(tmp_path, '')
52+
53+
cli = MyCli(defaults_file=str(defaults_file), myclirc=myclirc)
54+
55+
assert cli.cnf_files == [str(defaults_file)]
56+
57+
58+
def test_init_honors_explicit_show_warnings(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
59+
patch_constructor_side_effects(monkeypatch)
60+
show_warnings_calls: list[bool] = []
61+
monkeypatch.setattr(client_module.special, 'set_show_warnings_enabled', lambda value: show_warnings_calls.append(value))
62+
myclirc = write_myclirc(tmp_path, '')
63+
64+
MyCli(myclirc=myclirc, show_warnings=True)
65+
66+
assert show_warnings_calls == [True]
67+
68+
69+
def test_init_uses_cli_verbosity_override(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
70+
patch_constructor_side_effects(monkeypatch)
71+
myclirc = write_myclirc(tmp_path, '')
72+
73+
cli = MyCli(myclirc=myclirc, cli_verbosity=2)
74+
75+
assert cli.verbosity == 2
76+
77+
78+
def test_init_writes_default_config_when_user_config_is_missing(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
79+
patch_constructor_side_effects(monkeypatch)
80+
write_calls: list[str] = []
81+
myclirc = tmp_path / 'missing-myclirc'
82+
monkeypatch.setattr(client_module, 'write_default_config', lambda destination: write_calls.append(destination))
83+
84+
MyCli(myclirc=str(myclirc))
85+
86+
assert write_calls == [str(myclirc)]
87+
88+
89+
def test_init_opens_audit_log_from_config(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
90+
patch_constructor_side_effects(monkeypatch)
91+
audit_log = tmp_path / 'audit.log'
92+
myclirc = write_myclirc(
93+
tmp_path,
94+
f"""
95+
[main]
96+
audit_log = {audit_log}
97+
""",
98+
)
99+
100+
cli = MyCli(myclirc=myclirc)
101+
try:
102+
assert isinstance(cli.logfile, TextIOWrapper)
103+
assert Path(cli.logfile.name) == audit_log
104+
finally:
105+
if isinstance(cli.logfile, TextIOWrapper):
106+
cli.logfile.close()
107+
108+
109+
def test_init_disables_audit_log_when_file_cannot_be_opened(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
110+
patch_constructor_side_effects(monkeypatch)
111+
echo_calls: list[tuple[str, dict[str, Any]]] = []
112+
monkeypatch.setattr(MyCli, 'echo', lambda self, message, **kwargs: echo_calls.append((message, kwargs)))
113+
myclirc = write_myclirc(
114+
tmp_path,
115+
f"""
116+
[main]
117+
audit_log = {tmp_path}
118+
""",
119+
)
120+
121+
cli = MyCli(myclirc=myclirc)
122+
123+
assert cli.logfile is False
124+
assert echo_calls == [
125+
(
126+
'Error: Unable to open the audit log file. Your queries will not be logged.',
127+
{'err': True, 'fg': 'red'},
128+
)
129+
]
130+
131+
132+
def test_init_reports_unreadable_mylogin_cnf(monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
133+
patch_constructor_side_effects(monkeypatch)
134+
mylogin_path = tmp_path / 'mylogin.cnf'
135+
mylogin_path.write_text('bad', encoding='utf-8')
136+
monkeypatch.setattr(client_module, 'get_mylogin_cnf_path', lambda: str(mylogin_path))
137+
monkeypatch.setattr(client_module, 'open_mylogin_cnf', lambda path: None)
138+
myclirc = write_myclirc(tmp_path, '')
139+
140+
MyCli(myclirc=myclirc)
141+
142+
assert 'Error: Unable to read login path file.' in capsys.readouterr().out
143+
144+
145+
def test_init_appends_readable_mylogin_cnf(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
146+
patch_constructor_side_effects(monkeypatch)
147+
mylogin_cnf = StringIO('[client]\nuser = alice\n')
148+
monkeypatch.setattr(client_module, 'get_mylogin_cnf_path', lambda: '/tmp/mylogin.cnf')
149+
monkeypatch.setattr(client_module, 'open_mylogin_cnf', lambda path: mylogin_cnf)
150+
myclirc = write_myclirc(tmp_path, '')
151+
152+
cli = MyCli(myclirc=myclirc)
153+
154+
assert cli.cnf_files[-1] is mylogin_cnf
155+
156+
157+
def test_close_stops_schema_prefetcher_and_closes_sqlexecute() -> None:
158+
cli = MyCli.__new__(MyCli)
159+
stopped: list[bool] = []
160+
closed: list[bool] = []
161+
cli.schema_prefetcher = SimpleNamespace(stop=lambda: stopped.append(True))
162+
cli.sqlexecute = SimpleNamespace(close=lambda: closed.append(True)) # type: ignore[assignment]
163+
164+
MyCli.close(cli)
165+
166+
assert stopped == [True]
167+
assert closed == [True]
168+
169+
170+
def test_run_cli_delegates_to_main_repl(monkeypatch: pytest.MonkeyPatch) -> None:
171+
cli = MyCli.__new__(MyCli)
172+
calls: list[MyCli] = []
173+
monkeypatch.setattr(client_module.repl_package, 'main_repl', lambda target: calls.append(target))
174+
175+
MyCli.run_cli(cli)
176+
177+
assert calls == [cli]

0 commit comments

Comments
 (0)