[Python-modules-commits] [transifex-client] 01/01: New upstream version 0.13.0

Hans-Christoph Steiner eighthave at moszumanska.debian.org
Tue Jan 9 22:33:54 UTC 2018


This is an automated email from the git hooks/post-receive script.

eighthave pushed a commit to branch upstream
in repository transifex-client.

commit 77efc152aa7627ed25eec2c9f116c846427c892a
Author: Hans-Christoph Steiner <hans at eds.org>
Date:   Tue Jan 9 22:41:44 2018 +0100

    New upstream version 0.13.0
---
 PKG-INFO                                   |   8 +-
 requirements.txt                           |   2 +
 setup.cfg                                  |   1 -
 setup.py                                   |   3 +-
 tests/__init__.pyc                         | Bin 0 -> 144 bytes
 tests/project_dir/.transifexrc             |   6 +
 tests/project_dir/test.txt                 |   0
 tests/project_dir/translations/en/test.txt |   0
 tests/templates/config                     |   8 +
 tests/templates/transifexrc                |   4 +
 tests/test_api.py                          |  74 ++++
 tests/test_api.pyc                         | Bin 0 -> 2558 bytes
 tests/test_commands.py                     | 397 +++++++++++++++++++++
 tests/test_commands.pyc                    | Bin 0 -> 18602 bytes
 tests/test_parsers.pyc                     | Bin 0 -> 1665 bytes
 tests/test_paths.pyc                       | Bin 0 -> 2328 bytes
 tests/test_processors.pyc                  | Bin 0 -> 2931 bytes
 tests/test_project.py                      | 245 ++++++++++++-
 tests/test_project.pyc                     | Bin 0 -> 30050 bytes
 tests/test_utils.py                        |  98 +++++-
 tests/test_utils.pyc                       | Bin 0 -> 8539 bytes
 tests/test_wizard.py                       | 146 ++++++++
 tests/test_wizard.pyc                      | Bin 0 -> 4512 bytes
 transifex_client.egg-info/PKG-INFO         |   8 +-
 transifex_client.egg-info/SOURCES.txt      |  23 +-
 transifex_client.egg-info/pbr.json         |   1 +
 transifex_client.egg-info/requires.txt     |   2 +
 txclib/__init__.py                         |   2 +-
 txclib/api.py                              |  77 +++++
 txclib/cmdline.py                          |  78 ++---
 txclib/commands.py                         | 385 +++++++++++++--------
 txclib/config.py                           |   6 +-
 txclib/exceptions.py                       |  25 ++
 txclib/log.py                              |   2 +-
 txclib/messages.py                         | 115 +++++++
 txclib/parsers.py                          | 533 ++++++++++++++++++++---------
 txclib/project.py                          | 493 +++++++++++++-------------
 txclib/urls.py                             |  16 +
 txclib/utils.py                            | 228 +++++++++++-
 txclib/wizard.py                           | 193 +++++++++++
 40 files changed, 2518 insertions(+), 661 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 80853f5..6eed977 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,11 +1,12 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: transifex-client
-Version: 0.12.4
+Version: 0.13.0
 Summary: A command line interface for Transifex
 Home-page: https://www.transifex.com
 Author: Transifex
 Author-email: admin at transifex.com
 License: GPLv2
