mirror of
https://github.com/jupyterhub/the-littlest-jupyterhub.git
synced 2025-12-18 21:54:05 +08:00
pre-commit: run black without string normalization
This commit is contained in:
@@ -16,16 +16,22 @@ def prefix():
|
||||
# see https://github.com/conda-forge/miniforge/releases
|
||||
mambaforge_version = '4.10.3-7'
|
||||
if os.uname().machine == 'aarch64':
|
||||
installer_sha256 = "ac95f137b287b3408e4f67f07a284357b1119ee157373b788b34e770ef2392b2"
|
||||
installer_sha256 = (
|
||||
"ac95f137b287b3408e4f67f07a284357b1119ee157373b788b34e770ef2392b2"
|
||||
)
|
||||
elif os.uname().machine == 'x86_64':
|
||||
installer_sha256 = "fc872522ec427fcab10167a93e802efaf251024b58cc27b084b915a9a73c4474"
|
||||
installer_url = "https://github.com/conda-forge/miniforge/releases/download/{v}/Mambaforge-{v}-Linux-{arch}.sh".format(v=mambaforge_version, arch=os.uname().machine)
|
||||
installer_sha256 = (
|
||||
"fc872522ec427fcab10167a93e802efaf251024b58cc27b084b915a9a73c4474"
|
||||
)
|
||||
installer_url = "https://github.com/conda-forge/miniforge/releases/download/{v}/Mambaforge-{v}-Linux-{arch}.sh".format(
|
||||
v=mambaforge_version, arch=os.uname().machine
|
||||
)
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with conda.download_miniconda_installer(installer_url, installer_sha256) as installer_path:
|
||||
with conda.download_miniconda_installer(
|
||||
installer_url, installer_sha256
|
||||
) as installer_path:
|
||||
conda.install_miniconda(installer_path, tmpdir)
|
||||
conda.ensure_conda_packages(tmpdir, [
|
||||
'conda==4.10.3'
|
||||
])
|
||||
conda.ensure_conda_packages(tmpdir, ['conda==4.10.3'])
|
||||
yield tmpdir
|
||||
|
||||
|
||||
@@ -35,11 +41,7 @@ def test_ensure_packages(prefix):
|
||||
"""
|
||||
conda.ensure_conda_packages(prefix, ['numpy'])
|
||||
# Throws an error if this fails
|
||||
subprocess.check_call([
|
||||
os.path.join(prefix, 'bin', 'python'),
|
||||
'-c',
|
||||
'import numpy'
|
||||
])
|
||||
subprocess.check_call([os.path.join(prefix, 'bin', 'python'), '-c', 'import numpy'])
|
||||
|
||||
|
||||
def test_ensure_pip_packages(prefix):
|
||||
@@ -49,11 +51,7 @@ def test_ensure_pip_packages(prefix):
|
||||
conda.ensure_conda_packages(prefix, ['pip'])
|
||||
conda.ensure_pip_packages(prefix, ['numpy'])
|
||||
# Throws an error if this fails
|
||||
subprocess.check_call([
|
||||
os.path.join(prefix, 'bin', 'python'),
|
||||
'-c',
|
||||
'import numpy'
|
||||
])
|
||||
subprocess.check_call([os.path.join(prefix, 'bin', 'python'), '-c', 'import numpy'])
|
||||
|
||||
|
||||
def test_ensure_pip_requirements(prefix):
|
||||
@@ -66,8 +64,4 @@ def test_ensure_pip_requirements(prefix):
|
||||
f.write(b'there')
|
||||
f.flush()
|
||||
conda.ensure_pip_requirements(prefix, f.name)
|
||||
subprocess.check_call([
|
||||
os.path.join(prefix, 'bin', 'python'),
|
||||
'-c',
|
||||
'import there'
|
||||
])
|
||||
subprocess.check_call([os.path.join(prefix, 'bin', 'python'), '-c', 'import there'])
|
||||
|
||||
@@ -32,10 +32,7 @@ def test_set_multi_level():
|
||||
new_conf = config.set_item_in_config(conf, 'a.b', 'c')
|
||||
new_conf = config.set_item_in_config(new_conf, 'a.d', 'e')
|
||||
new_conf = config.set_item_in_config(new_conf, 'f', 'g')
|
||||
assert new_conf == {
|
||||
'a': {'b': 'c', 'd': 'e'},
|
||||
'f': 'g'
|
||||
}
|
||||
assert new_conf == {'a': {'b': 'c', 'd': 'e'}, 'f': 'g'}
|
||||
|
||||
|
||||
def test_set_overwrite():
|
||||
@@ -44,9 +41,7 @@ def test_set_overwrite():
|
||||
|
||||
This might be surprising destructive behavior to some :D
|
||||
"""
|
||||
conf = {
|
||||
'a': 'b'
|
||||
}
|
||||
conf = {'a': 'b'}
|
||||
|
||||
new_conf = config.set_item_in_config(conf, 'a', 'c')
|
||||
assert new_conf == {'a': 'c'}
|
||||
@@ -73,16 +68,10 @@ def test_unset_one_level():
|
||||
|
||||
|
||||
def test_unset_multi_level():
|
||||
conf = {
|
||||
'a': {'b': 'c', 'd': 'e'},
|
||||
'f': 'g'
|
||||
}
|
||||
conf = {'a': {'b': 'c', 'd': 'e'}, 'f': 'g'}
|
||||
|
||||
new_conf = config.unset_item_from_config(conf, 'a.b')
|
||||
assert new_conf == {
|
||||
'a': {'d': 'e'},
|
||||
'f': 'g'
|
||||
}
|
||||
assert new_conf == {'a': {'d': 'e'}, 'f': 'g'}
|
||||
new_conf = config.unset_item_from_config(new_conf, 'a.d')
|
||||
assert new_conf == {'f': 'g'}
|
||||
new_conf = config.unset_item_from_config(new_conf, 'f')
|
||||
@@ -90,9 +79,7 @@ def test_unset_multi_level():
|
||||
|
||||
|
||||
def test_unset_and_clean_empty_configs():
|
||||
conf = {
|
||||
'a': {'b': {'c': {'d': {'e': 'f'}}}}
|
||||
}
|
||||
conf = {'a': {'b': {'c': {'d': {'e': 'f'}}}}}
|
||||
|
||||
new_conf = config.unset_item_from_config(conf, 'a.b.c.d.e')
|
||||
assert new_conf == {}
|
||||
@@ -113,32 +100,24 @@ def test_add_to_config_one_level():
|
||||
conf = {}
|
||||
|
||||
new_conf = config.add_item_to_config(conf, 'a.b', 'c')
|
||||
assert new_conf == {
|
||||
'a': {'b': ['c']}
|
||||
}
|
||||
assert new_conf == {'a': {'b': ['c']}}
|
||||
|
||||
|
||||
def test_add_to_config_zero_level():
|
||||
conf = {}
|
||||
|
||||
new_conf = config.add_item_to_config(conf, 'a', 'b')
|
||||
assert new_conf == {
|
||||
'a': ['b']
|
||||
}
|
||||
assert new_conf == {'a': ['b']}
|
||||
|
||||
|
||||
def test_add_to_config_multiple():
|
||||
conf = {}
|
||||
|
||||
new_conf = config.add_item_to_config(conf, 'a.b.c', 'd')
|
||||
assert new_conf == {
|
||||
'a': {'b': {'c': ['d']}}
|
||||
}
|
||||
assert new_conf == {'a': {'b': {'c': ['d']}}}
|
||||
|
||||
new_conf = config.add_item_to_config(new_conf, 'a.b.c', 'e')
|
||||
assert new_conf == {
|
||||
'a': {'b': {'c': ['d', 'e']}}
|
||||
}
|
||||
assert new_conf == {'a': {'b': {'c': ['d', 'e']}}}
|
||||
|
||||
|
||||
def test_remove_from_config():
|
||||
@@ -146,14 +125,10 @@ def test_remove_from_config():
|
||||
|
||||
new_conf = config.add_item_to_config(conf, 'a.b.c', 'd')
|
||||
new_conf = config.add_item_to_config(new_conf, 'a.b.c', 'e')
|
||||
assert new_conf == {
|
||||
'a': {'b': {'c': ['d', 'e']}}
|
||||
}
|
||||
assert new_conf == {'a': {'b': {'c': ['d', 'e']}}}
|
||||
|
||||
new_conf = config.remove_item_from_config(new_conf, 'a.b.c', 'e')
|
||||
assert new_conf == {
|
||||
'a': {'b': {'c': ['d']}}
|
||||
}
|
||||
assert new_conf == {'a': {'b': {'c': ['d']}}}
|
||||
|
||||
|
||||
def test_remove_from_config_error():
|
||||
@@ -193,13 +168,7 @@ def test_cli_no_command(capsys):
|
||||
assert "positional arguments:" in captured.out
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"arg, value",
|
||||
[
|
||||
("true", True),
|
||||
("FALSE", False)
|
||||
]
|
||||
)
|
||||
@pytest.mark.parametrize("arg, value", [("true", True), ("FALSE", False)])
|
||||
def test_cli_set_bool(tljh_dir, arg, value):
|
||||
config.main(["set", "https.enabled", arg])
|
||||
cfg = configurer.load_config()
|
||||
|
||||
@@ -9,9 +9,6 @@ import sys
|
||||
from tljh import configurer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def apply_mock_config(overrides):
|
||||
"""
|
||||
Configure a mock configurer with given overrides.
|
||||
@@ -86,7 +83,10 @@ def test_auth_default():
|
||||
"""
|
||||
c = apply_mock_config({})
|
||||
|
||||
assert c.JupyterHub.authenticator_class == 'firstuseauthenticator.FirstUseAuthenticator'
|
||||
assert (
|
||||
c.JupyterHub.authenticator_class
|
||||
== 'firstuseauthenticator.FirstUseAuthenticator'
|
||||
)
|
||||
# Do not auto create users who haven't been manually added by default
|
||||
assert not c.FirstUseAuthenticator.create_users
|
||||
|
||||
@@ -95,14 +95,9 @@ def test_auth_dummy():
|
||||
"""
|
||||
Test setting Dummy Authenticator & password
|
||||
"""
|
||||
c = apply_mock_config({
|
||||
'auth': {
|
||||
'type': 'dummy',
|
||||
'DummyAuthenticator': {
|
||||
'password': 'test'
|
||||
}
|
||||
}
|
||||
})
|
||||
c = apply_mock_config(
|
||||
{'auth': {'type': 'dummy', 'DummyAuthenticator': {'password': 'test'}}}
|
||||
)
|
||||
assert c.JupyterHub.authenticator_class == 'dummy'
|
||||
assert c.DummyAuthenticator.password == 'test'
|
||||
|
||||
@@ -111,33 +106,32 @@ def test_user_groups():
|
||||
"""
|
||||
Test setting user groups
|
||||
"""
|
||||
c = apply_mock_config({
|
||||
'users': {
|
||||
'extra_user_groups': {
|
||||
"g1": ["u1", "u2"],
|
||||
"g2": ["u3", "u4"]
|
||||
},
|
||||
c = apply_mock_config(
|
||||
{
|
||||
'users': {
|
||||
'extra_user_groups': {"g1": ["u1", "u2"], "g2": ["u3", "u4"]},
|
||||
}
|
||||
}
|
||||
})
|
||||
assert c.UserCreatingSpawner.user_groups == {
|
||||
"g1": ["u1", "u2"],
|
||||
"g2": ["u3", "u4"]
|
||||
}
|
||||
)
|
||||
assert c.UserCreatingSpawner.user_groups == {"g1": ["u1", "u2"], "g2": ["u3", "u4"]}
|
||||
|
||||
|
||||
def test_auth_firstuse():
|
||||
"""
|
||||
Test setting FirstUse Authenticator options
|
||||
"""
|
||||
c = apply_mock_config({
|
||||
'auth': {
|
||||
'type': 'firstuseauthenticator.FirstUseAuthenticator',
|
||||
'FirstUseAuthenticator': {
|
||||
'create_users': True
|
||||
c = apply_mock_config(
|
||||
{
|
||||
'auth': {
|
||||
'type': 'firstuseauthenticator.FirstUseAuthenticator',
|
||||
'FirstUseAuthenticator': {'create_users': True},
|
||||
}
|
||||
}
|
||||
})
|
||||
assert c.JupyterHub.authenticator_class == 'firstuseauthenticator.FirstUseAuthenticator'
|
||||
)
|
||||
assert (
|
||||
c.JupyterHub.authenticator_class
|
||||
== 'firstuseauthenticator.FirstUseAuthenticator'
|
||||
)
|
||||
assert c.FirstUseAuthenticator.create_users
|
||||
|
||||
|
||||
@@ -145,16 +139,20 @@ def test_auth_github():
|
||||
"""
|
||||
Test using GitHub authenticator
|
||||
"""
|
||||
c = apply_mock_config({
|
||||
'auth': {
|
||||
'type': 'oauthenticator.github.GitHubOAuthenticator',
|
||||
'GitHubOAuthenticator': {
|
||||
'client_id': 'something',
|
||||
'client_secret': 'something-else'
|
||||
c = apply_mock_config(
|
||||
{
|
||||
'auth': {
|
||||
'type': 'oauthenticator.github.GitHubOAuthenticator',
|
||||
'GitHubOAuthenticator': {
|
||||
'client_id': 'something',
|
||||
'client_secret': 'something-else',
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
assert c.JupyterHub.authenticator_class == 'oauthenticator.github.GitHubOAuthenticator'
|
||||
)
|
||||
assert (
|
||||
c.JupyterHub.authenticator_class == 'oauthenticator.github.GitHubOAuthenticator'
|
||||
)
|
||||
assert c.GitHubOAuthenticator.client_id == 'something'
|
||||
assert c.GitHubOAuthenticator.client_secret == 'something-else'
|
||||
|
||||
@@ -173,12 +171,9 @@ def test_set_traefik_api():
|
||||
"""
|
||||
Test setting per traefik api credentials
|
||||
"""
|
||||
c = apply_mock_config({
|
||||
'traefik_api': {
|
||||
'username': 'some_user',
|
||||
'password': '1234'
|
||||
}
|
||||
})
|
||||
c = apply_mock_config(
|
||||
{'traefik_api': {'username': 'some_user', 'password': '1234'}}
|
||||
)
|
||||
assert c.TraefikTomlProxy.traefik_api_username == 'some_user'
|
||||
assert c.TraefikTomlProxy.traefik_api_password == '1234'
|
||||
|
||||
@@ -190,40 +185,47 @@ def test_cull_service_default():
|
||||
c = apply_mock_config({})
|
||||
|
||||
cull_cmd = [
|
||||
sys.executable, '-m', 'jupyterhub_idle_culler',
|
||||
'--timeout=600', '--cull-every=60', '--concurrency=5',
|
||||
'--max-age=0'
|
||||
sys.executable,
|
||||
'-m',
|
||||
'jupyterhub_idle_culler',
|
||||
'--timeout=600',
|
||||
'--cull-every=60',
|
||||
'--concurrency=5',
|
||||
'--max-age=0',
|
||||
]
|
||||
assert c.JupyterHub.services == [
|
||||
{
|
||||
'name': 'cull-idle',
|
||||
'admin': True,
|
||||
'command': cull_cmd,
|
||||
}
|
||||
]
|
||||
assert c.JupyterHub.services == [{
|
||||
'name': 'cull-idle',
|
||||
'admin': True,
|
||||
'command': cull_cmd,
|
||||
}]
|
||||
|
||||
|
||||
def test_set_cull_service():
|
||||
"""
|
||||
Test setting cull service options
|
||||
"""
|
||||
c = apply_mock_config({
|
||||
'services': {
|
||||
'cull': {
|
||||
'every': 10,
|
||||
'users': True,
|
||||
'max_age': 60
|
||||
}
|
||||
}
|
||||
})
|
||||
c = apply_mock_config(
|
||||
{'services': {'cull': {'every': 10, 'users': True, 'max_age': 60}}}
|
||||
)
|
||||
cull_cmd = [
|
||||
sys.executable, '-m', 'jupyterhub_idle_culler',
|
||||
'--timeout=600', '--cull-every=10', '--concurrency=5',
|
||||
'--max-age=60', '--cull-users'
|
||||
sys.executable,
|
||||
'-m',
|
||||
'jupyterhub_idle_culler',
|
||||
'--timeout=600',
|
||||
'--cull-every=10',
|
||||
'--concurrency=5',
|
||||
'--max-age=60',
|
||||
'--cull-users',
|
||||
]
|
||||
assert c.JupyterHub.services == [
|
||||
{
|
||||
'name': 'cull-idle',
|
||||
'admin': True,
|
||||
'command': cull_cmd,
|
||||
}
|
||||
]
|
||||
assert c.JupyterHub.services == [{
|
||||
'name': 'cull-idle',
|
||||
'admin': True,
|
||||
'command': cull_cmd,
|
||||
}]
|
||||
|
||||
|
||||
def test_load_secrets(tljh_dir):
|
||||
@@ -243,13 +245,15 @@ def test_auth_native():
|
||||
"""
|
||||
Test setting Native Authenticator
|
||||
"""
|
||||
c = apply_mock_config({
|
||||
'auth': {
|
||||
'type': 'nativeauthenticator.NativeAuthenticator',
|
||||
'NativeAuthenticator': {
|
||||
'open_signup': True,
|
||||
c = apply_mock_config(
|
||||
{
|
||||
'auth': {
|
||||
'type': 'nativeauthenticator.NativeAuthenticator',
|
||||
'NativeAuthenticator': {
|
||||
'open_signup': True,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
assert c.JupyterHub.authenticator_class == 'nativeauthenticator.NativeAuthenticator'
|
||||
assert c.NativeAuthenticator.open_signup == True
|
||||
|
||||
@@ -19,20 +19,20 @@ def test_ensure_config_yaml(tljh_dir):
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"admins, expected_config",
|
||||
[
|
||||
([['a1'], ['a2'], ['a3']], ['a1', 'a2', 'a3']),
|
||||
([['a1:p1'], ['a2']], ['a1', 'a2']),
|
||||
],
|
||||
"admins, expected_config",
|
||||
[
|
||||
([['a1'], ['a2'], ['a3']], ['a1', 'a2', 'a3']),
|
||||
([['a1:p1'], ['a2']], ['a1', 'a2']),
|
||||
],
|
||||
)
|
||||
def test_ensure_admins(tljh_dir, admins, expected_config):
|
||||
# --admin option called multiple times on the installer
|
||||
# creates a list of argument lists.
|
||||
installer.ensure_admins(admins)
|
||||
# --admin option called multiple times on the installer
|
||||
# creates a list of argument lists.
|
||||
installer.ensure_admins(admins)
|
||||
|
||||
config_path = installer.CONFIG_FILE
|
||||
with open(config_path) as f:
|
||||
config = yaml.load(f)
|
||||
config_path = installer.CONFIG_FILE
|
||||
with open(config_path) as f:
|
||||
config = yaml.load(f)
|
||||
|
||||
# verify the list was flattened
|
||||
assert config['users']['admin'] == expected_config
|
||||
# verify the list was flattened
|
||||
assert config['users']['admin'] == expected_config
|
||||
|
||||
@@ -17,8 +17,7 @@ def test_generate_username():
|
||||
'jupyter-abcdefghijklmnopq': 'jupyter-abcdefghijklmnopq',
|
||||
# 27 characters, just above our cutoff for hashing
|
||||
'jupyter-abcdefghijklmnopqr': 'jupyter-abcdefghijklmnopqr-e375e',
|
||||
|
||||
}
|
||||
for hub_user, system_user in usernames.items():
|
||||
assert generate_system_username(hub_user) == system_user
|
||||
assert len(system_user) <= 32
|
||||
assert len(system_user) <= 32
|
||||
|
||||
@@ -36,10 +36,8 @@ def test_default_config(tmpdir, tljh_dir):
|
||||
"http": {"address": ":80"},
|
||||
"auth_api": {
|
||||
"address": "127.0.0.1:8099",
|
||||
"auth": {
|
||||
"basic": {"users": [""]}
|
||||
},
|
||||
"whiteList": {"sourceRange": ["127.0.0.1"]}
|
||||
"auth": {"basic": {"users": [""]}},
|
||||
"whiteList": {"sourceRange": ["127.0.0.1"]},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -72,10 +70,8 @@ def test_letsencrypt_config(tljh_dir):
|
||||
"https": {"address": ":443", "tls": {"minVersion": "VersionTLS12"}},
|
||||
"auth_api": {
|
||||
"address": "127.0.0.1:8099",
|
||||
"auth": {
|
||||
"basic": {"users": [""]}
|
||||
},
|
||||
"whiteList": {"sourceRange": ["127.0.0.1"]}
|
||||
"auth": {"basic": {"users": [""]}},
|
||||
"whiteList": {"sourceRange": ["127.0.0.1"]},
|
||||
},
|
||||
}
|
||||
assert cfg["acme"] == {
|
||||
@@ -113,18 +109,17 @@ def test_manual_ssl_config(tljh_dir):
|
||||
"minVersion": "VersionTLS12",
|
||||
"certificates": [
|
||||
{"certFile": "/path/to/ssl.cert", "keyFile": "/path/to/ssl.key"}
|
||||
]
|
||||
],
|
||||
},
|
||||
},
|
||||
"auth_api": {
|
||||
"address": "127.0.0.1:8099",
|
||||
"auth": {
|
||||
"basic": {"users": [""]}
|
||||
},
|
||||
"whiteList": {"sourceRange": ["127.0.0.1"]}
|
||||
"auth": {"basic": {"users": [""]}},
|
||||
"whiteList": {"sourceRange": ["127.0.0.1"]},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_extra_config(tmpdir, tljh_dir):
|
||||
extra_config_dir = os.path.join(tljh_dir, config.CONFIG_DIR, "traefik_config.d")
|
||||
state_dir = tmpdir.mkdir("state")
|
||||
@@ -146,13 +141,9 @@ def test_extra_config(tmpdir, tljh_dir):
|
||||
# modify existing value
|
||||
"logLevel": "ERROR",
|
||||
# modify existing value with multiple levels
|
||||
"entryPoints": {
|
||||
"auth_api": {
|
||||
"address": "127.0.0.1:9999"
|
||||
}
|
||||
},
|
||||
"entryPoints": {"auth_api": {"address": "127.0.0.1:9999"}},
|
||||
# add new setting
|
||||
"checkNewVersion": False
|
||||
"checkNewVersion": False,
|
||||
}
|
||||
|
||||
with open(os.path.join(extra_config_dir, "extra.toml"), "w+") as extra_config_file:
|
||||
|
||||
@@ -31,9 +31,15 @@ def test_ensure_user():
|
||||
assert os.path.exists(home_dir)
|
||||
# Ensure not word readable/writable especially in teaching context
|
||||
homedir_stats = os.stat(home_dir).st_mode
|
||||
assert not (homedir_stats & stat.S_IROTH), "Everyone should not be able to read users home directory"
|
||||
assert not (homedir_stats & stat.S_IWOTH), "Everyone should not be able to write users home directory"
|
||||
assert not (homedir_stats & stat.S_IXOTH), "Everyone should not be able to list what is in users home directory"
|
||||
assert not (
|
||||
homedir_stats & stat.S_IROTH
|
||||
), "Everyone should not be able to read users home directory"
|
||||
assert not (
|
||||
homedir_stats & stat.S_IWOTH
|
||||
), "Everyone should not be able to write users home directory"
|
||||
assert not (
|
||||
homedir_stats & stat.S_IXOTH
|
||||
), "Everyone should not be able to list what is in users home directory"
|
||||
|
||||
# Run ensure_user again, should be a noop
|
||||
user.ensure_user(username)
|
||||
|
||||
@@ -8,9 +8,7 @@ def test_run_subprocess_exception(mocker):
|
||||
logger = logging.getLogger('tljh')
|
||||
mocker.patch.object(logger, 'error')
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
utils.run_subprocess(
|
||||
['/bin/bash', '-c', 'echo error; exit 1']
|
||||
)
|
||||
utils.run_subprocess(['/bin/bash', '-c', 'echo error; exit 1'])
|
||||
logger.error.assert_called_with('error\n')
|
||||
|
||||
|
||||
@@ -18,4 +16,4 @@ def test_run_subprocess(mocker):
|
||||
logger = logging.getLogger('tljh')
|
||||
mocker.patch.object(logger, 'debug')
|
||||
utils.run_subprocess(['/bin/bash', '-c', 'echo success'])
|
||||
logger.debug.assert_called_with('success\n')
|
||||
logger.debug.assert_called_with('success\n')
|
||||
|
||||
Reference in New Issue
Block a user