Skip to content

Commit 741df10

Browse files
committed
More validations and make tests better
* Reduce copy-paste amount between tests. * Do not require external mock: py36 version contains everything Signed-off-by: Aleksei Stepanov <penguinolog@gmail.com>
1 parent 3323001 commit 741df10

19 files changed

+137
-176
lines changed

CI_REQUIREMENTS.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
mock # no assert_called_once in py35
21
-r requirements.txt

exec_helpers/async_api/subprocess_runner.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ class Subprocess(api.ExecHelper, metaclass=metaclasses.SingleLock):
6868
__slots__ = ()
6969

7070
def __init__(
71-
self, log_mask_re: typing.Optional[str] = None, *, logger: logging.Logger = logging.getLogger(__name__)
71+
self,
72+
log_mask_re: typing.Optional[str] = None,
73+
*,
74+
logger: logging.Logger = logging.getLogger(__name__), # noqa: B008
7275
) -> None:
7376
"""Subprocess helper with timeouts and lock-free FIFO.
7477

exec_helpers/subprocess_runner.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ class Subprocess(api.ExecHelper, metaclass=metaclasses.SingleLock):
6464
"""Subprocess helper with timeouts and lock-free FIFO."""
6565

6666
def __init__(
67-
self, log_mask_re: typing.Optional[str] = None, *, logger: logging.Logger = logging.getLogger(__name__)
67+
self,
68+
log_mask_re: typing.Optional[str] = None,
69+
*,
70+
logger: logging.Logger = logging.getLogger(__name__), # noqa: B008
6871
) -> None:
6972
"""Subprocess helper with timeouts and lock-free FIFO.
7073

pytest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[pytest]
22
addopts = -vvv -s -p no:django -p no:ipdb
33
testpaths = test
4-
mock_use_standalone_module = true
4+
mock_use_standalone_module = false

test/async_api/test_subprocess.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
import logging
1717
import random
1818
import typing
19+
from unittest import mock
1920

2021
import asynctest
21-
import mock
2222
import pytest
2323

2424
import exec_helpers
@@ -104,6 +104,7 @@ async def read_stream(stream: FakeFileStream):
104104

105105

