tests/components/test_command.py

Summary

Maintainability
A
1 hr
Test Coverage
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Tests for `then` package."""
import subprocess
import unittest
from io import BytesIO
from unittest.mock import patch, Mock

from then.components.command import Command, execute_over_ssh, get_shell, get_execute_command
from then.exceptions import ExecuteError, ValidationError


class TestExecuteCmdFunction(unittest.TestCase):

    @patch.object(subprocess, 'Popen')
    def test_success(self, m):
        process_mock = Mock()
        process_mock.configure_mock(**{'returncode': 0, 'stdout': BytesIO(bytes('foo', 'utf-8'))})
        m.return_value = process_mock
        out = Command().send(cmd=['ls'])
        self.assertEqual(out, 'foo')

    @patch.object(subprocess, 'Popen')
    def test_success_without_list(self, m):
        process_mock = Mock()
        process_mock.configure_mock(**{'returncode': 0, 'stdout': BytesIO(bytes('foo', 'utf-8'))})
        m.return_value = process_mock
        out = Command().send(cmd='ls -l')
        self.assertEqual(out, 'foo')
        m.assert_called_once_with(get_execute_command('ls -l'), cwd=None, stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)

    @patch.object(subprocess, 'Popen')
    def test_error(self, m):
        process_mock = Mock()
        process_mock.configure_mock(**{'returncode': 1})
        m.return_value = process_mock
        with self.assertRaises(ExecuteError):
            Command().send(cmd=['ls'])

    @patch.object(subprocess, 'Popen')
    def test_timeout(self, m):
        def side_effect(timeout=None):
            raise subprocess.TimeoutExpired('', timeout)

        process_mock = Mock()
        process_mock.configure_mock(**{'wait.side_effect': side_effect})
        m.return_value = process_mock
        self.assertEqual(Command().send(cmd=['ls']), None)


class TestCommandSsh(unittest.TestCase):
    def test_invalid_port(self):
        with self.assertRaises(ValidationError):
            Command(ssh='machine:spam')

    def test_execute_without_port(self):
        cmd = execute_over_ssh('ls', 'machine')
        self.assertEqual(['ssh', 'machine', '-C', "/usr/bin/env bash -c ' ls '"], cmd)

    def test_execute_with_port(self):
        cmd = execute_over_ssh('ls', 'machine:222')
        self.assertEqual(['ssh', 'machine', '-p', '222', '-C', "/usr/bin/env bash -c ' ls '"], cmd)

    def test_execute_double_quotes(self):
        cmd = execute_over_ssh('"ls"', 'machine:222')
        self.assertEqual(['ssh', 'machine', '-p', '222', '-C', "/usr/bin/env bash -c ' \"ls\" '"], cmd)

    def test_execute_single_quotes(self):
        cmd = execute_over_ssh('\'ls\'', 'machine:222')
        self.assertEqual(['ssh', 'machine', '-p', '222', '-C', "/usr/bin/env bash -c ' '\"'\"'ls'\"'\"' '"], cmd)


class TestCommand(unittest.TestCase):
    subject = 'subject'
    body = 'body'

    @patch('subprocess.Popen', autospec=True)
    def test_execution_success(self, popen_mock):
        popen_mock.return_value = Mock()
        popen_mock_obj = popen_mock.return_value

        popen_mock_obj.communicate.return_value = ("OUT", "")
        popen_mock_obj.returncode = 0
        Command().send(cmd='ls')
        popen_mock.assert_called_once()

    @patch('subprocess.Popen', autospec=True)
    def test_execution_error(self, popen_mock):
        popen_mock.return_value = Mock()
        popen_mock_obj = popen_mock.return_value

        popen_mock_obj.communicate.return_value = ("OUT", "ERR")
        popen_mock_obj.returncode = 1
        with self.assertRaises(ExecuteError):
            Command().send(cmd='ls')

    @patch('then.components.command.execute_cmd')
    def test_ssh(self, mock):
        component = Command(ssh='foo@bar')
        with patch('then.components.command.execute_over_ssh') as execute_over_ssh_mock:
            component.send(cmd='ls')
            execute_over_ssh_mock.assert_called_once()
        mock.assert_called_once()

    def test_get_shell(self):
        self.assertEqual(get_shell('/usr/bin/command'), ['/usr/bin/command'])
        self.assertEqual(get_shell('command'), ['/usr/bin/env', 'command'])