kangasta/pullnrun

View on GitHub
tst/test_main.py

Summary

Maintainability
A
0 mins
Test Coverage
import json
import os
from sys import stdout
import yaml

from unittest import TestCase
from unittest.mock import patch

from _push_target import request_mock_implementation

from pullnrun.execute import TempWorkDir
from pullnrun.main import entrypoint, NO_PLAN, INVALID_PLAN
from pullnrun import __version__

TST_DIR = os.path.dirname(os.path.realpath(__file__))

class TestConsole:
    def __init__(self):
        self.content = ''

    def print_mock_implementation(self, *args, **kwargs):
        self.content += ' '.join(str(a) for a in args) + '\n'

class TestExit(Exception):
    def __init__(self, exit_code):
        super().__init__()
        self.exit_code = exit_code

def exit_mock_implementation(exit_code):
    raise TestExit(exit_code)

class MainTest(TestCase):
    @patch('builtins.exit')
    @patch('builtins.print')
    def test_main_version(self, print_mock, exit_mock):
        with patch('sys.argv', ['pullnrun', '--version']):
            entrypoint()

        print_mock.assert_called_with(f'pullnrun {__version__}')

    @patch('builtins.exit', side_effect=exit_mock_implementation)
    @patch('builtins.print')
    def test_main_exit_codes(self, print_mock, exit_mock):
        for args, exit_code in [
            ([], NO_PLAN),
            ([f'{TST_DIR}/invalid_plan.yml'], INVALID_PLAN),
        ]:
            with self.assertRaises(TestExit):
                with patch('sys.argv', ['pullnrun', *args]):
                    entrypoint()

            exit_mock.assert_called_with(exit_code)

    @patch('builtins.exit')
    @patch('builtins.print')
    def test_main_errors(self, print_mock, exit_mock):
        console = TestConsole()
        print_mock.side_effect = console.print_mock_implementation

        with patch('sys.argv', ['pullnrun', f'{TST_DIR}/../examples/errors.yml']):
            entrypoint()

        print_mock.assert_any_call(f'pullnrun {__version__}', file=stdout)
        exit_mock.assert_called_with(1)
        self.assertRegex(console.content, r'Ignored:\s*1')

    @patch('builtins.exit')
    @patch('builtins.print')
    def test_main_pull_http(self, print_mock, exit_mock):
        with TempWorkDir():
            with patch('sys.argv', ['pullnrun', f'{TST_DIR}/../examples/fizzbuzz.json']):
                entrypoint()

            exit_mock.assert_called_with(0)

    @patch('builtins.exit')
    @patch('builtins.print')
    def test_main_pull_git(self, print_mock, exit_mock):
        with TempWorkDir():
            with patch('sys.argv', ['pullnrun', f'{TST_DIR}/../examples/fizzbuzz_git.json']):
                entrypoint()

            exit_mock.assert_called_with(0)

    @patch('builtins.exit')
    @patch('builtins.print')
    @patch('pullnrun.builtin._push.request')
    def test_main_push_http(self, request_mock, print_mock, exit_mock):
        request_mock.side_effect = request_mock_implementation
        with TempWorkDir():
            with open('test_file.txt', 'w') as f:
                f.write('test_content')

            with patch('sys.argv', ['pullnrun', f'{TST_DIR}/../examples/push.yml']):
                entrypoint()

        exit_mock.assert_called_with(1)

    @patch('builtins.exit')
    @patch('builtins.print')
    def test_main_invalid_tasks(self, print_mock, exit_mock):
        console = TestConsole()
        print_mock.side_effect = console.print_mock_implementation

        with TempWorkDir():
            with patch('sys.argv', ['pullnrun', f'{TST_DIR}/invalid_tasks.yml']):
                entrypoint()

        exit_mock.assert_called_with(0)
        self.assertRegex(console.content, r'Ignored:\s*4')
        for i in [0, 2]:
            self.assertIn(f'Task must contain exactly one function key, but {i} were given', console.content)
        self.assertIn('Function not found for cow.', console.content)
        self.assertIn('Caught error raised from task', console.content)

    @patch('builtins.exit')
    @patch('builtins.print')
    def test_main_invalid_task_stop_on_errors(self, print_mock, exit_mock):
        console = TestConsole()
        print_mock.side_effect = console.print_mock_implementation

        with open(f'{TST_DIR}/invalid_tasks.yml', 'r') as f:
            plan = yaml.load(f, Loader=yaml.SafeLoader)
            tasks = plan.get('tasks')

        for task in tasks:
            with TempWorkDir() as tmp_dir_path:
                with open('plan.yml', 'w') as f:
                    json.dump(dict(tasks=[task]), f)

                with patch('sys.argv', ['pullnrun', f'{tmp_dir_path}/plan.yml']):
                    entrypoint()

            exit_mock.assert_called_with(1)

    @patch('builtins.exit')
    @patch('builtins.print')
    def test_main_when(self, print_mock, exit_mock):
        console = TestConsole()
        print_mock.side_effect = console.print_mock_implementation

        with TempWorkDir():
            with patch('sys.argv', ['pullnrun', f'{TST_DIR}/../examples/when.yml']):
                entrypoint()

        exit_mock.assert_called_with(0)
        self.assertRegex(console.content, r'Skipped:\s*3')
        self.assertRegex(console.content, r'Ignored:\s*1')

    @patch('builtins.exit')
    @patch('builtins.print')
    def test_main_tmp_work_dir(self, print_mock, exit_mock):
        with TempWorkDir():
            with patch('sys.argv', ['pullnrun', '-T', '--report', f'{TST_DIR}/../examples/fizzbuzz.json']):
                entrypoint()

            dir_ = os.listdir()
            self.assertIn('pullnrun_report.html', dir_)
            self.assertNotIn('fizzbuzz.py', dir_)