+Description-Content-Type: UNKNOWN
 Description: Transifex Command-Line Tool
         ===========================
         [![image](https://circleci.com/gh/transifex/transifex-client/tree/master.svg?style=shield&circle-token=33aafd984726261eff1b73278a0cf761382c478a)](https://circleci.com/gh/transifex/transifex-client/tree/master)
@@ -76,7 +77,6 @@ Keywords: translation,localization,internationalization
 Platform: UNKNOWN
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.5
+Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,<3.6
diff --git a/requirements.txt b/requirements.txt
index 71aa090..82f4e52 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,4 @@
 urllib3
 six
+requests
+python-slugify
diff --git a/setup.cfg b/setup.cfg
index 861a9f5..8bfd5a1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,4 @@
 [egg_info]
 tag_build = 
 tag_date = 0
-tag_svn_revision = 0
 
diff --git a/setup.py b/setup.py
index 6a27901..1370690 100755
--- a/setup.py
+++ b/setup.py
@@ -23,6 +23,7 @@ setup(
     license="GPLv2",
     dependency_links=[],
     setup_requires=[],
+    python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,<3.6',
     install_requires=get_file_content('requirements.txt').splitlines(),
     tests_require=["mock"],
     data_files=[],
@@ -35,9 +36,7 @@ setup(
     classifiers=[
         'Programming Language :: Python',
         'Programming Language :: Python :: 2',
-        'Programming Language :: Python :: 2.6',
         'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 3.3',
         'Programming Language :: Python :: 3.5',
     ],
 )
diff --git a/tests/__init__.pyc b/tests/__init__.pyc
new file mode 100644
index 0000000..6fb37b0
Binary files /dev/null and b/tests/__init__.pyc differ
diff --git a/tests/project_dir/.transifexrc b/tests/project_dir/.transifexrc
new file mode 100644
index 0000000..6bf92eb
--- /dev/null
+++ b/tests/project_dir/.transifexrc
@@ -0,0 +1,6 @@
+[https://www.transifex.com]
+api_hostname = https://api.transifex.com
+hostname = https://www.transifex.com
+password = foo
+username = bar
+
diff --git a/tests/project_dir/test.txt b/tests/project_dir/test.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/project_dir/translations/en/test.txt b/tests/project_dir/translations/en/test.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/templates/config b/tests/templates/config
new file mode 100755
index 0000000..f8d10b5
--- /dev/null
+++ b/tests/templates/config
@@ -0,0 +1,8 @@
+[main]
+host = https://fake.com
+
+[example.enpo]
+file_filter = translations/example.enpo/<lang>.po
+source_file = example.po
+source_lang = en
+type = PO
diff --git a/tests/templates/transifexrc b/tests/templates/transifexrc
new file mode 100755
index 0000000..d2719c6
--- /dev/null
+++ b/tests/templates/transifexrc
@@ -0,0 +1,4 @@
+[https://fake.com]
+hostname = https://fake.com
+password = dummy_api_token_password_12345
+username = api
diff --git a/tests/test_api.py b/tests/test_api.py
new file mode 100644
index 0000000..554da39
--- /dev/null
+++ b/tests/test_api.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+from mock import MagicMock, patch
+
+from txclib.api import Api
+
+
+class ApiTestCase(unittest.TestCase):
+
+    @patch('txclib.utils.get_api_domains')
+    @patch('requests.get')
+    def test_resolve_api_call(self, requests_mock, domains_mock):
+        token = 'blabla'
+        domains_mock.return_value = {
+            'hostname': 'https://www.foo.bar',
+            'api_hostname': 'https://api.foo.bar'
+        }
+        response_mock = MagicMock()
+        response_mock.json.return_value = {"some": "json data"}
+        response_mock.links = {}
+        requests_mock.return_value = response_mock
+        api = Api(token=token)
+        api.get('formats')
+        self.assertEqual(
+            requests_mock.call_args[0][0],
+            'https://www.foo.bar/api/2/formats/',
+        )
+        requests_mock.reset_mock()
+        requests_mock.return_value = response_mock
+        api.get('organizations')
+        self.assertEqual(
+            requests_mock.call_args[0][0],
+            'https://api.foo.bar/organizations/',
+        )
+
+    @patch('txclib.utils.get_api_domains')
+    @patch('requests.get')
+    def test_pagination(self, requests_mock, domains_mock):
+        token = 'blabla'
+        domains_mock.return_value = {
+            'hostname': 'https://www.foo.bar',
+            'api_hostname': 'https://api.foo.bar'
+        }
+        first_response_mock = MagicMock()
+        first_response_mock.json.return_value = [{"key1": "json data"}]
+        first_response_mock.links = {
+            'next': {'url': 'https://www.foo.bar/api/2/formats/?page=2'}}
+        second_response_mock = MagicMock()
+        second_response_mock.json.return_value = [{"key2": "json data"}]
+        second_response_mock.links = {}
+        requests_mock.side_effect = [
+            first_response_mock,
+            second_response_mock
+        ]
+        api = Api(token=token)
+        data = api.get('formats')
+        self.assertEqual(
+            requests_mock.call_args_list[0][0][0],
+            'https://www.foo.bar/api/2/formats/',
+        )
+        self.assertEqual(
+            requests_mock.call_args_list[1][0][0],
+            'https://www.foo.bar/api/2/formats/?page=2',
+        )
+        self.assertEqual(data, [
+            {"key1": "json data"},
+            {"key2": "json data"}
+        ])
+
+    def test_invalid_call(self):
+        api = Api(token='blabla')
+        with self.assertRaises(Exception):
+            api.get('invalid')
diff --git a/tests/test_api.pyc b/tests/test_api.pyc
new file mode 100644
index 0000000..66f384d
Binary files /dev/null and b/tests/test_api.pyc differ
diff --git a/tests/test_commands.py b/tests/test_commands.py
new file mode 100644
index 0000000..4314bf6
--- /dev/null
+++ b/tests/test_commands.py
@@ -0,0 +1,397 @@
+import os
+import shutil
+import unittest
+import sys
+from StringIO import StringIO
+from mock import patch, MagicMock, call
+from txclib.commands import _set_source_file, _set_translation, cmd_pull, \
+    cmd_init, cmd_config, cmd_status, cmd_help, UnInitializedError
+from txclib.cmdline import main
+from txclib.parsers import MAPPING, MAPPINGREMOTE, MAPPINGBULK
+
+
+class TestCommands(unittest.TestCase):
+    def test_cmd_pull_return_exception_when_dir_not_initialized(self):
+        """Test when tx is not instantiated, that proper error is thrown"""
+        with self.assertRaises(UnInitializedError):
+            cmd_pull([], None)
+
+    def test_set_source_file_when_dir_not_initialized(self):
+        with self.assertRaises(UnInitializedError):
+            _set_source_file(path_to_tx=None, resource='dummy_resource.en',
+                             lang='en', path_to_file='dummy')
+
+    def test_set_translation_when_dir_not_initialized(self):
+        with self.assertRaises(UnInitializedError):
+            _set_translation(path_to_tx=None, resource="dummy_resource.md",
+                             lang='en', path_to_file='invalid')
+
+
+class TestStatusCommand(unittest.TestCase):
+    @patch('txclib.commands.project')
+    def test_status(self, mock_p):
+        """Test status command"""
+        mock_project = MagicMock()
+        mock_project.get_chosen_resources.return_value = ['foo.bar']
+        mock_project.get_resource_files.return_value = {
+            "fr": "translations/foo.bar/fr.po",
+            "de": "translations/foo.bar/de.po"
+        }
+        mock_p.Project.return_value = mock_project
+        cmd_status([], None)
+        mock_project.get_chosen_resources.assert_called_once_with([])
+        self.assertEqual(mock_project.get_resource_files.call_count, 1)
+
+
+class TestHelpCommand(unittest.TestCase):
+    def test_help(self):
+        out = StringIO()
+        sys.stdout = out
+        cmd_help([], None)
+        output = out.getvalue().strip()
+        self.assertTrue(
+            all(
+                c in output for c in
+                ['delete', 'help', 'init', 'pull', 'push', 'set', 'status']
+            )
+        )
+
+        # call for specific command
+        with patch('txclib.commands.cmd_pull', spec=cmd_pull) as pull_mock:
+            cmd_help(['pull'], None)
+            pull_mock.assert_called_once_with(['--help'], None)
+
+
+class TestInitCommand(unittest.TestCase):
+
+    def setUp(self):
+        self.curr_dir = os.getcwd()
+        self.config_file = '.tx/config'
+        os.chdir('./tests/project_dir/')
+
+    def tearDown(self, *args, **kwargs):
+        shutil.rmtree('.tx', ignore_errors=False, onerror=None)
+        os.chdir(self.curr_dir)
+        super(TestInitCommand, self).tearDown(*args, **kwargs)
+
+    def test_init(self):
+        argv = []
+        config_text = "[main]\nhost = https://www.transifex.com\n\n"
+        with patch('txclib.commands.project.Project') as project_mock:
+            with patch('txclib.commands.cmd_config') as set_mock:
+                cmd_init(argv, '')
+                project_mock.assert_called()
+                set_mock.assert_called_once_with([], os.getcwd())
+        self.assertTrue(os.path.exists('./.tx'))
+        self.assertTrue(os.path.exists('./.tx/config'))
+        self.assertEqual(open(self.config_file).read(), config_text)
+
+    def test_init_skipsetup(self):
+        argv = ['--skipsetup']
+        with patch('txclib.commands.project.Project') as project_mock:
+            with patch('txclib.commands.cmd_config') as set_mock:
+                cmd_init(argv, '')
+                project_mock.assert_called()
+                self.assertEqual(set_mock.call_count, 0)
+        self.assertTrue(os.path.exists('./.tx'))
+        self.assertTrue(os.path.exists('./.tx/config'))
+
+    @patch('txclib.commands.utils.confirm')
+    def test_init_save_N(self, confirm_mock):
+        os.mkdir('./.tx')
+        open('./.tx/config', 'a').close()
+        argv = []
+        confirm_mock.return_value = False
+        with patch('txclib.commands.project.Project') as project_mock:
+                cmd_init(argv, '')
+                self.assertEqual(project_mock.call_count, 0)
+        self.assertTrue(os.path.exists('./.tx'))
+        self.assertEqual(confirm_mock.call_count, 1)
+
+    @patch('txclib.commands.utils.confirm')
+    def test_init_save_y(self, confirm_mock):
+        os.mkdir('./.tx')
+        open('./.tx/config', 'a').close()
+        argv = []
+        confirm_mock.return_value = True
+        with patch('txclib.commands.project.Project') as project_mock:
+            with patch('txclib.commands.cmd_config') as set_mock:
+                cmd_init(argv, '')
+                project_mock.assert_called()
+                set_mock.assert_called()
+        self.assertTrue(os.path.exists('./.tx'))
+        self.assertEqual(confirm_mock.call_count, 1)
+
+    def test_init_force_save(self):
+        os.mkdir('./.tx')
+        argv = ['--force-save']
+        with patch('txclib.commands.project.Project') as project_mock:
+            with patch('txclib.commands.cmd_config') as set_mock:
+                cmd_init(argv, '')
+                project_mock.assert_called()
+                set_mock.assert_called()
+        self.assertTrue(os.path.exists('./.tx'))
+        self.assertTrue(os.path.exists('./.tx/config'))
+
+
+class TestPullCommand(unittest.TestCase):
+    @patch('txclib.utils.get_current_branch')
+    @patch('txclib.commands.logger')
+    @patch('txclib.commands.project.Project')
+    def test_pull_with_branch_no_git_repo(self, project_mock, log_mock, bmock):
+        bmock.return_value = None
+        pr_instance = MagicMock()
+        project_mock.return_value = pr_instance
+        with self.assertRaises(SystemExit):
+            cmd_pull(['--branch'], '.')
+        log_mock.error.assert_called_once_with(
+            "You specified the --branch option but current "
+            "directory does not seem to belong in any git repo.")
+        self.assertEqual(pr_instance.pull.call_count, 0)
+
+    @patch('txclib.utils.get_current_branch')
+    @patch('txclib.commands.logger')
+    @patch('txclib.commands.project.Project')
+    def test_pull_branch_git_repo(self, project_mock, log_mock, bmock):
+        bmock.return_value = 'a-branch'
+        pr_instance = MagicMock()
+        project_mock.return_value = pr_instance
+        cmd_pull(['--branch'], '.')
+        self.assertEqual(pr_instance.pull.call_count, 1)
+        pull_call = call(
+            branch='a-branch', fetchall=False, fetchsource=False,
+            force=False, languages=[], minimum_perc=None, mode=None,
+            overwrite=True, pseudo=False, resources=[], skip=False, xliff=False
+        )
+        pr_instance.pull.assert_has_calls([pull_call])
+
+    @patch('txclib.utils.get_current_branch')
+    @patch('txclib.commands.logger')
+    @patch('txclib.commands.project.Project')
+    def test_pull_with_branch_and_branchname_option(
+        self, project_mock, log_mock, bmock
+    ):
+        pr_instance = MagicMock()
+        project_mock.return_value = pr_instance
+        bmock.return_value = None
+        cmd_pull(['--branch', 'somebranch'], '.')
+        self.assertEqual(pr_instance.pull.call_count, 1)
+        pull_call = call(
+            branch='somebranch', fetchall=False, fetchsource=False,
+            force=False, languages=[], minimum_perc=None, mode=None,
+            overwrite=True, pseudo=False, resources=[], skip=False, xliff=False
+        )
+        pr_instance.pull.assert_has_calls([pull_call])
+
+
+class TestConfigCommand(unittest.TestCase):
+
+    def setUp(self):
+        self.curr_dir = os.getcwd()
+        os.chdir('./tests/project_dir/')
+        os.mkdir('.tx')
+        self.path_to_tx = os.getcwd()
+        self.config_file = '.tx/config'
+        open(self.config_file, "w").write('[main]\nhost = https://foo.var\n')
+
+    def tearDown(self, *args, **kwargs):
+        shutil.rmtree('.tx', ignore_errors=False, onerror=None)
+        os.chdir(self.curr_dir)
+        super(TestConfigCommand, self).tearDown(*args, **kwargs)
+
+    def test_bare_set_too_few_arguments(self):
+        with self.assertRaises(SystemExit):
+            args = ["-r", "project1.resource1"]
+            cmd_config(args, None)
+
+    def test_bare_set_source_no_file(self):
+        with self.assertRaises(SystemExit):
+            args = ["-r", "project1.resource1", '--is-source', '-l', 'en']
+            cmd_config(args, None)
+
+        with self.assertRaises(Exception):
+            args = ['-r', 'project1.resource1', '--source', '-l', 'en',
+                    'noexistent-file.txt']
+            cmd_config(args, self.path_to_tx)
+
+    def test_bare_set_source_file(self):
+        expected = ("[main]\nhost = https://foo.var\n\n[project1.resource1]\n"
+                    "source_file = test.txt\nsource_lang = en\n\n")
+        args = ["-r", "project1.resource1", '--source', '-l', 'en', 'test.txt']
+        cmd_config(args, self.path_to_tx)
+        with open(self.config_file) as config:
+            self.assertEqual(config.read(), expected)
+
+        # set translation file for de
+        expected = ("[main]\nhost = https://foo.var\n\n[project1.resource1]\n"
+                    "source_file = test.txt\nsource_lang = en\n"
+                    "trans.de = translations/de.txt\n\n")
+        args = ["-r", "project1.resource1", '-l', 'de', 'translations/de.txt']
+        cmd_config(args, self.path_to_tx)
+        with open(self.config_file) as config:
+            self.assertEqual(config.read(), expected)
+
+    def test_auto_locale_no_expression(self):
+        with self.assertRaises(SystemExit):
+            args = [MAPPING, "-r", "project1.resource1",
+                    '--source-language', 'en']
+            cmd_config(args, self.path_to_tx)
+
+    def test_auto_locale(self):
+        expected = "[main]\nhost = https://foo.var\n"
+        args = [MAPPING, "-r", "project1.resource1", '--source-language',
+                'en', 'translations/<lang>/test.txt']
+        cmd_config(args, self.path_to_tx)
+        with open(self.config_file) as config:
+            self.assertEqual(config.read(), expected)
+
+    def test_auto_locale_is_backwards_compatible(self):
+        expected = ("[main]\nhost = https://foo.var\n\n[project1.resource1]\n"
+                    "file_filter = translations/<lang>/test.txt\n"
+                    "source_file = translations/en/test.txt\n"
+                    "source_lang = en\n\n")
+
+        args = ["--auto-local", "-r", "project1.resource1",
+                '--source-language', 'en', '--execute',
+                'translations/<lang>/test.txt']
+        cmd_config(args, self.path_to_tx)
+        with open(self.config_file) as config:
+            self.assertEqual(config.read(), expected)
+
+    def test_auto_locale_execute(self):
+        expected = ("[main]\nhost = https://foo.var\n\n[project1.resource1]\n"
+                    "file_filter = translations/<lang>/test.txt\n"
+                    "source_file = translations/en/test.txt\n"
+                    "source_lang = en\n\n")
+
+        args = [MAPPING, "-r", "project1.resource1", '--source-language',
+                'en', '--execute', 'translations/<lang>/test.txt']
+        cmd_config(args, self.path_to_tx)
+        with open(self.config_file) as config:
+            self.assertEqual(config.read(), expected)
+
+    def test_auto_remote_invalid_url(self):
+        # no project_url
+        args = [MAPPINGREMOTE]
+        with self.assertRaises(SystemExit):
+            cmd_config(args, self.path_to_tx)
+
+        # invalid project_url
+        args = [MAPPINGREMOTE, "http://some.random.url/"]
+        with self.assertRaises(Exception):
+            cmd_config(args, self.path_to_tx)
+
+    @patch('txclib.utils.get_details')
+    @patch('txclib.project.Project._extension_for')
+    def test_auto_remote_project(self, extension_mock, get_details_mock):
+        # change the host to tx
+        open(self.config_file, "w").write(
+            '[main]\nhost = https://www.transifex.com\n'
+        )
+        expected = ("[main]\nhost = https://www.transifex.com\n\n"
+                    "[proj.resource_1]\n"
+                    "file_filter = translations/proj.resource_1/<lang>.txt\n"
+                    "source_lang = fr\ntype = TXT\n\n[proj.resource_2]\n"
+                    "file_filter = translations/proj.resource_2/<lang>.txt\n"
+                    "source_lang = fr\ntype = TXT\n\n")
+        extension_mock.return_value = ".txt"
+        get_details_mock.side_effect = [
+            # project details
+            {
+                'resources': [
+                    {'slug': 'resource_1', 'name': 'resource 1'},
+                    {'slug': 'resource_2', 'name': 'resource 2'}
+                ]
+            },
+            # resources details
+            {
+                'source_language_code': 'fr',
+                'i18n_type': 'TXT',
+                'slug': 'resource_1',
+            }, {
+                'source_language_code': 'fr',
+                'i18n_type': 'TXT',
+                'slug': 'resource_2',
+            }
+        ]
+        args = [MAPPINGREMOTE, "https://www.transifex.com/test-org/proj/"]
+        cmd_config(args, self.path_to_tx)
+        with open(self.config_file) as config:
+            self.assertEqual(config.read(), expected)
+
+    @patch('txclib.utils.get_details')
+    @patch('txclib.project.Project._extension_for')
+    def test_auto_remote_is_backwards_compatible(self, extension_mock,
+                                                 get_details_mock):
+        # change the host to tx
+        open(self.config_file, "w").write(
+            '[main]\nhost = https://www.transifex.com\n'
+        )
+        expected = ("[main]\nhost = https://www.transifex.com\n\n"
+                    "[proj.resource_1]\n"
+                    "file_filter = translations/proj.resource_1/<lang>.txt\n"
+                    "source_lang = fr\ntype = TXT\n\n[proj.resource_2]\n"
+                    "file_filter = translations/proj.resource_2/<lang>.txt\n"
+                    "source_lang = fr\ntype = TXT\n\n")
+        extension_mock.return_value = ".txt"
+        get_details_mock.side_effect = [
+            # project details
+            {
+                'resources': [
+                    {'slug': 'resource_1', 'name': 'resource 1'},
+                    {'slug': 'resource_2', 'name': 'resource 2'}
+                ]
+            },
+            # resources details
+            {
+                'source_language_code': 'fr',
+                'i18n_type': 'TXT',
+                'slug': 'resource_1',
+            }, {
+                'source_language_code': 'fr',
+                'i18n_type': 'TXT',
+                'slug': 'resource_2',
+            }
+        ]
+        args = ["--auto-remote", "https://www.transifex.com/test-org/proj/"]
+        cmd_config(args, self.path_to_tx)
+        with open(self.config_file) as config:
+            self.assertEqual(config.read(), expected)
+
+    def test_bulk_missing_options(self):
+        with self.assertRaises(SystemExit):
+            args = [MAPPINGBULK]
+            cmd_config(args, self.path_to_tx)
+
+        with self.assertRaises(SystemExit):
+            args = [MAPPINGBULK, "-p", "test-project"]
+            cmd_config(args, self.path_to_tx)
+
+        with self.assertRaises(SystemExit):
+            args = [MAPPINGBULK, "-p", "test-project", "--source-language",
+                    "en", "--t", "TXT", "--file-extension", ".txt"]
+            cmd_config(args, self.path_to_tx)
+
+    def test_bulk(self):
+        expected = ("[main]\nhost = https://foo.var\n\n"
+                    "[test-project.translations_en_test]\n"
+                    "file_filter = translations/<lang>/en/test.txt\n"
+                    "source_file = translations/en/test.txt\n"
+                    "source_lang = en\ntype = TXT\n\n")
+        args = [MAPPINGBULK, "-p", "test-project", "--source-file-dir",
+                "translations", "--source-language", "en", "-t", "TXT",
+                "--file-extension", ".txt", "--execute", "--expression",
+                "translations/<lang>/{filepath}/{filename}{extension}"]
+        cmd_config(args, self.path_to_tx)
+        with open(self.config_file) as config:
+            self.assertEqual(config.read(), expected)
+
+
+class TestMainCommand(unittest.TestCase):
+    def test_call_tx_with_no_command(self):
+        with self.assertRaises(SystemExit):
+            main(['tx'])
+
+    def test_call_tx_with_invalid_command(self):
+        with self.assertRaises(SystemExit):
+            main(['tx', 'random'])
diff --git a/tests/test_commands.pyc b/tests/test_commands.pyc
new file mode 100644
index 0000000..6fdee6e
Binary files /dev/null and b/tests/test_commands.pyc differ
diff --git a/tests/test_parsers.pyc b/tests/test_parsers.pyc
new file mode 100644
index 0000000..72474f5
Binary files /dev/null and b/tests/test_parsers.pyc differ
diff --git a/tests/test_paths.pyc b/tests/test_paths.pyc
new file mode 100644
index 0000000..1956226
Binary files /dev/null and b/tests/test_paths.pyc differ
diff --git a/tests/test_processors.pyc b/tests/test_processors.pyc
new file mode 100644
index 0000000..1a05136
Binary files /dev/null and b/tests/test_processors.pyc differ
diff --git a/tests/test_project.py b/tests/test_project.py
index 823d902..3e2b91b 100644
--- a/tests/test_project.py
+++ b/tests/test_project.py
@@ -6,10 +6,22 @@ try:
     import json
 except ImportError:
     import simplejson as json
+try:
+    import configparser
+except ImportError:
+    import ConfigParser as configparser
+
+from functools import wraps
 from mock import Mock, patch
+from collections import namedtuple
+from os.path import dirname
+from sys import modules
+
+from txclib.exceptions import AuthenticationError, TXConnectionError
+from txclib.project import (Project, DEFAULT_PULL_URL)
 
-from txclib.project import Project, ProjectNotInit
 from txclib.config import Flipdict
+from txclib import utils
 
 
 class TestProject(unittest.TestCase):
@@ -19,32 +31,29 @@ class TestProject(unittest.TestCase):
         """Test _get_tx_dir_path function"""
         expected_path = '/tmp/'
         m_find_dot_tx.return_value = expected_path
-        p = Project(init=False)
-        path = p._get_tx_dir_path(path_to_tx=None)
+        path = utils.get_tx_dir_path(path_to_tx=None)
         self.assertEqual(path, expected_path)
         m_find_dot_tx.assert_called_once_with()
 
         expected_path = '/opt/'
-        path = p._get_tx_dir_path(path_to_tx=expected_path)
+        path = utils.get_tx_dir_path(path_to_tx=expected_path)
         self.assertEqual(path, expected_path)
         # make sure it has not been called twice
         m_find_dot_tx.assert_called_once_with()
 
     @patch('os.path.exists')
     def test_get_config_file_path(self, m_exists):
-        """Test _get_config_file_path function"""
-        p = Project(init=False)
+        """Test get_config_file_path function"""
         m_exists.return_value = True
-        p._get_config_file_path('/tmp/')
+        utils.get_config_file_path('/tmp/')
         m_exists.assert_called_once_with('/tmp/.tx/config')
 
         m_exists.return_value = False
-        with self.assertRaises(ProjectNotInit):
-            p._get_config_file_path('/tmp/')
+        with self.assertRaises(utils.ProjectNotInit):
+            utils.get_config_file_path('/tmp/')
 
-    @patch('txclib.utils.confirm')
     @patch('txclib.config.configparser')
-    def test_getset_host_credentials(self, m_parser, m_confirm):
+    def test_getset_host_credentials(self, m_parser):
         p = Project(init=False)
         # let suppose a token has been set at the config
         dummy_token = 'salala'
@@ -68,6 +77,76 @@ class TestProject(unittest.TestCase):
         self.assertEqual(username, 'username')
         self.assertEqual(password, 'passw0rdz')
 
+    @patch('txclib.project.input')
+    @patch('txclib.config.configparser')
+    def test_getset_host_credentials_no_transifexrc(
+            self, m_parser, m_input):
+        p = Project(init=False)
+        # let suppose a token has been set at the config
+        dummy_token = 'salala'
+        p.txrc = m_parser
+        p.save = Mock()
+        p.validate_credentials = Mock(return_value=True)
+        p.txrc_file = '/tmp'
+        p.txrc.get.side_effect = configparser.NoSectionError('test')
+        m_input.return_value = dummy_token
+        username, password = p.getset_host_credentials('test')
+        self.assertEqual(username, 'api')
+        self.assertEqual(password, dummy_token)
+        self.assertEqual(p.txrc.set.call_count, 4)
+        self.assertEqual(m_input.call_count, 1)
+        p.save.assert_called()
+
+    @patch('txclib.project.utils.confirm')
+    @patch('txclib.config.configparser')
+    def test_getset_host_credentials_update_transifexrc(
+            self, m_parser, m_input):
+        p = Project(init=False)
+        dummy_token = 'salala'
+        p.txrc = m_parser
+        p.save = Mock()
+        p.txrc_file = '/tmp'
+        p.validate_credentials = Mock(return_value=True)
+        p.txrc.get.side_effect = [
+            'foo', 'bar'
+        ]
+        # transifexrc does not get updated if credentials are the same
+        username, password = p.getset_host_credentials(
+            'test', username='foo', password='bar'
+        )
+        self.assertEqual(username, 'foo')
+        self.assertEqual(password, 'bar')
+        self.assertEqual(p.txrc.set.call_count, 0)
+        self.assertEqual(m_input.call_count, 0)
+        self.assertEqual(p.save.call_count, 0)
+
+        # transifexrc is not updated if confirm is no
+        p.txrc.get.side_effect = [
+            'foo', 'bar'
+        ]
+        m_input.return_value = False
+        username, password = p.getset_host_credentials('test',
+                                                       token=dummy_token)
+        self.assertEqual(username, 'api')
+        self.assertEqual(password, dummy_token)
+        self.assertEqual(p.txrc.set.call_count, 0)
+        self.assertEqual(m_input.call_count, 1)
+        self.assertEqual(p.save.call_count, 0)
+
+        # transifexrc is not updated if confirm is yes
+        p.txrc.get.side_effect = [
+            'foo', 'bar'
+        ]
+        m_input.return_value = True
+        m_input.reset_mock()
+        username, password = p.getset_host_credentials('test',
+                                                       token=dummy_token)
+        self.assertEqual(username, 'api')
+        self.assertEqual(password, dummy_token)
+        self.assertEqual(p.txrc.set.call_count, 4)
+        self.assertEqual(m_input.call_count, 1)
+        p.save.assert_called()
+
     def test_extract_fields(self):
         """Test the functions that extract a field from a stats object."""
         stats = {
@@ -569,6 +648,150 @@ class TestProjectPull(unittest.TestCase):
                 res = self.p._should_download('pt', self.stats, None, True)
                 self.assertEqual(res, True)
 
+    def test_get_url_by_pull_mode(self):
+        self.assertEqual(
+            'pull_sourceastranslation_file',
+            self.p._get_url_by_pull_mode(mode='sourceastranslation')
+        )
+        self.assertEqual(
+            DEFAULT_PULL_URL,
+            self.p._get_url_by_pull_mode(mode='invalid mode')
+        )
+        self.assertEqual(
+            DEFAULT_PULL_URL,
+            self.p._get_url_by_pull_mode(mode=None)
+        )
+
+    def fixture_mocked_project(func):
+        """A mock object with main os and http operations mocked"""
+        @wraps(func)
+        def wrapper(*args, **kwargs):
+            app_dir = dirname(modules['txclib'].__file__)
+            config_file = app_dir + "/../tests/templates/config"
+            transifex_file = app_dir + "/../tests/templates/transifexrc"
+            with patch("txclib.utils.encode_args") as mock_encode_args, \
+                patch("txclib.utils.determine_charset")\
+                    as mock_determine_charset, \
+                    patch("txclib.utils.get_transifex_file",
+                          return_value=transifex_file) \
+                    as mock_get_transifex_file, \
+                    patch("txclib.utils.get_config_file_path",
+                          return_value=config_file) \
+                    as mock_get_config_file_path, \
+                    patch("txclib.utils.save_txrc_file") \
+                    as mock_save_txrc_file, \
+                    patch("txclib.project.Project._get_stats_for_resource") \
+                    as mock_get_stats_for_resource:
+
+                # Create fake https response
+                def encode_args(*args, **kwargs):
+                    struct = namedtuple("response", "data status close")
+                    return struct(status=401, data="mock_response", close=Mock())
+                mock_determine_charset.return_value = "utf-8"
+                mock_encode_args.return_value = encode_args
+
+                # Mock configuration files
+                p = Project(init=False)
+                p._init(path_to_tx=app_dir + "/../templates")
+
+                kwargs['mock_project'] = p
+                kwargs['mocks'] = {
+                    'mock_determine_charset': mock_determine_charset,
+                    "mock_encode_args": mock_encode_args,
+                    "mock_get_config_file_path": mock_get_config_file_path,
+                    "mock_get_stats_for_resource": mock_get_stats_for_resource,
+                    "mock_get_transifex_file": mock_get_transifex_file,
+                    "mock_save_txrc_file": mock_save_txrc_file
+                }
+                return func(*args, **kwargs)
+        return wrapper
+
+    @fixture_mocked_project
+    @patch("txclib.project.logger.error")
+    def test_pull_raises_authentication_exception(self, mock_logger, **kwargs):
+        project = kwargs['mock_project']
+        with self.assertRaises(AuthenticationError):
+            project.pull()
+            mock_logger.assert_called_once_with(
+                Project.AUTHENTICATION_FAILED_MESSAGE)
+
+    @fixture_mocked_project
+    @patch('txclib.project.Project.do_url_request')
+    def test_pull_with_branch_pulls_from_right_resource(self, m, **kwargs):
+        m.return_value = ('{"i18n_type": "PO"}', '')
+        project = kwargs['mock_project']
+        project.pull(branch='somebranch')
+        self.assertDictEqual(project.url_info, {
+            'host': 'https://fake.com',
+            'project': 'example',
+            'resource': 'somebranch--enpo'
+        })
+
+    @fixture_mocked_project
+    @patch('txclib.project.Project.do_url_request')
+    def test_push_with_branch_pushes_to_right_resource(self, m, **kwargs):
+        m.return_value = ('{"i18n_type": "PO"}', '')
+        project = kwargs['mock_project']
+        project.push(source=True, branch='somebranch')
+        self.assertDictEqual(project.url_info, {
+            'host': 'https://fake.com',
+            'project': 'example',
+            'resource': 'somebranch--enpo'
+        })
+
+    @fixture_mocked_project
+    @patch('txclib.project.Project.do_url_request')
+    def test_push_with_branch_weird_characters_are_handled(self, m, **kwargs):
+        m.return_value = ('{"i18n_type": "PO"}', '')
+        project = kwargs['mock_project']
+        project.push(source=True, branch='some/b**r:a&n!ch')
+        self.assertDictEqual(project.url_info, {
+            'host': 'https://fake.com',
+            'project': 'example',
+            'resource': 'some-b-r-a-n-ch--enpo'
+        })
+
+    @fixture_mocked_project
+    @patch("txclib.project.logger.error")
+    def test_push_raises_authentication_exception(self, mock_logger, **kwargs):
+        project = kwargs['mock_project']
+        with self.assertRaises(AuthenticationError):
+            project.push()
+
+    @fixture_mocked_project
+    @patch("txclib.project.logger.error")
+    def test_delete_raises_authentication_exception(self, mock_logger,
+                                                    **kwargs):
+        project = kwargs['mock_project']
+        with self.assertRaises(AuthenticationError):
+            project.delete()
+
+    @fixture_mocked_project
+    @patch("txclib.project.logger.error")
+    @patch("txclib.utils.make_request")
+    def test_pull_raises_connection_exception(self, mock_request, mock_logger,
+                                              **kwargs):
+        """Test that all connection errors are properly handled."""
+        project = kwargs["mock_project"]
+        response = 502
+        msg = "Failed with code %d" % response
+        mock_request.side_effect = TXConnectionError(msg, code=response)
+        with self.assertRaises(TXConnectionError):
+            project.pull()
+
+    @fixture_mocked_project
+    @patch("txclib.project.logger.error")
+    @patch("txclib.utils.make_request")
+    def test_push_raises_connection_exception(self, mock_request, mock_logger,
+                                              **kwargs):
+        """Test that all connection errors are properly handled."""
+        project = kwargs["mock_project"]
+        response = 500
+        msg = "Failed with code %d" % response
+        mock_request.side_effect = TXConnectionError(msg, code=response)
+        with self.assertRaises(TXConnectionError):
+            project.push()
+
 
 class TestFormats(unittest.TestCase):
     """Tests for the supported formats."""
diff --git a/tests/test_project.pyc b/tests/test_project.pyc
new file mode 100644
index 0000000..3f8fdfb
Binary files /dev/null and b/tests/test_project.pyc differ
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 33ad6ab..0fc875d 100755
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,10 +1,19 @@
 import unittest
-from mock import patch, MagicMock
+from mock import patch, MagicMock, mock_open
 from urllib3.exceptions import SSLError
 
 from txclib import utils, exceptions
 
 
+# XXX: Taken from https://stackoverflow.com/a/21611963
+def Any():
+    """A method that will match any parameter."""
+    class Any(object):
+        def __eq__(self, *args):
+            return True
+    return Any()
+
+
 class MakeRequestTestCase(unittest.TestCase):
 
     @patch('urllib3.PoolManager')
@@ -109,7 +118,7 @@ class MakeRequestTestCase(unittest.TestCase):
         host = 'http://whynotestsforthisstuff.com'
         url = '/my_test_url/'
         self.assertRaises(
-            Exception,
+            exceptions.HttpNotAuthorized,
             utils.make_request,
             'GET',
             host,
... 3310 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/transifex-client.git



More information about the Python-modules-commits mailing list