diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d398ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,352 @@ +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +staticfiles/ + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# pyenv +.python-version + + + +# Environments +.venv +venv/ +ENV/ + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + + +### Node template +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + + +### Linux template +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + + +### VisualStudioCode template +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + + +# Provided default Pycharm Run/Debug Configurations should be tracked by git +# In case of local modifications made by Pycharm, use update-index command +# for each changed file, like this: +# git update-index --assume-unchanged .idea/zipsale_web.iml +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +../.idea +.idea +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries +.idea/inspectionProfiles/Project_Default.xml +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + + + +### Windows template +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + + +### macOS template +# General +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### SublimeText template +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + + +### Vim template +# Swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] + +# Session +Session.vim + +# Temporary +.netrwhist + +# Auto-generated tag files +tags + + +### Project template + +zipsale_web/media/ + +.pytest_cache/ + + +.ipython/ +.pyenv +.envrc +*.env +.envs/* +!.envs/.local/ +../.bashrc +db.sqlite +/.envrc +.bashrc + +.gitmodules +environment-template/ + +mps_poc/ + +libiconv2.dll +libintl3.dll +make.exe + +*.json +*sqlite3 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..1636c7f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: python +python: + - "3.9" +install: + - pip install -r requirements.txt +script: + - flake8 . + - pytest --cov=. --cov-branch tests +after_success: + - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/README.md b/README.md index 6b3a3c4..71d8b74 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +[![Build Status](https://travis-ci.org/katyaaa86/level_2_2.svg?branch=main)](https://travis-ci.org/katyaaa86/level_2_2) +[![codecov](https://codecov.io/gh/katyaaa86/level_2_2/branch/main/graph/badge.svg)](https://codecov.io/gh/katyaaa86/level_2_2) + # Testing sandbox level 2.2 Welcome to the testing sandbox: practice-driven testing instrument. diff --git a/code/__init__.py b/filecode/__init__.py similarity index 100% rename from code/__init__.py rename to filecode/__init__.py diff --git a/code/flake_master/__init__.py b/filecode/flake_master/__init__.py similarity index 100% rename from code/flake_master/__init__.py rename to filecode/flake_master/__init__.py diff --git a/code/flake_master/common_types.py b/filecode/flake_master/common_types.py similarity index 100% rename from code/flake_master/common_types.py rename to filecode/flake_master/common_types.py diff --git a/code/flake_master/run.py b/filecode/flake_master/run.py similarity index 89% rename from code/flake_master/run.py rename to filecode/flake_master/run.py index 3d2574d..7e02d4a 100644 --- a/code/flake_master/run.py +++ b/filecode/flake_master/run.py @@ -3,19 +3,19 @@ from click import command, group, argument, Path, echo, pass_context -from code.flake_master.utils.presets import fetch_preset, apply_preset_to_path +from filecode.flake_master.utils.presets import fetch_preset, apply_preset_to_path @group() def cli(): - pass + pass # pragma: no cover @command() @argument('preset_name') @argument('project_path', type=Path(exists=True)) @pass_context -def setup(ctx, preset_name, project_path): +def flake_setup(ctx, preset_name, project_path): """Setup flake8 preset to specified directory.""" preset_file_name = ctx.obj['preset_file_name'] preset_file_path = os.path.join(project_path, preset_file_name) @@ -26,7 +26,6 @@ def setup(ctx, preset_name, project_path): err=True, ) exit(1) - preset = fetch_preset(preset_name_or_url_or_path=preset_name) if not preset: echo(f'Error fetching preset {preset_name}.', err=True) @@ -62,15 +61,15 @@ def upgrade(ctx, project_path): apply_preset_to_path(fresh_preset, project_path, preset_file_name=preset_file_name) -cli.add_command(setup) +cli.add_command(flake_setup) cli.add_command(upgrade) def main(): - cli( + cli( # pragma: no cover obj={'preset_file_name': '.flake_master'}, ) if __name__ == '__main__': - main() + main() # pragma: no cover diff --git a/code/flake_master/utils/__init__.py b/filecode/flake_master/utils/__init__.py similarity index 100% rename from code/flake_master/utils/__init__.py rename to filecode/flake_master/utils/__init__.py diff --git a/code/flake_master/utils/preset_fetchers.py b/filecode/flake_master/utils/preset_fetchers.py similarity index 94% rename from code/flake_master/utils/preset_fetchers.py rename to filecode/flake_master/utils/preset_fetchers.py index f2da9b6..15d9f3b 100644 --- a/code/flake_master/utils/preset_fetchers.py +++ b/filecode/flake_master/utils/preset_fetchers.py @@ -4,7 +4,7 @@ import deal from requests import get -from code.flake_master.common_types import Flake8Preset +from filecode.flake_master.common_types import Flake8Preset def load_preset_from_file(preset_file_path: str) -> Optional[Flake8Preset]: diff --git a/code/flake_master/utils/presets.py b/filecode/flake_master/utils/presets.py similarity index 94% rename from code/flake_master/utils/presets.py rename to filecode/flake_master/utils/presets.py index c6449c2..44507b8 100644 --- a/code/flake_master/utils/presets.py +++ b/filecode/flake_master/utils/presets.py @@ -6,9 +6,9 @@ import deal from click import echo -from code.flake_master.common_types import Flake8Preset, Flake8PresetInfo -from code.flake_master.utils.preset_fetchers import load_preset_from_file, load_preset_from_url -from code.flake_master.utils.requirements import merge_requirements_data +from filecode.flake_master.common_types import Flake8Preset, Flake8PresetInfo +from filecode.flake_master.utils.preset_fetchers import load_preset_from_file, load_preset_from_url +from filecode.flake_master.utils.requirements import merge_requirements_data def fetch_preset( @@ -130,8 +130,7 @@ def add_flake8_config( else: echo(f'\t\tUpdating {config_path}...') parser.read(config_path) - - if flake8_section_name not in parser: + if flake8_section_name not in parser: # pragma: no cover parser.add_section(flake8_section_name) for param_name, param_value in flake8_config: parser[flake8_section_name][param_name] = param_value diff --git a/code/flake_master/utils/requirements.py b/filecode/flake_master/utils/requirements.py similarity index 100% rename from code/flake_master/utils/requirements.py rename to filecode/flake_master/utils/requirements.py diff --git a/path b/path new file mode 100644 index 0000000..789de44 --- /dev/null +++ b/path @@ -0,0 +1,2 @@ +flake==3 +flake==8 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 7438561..2569e52 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,9 @@ typing-extensions==3.7.4.2 deal==4.5.0 safety==1.9.0 +pytest==6.2.2 +pytest-mock==3.5.1 +pytest-cov==2.11.1 +coverage==5.5 +mock-open==1.4.0 +flake8==3.8.4 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..b3c9a2e --- /dev/null +++ b/setup.cfg @@ -0,0 +1,13 @@ +[flake8] +max-complexity = 8 +max-annotations-complexity = 4 +max-line-length = 120 +ignore = W503, P103, D +exclude = node_modules,env,venv,venv36,filecode +var_names_exclude_pathes = node_modules,env,venv,venv36 +assert_allowed_in_pathes = tests,migrations,env,venv,venv36 +adjustable-default-max-complexity = 8 +per-file-ignores = + __init__.py: F401 +ban-relative-imports = True +min-coverage-percents = 100 \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..3c50659 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,38 @@ +import pytest + +from filecode.flake_master.common_types import Flake8Preset + + +@pytest.fixture() +def config_parser_dict(): + return { + 'info': {'name': 'name_value', 'revision': 'revision_value'}, + 'flake8_plugins': {'key_1': 'value_1'}, + 'flake8_config': {'key_2': 'value_2'} + } + + +@pytest.fixture() +def os(mocker): + return mocker.patch('filecode.flake_master.utils.presets.os', spec=['path']) + + +@pytest.fixture() +def flake8_preset_factory(config_parser_dict): + def flake8_preset(config_url=None, filepath=None): + return Flake8Preset( + name=config_parser_dict['info']['name'], + revision=config_parser_dict['info']['revision'], + config_url=config_url, + filepath=filepath, + flake8_plugins=list(config_parser_dict['flake8_plugins'].items()), + flake8_config=list(config_parser_dict['flake8_config'].items()), + ) + return flake8_preset + + +@pytest.fixture() +def read_data(): + return ('[info]\nname = name_value\nrevision = revision_value\n\n' + '[flake8_plugins]\nkey_1 = value_1\n' + '[flake8_config]\nkey_2 = value_2\n') diff --git a/tests/test_run.py b/tests/test_run.py new file mode 100644 index 0000000..3279d16 --- /dev/null +++ b/tests/test_run.py @@ -0,0 +1,60 @@ +import pytest +from click.testing import CliRunner + +from filecode.flake_master.run import flake_setup, upgrade + + +@pytest.mark.parametrize( + 'exists, is_fetch, output, exit_code', + [ + (True, False, 'Preset file (path) already exists. Looks like flake master has already ' + 'been deployed to path. May be you mean `upgrade`, not `setup`?\n', 1), + (False, True, 'Fetched name v. revision\nPreset name applied.\n', 0), + (False, False, 'Error fetching preset rose.\n', 1), + ], +) +def test_flake_setup(mocker, exists, is_fetch, output, exit_code): + obj = {'preset_file_name': '.flake_master'} + project_path = 'path' + mocker.patch('filecode.flake_master.run.Path.convert', mocker.Mock(return_value=project_path)) + fetch_preset = mocker.patch('filecode.flake_master.run.fetch_preset', return_value=None) + if is_fetch: + fetch_preset.return_value = mocker.Mock() + fetch_preset().name = 'name' + fetch_preset().revision = 'revision' + mocker.patch('filecode.flake_master.run.apply_preset_to_path') + os = mocker.patch('filecode.flake_master.run.os', spec=[project_path]) + os.path.join.return_value = project_path + os.path.exists.return_value = exists + + runner = CliRunner() + result = runner.invoke(flake_setup, ['rose', project_path], obj=obj) + assert result.output == output + assert result.exit_code == exit_code + + +@pytest.mark.parametrize( + 'exists, preset_info, output, exit_code', + [ + (True, '{"revision": 0, "name": "name"}', 'Updating preset name from rev. 0 to 1...\n', 0), + (True, '{"revision": 1}', '', 0), + (False, '{}', 'Preset file (path) not found. Looks like flake master was not ' + 'deployed to path. May be you mean `setup`, not `upgrade`?\n', 1), + ], +) +def test_upgrade(mocker, exists, preset_info, output, exit_code): + obj = {'preset_file_name': '.flake_master'} + project_path = 'path' + mocker.patch('filecode.flake_master.run.Path.convert', mocker.Mock(return_value=project_path)) + os = mocker.patch('filecode.flake_master.run.os', spec=[project_path]) + os.path.join.return_value = project_path + os.path.exists.return_value = exists + mocker.patch('filecode.flake_master.run.open', mocker.mock_open(read_data=preset_info)) + fetch_preset = mocker.patch('filecode.flake_master.run.fetch_preset', return_value=mocker.Mock()) + fetch_preset().revision = 1 + mocker.patch('filecode.flake_master.run.apply_preset_to_path') + + runner = CliRunner() + result = runner.invoke(upgrade, [project_path], obj=obj) + assert result.output == output + assert result.exit_code == exit_code diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..94ae8d0 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,225 @@ +import pytest + +import filecode + +from filecode.flake_master.utils.preset_fetchers import load_preset_from_file, load_preset_from_url, \ + parse_preset_from_str_config +from filecode.flake_master.utils.presets import ( + extract_preset_url, extract_preset_file_path, apply_preset_to_path, + add_packages_to_requirements_file, add_flake8_config, create_preset_file, extract_preset_credentials) +from filecode.flake_master.utils.requirements import merge_requirements_data, find_requirement_in_list + + +def test_load_preset_from_file(mocker, flake8_preset_factory, read_data): + mocker.patch('filecode.flake_master.utils.preset_fetchers.open', mocker.mock_open(read_data=read_data)) + expected = flake8_preset_factory(filepath='filepath') + assert load_preset_from_file('filepath') == expected + + +def test_load_preset_from_url(mocker, flake8_preset_factory, read_data): + get = mocker.patch('filecode.flake_master.utils.preset_fetchers.get') + get().text = read_data + expected = flake8_preset_factory(config_url='url') + assert load_preset_from_url('url') == expected + get.assert_called_with('url') + + +def test_parse_preset_from_str_config(mocker, flake8_preset_factory, config_parser_dict): + parser = mocker.patch('filecode.flake_master.utils.preset_fetchers.configparser') + parser.ConfigParser().__getitem__.side_effect = config_parser_dict.__getitem__ + expected = flake8_preset_factory(config_url='url', filepath='filepath') + assert parse_preset_from_str_config('text', 'filepath', 'url') == expected + + +@pytest.mark.parametrize( + 'filepath, url, expected', + [ + ('filepath', 'url', 'load_preset_from_file'), + ('filepath', '', 'load_preset_from_file'), + ('', 'url', 'load_preset_from_url'), + ('', '', None), + ] +) +def test_fetch_preset(mocker, filepath, url, expected): + mocker.patch('filecode.flake_master.utils.presets.extract_preset_credentials', return_value=(filepath, url)) + mocker.patch('filecode.flake_master.utils.presets.load_preset_from_file', return_value='load_preset_from_file') + mocker.patch('filecode.flake_master.utils.presets.load_preset_from_url', return_value='load_preset_from_url') + assert filecode.flake_master.utils.presets.fetch_preset('name', 'info', 'url') == expected + + +def test_extract_preset_credentials(mocker): + mocker.patch( + 'filecode.flake_master.utils.presets.extract_preset_file_path', + return_value='extract_preset_file_path', + ) + mocker.patch('filecode.flake_master.utils.presets.extract_preset_url', return_value='extract_preset_url') + assert extract_preset_credentials('name', 'info', 'url') == ('extract_preset_file_path', 'extract_preset_url') + + +@pytest.mark.parametrize( + 'preset_name_or_url_or_path, preset_info, is_path_exists, expected', + [ + ('name.cfg', {'filepath': 'path'}, True, 'path'), + ('name.cfg', {'filepath': ''}, True, 'abspath'), + ('name', {'filepath': ''}, True, None), + ('', {'filepath': 'path'}, True, 'path'), + ('', {}, False, None), + ], +) +def test_extract_preset_file_path(os, preset_name_or_url_or_path, preset_info, is_path_exists, expected): + os.path.exists.return_value = is_path_exists + os.path.abspath.return_value = 'abspath' + assert extract_preset_file_path(preset_name_or_url_or_path, preset_info) == expected + + +@pytest.mark.parametrize( + 'preset_name_or_url_or_path, preset_info, presets_repo_url, expected', + [ + ('http://url', {'url': 'preset url'}, 'github', 'preset url'), + ('url', {}, 'github', 'github/master/presets/url.cfg'), + ('', {}, 'github', None), + ('url', {'url': ''}, 'github', 'github/master/presets/url.cfg'), + ], +) +def test_extract_preset_url(preset_name_or_url_or_path, preset_info, presets_repo_url, expected): + assert extract_preset_url(preset_name_or_url_or_path, preset_info, presets_repo_url) == expected + + +def test_apply_preset_to_path(mocker, capsys): + path = 'path' + add_packages_to_requirements_file = mocker.patch( + 'filecode.flake_master.utils.presets.add_packages_to_requirements_file', + ) + add_flake8_config = mocker.patch('filecode.flake_master.utils.presets.add_flake8_config') + create_preset_file = mocker.patch('filecode.flake_master.utils.presets.create_preset_file') + preset = mocker.Mock() + preset.flake8_plugins = ['plugins'] + preset.flake8_config = 'config' + + apply_preset_to_path(preset, path, 'file_name') + out, err = capsys.readouterr() + + assert out == '\tAdding 1 requirements...\n\tCreating flake8 config...\n\tCreating preset file...\n' + add_packages_to_requirements_file.assert_called_with( + preset.flake8_plugins, + path, + requirements_files_names=['requirements.txt', 'requirements.txt'], + default_requirements_file_name='requirements.txt', + ) + add_flake8_config.assert_called_with('config', path, config_file_name='setup.cfg') + create_preset_file.assert_called_with(preset, path, preset_file_name='file_name') + + +@pytest.mark.parametrize( + 'is_path_exists', + [ + (True), + (False), + ], +) +def test_add_packages_to_requirements_file(os, tmpdir, capsys, is_path_exists): + raw_old_requirements = 'coverage==5.5\npytest==6.2.2\n' + flake_plugins = [('flake', '3'), ('flake', '8')] + + file = tmpdir.join('example.txt') + file.write(raw_old_requirements) + os.path.join.return_value = file.strpath + os.path.exists.return_value = is_path_exists + add_packages_to_requirements_file(flake_plugins, 'path', ['filename'], 'default_filename') + out, err = capsys.readouterr() + + if is_path_exists: + assert out == f'\t\tadding to {file.strpath}...\n' + assert file.read() == 'coverage==5.5\npytest==6.2.2\nflake==3\nflake==8\n' + else: + assert out == '\t\tcreating default_filename...\n' + assert file.read() == 'flake==3\nflake==8' + + +@pytest.mark.parametrize( + 'is_path_exists, params', + [ + (True, {'flake8': {'flake': 'param', 'key': 'value'}}), + (True, {}), + (False, {'flake': 'flake8'}), + ], +) +def test_add_flake8_config(mocker, os, capsys, is_path_exists, params): + mock_open = mocker.patch( + 'filecode.flake_master.utils.presets.open', + mocker.mock_open(read_data='data\n'), create=True) + parser = mocker.patch('filecode.flake_master.utils.presets.configparser.ConfigParser', autospec=True) + parser().__getitem__.return_value = params + + os.path.join.return_value = 'path' + os.path.exists.return_value = is_path_exists + + add_flake8_config([('flake', 'key')], 'path', 'filename') + out, err = capsys.readouterr() + + if is_path_exists: + assert out == f'\t\tUpdating {os.path.join.return_value}...\n' + parser().add_section.assert_called_with('flake8') + else: + assert err == f'\t\tconfig file {os.path.join.return_value} not found\n' + parser().write.assert_called_with(mock_open()) + + +def test_create_preset_file(mocker, os): + os.path.join.return_value = 'path' + mock_open = mocker.patch( + 'filecode.flake_master.utils.presets.open', + mocker.mock_open(read_data='data\n'), + create=True, + ) + json = mocker.patch('filecode.flake_master.utils.presets.json', spec=['dump']) + preset = mocker.Mock() + preset.name, preset.revision, preset.config_url, preset.filepath = 'name', 'revision', 'url', 'filepath' + dump = {'name': preset.name, + 'revision': preset.revision, + 'url': preset.config_url, + 'filepath': preset.filepath} + + create_preset_file(preset, 'path', 'filename') + json.dump.assert_called_with(dump, mock_open()) + + +@pytest.mark.parametrize( + 'match_line_num, expected', + [ + (0, 'coverage==5.5\npytest==6.2.2\n'), + (1, 'safety==1.9.0\ncoverage==5.5\n'), + (None, 'safety==1.9.0\npytest==6.2.2\ncoverage==5.5\n'), + ], +) +def test_merge_requirements_data(mocker, match_line_num, expected): + mocker.patch('filecode.flake_master.utils.requirements.find_requirement_in_list', return_value=match_line_num) + raw_old_requirements = ['safety==1.9.0', 'pytest==6.2.2'] + assert merge_requirements_data(raw_old_requirements, [('coverage', '5.5')]) == expected + + +@pytest.mark.parametrize( + 'raw_old_requirements, parse_name, expected', + [ + ([' #requirement '], '', None), + (['requirement '], 'package_name', 0), + (['requirement '], 'name', None), + ([''], 'name', None), + ], +) +def test_find_requirement_in_list(mocker, raw_old_requirements, parse_name, expected): + requirement = mocker.patch('filecode.flake_master.utils.requirements.Requirement', spec=['parse']) + requirement.parse().name = parse_name + assert find_requirement_in_list('package_name', raw_old_requirements) == expected + + +@pytest.mark.parametrize( + 'raw_old_requirements, expected', + [ + (['requirement'], None), + ], +) +def test_find_requirement_in_list_with_exception(mocker, raw_old_requirements, expected): + requirement = mocker.patch('filecode.flake_master.utils.requirements.Requirement') + requirement.parse.side_effect = ValueError + assert find_requirement_in_list('package_name', raw_old_requirements) == expected