Repository: typesafehub/conductr-cli

Issue:
Improve CLI message
From @rpokrywka 

shazar: “Created digested ZIP archive at None” - the message should be giving more information.

@rpokrywka Can you provide a reproducible test case?

Discussion:

Patch:
diff --git a/.gitignore b/.gitignore
index b8b187e..896a06a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ deb_dist/*
 .idea
 *.iml
 *.pyc
+.tox
diff --git a/.travis.yml b/.travis.yml
index 4374097..817f35a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,8 @@
 language: python
-python:
-- '3.4'
-script: python3 setup.py test
+install:
+  - pip3 install --user virtualenv
+script:
+  - python3 setup.py test
 deploy:
   provider: pypi
   user: typesafehub
diff --git a/conductr_cli/conduct_logging.py b/conductr_cli/conduct_logging.py
index 159999f..3d9feb8 100644
--- a/conductr_cli/conduct_logging.py
+++ b/conductr_cli/conduct_logging.py
@@ -26,7 +26,7 @@ def connection_error(err, args):
 
 def pretty_json(s):
     s_json = json.loads(s)
-    print(json.dumps(s_json, sort_keys=True, indent=2))
+    print(json.dumps(s_json, sort_keys=True, indent=2, separators=(',', ': ')))
 
 
 def handle_connection_error(func):
diff --git a/conductr_cli/shazar.py b/conductr_cli/shazar.py
index 5c97b3d..cac7342 100755
--- a/conductr_cli/shazar.py
+++ b/conductr_cli/shazar.py
@@ -11,10 +11,10 @@ import tempfile
 import zipfile
 
 
-def run():
+def run(argv=None):
     parser = build_parser()
     argcomplete.autocomplete(parser)
-    args = parser.parse_args()
+    args = parser.parse_args(argv)
     args.func(args)
 
 
@@ -45,10 +45,8 @@ def shazar(args):
         else:
             zip_file.write(args.source, source_base_name)
 
-    dest = shutil.move(
-        temp_file,
-        os.path.join(args.output_dir, '{}-{}.zip'.format(source_base_name, create_digest(temp_file)))
-    )
+    dest = os.path.join(args.output_dir, '{}-{}.zip'.format(source_base_name, create_digest(temp_file)))
+    shutil.move(temp_file, dest)
     print('Created digested ZIP archive at {}'.format(dest))
 
 
diff --git a/setup.py b/setup.py
index 535fada..03f23f9 100644
--- a/setup.py
+++ b/setup.py
@@ -1,10 +1,46 @@
 import os
+import sys
 from setuptools import setup, find_packages
 from conductr_cli import __version__
+from setuptools.command.test import test
 
 # allow setup.py to be run from any path
 os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
 
+install_requires = [
+    'requests>=2.3.0',
+    'argcomplete>=0.8.1',
+    'pyhocon==0.2.1'
+]
+if sys.version_info[:2] == (3, 2):
+    install_requires.extend([
+        'pathlib==1.0.1',
+        'mock==1.0.1'
+    ])
+
+
+class Tox(test):
+    user_options = [('tox-args=', 'a', 'Arguments to pass to tox')]
+
+    def initialize_options(self):
+        test.initialize_options(self)
+        self.tox_args = None
+
+    def finalize_options(self):
+        test.finalize_options(self)
+        self.test_args = []
+        self.test_suite = True
+
+    def run_tests(self):
+        # import here, cause outside the eggs aren't loaded
+        import tox
+        import shlex
+        args = self.tox_args
+        if args:
+            args = shlex.split(self.tox_args)
+        errno = tox.cmdline(args=args)
+        sys.exit(errno)
+
 setup(
     name='conductr-cli',
     version=__version__,
@@ -17,13 +53,12 @@ setup(
         ],
     },
 
-    install_requires=[
-        'requests>=2.3.0',
-        'argcomplete>=0.8.1',
-        'pyhocon==0.2.1'
-    ],
+    install_requires=install_requires,
+    tests_require=['tox'],
     test_suite='conductr_cli.test',
 
+    cmdclass={'test': Tox},
+
     license='Apache 2',
     description='A CLI client for Typesafe ConductR',
     url='https://github.com/typesafehub/conductr-cli',
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..5b12aef
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,6 @@
+[tox]
+envlist = py32, py34
+
+[testenv]
+deps = nose
+commands = nosetests


Test patch:
diff --git a/conductr_cli/test/cli_test_case.py b/conductr_cli/test/cli_test_case.py
index cbad63c..7467a38 100644
--- a/conductr_cli/test/cli_test_case.py
+++ b/conductr_cli/test/cli_test_case.py
@@ -1,9 +1,13 @@
 import os
 import shutil
 import tempfile
-from unittest.mock import MagicMock
 from requests.exceptions import ConnectionError, HTTPError
 
+try:
+    from unittest.mock import MagicMock  # 3.3 and beyond
+except ImportError:
+    from mock import MagicMock
+
 
 class CliTestCase():
     """Provides test case common functionality"""
diff --git a/conductr_cli/test/test_conduct_info.py b/conductr_cli/test/test_conduct_info.py
index 6188067..234f94d 100644
--- a/conductr_cli/test/test_conduct_info.py
+++ b/conductr_cli/test/test_conduct_info.py
@@ -1,8 +1,12 @@
 from unittest import TestCase
-from unittest.mock import patch, MagicMock
 from conductr_cli.test.cli_test_case import CliTestCase, strip_margin
 from conductr_cli import conduct_info
 
+try:
+    from unittest.mock import patch, MagicMock  # 3.3 and beyond
+except ImportError:
+    from mock import patch, MagicMock
+
 
 class TestConductInfoCommand(TestCase, CliTestCase):
 
diff --git a/conductr_cli/test/test_conduct_load.py b/conductr_cli/test/test_conduct_load.py
index 378215f..eb3b983 100644
--- a/conductr_cli/test/test_conduct_load.py
+++ b/conductr_cli/test/test_conduct_load.py
@@ -1,10 +1,14 @@
 from unittest import TestCase
-from unittest.mock import call, patch, MagicMock
 from conductr_cli.test.cli_test_case import CliTestCase, create_temp_bundle, create_temp_bundle_with_contents, strip_margin
 from conductr_cli import conduct_load
 from urllib.error import URLError
 import shutil
 
+try:
+    from unittest.mock import call, patch, MagicMock  # 3.3 and beyond
+except ImportError:
+    from mock import call, patch, MagicMock
+
 
 class TestConductLoadCommand(TestCase, CliTestCase):
 
diff --git a/conductr_cli/test/test_conduct_run.py b/conductr_cli/test/test_conduct_run.py
index 9f18628..2bdc26b 100644
--- a/conductr_cli/test/test_conduct_run.py
+++ b/conductr_cli/test/test_conduct_run.py
@@ -1,8 +1,12 @@
 from unittest import TestCase
-from unittest.mock import patch, MagicMock
 from conductr_cli.test.cli_test_case import CliTestCase, strip_margin
 from conductr_cli import conduct_run
 
+try:
+    from unittest.mock import patch, MagicMock  # 3.3 and beyond
+except ImportError:
+    from mock import patch, MagicMock
+
 
 class TestConductRunCommand(TestCase, CliTestCase):
 
diff --git a/conductr_cli/test/test_conduct_services.py b/conductr_cli/test/test_conduct_services.py
index 590794e..431afef 100644
--- a/conductr_cli/test/test_conduct_services.py
+++ b/conductr_cli/test/test_conduct_services.py
@@ -1,8 +1,12 @@
 from unittest import TestCase
-from unittest.mock import patch, MagicMock
 from conductr_cli.test.cli_test_case import CliTestCase, strip_margin
 from conductr_cli import conduct_services
 
+try:
+    from unittest.mock import patch, MagicMock  # 3.3 and beyond
+except ImportError:
+    from mock import patch, MagicMock
+
 
 class TestConductServicesCommand(TestCase, CliTestCase):
 
diff --git a/conductr_cli/test/test_conduct_stop.py b/conductr_cli/test/test_conduct_stop.py
index 2c3ab19..39a4e00 100644
--- a/conductr_cli/test/test_conduct_stop.py
+++ b/conductr_cli/test/test_conduct_stop.py
@@ -1,8 +1,12 @@
 from unittest import TestCase
-from unittest.mock import patch, MagicMock
 from conductr_cli.test.cli_test_case import CliTestCase, strip_margin
 from conductr_cli import conduct_stop
 
+try:
+    from unittest.mock import patch, MagicMock  # 3.3 and beyond
+except ImportError:
+    from mock import patch, MagicMock
+
 
 class TestConductStopCommand(TestCase, CliTestCase):
 
diff --git a/conductr_cli/test/test_conduct_unload.py b/conductr_cli/test/test_conduct_unload.py
index 1cbf7c2..bcff6ac 100644
--- a/conductr_cli/test/test_conduct_unload.py
+++ b/conductr_cli/test/test_conduct_unload.py
@@ -1,8 +1,12 @@
 from unittest import TestCase
-from unittest.mock import patch, MagicMock
 from conductr_cli.test.cli_test_case import CliTestCase, strip_margin
 from conductr_cli import conduct_unload
 
+try:
+    from unittest.mock import patch, MagicMock  # 3.3 and beyond
+except ImportError:
+    from mock import patch, MagicMock
+
 
 class TestConductUnloadCommand(TestCase, CliTestCase):
 
diff --git a/conductr_cli/test/test_shazar.py b/conductr_cli/test/test_shazar.py
index c8d0407..adfd475 100644
--- a/conductr_cli/test/test_shazar.py
+++ b/conductr_cli/test/test_shazar.py
@@ -1,7 +1,14 @@
 from unittest import TestCase
+import shutil
 import tempfile
 from os import remove
-from conductr_cli.shazar import create_digest, build_parser
+from conductr_cli.shazar import create_digest, build_parser, run
+from conductr_cli.test.cli_test_case import CliTestCase
+
+try:
+    from unittest.mock import patch, MagicMock  # 3.3 and beyond
+except ImportError:
+    from mock import patch, MagicMock
 
 
 class TestShazar(TestCase):
@@ -23,3 +30,25 @@ class TestShazar(TestCase):
 
         self.assertEqual(args.output_dir, 'output-dir')
         self.assertEqual(args.source, 'source')
+
+
+class TestIntegration(TestCase, CliTestCase):
+
+    def setUp(self):  # noqa
+        self.tmpdir = tempfile.mkdtemp()
+        self.tmpfile = tempfile.NamedTemporaryFile(mode='w+b', delete=False)
+        self.tmpfile.write(b'test file data')
+
+    def test(self):
+        stdout = MagicMock()
+        with patch('sys.stdout', stdout):
+            run('--output-dir {} {}'.format(self.tmpdir, self.tmpfile.name).split())
+
+        self.assertRegex(
+            self.output(stdout),
+            'Created digested ZIP archive at /tmp/tmp[a-z0-9_]{6,8}/tmp[a-z0-9_]{6,8}-[a-f0-9]{64}\.zip'
+        )
+
+    def tearDown(self):  # noqa
+        shutil.rmtree(self.tmpdir)
+        remove(self.tmpfile.name)