Merge pull request #548 from mesosphere/test-cleanup
cleanup unit/integration test seperation
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script expects the following env var:
|
||||
# CLI_TEST_SSH_KEY_PATH
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
set -x
|
||||
|
||||
# run tests
|
||||
DCOS_PRODUCTION=false \
|
||||
CLI_TEST_MASTER_PROXY=true \
|
||||
CLI_TEST_SSH_KEY_PATH=/dcos-cli/mesosphere-aws.key \
|
||||
./bin/start_tests.sh
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import subprocess
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
import dcoscli
|
||||
import docopt
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from dcos import cmds, emitting, options, subcommand, util
|
||||
from dcos.errors import DCOSException
|
||||
from dcoscli.subcommand import (default_command_documentation,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
import dcoscli
|
||||
import docopt
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from dcos import (auth, constants, emitting, errors, http, mesos, subcommand,
|
||||
util)
|
||||
from dcos.errors import DCOSAuthenticationException, DCOSException
|
||||
|
||||
@@ -4,14 +4,12 @@ import json
|
||||
import os
|
||||
import pty
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
import requests
|
||||
import six
|
||||
from dcos import util
|
||||
|
||||
import mock
|
||||
from six.moves import urllib
|
||||
|
||||
|
||||
@@ -86,85 +84,6 @@ def assert_command(
|
||||
assert stderr_ == stderr
|
||||
|
||||
|
||||
def exec_mock(main, args):
|
||||
"""Call a main function with sys.args mocked, and capture
|
||||
stdout/stderr
|
||||
|
||||
:param main: main function to call
|
||||
:type main: function
|
||||
:param args: sys.args to mock, excluding the initial 'dcos'
|
||||
:type args: [str]
|
||||
:returns: (returncode, stdout, stderr)
|
||||
:rtype: (int, bytes, bytes)
|
||||
"""
|
||||
|
||||
print('MOCK ARGS: {}'.format(' '.join(args)))
|
||||
|
||||
with mock_args(args) as (stdout, stderr):
|
||||
returncode = main(args)
|
||||
|
||||
stdout_val = six.b(stdout.getvalue())
|
||||
stderr_val = six.b(stderr.getvalue())
|
||||
|
||||
print('STDOUT: {}'.format(stdout_val))
|
||||
print('STDERR: {}'.format(stderr_val))
|
||||
|
||||
return (returncode, stdout_val, stderr_val)
|
||||
|
||||
|
||||
def assert_mock(main,
|
||||
args,
|
||||
returncode=0,
|
||||
stdout=b'',
|
||||
stderr=b''):
|
||||
"""Mock and call a main function, and assert expected behavior.
|
||||
|
||||
:param main: main function to call
|
||||
:type main: function
|
||||
:param args: sys.args to mock, excluding the initial 'dcos'
|
||||
:type args: [str]
|
||||
:type returncode: int
|
||||
:param stdout: Expected stdout
|
||||
:type stdout: str
|
||||
:param stderr: Expected stderr
|
||||
:type stderr: str
|
||||
:rtype: None
|
||||
"""
|
||||
|
||||
returncode_, stdout_, stderr_ = exec_mock(main, args)
|
||||
|
||||
assert returncode_ == returncode
|
||||
assert stdout_ == stdout
|
||||
assert stderr_ == stderr
|
||||
|
||||
|
||||
def mock_called_some_args(mock, *args, **kwargs):
|
||||
"""Convience method for some mock assertions. Returns True if the
|
||||
arguments to one of the calls of `mock` contains `args` and
|
||||
`kwargs`.
|
||||
|
||||
:param mock: the mock to check
|
||||
:type mock: mock.Mock
|
||||
:returns: True if the arguments to one of the calls for `mock`
|
||||
contains `args` and `kwargs`.
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
for call in mock.call_args_list:
|
||||
call_args, call_kwargs = call
|
||||
|
||||
if any(arg not in call_args for arg in args):
|
||||
continue
|
||||
|
||||
if any(k not in call_kwargs or call_kwargs[k] != v
|
||||
for k, v in kwargs.items()):
|
||||
continue
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def watch_deployment(deployment_id, count):
|
||||
""" Wait for a deployment to complete.
|
||||
|
||||
@@ -498,23 +417,6 @@ def package(package_name, deploy=False, args=[]):
|
||||
watch_all_deployments()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def mock_args(args):
|
||||
""" Context manager that mocks sys.args and captures stdout/stderr
|
||||
|
||||
:param args: sys.args values to mock
|
||||
:type args: [str]
|
||||
:rtype: None
|
||||
"""
|
||||
with mock.patch('sys.argv', [util.which('dcos')] + args):
|
||||
stdout, stderr = sys.stdout, sys.stderr
|
||||
sys.stdout, sys.stderr = six.StringIO(), six.StringIO()
|
||||
try:
|
||||
yield sys.stdout, sys.stderr
|
||||
finally:
|
||||
sys.stdout, sys.stderr = stdout, stderr
|
||||
|
||||
|
||||
def popen_tty(cmd):
|
||||
"""Open a process with stdin connected to a pseudo-tty. Returns a
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
import dcoscli.constants as cli_constants
|
||||
import six
|
||||
from dcos import constants
|
||||
|
||||
@@ -16,7 +15,6 @@ def env():
|
||||
r.update({
|
||||
constants.PATH_ENV: os.environ[constants.PATH_ENV],
|
||||
constants.DCOS_CONFIG_ENV: os.path.join("tests", "data", "dcos.toml"),
|
||||
cli_constants.DCOS_PRODUCTION_ENV: 'false'
|
||||
})
|
||||
|
||||
return r
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import os
|
||||
|
||||
import dcoscli.constants as cli_constants
|
||||
from dcos import constants
|
||||
|
||||
import pytest
|
||||
@@ -15,7 +14,6 @@ def env():
|
||||
constants.PATH_ENV: os.environ[constants.PATH_ENV],
|
||||
constants.DCOS_CONFIG_ENV: os.path.join("tests",
|
||||
"data", "ssl", "ssl.toml"),
|
||||
cli_constants.DCOS_PRODUCTION_ENV: 'false'
|
||||
})
|
||||
|
||||
return r
|
||||
|
||||
@@ -7,15 +7,10 @@ import subprocess
|
||||
import time
|
||||
|
||||
import dcos.util as util
|
||||
from dcos import mesos
|
||||
from dcos.errors import DCOSException
|
||||
from dcos.util import create_schema
|
||||
from dcoscli.task.main import main
|
||||
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from ..fixtures.task import task_fixture
|
||||
from .common import (add_app, app, assert_command, assert_lines, assert_mock,
|
||||
from .common import (add_app, app, assert_command, assert_lines,
|
||||
exec_command, remove_app, watch_all_deployments)
|
||||
|
||||
SLEEP_COMPLETED = 'tests/data/marathon/apps/sleep-completed.json'
|
||||
@@ -229,39 +224,6 @@ def test_log_completed():
|
||||
assert len(stdout.decode('utf-8').split('\n')) > 4
|
||||
|
||||
|
||||
def test_log_master_unavailable():
|
||||
""" Test master's state.json being unavailable """
|
||||
client = mesos.DCOSClient()
|
||||
client.get_master_state = _mock_exception()
|
||||
|
||||
with patch('dcos.mesos.DCOSClient', return_value=client):
|
||||
args = ['task', 'log', '_']
|
||||
assert_mock(main, args, returncode=1, stderr=(b"exception\n"))
|
||||
|
||||
|
||||
def test_log_slave_unavailable():
|
||||
""" Test slave's state.json being unavailable """
|
||||
client = mesos.DCOSClient()
|
||||
client.get_slave_state = _mock_exception()
|
||||
|
||||
with patch('dcos.mesos.DCOSClient', return_value=client):
|
||||
args = ['task', 'log', 'test-app1']
|
||||
stderr = (b"""Error accessing slave: exception\n"""
|
||||
b"""No matching tasks. Exiting.\n""")
|
||||
assert_mock(main, args, returncode=1, stderr=stderr)
|
||||
|
||||
|
||||
def test_log_file_unavailable():
|
||||
""" Test a file's read.json being unavailable """
|
||||
files = [mesos.MesosFile('bogus')]
|
||||
files[0].read = _mock_exception('exception')
|
||||
|
||||
with patch('dcoscli.task.main._mesos_files', return_value=files):
|
||||
args = ['task', 'log', 'test-app1']
|
||||
stderr = b"No files exist. Exiting.\n"
|
||||
assert_mock(main, args, returncode=1, stderr=stderr)
|
||||
|
||||
|
||||
def test_ls():
|
||||
stdout = b'stderr stderr.logrotate.conf stdout stdout.logrotate.conf\n'
|
||||
assert_command(['dcos', 'task', 'ls', 'test-app1'],
|
||||
@@ -294,10 +256,6 @@ def test_ls_bad_path():
|
||||
returncode=1)
|
||||
|
||||
|
||||
def _mock_exception(contents='exception'):
|
||||
return MagicMock(side_effect=DCOSException(contents))
|
||||
|
||||
|
||||
def _mark_non_blocking(file_):
|
||||
fcntl.fcntl(file_.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
|
||||
|
||||
|
||||
102
cli/tests/unit/common.py
Normal file
102
cli/tests/unit/common.py
Normal file
@@ -0,0 +1,102 @@
|
||||
import contextlib
|
||||
import sys
|
||||
|
||||
import six
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def mock_args(args):
|
||||
""" Context manager that mocks sys.args and captures stdout/stderr
|
||||
|
||||
:param args: sys.args values to mock
|
||||
:type args: [str]
|
||||
:rtype: None
|
||||
"""
|
||||
with mock.patch('sys.argv', ['dcos'] + args):
|
||||
stdout, stderr = sys.stdout, sys.stderr
|
||||
sys.stdout, sys.stderr = six.StringIO(), six.StringIO()
|
||||
try:
|
||||
yield sys.stdout, sys.stderr
|
||||
finally:
|
||||
sys.stdout, sys.stderr = stdout, stderr
|
||||
|
||||
|
||||
def mock_called_some_args(mock, *args, **kwargs):
|
||||
"""Convience method for some mock assertions. Returns True if the
|
||||
arguments to one of the calls of `mock` contains `args` and
|
||||
`kwargs`.
|
||||
|
||||
:param mock: the mock to check
|
||||
:type mock: mock.Mock
|
||||
:returns: True if the arguments to one of the calls for `mock`
|
||||
contains `args` and `kwargs`.
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
for call in mock.call_args_list:
|
||||
call_args, call_kwargs = call
|
||||
|
||||
if any(arg not in call_args for arg in args):
|
||||
continue
|
||||
|
||||
if any(k not in call_kwargs or call_kwargs[k] != v
|
||||
for k, v in kwargs.items()):
|
||||
continue
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def exec_mock(main, args):
|
||||
"""Call a main function with sys.args mocked, and capture
|
||||
stdout/stderr
|
||||
|
||||
:param main: main function to call
|
||||
:type main: function
|
||||
:param args: sys.args to mock, excluding the initial 'dcos'
|
||||
:type args: [str]
|
||||
:returns: (returncode, stdout, stderr)
|
||||
:rtype: (int, bytes, bytes)
|
||||
"""
|
||||
|
||||
print('MOCK ARGS: {}'.format(' '.join(args)))
|
||||
|
||||
with mock_args(args) as (stdout, stderr):
|
||||
returncode = main(args)
|
||||
|
||||
stdout_val = six.b(stdout.getvalue())
|
||||
stderr_val = six.b(stderr.getvalue())
|
||||
|
||||
print('STDOUT: {}'.format(stdout_val))
|
||||
print('STDERR: {}'.format(stderr_val))
|
||||
|
||||
return (returncode, stdout_val, stderr_val)
|
||||
|
||||
|
||||
def assert_mock(main,
|
||||
args,
|
||||
returncode=0,
|
||||
stdout=b'',
|
||||
stderr=b''):
|
||||
"""Mock and call a main function, and assert expected behavior.
|
||||
|
||||
:param main: main function to call
|
||||
:type main: function
|
||||
:param args: sys.args to mock, excluding the initial 'dcos'
|
||||
:type args: [str]
|
||||
:type returncode: int
|
||||
:param stdout: Expected stdout
|
||||
:type stdout: str
|
||||
:param stderr: Expected stderr
|
||||
:type stderr: str
|
||||
:rtype: None
|
||||
"""
|
||||
|
||||
returncode_, stdout_, stderr_ = exec_mock(main, args)
|
||||
|
||||
assert returncode_ == returncode
|
||||
assert stdout_ == stdout
|
||||
assert stderr_ == stderr
|
||||
@@ -3,10 +3,10 @@ from functools import wraps
|
||||
|
||||
import dcoscli.analytics
|
||||
import rollbar
|
||||
from dcos import constants, http, util
|
||||
from dcoscli.config.main import main as config_main
|
||||
from dcos import constants, http
|
||||
from dcoscli.constants import SEGMENT_URL
|
||||
from dcoscli.main import main
|
||||
from dcoscli.subcommand import SubcommandMain
|
||||
|
||||
from mock import patch
|
||||
|
||||
@@ -33,33 +33,29 @@ def _mock(fn):
|
||||
|
||||
@_mock
|
||||
def test_config_set():
|
||||
'''Tests that a `dcos config set core.email <email>` makes a
|
||||
segment.io identify call'''
|
||||
argv = ['set', 'core.email', 'test@mail.com']
|
||||
|
||||
argv = ['config', 'set', 'core.email', 'test@mail.com']
|
||||
env = _env_reporting()
|
||||
config_thread = SubcommandMain("config", argv)
|
||||
exitcode, err = config_thread.run_and_capture()
|
||||
assert exitcode == 0
|
||||
assert err is None
|
||||
|
||||
with patch.dict(os.environ, env):
|
||||
assert config_main(argv) == 0
|
||||
|
||||
# segment.io
|
||||
assert mock_called_some_args(http.post,
|
||||
'{}/identify'.format(SEGMENT_URL),
|
||||
json={'userId': 'test@mail.com'},
|
||||
timeout=(1, 1))
|
||||
# segment.io
|
||||
assert mock_called_some_args(http.post,
|
||||
'{}/identify'.format(SEGMENT_URL),
|
||||
json={'userId': 'test@mail.com'},
|
||||
timeout=(1, 1))
|
||||
|
||||
|
||||
@_mock
|
||||
def test_cluster_id_not_sent():
|
||||
'''Tests that cluster_id is sent to segment.io'''
|
||||
def test_cluster_id_not_sent_on_config_call():
|
||||
"""Tests that cluster_id is not sent to segment.io on call to config
|
||||
subcommand
|
||||
"""
|
||||
|
||||
args = [util.which('dcos'), 'config', 'show']
|
||||
env = _env_reporting_with_url()
|
||||
version = 'release'
|
||||
args = ['dcos', 'config', 'show']
|
||||
|
||||
with patch('sys.argv', args), \
|
||||
patch.dict(os.environ, env), \
|
||||
patch('dcoscli.version', version), \
|
||||
patch('dcos.mesos.DCOSClient.metadata') as get_cluster_id:
|
||||
assert main() == 0
|
||||
|
||||
@@ -73,7 +69,7 @@ def test_no_exc():
|
||||
|
||||
'''
|
||||
|
||||
args = [util.which('dcos')]
|
||||
args = ['dcos']
|
||||
env = _env_reporting()
|
||||
version = 'release'
|
||||
|
||||
@@ -92,7 +88,7 @@ def test_exc():
|
||||
|
||||
'''
|
||||
|
||||
args = [util.which('dcos')]
|
||||
args = ['dcos']
|
||||
env = _env_reporting()
|
||||
version = 'release'
|
||||
with patch('sys.argv', args), \
|
||||
@@ -107,11 +103,16 @@ def test_exc():
|
||||
assert rollbar.report_message.call_count == 1
|
||||
|
||||
|
||||
def _env_reporting():
|
||||
path = os.path.join('tests', 'data', 'analytics', 'dcos_reporting.toml')
|
||||
return {constants.DCOS_CONFIG_ENV: path}
|
||||
|
||||
|
||||
@_mock
|
||||
def test_config_reporting_false():
|
||||
'''Test that "core.reporting = false" blocks exception reporting.'''
|
||||
|
||||
args = [util.which('dcos')]
|
||||
args = ['dcos']
|
||||
env = _env_no_reporting()
|
||||
version = 'release'
|
||||
|
||||
@@ -126,17 +127,6 @@ def test_config_reporting_false():
|
||||
assert track.call_count == 0
|
||||
|
||||
|
||||
def _env_reporting():
|
||||
path = os.path.join('tests', 'data', 'analytics', 'dcos_reporting.toml')
|
||||
return {constants.DCOS_CONFIG_ENV: path}
|
||||
|
||||
|
||||
def _env_no_reporting():
|
||||
path = os.path.join('tests', 'data', 'analytics', 'dcos_no_reporting.toml')
|
||||
return {constants.DCOS_CONFIG_ENV: path}
|
||||
|
||||
|
||||
def _env_reporting_with_url():
|
||||
path = os.path.join('tests', 'data', 'analytics',
|
||||
'dcos_reporting_with_url.toml')
|
||||
return {constants.DCOS_CONFIG_ENV: path}
|
||||
@@ -14,23 +14,15 @@ def test_no_browser_auth():
|
||||
assert op.call_count == 0
|
||||
|
||||
|
||||
def test_when_authenticated():
|
||||
with patch('dcos.auth.force_auth'):
|
||||
|
||||
_mock_dcos_run([util.which('dcos')], True)
|
||||
assert auth.force_auth.call_count == 0
|
||||
|
||||
|
||||
def test_anonymous_login():
|
||||
with patch('sys.stdin.readline', return_value='\n'), \
|
||||
patch('uuid.uuid1', return_value='anonymous@email'):
|
||||
|
||||
assert _mock_dcos_run([util.which('dcos'),
|
||||
'help'], False) == 0
|
||||
assert _mock_dcos_run([util.which('dcos'), 'config',
|
||||
'show', 'core.email'], False) == 0
|
||||
assert _mock_dcos_run([util.which('dcos'), 'config',
|
||||
'unset', 'core.email'], False) == 0
|
||||
assert _mock_dcos_run(['dcos', 'help'], False) == 0
|
||||
assert _mock_dcos_run(['dcos',
|
||||
'config', 'show', 'core.email'], False) == 0
|
||||
assert _mock_dcos_run(['dcos',
|
||||
'config', 'unset', 'core.email'], False) == 0
|
||||
|
||||
|
||||
def _mock_dcos_run(args, authenticated=True):
|
||||
@@ -43,6 +35,13 @@ def _mock_dcos_run(args, authenticated=True):
|
||||
return main()
|
||||
|
||||
|
||||
def test_when_authenticated():
|
||||
with patch('dcos.auth.force_auth'):
|
||||
|
||||
_mock_dcos_run(['dcos'], True)
|
||||
assert auth.force_auth.call_count == 0
|
||||
|
||||
|
||||
def _config_with_credentials():
|
||||
return {
|
||||
constants.DCOS_CONFIG_ENV: os.path.join(
|
||||
46
cli/tests/unit/test_task.py
Normal file
46
cli/tests/unit/test_task.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from dcos import mesos
|
||||
from dcos.errors import DCOSException
|
||||
from dcoscli.log import log_files
|
||||
from dcoscli.task.main import main
|
||||
|
||||
import pytest
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from .common import assert_mock
|
||||
|
||||
|
||||
def test_log_master_unavailable():
|
||||
""" Test master's state.json being unavailable """
|
||||
client = mesos.DCOSClient()
|
||||
client.get_master_state = _mock_exception()
|
||||
|
||||
with patch('dcos.mesos.DCOSClient', return_value=client):
|
||||
args = ['task', 'log', '_']
|
||||
assert_mock(main, args, returncode=1, stderr=(b"exception\n"))
|
||||
|
||||
|
||||
def test_log_no_tasks():
|
||||
""" Test slave's state.json being unavailable """
|
||||
with patch('dcos.mesos.DCOSClient.get_master_state', return_value={}), \
|
||||
patch('dcos.mesos.DCOSClient.get_master_state', return_value={}), \
|
||||
patch('dcos.mesos.Master.tasks', return_value={}):
|
||||
|
||||
stderr = b"""No matching tasks. Exiting.\n"""
|
||||
args = ['task', 'log', 'test-app-1']
|
||||
assert_mock(main, args, returncode=1, stderr=stderr)
|
||||
|
||||
|
||||
def test_log_file_unavailable():
|
||||
""" Test a file's read.json being unavailable """
|
||||
files = [mesos.MesosFile('bogus')]
|
||||
files[0].read = _mock_exception('exception')
|
||||
|
||||
with pytest.raises(DCOSException) as e:
|
||||
log_files(files, True, 10)
|
||||
|
||||
msg = "No files exist. Exiting."
|
||||
assert e.exconly().split(':', 1)[1].strip() == msg
|
||||
|
||||
|
||||
def _mock_exception(contents='exception'):
|
||||
return MagicMock(side_effect=DCOSException(contents))
|
||||
@@ -1,17 +1,17 @@
|
||||
[tox]
|
||||
envlist = syntax, py{27,34}-unit, py{27,34}-integration
|
||||
envlist = py34-syntax, py{27,34}-unit, py{27,34}-integration
|
||||
|
||||
[testenv]
|
||||
deps =
|
||||
teamcity-messages
|
||||
mock
|
||||
pytest
|
||||
pytest-cov
|
||||
mock
|
||||
pytz
|
||||
-e..
|
||||
passenv = DCOS_* CI_FLAGS CLI_TEST_SSH_KEY_PATH CLI_TEST_MASTER_PROXY
|
||||
|
||||
[testenv:syntax]
|
||||
[testenv:py34-syntax]
|
||||
deps =
|
||||
teamcity-messages
|
||||
flake8
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import collections
|
||||
import concurrent.futures
|
||||
import contextlib
|
||||
import functools
|
||||
import json
|
||||
@@ -11,7 +12,6 @@ import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import concurrent.futures
|
||||
import jsonschema
|
||||
import png
|
||||
import pystache
|
||||
|
||||
Reference in New Issue
Block a user