| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 # This file is part of the Adblock Plus web scripts, | 
|  | 2 # Copyright (C) 2006-present eyeo GmbH | 
|  | 3 # | 
|  | 4 # Adblock Plus is free software: you can redistribute it and/or modify | 
|  | 5 # it under the terms of the GNU General Public License version 3 as | 
|  | 6 # published by the Free Software Foundation. | 
|  | 7 # | 
|  | 8 # Adblock Plus is distributed in the hope that it will be useful, | 
|  | 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 11 # GNU General Public License for more details. | 
|  | 12 # | 
|  | 13 # You should have received a copy of the GNU General Public License | 
|  | 14 # along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|  | 15 | 
|  | 16 from __future__ import unicode_literals | 
|  | 17 | 
|  | 18 import os | 
|  | 19 from ConfigParser import SafeConfigParser | 
|  | 20 import io | 
|  | 21 import sys | 
|  | 22 import json | 
|  | 23 | 
|  | 24 import pytest | 
|  | 25 | 
|  | 26 from cms.translations.xtm import constants as const | 
|  | 27 from cms.translations.xtm.projects_handler import ( | 
|  | 28     create_project, upload_files, download_files, | 
|  | 29 ) | 
|  | 30 from cms.translations.xtm.cli import ( | 
|  | 31     generate_token, handle_projects as main_project_handler, | 
|  | 32 ) | 
|  | 33 | 
|  | 34 _CMD_START = ['python', '-m', 'cms.bin.xtm_translations'] | 
|  | 35 | 
|  | 36 _ENV_NO_TOKEN = dict(os.environ) | 
|  | 37 _ENV_NO_TOKEN.pop(const.Token.ENV_VAR, None) | 
|  | 38 | 
|  | 39 _ENV_TOKEN_VALID = dict(os.environ) | 
|  | 40 _ENV_TOKEN_VALID[const.Token.ENV_VAR] = 'TheXTM-APIToken-VALID' | 
|  | 41 | 
|  | 42 _ENV_TOKEN_INVALID = dict(os.environ) | 
|  | 43 _ENV_TOKEN_INVALID[const.Token.ENV_VAR] = 'TheXTM-APIToken-INVALID' | 
|  | 44 | 
|  | 45 | 
|  | 46 class _CreationArgsNamespace: | 
|  | 47     def __init__(self): | 
|  | 48         pass | 
|  | 49 | 
|  | 50     name = 'bar' | 
|  | 51     desc = 'foo' | 
|  | 52     client_id = 10 | 
|  | 53     ref_id = 'faz' | 
|  | 54     workflow_id = 20 | 
|  | 55     save_id = False | 
|  | 56     source_dir = None | 
|  | 57     projects_func = staticmethod(create_project) | 
|  | 58     source_lang = 'en_US' | 
|  | 59 | 
|  | 60 | 
|  | 61 class _UploadArgsNamespace: | 
|  | 62     def __init__(self): | 
|  | 63         pass | 
|  | 64 | 
|  | 65     source_dir = None | 
|  | 66     projects_func = staticmethod(upload_files) | 
|  | 67     no_overwrite = False | 
|  | 68 | 
|  | 69 | 
|  | 70 class _DownloadArgsNamespace: | 
|  | 71     def __init__(self): | 
|  | 72         pass | 
|  | 73 | 
|  | 74     source_dir = None | 
|  | 75     projects_func = staticmethod(download_files) | 
|  | 76 | 
|  | 77 | 
|  | 78 _CREATION_ARGS_DEFAULT = ['--name', 'bar', '--desc', 'foo', '--client-id', | 
|  | 79                           '10', '--ref-id', 'faz', '--workflow-id', '20'] | 
|  | 80 _CREATION_ARGS_INVALID_TYPE_1 = ['--name', 'bar', '--desc', 'foo', | 
|  | 81                                  '--client-id', 'foo', '--ref-id', 'faz', | 
|  | 82                                  '--workflow-id', '3'] | 
|  | 83 _CREATION_ARGS_INVALID_TYPE_2 = ['--name', 'bar', '--desc', 'foo', | 
|  | 84                                  '--client-id', '23', '--ref-id', 'faz', | 
|  | 85                                  '--workflow-id', 'foo'] | 
|  | 86 | 
|  | 87 | 
|  | 88 @pytest.fixture | 
|  | 89 def env_valid_token(): | 
|  | 90     old_env = os.environ | 
|  | 91     os.environ = _ENV_TOKEN_VALID | 
|  | 92     yield True | 
|  | 93     os.environ = old_env | 
|  | 94 | 
|  | 95 | 
|  | 96 @pytest.mark.script_launch_mode('subprocess') | 
|  | 97 @pytest.mark.parametrize('args,exp_msg', [ | 
|  | 98     (['-h'], 'usage: xtm_translations.py [-h] [-v] ' | 
|  | 99              '{login,create,upload,download} ...'), | 
|  | 100     (['create', '-h'], 'usage: xtm_translations.py create [-h] --name NAME ' | 
|  | 101                        '--desc DESC --client-id CLIENT_ID --ref-id REF_ID ' | 
|  | 102                        '--workflow-id WORKFLOW_ID [--source-lang SOURCE_LANG] ' | 
|  | 103                        '[--save-id] [source_dir]'), | 
|  | 104     (['upload', '-h'], 'usage: xtm_translations.py upload [-h] ' | 
|  | 105                        '[--no-overwrite] [source_dir]'), | 
|  | 106     (['download', '-h'], 'usage: xtm_translations.py download [-h] ' | 
|  | 107                          '[source_dir]'), | 
|  | 108 ]) | 
|  | 109 def test_usage_messages(args, exp_msg, script_runner): | 
|  | 110     """Test if appropriate usage messages are displayed.""" | 
|  | 111     cmd = list(_CMD_START) | 
|  | 112     cmd.extend(args) | 
|  | 113     ret = script_runner.run(*cmd) | 
|  | 114 | 
|  | 115     usg_msg = ret.stdout.replace('\n', '').replace(' ', '') | 
|  | 116 | 
|  | 117     assert exp_msg.replace(' ', '') in usg_msg | 
|  | 118 | 
|  | 119 | 
|  | 120 @pytest.mark.script_launch_mode('subprocess') | 
|  | 121 @pytest.mark.parametrize('args', [ | 
|  | 122     ['create', '--name', 'bar', '--desc', 'foo', '--client-id', '1', | 
|  | 123      '--ref-id', 'faz', '--workflow-id', '3'], | 
|  | 124     ['upload'], | 
|  | 125     ['download'], | 
|  | 126 ]) | 
|  | 127 def test_default_source_directory(args, script_runner): | 
|  | 128     """Test if the source directory if set to default if not provided.""" | 
|  | 129     exp = const.ErrorMessages.NO_TOKEN_PROVIDED.split('\n')[0] | 
|  | 130     cmd = list(_CMD_START) | 
|  | 131     cmd.extend(args) | 
|  | 132 | 
|  | 133     ret = script_runner.run(*cmd) | 
|  | 134 | 
|  | 135     assert not ret.success | 
|  | 136     assert exp in ret.stderr | 
|  | 137 | 
|  | 138 | 
|  | 139 @pytest.mark.script_launch_mode('subprocess') | 
|  | 140 @pytest.mark.parametrize('source_dir,args,env,exp_msg', [ | 
|  | 141     ('str(temp_site)', _CREATION_ARGS_INVALID_TYPE_1, _ENV_NO_TOKEN, | 
|  | 142      "--client-id: invalid int value: 'foo'"), | 
|  | 143     ('str(temp_site)', _CREATION_ARGS_INVALID_TYPE_2, _ENV_NO_TOKEN, | 
|  | 144      "--workflow-id: invalid int value: 'foo'"), | 
|  | 145     ('str(temp_site)', _CREATION_ARGS_DEFAULT, _ENV_NO_TOKEN, | 
|  | 146      const.ErrorMessages.NO_TOKEN_PROVIDED.split('\n')[0]), | 
|  | 147     ('str(temp_site_valid_project)', _CREATION_ARGS_DEFAULT, _ENV_TOKEN_VALID, | 
|  | 148      const.ErrorMessages.PROJECT_EXISTS.format(1234)), | 
|  | 149     ('str(temp_site)', _CREATION_ARGS_DEFAULT, _ENV_TOKEN_INVALID, | 
|  | 150      'Authentication failed'), | 
|  | 151 ]) | 
|  | 152 def test_creation_error_messages(temp_site, intercept, script_runner, args, | 
|  | 153                                  source_dir, temp_site_valid_project, env, | 
|  | 154                                  exp_msg): | 
|  | 155     """Test if error cases are treated correctly when creating a project.""" | 
|  | 156     cmd = list(_CMD_START) | 
|  | 157     cmd.extend(['create', eval(source_dir)]) | 
|  | 158     cmd.extend(args) | 
|  | 159 | 
|  | 160     ret = script_runner.run(*cmd, env=env) | 
|  | 161 | 
|  | 162     assert not ret.success | 
|  | 163     assert exp_msg in ret.stderr | 
|  | 164 | 
|  | 165 | 
|  | 166 def test_creation_correct(temp_site, intercept, env_valid_token): | 
|  | 167     """Test if a project is created correctly, given the appropriate args.""" | 
|  | 168     namespace = _CreationArgsNamespace() | 
|  | 169     namespace.source_dir = str(temp_site) | 
|  | 170     main_project_handler(namespace) | 
|  | 171 | 
|  | 172 | 
|  | 173 def test_creation_save_id(temp_site, intercept, env_valid_token): | 
|  | 174     """Test the project id is saved after successfully creating a project.""" | 
|  | 175     namespace = _CreationArgsNamespace() | 
|  | 176     namespace.source_dir = str(temp_site) | 
|  | 177     namespace.save_id = True | 
|  | 178     main_project_handler(namespace) | 
|  | 179     cnf = SafeConfigParser() | 
|  | 180     try: | 
|  | 181         with io.open(os.path.join(temp_site, 'settings.ini'), | 
|  | 182                      encoding='utf-8') as f: | 
|  | 183             cnf_data = f.read() | 
|  | 184             cnf.readfp(io.StringIO(cnf_data)) | 
|  | 185             project_id = cnf.get(const.Config.XTM_SECTION, | 
|  | 186                                  const.Config.PROJECT_OPTION) | 
|  | 187 | 
|  | 188             assert int(project_id) == 1234 | 
|  | 189     finally: | 
|  | 190         cnf.remove_option(const.Config.XTM_SECTION, | 
|  | 191                           const.Config.PROJECT_OPTION) | 
|  | 192         cnf.write(open(os.path.join(temp_site, 'settings.ini'), 'w')) | 
|  | 193 | 
|  | 194 | 
|  | 195 def test_login(intercept, monkeypatch, capsys): | 
|  | 196     """Test if the login functionality works as expected.""" | 
|  | 197     exp_output = const.Token.SAVE_COMMAND.format(const.Token.ENV_VAR, | 
|  | 198                                                  'TheXTM-APIToken-VALID') | 
|  | 199     monkeypatch.setattr( | 
|  | 200         'cms.translations.xtm.cli.input_fn', | 
|  | 201         lambda inp: 'admin' if 'username' in inp.lower() else '20', | 
|  | 202     ) | 
|  | 203     monkeypatch.setattr('getpass.getpass', lambda prompt: 'pass') | 
|  | 204 | 
|  | 205     generate_token(None) | 
|  | 206     out, err = capsys.readouterr() | 
|  | 207 | 
|  | 208     assert err == '' | 
|  | 209     assert exp_output in out | 
|  | 210 | 
|  | 211 | 
|  | 212 def test_login_wrong_credentials(intercept, monkeypatch, capsys): | 
|  | 213     """Test exception handling when generating the tokens.""" | 
|  | 214     monkeypatch.setattr( | 
|  | 215         'cms.translations.xtm.cli.input_fn', | 
|  | 216         lambda inp: 'foo' if 'username' in inp.lower() else '50', | 
|  | 217     ) | 
|  | 218     monkeypatch.setattr('getpass.getpass', lambda prompt: 'pass') | 
|  | 219     monkeypatch.setattr('sys.exit', lambda x: sys.stderr.write(str(x))) | 
|  | 220 | 
|  | 221     generate_token(None) | 
|  | 222     out, err = capsys.readouterr() | 
|  | 223 | 
|  | 224     assert 'Invalid credentials' in err | 
|  | 225     assert out == '' | 
|  | 226 | 
|  | 227 | 
|  | 228 @pytest.mark.script_launch_mode('subprocess') | 
|  | 229 @pytest.mark.parametrize('args,env,exp_msg', [ | 
|  | 230     (['str(temp_site)'], _ENV_NO_TOKEN, | 
|  | 231      const.ErrorMessages.NO_TOKEN_PROVIDED.split('\n')[0]), | 
|  | 232     (['str(temp_site)'], _ENV_TOKEN_VALID, 'No project configured'), | 
|  | 233 ]) | 
|  | 234 def test_upload_error_messages_as_script(temp_site, script_runner, args, env, | 
|  | 235                                          exp_msg): | 
|  | 236     cmd = list(_CMD_START) | 
|  | 237     cmd.append('upload') | 
|  | 238 | 
|  | 239     cmd.extend(list(map(eval, args))) | 
|  | 240 | 
|  | 241     ret = script_runner.run(*cmd, env=env) | 
|  | 242 | 
|  | 243     assert not ret.success | 
|  | 244     assert exp_msg in ret.stderr | 
|  | 245 | 
|  | 246 | 
|  | 247 def test_upload_too_many_languages(intercept_too_many_targets, | 
|  | 248                                    env_valid_token, temp_site_valid_project): | 
|  | 249     namespace = _UploadArgsNamespace() | 
|  | 250     namespace.source_dir = str(temp_site_valid_project) | 
|  | 251 | 
|  | 252     with pytest.raises(Exception) as err: | 
|  | 253         main_project_handler(namespace) | 
|  | 254 | 
|  | 255     assert 'languages are enabled in the API, but not listed in locales' in \ | 
|  | 256            str(err.value) | 
|  | 257 | 
|  | 258 | 
|  | 259 def test_upload_successful(intercept, env_valid_token, | 
|  | 260                            temp_site_valid_project, monkeypatch, capsys): | 
|  | 261     namespace = _UploadArgsNamespace() | 
|  | 262     namespace.source_dir = str(temp_site_valid_project) | 
|  | 263     monkeypatch.setattr('logging.info', lambda x: sys.stderr.write(x)) | 
|  | 264 | 
|  | 265     main_project_handler(namespace) | 
|  | 266     _, err = capsys.readouterr() | 
|  | 267 | 
|  | 268     assert const.InfoMessages.FILES_UPLOADED in err | 
|  | 269     assert 'Created job 1' in err | 
|  | 270 | 
|  | 271 | 
|  | 272 @pytest.mark.script_launch_mode('subprocess') | 
|  | 273 @pytest.mark.parametrize('env,exp_msg', [ | 
|  | 274     (_ENV_NO_TOKEN, const.ErrorMessages.NO_TOKEN_PROVIDED.split('\n')[0]), | 
|  | 275     (_ENV_TOKEN_VALID, 'No project configured'), | 
|  | 276 ]) | 
|  | 277 def test_download_error_messages_as_script(temp_site, script_runner, env, | 
|  | 278                                            exp_msg): | 
|  | 279     cmd = list(_CMD_START) | 
|  | 280     cmd.extend(['download', str(temp_site)]) | 
|  | 281 | 
|  | 282     ret = script_runner.run(*cmd, env=env) | 
|  | 283 | 
|  | 284     assert not ret.success | 
|  | 285     assert exp_msg in ret.stderr | 
|  | 286 | 
|  | 287 | 
|  | 288 def test_download_no_target_files(temp_site_no_target_files, env_valid_token, | 
|  | 289                                   intercept, monkeypatch, capsys): | 
|  | 290     namespace = _DownloadArgsNamespace() | 
|  | 291     namespace.source_dir = str(temp_site_no_target_files) | 
|  | 292     monkeypatch.setattr('sys.exit', lambda x: sys.stderr.write(x)) | 
|  | 293 | 
|  | 294     main_project_handler(namespace) | 
|  | 295     out, err = capsys.readouterr() | 
|  | 296 | 
|  | 297     assert const.ErrorMessages.NO_TARGET_FILES_FOUND in err | 
|  | 298 | 
|  | 299 | 
|  | 300 def test_download_saves_to_disk(temp_site_valid_project, env_valid_token, | 
|  | 301                                 intercept_populated): | 
|  | 302     namespace = _DownloadArgsNamespace() | 
|  | 303     namespace.source_dir = str(temp_site_valid_project) | 
|  | 304 | 
|  | 305     main_project_handler(namespace) | 
|  | 306 | 
|  | 307     with open(os.path.join(temp_site_valid_project, 'locales', 'de', | 
|  | 308                            'file.json')) as f: | 
|  | 309         assert json.dumps({'foo': 'bar', 'faz': 'baz'}) == f.read() | 
|  | 310 | 
|  | 311     with open(os.path.join(temp_site_valid_project, 'locales', 'de', 'foo', | 
|  | 312                            'file-utf8.json'), 'rb') as f: | 
|  | 313         assert json.loads(f.read()) == {'foo': '\u1234'} | 
| OLD | NEW | 
|---|