| Index: tests/xtm_mock_api.py |
| diff --git a/tests/xtm_mock_api.py b/tests/xtm_mock_api.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..20d2fccbf3af60548893f4fdf290d437163cfd25 |
| --- /dev/null |
| +++ b/tests/xtm_mock_api.py |
| @@ -0,0 +1,273 @@ |
| +# This file is part of the Adblock Plus web scripts, |
| +# Copyright (C) 2006-present eyeo GmbH |
| +# |
| +# Adblock Plus is free software: you can redistribute it and/or modify |
| +# it under the terms of the GNU General Public License version 3 as |
| +# published by the Free Software Foundation. |
| +# |
| +# Adblock Plus is distributed in the hope that it will be useful, |
| +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| +# GNU General Public License for more details. |
| +# |
| +# You should have received a copy of the GNU General Public License |
| +# along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| + |
| +from flask import Flask, request, jsonify, Response, send_file |
| + |
| +import json |
| + |
| +from tests.utils import create_in_memory_zip |
| +from cms.translations.xtm.constants import SUPPORTED_LOCALES |
| + |
| +__all__ = [ |
| + 'xtm_mock', |
| + 'get_configured_app', |
| +] |
| + |
| +_CREDENTIALS = { |
| + 'token_gen_valid': {'client': 'admin', 'password': 'pass', 'userId': 20}, |
| + 'token_gen_invalid': {'client': 'user', 'password': 'pass', 'userId': 50}, |
| + 'token_valid': 'TheXTM-APIToken-VALID', |
| + 'token_invalid': 'TheXTM-APIToken-INVALID', |
| +} |
| + |
| +_PROJECT_IDS = { |
| + 'valid': 1234, |
| + 'invalid': 1111, |
| + 'valid-no-targetfiles': 9999, |
| +} |
| + |
| +_CREATION_MANDATORY_KEYS = {'workflowId', 'referenceId', 'name', |
| + 'targetLanguages', 'customerId', 'description'} |
| + |
| +_CREATION_OPTIONAL_KEYS = {r'translationFiles\[(?P<idx>\d+)\].file', |
| + r'translationFiles\[(?P<idx>\d+)\].name'} |
| + |
| +xtm_mock = Flask('mocking-XTM') |
| + |
| + |
| +def get_configured_app(**kwargs): |
| + xtm_mock.config['rootdir'] = kwargs.pop('rootdir', '') |
| + xtm_mock.config['target_languages'] = kwargs.pop('target_langs', ['de_DE']) |
| + xtm_mock.config['files'] = kwargs.pop('files', []) |
| + xtm_mock.config['source_language'] = kwargs.pop('source_lang', 'en_US') |
| + return xtm_mock.wsgi_app |
| + |
| + |
| +def _check_auth(): |
| + try: |
| + auth = request.headers['Authorization'] |
| + except Exception: |
| + return False |
| + |
| + if auth != 'XTM-Basic ' + _CREDENTIALS['token_valid']: |
| + return False |
| + |
| + return True |
| + |
| + |
| +def _generate_jobs_list(): |
| + jobs = [] |
| + for lng in xtm_mock.config['target_languages']: |
| + for f in xtm_mock.config['files']: |
| + jobs.append({ |
| + 'fileName': f['name'], |
| + 'jobId': 1, |
| + 'sourceLanguage': xtm_mock.config['source_language'], |
| + 'targetLanguage': lng, |
| + }) |
| + |
| + return jobs |
| + |
| + |
| +@xtm_mock.route('/rest-api/auth/token', methods=['POST']) |
| +def authenticate(): |
| + if not request.is_json: |
| + return Response( |
| + status=400, |
| + response=json.dumps({'reason': 'Request body not a JSON.'}), |
| + ) |
| + |
| + credentials = request.json |
| + missing_keys = \ |
| + set(_CREDENTIALS['token_gen_valid'].keys()) - set(credentials.keys()) |
| + |
| + if len(missing_keys) > 0: |
| + return Response( |
| + status=400, |
| + response=json.dumps({'reason': 'Missing keys: {}'.format( |
| + missing_keys)}), |
| + ) |
| + |
| + if credentials == _CREDENTIALS['token_gen_valid']: |
| + return jsonify({'token': _CREDENTIALS['token_valid']}) |
| + |
| + if credentials == _CREDENTIALS['token_gen_invalid']: |
| + return jsonify({'token': _CREDENTIALS['token_invalid']}) |
| + |
| + return Response( |
| + status=400, |
| + response=json.dumps({'reason': 'Invalid credentials.'}), |
| + ) |
| + |
| + |
| +@xtm_mock.route('/rest-api/projects', methods=['POST']) |
| +def create_project(): |
| + if not _check_auth(): |
| + return Response( |
| + status=401, |
| + response=json.dumps({'reason': 'Authentication failed.'}), |
| + ) |
| + |
| + form_data = request.form.to_dict(flat=False) |
| + files = request.files.to_dict() |
| + xtm_mock.config['target_languages'] = form_data['targetLanguages'] |
| + keys_diff = _CREATION_MANDATORY_KEYS - set(form_data.keys()) |
| + |
| + if len(keys_diff) != 0: |
| + return Response( |
| + status=400, |
| + response=json.dumps( |
| + {'reason': 'Keys missing: {}'.format(keys_diff)}, |
| + ), |
| + ) |
| + |
| + # To make everything easier, just add the 1st file to the class |
| + if 'translationFiles[0].name' in form_data.keys(): |
| + xtm_mock.config['files'].append({ |
| + 'name': form_data['translationFiles[0].name'][0], |
| + 'data': files['translationFiles[0].file'], |
| + }) |
| + |
| + response_json = { |
| + 'name': form_data['name'][0], |
| + 'projectId': _PROJECT_IDS['valid'], |
| + 'jobs': _generate_jobs_list(), |
| + } |
| + |
| + return Response(status=201, response=json.dumps(response_json)) |
| + |
| + |
| +@xtm_mock.route('/rest-api/projects/<int:project_id>/metrics', methods=['GET']) |
| +def get_metrics(project_id): |
| + if not _check_auth(): |
| + return Response( |
| + status=401, |
| + response=json.dumps({'reason': 'Authentication failed.'}), |
| + ) |
| + |
| + reply = [{ |
| + 'coreMetrics': {}, |
| + 'jobsMetrics': [], |
| + 'metricsProgress': {}, |
| + 'targetLanguage': tl, |
| + } for tl in xtm_mock.config['target_languages']] |
| + |
| + if project_id == _PROJECT_IDS['valid']: |
| + return jsonify(reply) |
| + |
| + return Response(status=404, response=json.dumps({'reason': 'Project not ' |
| + 'found!'})) |
| + |
| + |
| +@xtm_mock.route('/rest-api/projects/<int:project_id>/target-languages', |
| + methods=['POST']) |
| +def add_languages(project_id): |
| + if not _check_auth(): |
| + return Response( |
| + status=401, |
| + response=json.dumps({'reason': 'Authentication failed.'}), |
| + ) |
| + if not request.is_json: |
| + return Response( |
| + status=400, |
| + response=json.dumps({'reason': 'Not a JSON.'}), |
| + ) |
| + |
| + for language in request.json['targetLanguages']: |
| + if language not in SUPPORTED_LOCALES: |
| + return Response( |
| + status=400, |
| + response=json.dumps( |
| + {'reason': 'Unsupported language: {''}'.format(language)}, |
| + ), |
| + ) |
| + |
| + xtm_mock.config['target_languages'] += request.json['targetLanguages'] |
| + response = { |
| + 'jobs': _generate_jobs_list(), |
| + 'projectId': project_id, |
| + 'projectName': 'test-name', |
| + } |
| + |
| + if project_id == _PROJECT_IDS['valid']: |
| + return jsonify(response) |
| + |
| + return Response( |
| + status=404, |
| + response=json.dumps({'reason': 'Project not found!'}), |
| + ) |
| + |
| + |
| +@xtm_mock.route('/rest-api/projects/<int:project_id>/files/upload', |
| + methods=['POST']) |
| +def upload_and_update(project_id): |
| + if not _check_auth(): |
| + return Response( |
| + status=401, |
| + response='Authentication failed.', |
| + ) |
| + if project_id == _PROJECT_IDS['invalid']: |
| + return Response( |
| + status=404, |
| + response='Project not found!', |
| + ) |
| + |
| + data = request.form.to_dict() |
| + files = request.files.to_dict() |
| + |
| + xtm_mock.config['files'].append({ |
| + 'name': data['files[0].name'], 'data': files['files[0].file'], |
| + }) |
| + |
| + return jsonify({ |
| + 'project_id': 1234, |
| + 'jobs': _generate_jobs_list(), |
| + }) |
| + |
| + |
| +@xtm_mock.route('/rest-api/projects/<int:project_id>/files/download', |
| + methods=['GET']) |
| +def download(project_id): |
| + if not _check_auth(): |
| + return Response( |
| + status=401, |
| + response=json.dumps({'reason': 'Authentication failed.'}), |
| + ) |
| + if project_id == _PROJECT_IDS['invalid']: |
| + return Response( |
| + status=404, |
| + response=json.dumps({'reason': 'Project not found.'}), |
| + ) |
| + if project_id == _PROJECT_IDS['valid-no-targetfiles']: |
| + return Response(status=200) |
| + |
| + names = [ |
| + '/'.join([target_lang, f['name']]) for f in xtm_mock.config['files'] |
| + for target_lang in xtm_mock.config['target_languages'] |
| + ] |
| + data = [ |
| + f['data'] for f in xtm_mock.config['files'] |
| + for _ in xtm_mock.config['target_languages'] |
| + ] |
| + |
| + zip_file = create_in_memory_zip(names, data) |
| + return send_file( |
| + zip_file, |
| + mimetype='application/zip', |
| + ) |
| + |
| + |
| +if __name__ == '__main__': |
| + xtm_mock.run(debug=True) |