From 978fe81a6d047391990298aa5081214dd3f74d98 Mon Sep 17 00:00:00 2001
From: Jiri Hnidek <jhnidek@redhat.com>
Date: Mon, 29 Jul 2019 22:11:29 +0200
Subject: [PATCH] Added unit tests for redhat_subscription module (#58665)

* Fixed the redhat_subscription module:
  - Option 'pool_ids' works in Python3 now
  - It tries to attach only pools IDs that are available
  - Optimization of code: do not call list --available, when
    no pool is requested
  - Simplified configure() method
  - Small changes to generate same commands on Python2 and Python3.
    Order of arguments/options and pool IDs have to be same to
    be able to run unit test using Python2 and Python3.
  - Added fragments file for redhat_subscribtion module
---
 changelogs/fragments/redhat_subscribtion.yml  |   5 +
 .../packaging/os/redhat_subscription.py       |  48 +-
 .../packaging/os/test_redhat_subscription.py  | 851 ++++++++++++++++++
 3 files changed, 892 insertions(+), 12 deletions(-)
 create mode 100644 changelogs/fragments/redhat_subscribtion.yml
 create mode 100644 test/units/modules/packaging/os/test_redhat_subscription.py

diff --git a/changelogs/fragments/redhat_subscribtion.yml b/changelogs/fragments/redhat_subscribtion.yml
new file mode 100644
index 00000000000..0791d35b2ce
--- /dev/null
+++ b/changelogs/fragments/redhat_subscribtion.yml
@@ -0,0 +1,5 @@
+bugfixes:
+  - redhat_subscription - made code compatible with Python3 (https://github.com/ansible/ansible/pull/58665)
+minor_changes:
+  - redhat_subscription - do not call ``subscribtion-manager`` command, when it is not necessary (https://github.com/ansible/ansible/pull/58665)
+  - redhat_subscription - made code more testable (https://github.com/ansible/ansible/pull/58665)
\ No newline at end of file
diff --git a/lib/ansible/modules/packaging/os/redhat_subscription.py b/lib/ansible/modules/packaging/os/redhat_subscription.py
index fa1beb57282..ad6b55fc25f 100644
--- a/lib/ansible/modules/packaging/os/redhat_subscription.py
+++ b/lib/ansible/modules/packaging/os/redhat_subscription.py
@@ -227,6 +227,9 @@ SUBMAN_CMD = None
 
 
 class RegistrationBase(object):
+
+    REDHAT_REPO = "/etc/yum.repos.d/redhat.repo"
+
     def __init__(self, module, username=None, password=None):
         self.module = module
         self.username = username
@@ -237,9 +240,8 @@ class RegistrationBase(object):
 
     def enable(self):
         # Remove any existing redhat.repo
-        redhat_repo = '/etc/yum.repos.d/redhat.repo'
-        if os.path.isfile(redhat_repo):
-            os.unlink(redhat_repo)
+        if os.path.isfile(self.REDHAT_REPO):
+            os.unlink(self.REDHAT_REPO)
 
     def register(self):
         raise NotImplementedError("Must be implemented by a sub-class")
@@ -294,14 +296,24 @@ class Rhsm(RegistrationBase):
             Raises:
               * Exception - if error occurs while running command
         '''
+
         args = [SUBMAN_CMD, 'config']
 
         # Pass supplied **kwargs as parameters to subscription-manager.  Ignore
         # non-configuration parameters and replace '_' with '.'.  For example,
         # 'server_hostname' becomes '--server.hostname'.
-        for k, v in kwargs.items():
+        options = []
+        for k, v in sorted(kwargs.items()):
             if re.search(r'^(server|rhsm)_', k) and v is not None:
-                args.append('--%s=%s' % (k.replace('_', '.', 1), v))
+                options.append('--%s=%s' % (k.replace('_', '.', 1), v))
+
+        # When there is nothing to configure, then it is not necessary
+        # to run config command, because it only returns current
+        # content of current configuration file
+        if len(options) == 0:
+            return
+
+        args.extend(options)
 
         self.module.run_command(args, check_rc=True)
 
@@ -449,9 +461,19 @@ class Rhsm(RegistrationBase):
         return []
 
     def subscribe_by_pool_ids(self, pool_ids):
-        for pool_id, quantity in pool_ids.items():
-            args = [SUBMAN_CMD, 'attach', '--pool', pool_id, '--quantity', quantity]
-            rc, stderr, stdout = self.module.run_command(args, check_rc=True)
+        """
+        Try to subscribe to the list of pool IDs
+        """
+        available_pools = RhsmPools(self.module)
+
+        available_pool_ids = [p.get_pool_id() for p in available_pools]
+
+        for pool_id, quantity in sorted(pool_ids.items()):
+            if pool_id in available_pool_ids:
+                args = [SUBMAN_CMD, 'attach', '--pool', pool_id, '--quantity', quantity]
+                rc, stderr, stdout = self.module.run_command(args, check_rc=True)
+            else:
+                self.module.fail_json(msg='Pool ID: %s not in list of available pools' % pool_id)
         return pool_ids
 
     def subscribe_pool(self, regexp):
@@ -516,7 +538,7 @@ class Rhsm(RegistrationBase):
         serials = self.unsubscribe(serials=serials_to_remove)
 
         missing_pools = {}
-        for pool_id, quantity in pool_ids.items():
+        for pool_id, quantity in sorted(pool_ids.items()):
             if existing_pools.get(pool_id, 0) != quantity:
                 missing_pools[pool_id] = quantity
 
@@ -545,7 +567,7 @@ class RhsmPool(object):
         return getattr(self, 'PoolId', getattr(self, 'PoolID'))
 
     def subscribe(self):
-        args = "subscription-manager subscribe --pool %s" % self.get_pool_id()
+        args = "subscription-manager attach --pool %s" % self.get_pool_id()
         rc, stdout, stderr = self.module.run_command(args, check_rc=True)
         if rc == 0:
             return True
@@ -710,7 +732,7 @@ def main():
         if isinstance(value, dict):
             if len(value) != 1:
                 module.fail_json(msg='Unable to parse pool_ids option.')
-            pool_id, quantity = value.items()[0]
+            pool_id, quantity = list(value.items())[0]
         else:
             pool_id, quantity = value, 1
         pool_ids[pool_id] = str(quantity)
@@ -754,8 +776,10 @@ def main():
                               server_proxy_hostname, server_proxy_port, server_proxy_user, server_proxy_password, release)
                 if pool_ids:
                     subscribed_pool_ids = rhsm.subscribe_by_pool_ids(pool_ids)
-                else:
+                elif pool != '^$':
                     subscribed_pool_ids = rhsm.subscribe(pool)
+                else:
+                    subscribed_pool_ids = []
             except Exception as e:
                 module.fail_json(msg="Failed to register with '%s': %s" % (server_hostname, to_native(e)))
             else:
diff --git a/test/units/modules/packaging/os/test_redhat_subscription.py b/test/units/modules/packaging/os/test_redhat_subscription.py
new file mode 100644
index 00000000000..8e9beca7019
--- /dev/null
+++ b/test/units/modules/packaging/os/test_redhat_subscription.py
@@ -0,0 +1,851 @@
+# Author: Jiri Hnidek (jhnidek@redhat.com)
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+
+from ansible.module_utils import basic
+from ansible.modules.packaging.os import redhat_subscription
+
+import pytest
+
+TESTED_MODULE = redhat_subscription.__name__
+
+
+@pytest.fixture
+def patch_redhat_subscription(mocker):
+    """
+    Function used for mocking some parts of redhat_subscribtion module
+    """
+    mocker.patch('ansible.modules.packaging.os.redhat_subscription.RegistrationBase.REDHAT_REPO')
+    mocker.patch('os.path.isfile', return_value=False)
+    mocker.patch('os.unlink', return_value=True)
+    mocker.patch('ansible.modules.packaging.os.redhat_subscription.AnsibleModule.get_bin_path',
+                 return_value='/testbin/subscription-manager')
+
+
+@pytest.mark.parametrize('patch_ansible_module', [{}], indirect=['patch_ansible_module'])
+@pytest.mark.usefixtures('patch_ansible_module')
+def test_without_required_parameters(capfd, patch_redhat_subscription):
+    """
+    Failure must occurs when all parameters are missing
+    """
+    with pytest.raises(SystemExit):
+        redhat_subscription.main()
+    out, err = capfd.readouterr()
+    results = json.loads(out)
+    assert results['failed']
+    assert 'state is present but any of the following are missing' in results['msg']
+
+
+TEST_CASES = [
+    # Test the case, when the system is already registered
+    [
+        {
+            'state': 'present',
+            'server_hostname': 'subscription.rhsm.redhat.com',
+            'username': 'admin',
+            'password': 'admin',
+            'org_id': 'admin'
+        },
+        {
+            'id': 'test_already_registered_system',
+            'run_command.calls': [
+                (
+                    # Calling of following command will be asserted
+                    ['/testbin/subscription-manager', 'identity'],
+                    # Was return code checked?
+                    {'check_rc': False},
+                    # Mock of returned code, stdout and stderr
+                    (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', '')
+                )
+            ],
+            'changed': False,
+            'msg': 'System already registered.'
+        }
+    ],
+    # Test simple registration using username and password
+    [
+        {
+            'state': 'present',
+            'server_hostname': 'satellite.company.com',
+            'username': 'admin',
+            'password': 'admin',
+        },
+        {
+            'id': 'test_registeration_username_password',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (1, '', '')
+                ),
+                (
+                    ['/testbin/subscription-manager', 'config', '--server.hostname=satellite.company.com'],
+                    {'check_rc': True},
+                    (0, '', '')
+                ),
+                (
+                    ['/testbin/subscription-manager', 'register',
+                        '--serverurl', 'satellite.company.com',
+                        '--username', 'admin',
+                        '--password', 'admin'],
+                    {'check_rc': True, 'expand_user_and_vars': False},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+            'msg': "System successfully registered to 'satellite.company.com'."
+        }
+    ],
+    # Test unregistration, when system is unregistered
+    [
+        {
+            'state': 'absent',
+            'server_hostname': 'subscription.rhsm.redhat.com',
+            'username': 'admin',
+            'password': 'admin',
+        },
+        {
+            'id': 'test_unregisteration',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', '')
+                ),
+                (
+                    ['/testbin/subscription-manager', 'unsubscribe', '--all'],
+                    {'check_rc': True},
+                    (0, '', '')
+                ),
+                (
+                    ['/testbin/subscription-manager', 'unregister'],
+                    {'check_rc': True},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+            'msg': "System successfully unregistered from subscription.rhsm.redhat.com."
+        }
+    ],
+    # Test unregistration of already unregistered system
+    [
+        {
+            'state': 'absent',
+            'server_hostname': 'subscription.rhsm.redhat.com',
+            'username': 'admin',
+            'password': 'admin',
+        },
+        {
+            'id': 'test_unregisteration_of_unregistered_system',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (1, 'This system is not yet registered.', '')
+                )
+            ],
+            'changed': False,
+            'msg': "System already unregistered."
+        }
+    ],
+    # Test registration using activation key
+    [
+        {
+            'state': 'present',
+            'server_hostname': 'satellite.company.com',
+            'activationkey': 'some-activation-key',
+            'org_id': 'admin'
+        },
+        {
+            'id': 'test_registeration_activation_key',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (1, 'This system is not yet registered.', '')
+                ),
+                (
+                    ['/testbin/subscription-manager', 'config', '--server.hostname=satellite.company.com'],
+                    {'check_rc': True},
+                    (0, '', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'register',
+                        '--serverurl', 'satellite.company.com',
+                        '--org', 'admin',
+                        '--activationkey', 'some-activation-key'
+                    ],
+                    {'check_rc': True, 'expand_user_and_vars': False},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+            'msg': "System successfully registered to 'satellite.company.com'."
+        }
+    ],
+    # Test of registration using username and password with auto-attach option
+    [
+        {
+            'state': 'present',
+            'username': 'admin',
+            'password': 'admin',
+            'org_id': 'admin',
+            'auto_attach': 'true'
+        },
+        {
+            'id': 'test_registeration_username_password_auto_attach',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (1, 'This system is not yet registered.', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'register',
+                        '--org', 'admin',
+                        '--auto-attach',
+                        '--username', 'admin',
+                        '--password', 'admin'
+                    ],
+                    {'check_rc': True, 'expand_user_and_vars': False},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+            'msg': "System successfully registered to 'None'."
+        }
+    ],
+    # Test of force registration despite the system is already registered
+    [
+        {
+            'state': 'present',
+            'username': 'admin',
+            'password': 'admin',
+            'org_id': 'admin',
+            'force_register': 'true'
+        },
+        {
+            'id': 'test_force_registeration_username_password',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (0, 'This system already registered.', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'register',
+                        '--force',
+                        '--org', 'admin',
+                        '--username', 'admin',
+                        '--password', 'admin'
+                    ],
+                    {'check_rc': True, 'expand_user_and_vars': False},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+            'msg': "System successfully registered to 'None'."
+        }
+    ],
+    # Test of registration using username, password and proxy options
+    [
+        {
+            'state': 'present',
+            'username': 'admin',
+            'password': 'admin',
+            'org_id': 'admin',
+            'force_register': 'true',
+            'server_proxy_hostname': 'proxy.company.com',
+            'server_proxy_port': '12345',
+            'server_proxy_user': 'proxy_user',
+            'server_proxy_password': 'secret_proxy_password'
+        },
+        {
+            'id': 'test_registeration_username_password_proxy_options',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (0, 'This system already registered.', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'config',
+                        '--server.proxy_hostname=proxy.company.com',
+                        '--server.proxy_password=secret_proxy_password',
+                        '--server.proxy_port=12345',
+                        '--server.proxy_user=proxy_user'
+                    ],
+                    {'check_rc': True},
+                    (0, '', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'register',
+                        '--force',
+                        '--org', 'admin',
+                        '--proxy', 'proxy.company.com:12345',
+                        '--proxyuser', 'proxy_user',
+                        '--proxypassword', 'secret_proxy_password',
+                        '--username', 'admin',
+                        '--password', 'admin'
+                    ],
+                    {'check_rc': True, 'expand_user_and_vars': False},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+            'msg': "System successfully registered to 'None'."
+        }
+    ],
+    # Test of registration using username and password and attach to pool
+    [
+        {
+            'state': 'present',
+            'username': 'admin',
+            'password': 'admin',
+            'org_id': 'admin',
+            'pool': 'ff8080816b8e967f016b8e99632804a6'
+        },
+        {
+            'id': 'test_registeration_username_password_pool',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (1, 'This system is not yet registered.', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'register',
+                        '--org', 'admin',
+                        '--username', 'admin',
+                        '--password', 'admin'
+                    ],
+                    {'check_rc': True, 'expand_user_and_vars': False},
+                    (0, '', '')
+                ),
+                (
+                    [
+                        'subscription-manager list --available',
+                        {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}},
+                        (0,
+                         '''
++-------------------------------------------+
+    Available Subscriptions
++-------------------------------------------+
+Subscription Name:   SP Server Premium (S: Premium, U: Production, R: SP Server)
+Provides:            SP Server Bits
+SKU:                 sp-server-prem-prod
+Contract:            0
+Pool ID:             ff8080816b8e967f016b8e99632804a6
+Provides Management: Yes
+Available:           5
+Suggested:           1
+Service Type:        L1-L3
+Roles:               SP Server
+Service Level:       Premium
+Usage:               Production
+Add-ons:
+Subscription Type:   Standard
+Starts:              06/25/19
+Ends:                06/24/20
+Entitlement Type:    Physical
+''', ''),
+                    ]
+                ),
+                (
+                    'subscription-manager attach --pool ff8080816b8e967f016b8e99632804a6',
+                    {'check_rc': True},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+            'msg': "System successfully registered to 'None'."
+        }
+    ],
+    # Test of registration using username and password and attach to pool ID and quantities
+    [
+        {
+            'state': 'present',
+            'username': 'admin',
+            'password': 'admin',
+            'org_id': 'admin',
+            'pool_ids': [{'ff8080816b8e967f016b8e99632804a6': 2}, {'ff8080816b8e967f016b8e99747107e9': 4}]
+        },
+        {
+            'id': 'test_registeration_username_password_pool_ids_quantities',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (1, 'This system is not yet registered.', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'register',
+                        '--org', 'admin',
+                        '--username', 'admin',
+                        '--password', 'admin'
+                    ],
+                    {'check_rc': True, 'expand_user_and_vars': False},
+                    (0, '', '')
+                ),
+                (
+                    [
+                        'subscription-manager list --available',
+                        {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}},
+                        (0,
+                         '''
++-------------------------------------------+
+    Available Subscriptions
++-------------------------------------------+
+Subscription Name:   SP Smart Management (A: ADDON1)
+Provides:            SP Addon 1 bits
+SKU:                 sp-with-addon-1
+Contract:            1
+Pool ID:             ff8080816b8e967f016b8e99747107e9
+Provides Management: Yes
+Available:           10
+Suggested:           1
+Service Type:
+Roles:
+Service Level:
+Usage:
+Add-ons:             ADDON1
+Subscription Type:   Standard
+Starts:              25.6.2019
+Ends:                24.6.2020
+Entitlement Type:    Physical
+
+Subscription Name:   SP Server Premium (S: Premium, U: Production, R: SP Server)
+Provides:            SP Server Bits
+SKU:                 sp-server-prem-prod
+Contract:            0
+Pool ID:             ff8080816b8e967f016b8e99632804a6
+Provides Management: Yes
+Available:           5
+Suggested:           1
+Service Type:        L1-L3
+Roles:               SP Server
+Service Level:       Premium
+Usage:               Production
+Add-ons:
+Subscription Type:   Standard
+Starts:              06/25/19
+Ends:                06/24/20
+Entitlement Type:    Physical
+''', '')
+                    ]
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'attach',
+                        '--pool', 'ff8080816b8e967f016b8e99632804a6',
+                        '--quantity', '2'
+                    ],
+                    {'check_rc': True},
+                    (0, '', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'attach',
+                        '--pool', 'ff8080816b8e967f016b8e99747107e9',
+                        '--quantity', '4'
+                    ],
+                    {'check_rc': True},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+            'msg': "System successfully registered to 'None'."
+        }
+    ],
+    # Test of registration using username and password and attach to pool ID without quantities
+    [
+        {
+            'state': 'present',
+            'username': 'admin',
+            'password': 'admin',
+            'org_id': 'admin',
+            'pool_ids': ['ff8080816b8e967f016b8e99632804a6', 'ff8080816b8e967f016b8e99747107e9']
+        },
+        {
+            'id': 'test_registeration_username_password_pool_ids',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (1, 'This system is not yet registered.', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'register',
+                        '--org', 'admin',
+                        '--username', 'admin',
+                        '--password', 'admin'
+                    ],
+                    {'check_rc': True, 'expand_user_and_vars': False},
+                    (0, '', '')
+                ),
+                (
+                    [
+                        'subscription-manager list --available',
+                        {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}},
+                        (0,
+                         '''
++-------------------------------------------+
+    Available Subscriptions
++-------------------------------------------+
+Subscription Name:   SP Smart Management (A: ADDON1)
+Provides:            SP Addon 1 bits
+SKU:                 sp-with-addon-1
+Contract:            1
+Pool ID:             ff8080816b8e967f016b8e99747107e9
+Provides Management: Yes
+Available:           10
+Suggested:           1
+Service Type:
+Roles:
+Service Level:
+Usage:
+Add-ons:             ADDON1
+Subscription Type:   Standard
+Starts:              25.6.2019
+Ends:                24.6.2020
+Entitlement Type:    Physical
+
+Subscription Name:   SP Server Premium (S: Premium, U: Production, R: SP Server)
+Provides:            SP Server Bits
+SKU:                 sp-server-prem-prod
+Contract:            0
+Pool ID:             ff8080816b8e967f016b8e99632804a6
+Provides Management: Yes
+Available:           5
+Suggested:           1
+Service Type:        L1-L3
+Roles:               SP Server
+Service Level:       Premium
+Usage:               Production
+Add-ons:
+Subscription Type:   Standard
+Starts:              06/25/19
+Ends:                06/24/20
+Entitlement Type:    Physical
+''', '')
+                    ]
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'attach',
+                        '--pool', 'ff8080816b8e967f016b8e99632804a6',
+                        '--quantity', '1'
+                    ],
+                    {'check_rc': True},
+                    (0, '', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'attach',
+                        '--pool', 'ff8080816b8e967f016b8e99747107e9',
+                        '--quantity', '1'
+                    ],
+                    {'check_rc': True},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+            'msg': "System successfully registered to 'None'."
+        }
+    ],
+    # Test of registration using username and password and attach to pool ID (one pool)
+    [
+        {
+            'state': 'present',
+            'username': 'admin',
+            'password': 'admin',
+            'org_id': 'admin',
+            'pool_ids': ['ff8080816b8e967f016b8e99632804a6']
+        },
+        {
+            'id': 'test_registeration_username_password_one_pool_id',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (1, 'This system is not yet registered.', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'register',
+                        '--org', 'admin',
+                        '--username', 'admin',
+                        '--password', 'admin'
+                    ],
+                    {'check_rc': True, 'expand_user_and_vars': False},
+                    (0, '', '')
+                ),
+                (
+                    [
+                        'subscription-manager list --available',
+                        {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}},
+                        (0,
+                         '''
++-------------------------------------------+
+    Available Subscriptions
++-------------------------------------------+
+Subscription Name:   SP Smart Management (A: ADDON1)
+Provides:            SP Addon 1 bits
+SKU:                 sp-with-addon-1
+Contract:            1
+Pool ID:             ff8080816b8e967f016b8e99747107e9
+Provides Management: Yes
+Available:           10
+Suggested:           1
+Service Type:
+Roles:
+Service Level:
+Usage:
+Add-ons:             ADDON1
+Subscription Type:   Standard
+Starts:              25.6.2019
+Ends:                24.6.2020
+Entitlement Type:    Physical
+
+Subscription Name:   SP Server Premium (S: Premium, U: Production, R: SP Server)
+Provides:            SP Server Bits
+SKU:                 sp-server-prem-prod
+Contract:            0
+Pool ID:             ff8080816b8e967f016b8e99632804a6
+Provides Management: Yes
+Available:           5
+Suggested:           1
+Service Type:        L1-L3
+Roles:               SP Server
+Service Level:       Premium
+Usage:               Production
+Add-ons:
+Subscription Type:   Standard
+Starts:              06/25/19
+Ends:                06/24/20
+Entitlement Type:    Physical
+''', '')
+                    ]
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'attach',
+                        '--pool', 'ff8080816b8e967f016b8e99632804a6',
+                        '--quantity', '1'
+                    ],
+                    {'check_rc': True},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+            'msg': "System successfully registered to 'None'."
+        }
+    ],
+    # Test attaching different set of pool IDs
+    [
+        {
+            'state': 'present',
+            'username': 'admin',
+            'password': 'admin',
+            'org_id': 'admin',
+            'pool_ids': [{'ff8080816b8e967f016b8e99632804a6': 2}, {'ff8080816b8e967f016b8e99747107e9': 4}]
+        },
+        {
+            'id': 'test_attaching_different_pool_ids',
+            'run_command.calls': [
+                (
+                    ['/testbin/subscription-manager', 'identity'],
+                    {'check_rc': False},
+                    (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', ''),
+                ),
+                (
+                    'subscription-manager list --consumed',
+                    {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}},
+                    (0, '''
++-------------------------------------------+
+   Consumed Subscriptions
++-------------------------------------------+
+Subscription Name:   Multi-Attribute Stackable (4 cores, no content)
+Provides:            Multi-Attribute Limited Product (no content)
+SKU:                 cores4-multiattr
+Contract:            1
+Account:             12331131231
+Serial:              7807912223970164816
+Pool ID:             ff8080816b8e967f016b8e995f5103b5
+Provides Management: No
+Active:              True
+Quantity Used:       1
+Service Type:        Level 3
+Roles:
+Service Level:       Premium
+Usage:
+Add-ons:
+Status Details:      Subscription is current
+Subscription Type:   Stackable
+Starts:              06/25/19
+Ends:                06/24/20
+Entitlement Type:    Physical
+''', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'unsubscribe',
+                        '--serial=7807912223970164816',
+                    ],
+                    {'check_rc': True},
+                    (0, '', '')
+                ),
+                (
+                    [
+                        'subscription-manager list --available',
+                        {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}},
+                        (0,
+                         '''
++-------------------------------------------+
+    Available Subscriptions
++-------------------------------------------+
+Subscription Name:   SP Smart Management (A: ADDON1)
+Provides:            SP Addon 1 bits
+SKU:                 sp-with-addon-1
+Contract:            1
+Pool ID:             ff8080816b8e967f016b8e99747107e9
+Provides Management: Yes
+Available:           10
+Suggested:           1
+Service Type:
+Roles:
+Service Level:
+Usage:
+Add-ons:             ADDON1
+Subscription Type:   Standard
+Starts:              25.6.2019
+Ends:                24.6.2020
+Entitlement Type:    Physical
+
+Subscription Name:   SP Server Premium (S: Premium, U: Production, R: SP Server)
+Provides:            SP Server Bits
+SKU:                 sp-server-prem-prod
+Contract:            0
+Pool ID:             ff8080816b8e967f016b8e99632804a6
+Provides Management: Yes
+Available:           5
+Suggested:           1
+Service Type:        L1-L3
+Roles:               SP Server
+Service Level:       Premium
+Usage:               Production
+Add-ons:
+Subscription Type:   Standard
+Starts:              06/25/19
+Ends:                06/24/20
+Entitlement Type:    Physical
+
+Subscription Name:   Multi-Attribute Stackable (4 cores, no content)
+Provides:            Multi-Attribute Limited Product (no content)
+SKU:                 cores4-multiattr
+Contract:            1
+Pool ID:             ff8080816b8e967f016b8e995f5103b5
+Provides Management: No
+Available:           10
+Suggested:           1
+Service Type:        Level 3
+Roles:
+Service Level:       Premium
+Usage:
+Add-ons:
+Subscription Type:   Stackable
+Starts:              11.7.2019
+Ends:                10.7.2020
+Entitlement Type:    Physical
+''', '')
+                    ]
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'attach',
+                        '--pool', 'ff8080816b8e967f016b8e99632804a6',
+                        '--quantity', '2'
+                    ],
+                    {'check_rc': True},
+                    (0, '', '')
+                ),
+                (
+                    [
+                        '/testbin/subscription-manager',
+                        'attach',
+                        '--pool', 'ff8080816b8e967f016b8e99747107e9',
+                        '--quantity', '4'
+                    ],
+                    {'check_rc': True},
+                    (0, '', '')
+                )
+            ],
+            'changed': True,
+        }
+    ],
+]
+
+
+TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
+
+
+@pytest.mark.parametrize('patch_ansible_module, testcase', TEST_CASES, ids=TEST_CASES_IDS, indirect=['patch_ansible_module'])
+@pytest.mark.usefixtures('patch_ansible_module')
+def test_redhat_subscribtion(mocker, capfd, patch_redhat_subscription, testcase):
+    """
+    Run unit tests for test cases listen in TEST_CASES
+    """
+
+    # Mock function used for running commands first
+    call_results = [item[2] for item in testcase['run_command.calls']]
+    mock_run_command = mocker.patch.object(
+        basic.AnsibleModule,
+        'run_command',
+        side_effect=call_results)
+
+    # Try to run test case
+    with pytest.raises(SystemExit):
+        redhat_subscription.main()
+
+    out, err = capfd.readouterr()
+    results = json.loads(out)
+
+    assert 'changed' in results
+    assert results['changed'] == testcase['changed']
+    if 'msg' in results:
+        assert results['msg'] == testcase['msg']
+
+    assert basic.AnsibleModule.run_command.call_count == len(testcase['run_command.calls'])
+    if basic.AnsibleModule.run_command.call_count:
+        call_args_list = [(item[0][0], item[1]) for item in basic.AnsibleModule.run_command.call_args_list]
+        expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
+        assert call_args_list == expected_call_args_list