106106
def pytest_generate_tests(metafunc):
107+
"""Tests parametrization."""
107108
if "run_parameters" in metafunc.fixturenames:
108109
metafunc.parametrize(
109110
"run_parameters",
@@ -123,6 +124,7 @@ def pytest_generate_tests(metafunc):
123124

124125
@pytest.fixture
125126
def run_parameters(request):
127+
"""Tests configuration apply."""
126128
return configs[request.param]
127129

128130

@@ -190,10 +192,12 @@ def create_mock(
190192

191193
@pytest.fixture
192194
def logger(mocker):
195+
"""Simple mock of logger instance."""
193196
return mocker.patch("exec_helpers.async_api.subprocess_runner.Subprocess.logger", autospec=True)
194197

195198

196199
async def test_001_execute_async(create_subprocess_shell, logger, run_parameters) -> None:
200+
"""Test low level API."""
197201
runner = exec_helpers.async_api.Subprocess()
198202
res = await runner.execute_async(
199203
command,
@@ -246,6 +250,7 @@ async def test_001_execute_async(create_subprocess_shell, logger, run_parameters
246250

247251

248252
async def test_002_execute(create_subprocess_shell, logger, exec_result, run_parameters) -> None:
253+
"""Test API without checkers."""
249254
runner = exec_helpers.async_api.Subprocess()
250255
res = await runner.execute(command, stdin=run_parameters["stdin"])
251256
assert isinstance(res, exec_helpers.async_api.ExecResult)
@@ -256,6 +261,7 @@ async def test_002_execute(create_subprocess_shell, logger, exec_result, run_par
256261

257262

258263
async def test_003_context_manager(monkeypatch, create_subprocess_shell, logger, exec_result, run_parameters) -> None:
264+
"""Test context manager for threads synchronization."""
259265
lock = asynctest.CoroutineMock()
260266
lock.attach_mock(asynctest.CoroutineMock("acquire"), "acquire")
261267
lock.attach_mock(mock.Mock("release"), "release")
@@ -272,6 +278,7 @@ async def test_003_context_manager(monkeypatch, create_subprocess_shell, logger,
272278

273279

274280
async def test_004_check_call(execute, exec_result, logger) -> None:
281+
"""Test exit code validator."""
275282
runner = exec_helpers.async_api.Subprocess()
276283
if exec_result.exit_code == exec_helpers.ExitCodes.EX_OK:
277284
assert await runner.check_call(command, stdin=exec_result.stdin) == exec_result
@@ -294,6 +301,7 @@ async def test_004_check_call(execute, exec_result, logger) -> None:
294301

295302

296303
async def test_005_check_call_no_raise(execute, exec_result, logger) -> None:
304+
"""Test exit code validator in permissive mode."""
297305
runner = exec_helpers.async_api.Subprocess()
298306
res = await runner.check_call(command, stdin=exec_result.stdin, raise_on_err=False)
299307
assert res == exec_result
@@ -306,11 +314,13 @@ async def test_005_check_call_no_raise(execute, exec_result, logger) -> None:
306314

307315

308316
async def test_006_check_call_expect(execute, exec_result, logger) -> None:
317+
"""Test exit code validator with custom return codes."""
309318
runner = exec_helpers.async_api.Subprocess()
310319
assert await runner.check_call(command, stdin=exec_result.stdin, expected=[exec_result.exit_code]) == exec_result
311320

312321

313322
async def test_007_check_stderr(execute, exec_result, logger) -> None:
323+
"""Test STDERR content validator."""
314324
runner = exec_helpers.async_api.Subprocess()
315325
if not exec_result.stderr:
316326
assert (
@@ -335,6 +345,7 @@ async def test_007_check_stderr(execute, exec_result, logger) -> None:
335345

336346

337347
async def test_008_check_stderr_no_raise(execute, exec_result, logger) -> None:
348+
"""Test STDERR content validator in permissive mode."""
338349
runner = exec_helpers.async_api.Subprocess()
339350
assert (
340351
await runner.check_stderr(
@@ -345,6 +356,7 @@ async def test_008_check_stderr_no_raise(execute, exec_result, logger) -> None:
345356

346357

347358
async def test_009_call(create_subprocess_shell, logger, exec_result, run_parameters) -> None:
359+
"""Test callable."""
348360
runner = exec_helpers.async_api.Subprocess()
349361
res = await runner(command, stdin=run_parameters["stdin"])
350362
assert isinstance(res, exec_helpers.async_api.ExecResult)

test/async_api/test_subprocess_special.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
import logging
1818
import random
1919
import typing
20+
from unittest import mock
2021

2122
import asynctest
22-
import mock
2323
import pytest
2424

2525
import exec_helpers
@@ -104,6 +104,7 @@ async def read_stream(stream: FakeFileStream):
104104

105105

106106
def pytest_generate_tests(metafunc):
107+
"""Tests parametrization."""
107108
if "run_parameters" in metafunc.fixturenames:
108109
metafunc.parametrize(
109110
"run_parameters",
@@ -129,6 +130,7 @@ def pytest_generate_tests(metafunc):
129130

130131
@pytest.fixture
131132
def run_parameters(request):
133+
"""Tests configuration apply."""
132134
return configs[request.param]
133135

134136

@@ -198,6 +200,7 @@ def logger(mocker):
198200

199201

200202
async def test_special_cases(create_subprocess_shell, exec_result, logger, run_parameters) -> None:
203+
"""Parametrized validation of special cases."""
201204
runner = exec_helpers.async_api.Subprocess(log_mask_re=run_parameters.get("init_log_mask_re", None))
202205
if "expect_exc" not in run_parameters:
203206
res = await runner.execute(

test/conftest.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright 2019 Alexey Stepanov aka penguinolog.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
16+
import pytest
17+
18+
19+
@pytest.fixture
20+
def paramiko_ssh_client(mocker):
21+
"""Minimal paramiko.SSHClient mock."""
22+
mocker.patch("time.sleep")
23+
return mocker.patch("paramiko.SSHClient")
24+
25+
26+
@pytest.fixture
27+
def auto_add_policy(mocker):
28+
"""Minimal paramiko.AutoAddPolicy mock."""
29+
return mocker.patch("paramiko.AutoAddPolicy", return_value="AutoAddPolicy")
30+
31+
32+
@pytest.fixture
33+
def ssh_auth_logger(mocker):
34+
"""Minimal exec_helpers.ssh_auth.logger mock."""
35+
return mocker.patch("exec_helpers.ssh_auth.logger")
36+
37+
38+
@pytest.fixture
39+
def subprocess_logger(mocker):
40+
"""Minimal exec_helpers.subprocess_runner.Subprocess.logger mock."""
41+
return mocker.patch("exec_helpers.subprocess_runner.Subprocess.logger", autospec=True)
42+
43+
44+
@pytest.fixture
45+
def get_logger(mocker):
46+
"""Minimal logging.getLogger mock."""
47+
return mocker.patch("logging.getLogger")

test/test_exec_result.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
class TestExecResult(unittest.TestCase):
3232
@mock.patch("exec_helpers.exec_result.logger")
3333
def test_create_minimal(self, logger):
34-
"""Test defaults"""
34+
"""Test defaults."""
3535
result = exec_helpers.ExecResult(cmd=cmd)
3636
self.assertEqual(result.cmd, cmd)
3737
self.assertEqual(result.cmd, result["cmd"])
@@ -86,14 +86,15 @@ def test_create_minimal(self, logger):
8686

8787
@mock.patch("exec_helpers.exec_result.logger", autospec=True)
8888
def test_not_implemented(self, logger):
89-
"""Test assertion on non implemented deserializer"""
89+
"""Test assertion on non implemented deserializer."""
9090
result = exec_helpers.ExecResult(cmd=cmd)
9191
deserialize = getattr(result, "_ExecResult__deserialize")
9292
with self.assertRaises(NotImplementedError):
9393
deserialize("tst")
9494
logger.assert_has_calls((mock.call.error("{fmt} deserialize target is not implemented".format(fmt="tst")),))
9595

9696
def test_setters(self):
97+
"""Test setters: unlocked and final."""
9798
result = exec_helpers.ExecResult(cmd=cmd)
9899
self.assertEqual(result.exit_code, exec_helpers.ExitCodes.EX_INVALID)
99100

@@ -136,12 +137,13 @@ def test_setters(self):
136137
self.assertEqual(result.stderr_brief, stderr_brief)
137138

138139
def test_json(self):
140+
"""Test json extraction."""
139141
result = exec_helpers.ExecResult("test", stdout=[b'{"test": true}'])
140142
self.assertEqual(result.stdout_json, {"test": True})
141143

142144
@mock.patch("exec_helpers.exec_result.logger", autospec=True)
143145
def test_wrong_result(self, logger):
144-
"""Test logging exception if stdout if not a correct json"""
146+
"""Test logging exception if stdout if not a correct json."""
145147
cmd = r"ls -la | awk '{print $1\}'"
146148
result = exec_helpers.ExecResult(cmd=cmd)
147149
with self.assertRaises(exec_helpers.ExecHelperError):
@@ -189,22 +191,27 @@ def test_finalize(self):
189191
result.read_stderr([b"err"])
190192

191193
def test_stdin_none(self):
194+
"""Test with empty STDIN."""
192195
result = exec_helpers.ExecResult(cmd, exit_code=0)
193196
self.assertIsNone(result.stdin)
194197

195198
def test_stdin_utf(self):
199+
"""Test with string in STDIN."""
196200
result = exec_helpers.ExecResult(cmd, stdin="STDIN", exit_code=0)
197201
self.assertEqual(result.stdin, "STDIN")
198202

199203
def test_stdin_bytes(self):
204+
"""Test with bytes STDIN."""
200205
result = exec_helpers.ExecResult(cmd, stdin=b"STDIN", exit_code=0)
201206
self.assertEqual(result.stdin, "STDIN")
202207

203208
def test_stdin_bytearray(self):
209+
"""Test with bytearray STDIN."""
204210
result = exec_helpers.ExecResult(cmd, stdin=bytearray(b"STDIN"), exit_code=0)
205211
self.assertEqual(result.stdin, "STDIN")
206212

207213
def test_started(self):
214+
"""Test timestamp."""
208215
started = datetime.datetime.utcnow()
209216
result = exec_helpers.ExecResult(cmd, exit_code=0, started=started)
210217
spent = (result.timestamp - started).seconds
@@ -229,6 +236,7 @@ def test_started(self):
229236
)
230237

231238
def test_indexed_lines_access(self):
239+
"""Test custom indexes usage for construction string from output."""
232240
result = exec_helpers.ExecResult(
233241
cmd,
234242
stdout=(

test/test_sftp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
import posixpath
2121
import stat
2222
import unittest
23+
from unittest import mock
2324

24-
import mock
2525
import paramiko
2626

2727
import exec_helpers

test/test_ssh_client_execute.py

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
import datetime
1616
import logging
1717
import typing
18+
from unittest import mock
1819

19-
import mock
2020
import pytest
2121

2222
import exec_helpers
@@ -122,6 +122,7 @@ def read_stream(stream: FakeFileStream) -> typing.Tuple[bytes, ...]:
122122

123123

124124
def pytest_generate_tests(metafunc):
125+
"""Tests parametrization."""
125126
if "run_parameters" in metafunc.fixturenames:
126127
metafunc.parametrize(
127128
"run_parameters",
@@ -143,20 +144,10 @@ def pytest_generate_tests(metafunc):
143144

144145
@pytest.fixture
145146
def run_parameters(request):
147+
"""Tests configuration apply."""
146148
return configs[request.param]
147149

148150

149-
@pytest.fixture
150-
def auto_add_policy(mocker):
151-
return mocker.patch("paramiko.AutoAddPolicy", return_value="AutoAddPolicy")
152-
153-
154-
@pytest.fixture
155-
def paramiko_ssh_client(mocker):
156-
mocker.patch("time.sleep")
157-
return mocker.patch("paramiko.SSHClient")
158-
159-
160151
@pytest.fixture
161152
def chan_makefile(run_parameters):
162153
class MkFile:
@@ -197,16 +188,6 @@ def ssh_transport_channel(paramiko_ssh_client, chan_makefile, run_parameters):
197188
return chan
198189

199190

200-
@pytest.fixture
201-
def ssh_auth_logger(mocker):
202-
return mocker.patch("exec_helpers.ssh_auth.logger")
203-
204-
205-
@pytest.fixture
206-
def get_logger(mocker):
207-
return mocker.patch("logging.getLogger")
208-
209-
210191
@pytest.fixture
211192
def ssh(paramiko_ssh_client, ssh_transport_channel, auto_add_policy, ssh_auth_logger, get_logger):
212193
return exec_helpers.SSHClient(host=host, port=port, auth=exec_helpers.SSHAuth(username=username, password=password))

0 commit comments

Comments
 (0)