""" Tests for policy.py """
import unittest

import datetime
import json
import mock
import dateutil.tz

from starttls_policy_cli import policy
from starttls_policy_cli import util
from starttls_policy_cli.tests.util import assertRaisesRegex, param, parametrize_over

test_json = '{\
    "author": "Electronic Frontier Foundation",\
        "expires": "2014-05-26T01:35:33+0000",\
        "timestamp": "2014-05-26T01:35:33+0000",\
        "policies": {\
            ".valid.example-recipient.com": {\
                    "mode": "enforce",\
                    "mxs": [".valid.example-recipient.com"]\
            }\
        }\
    }'

class TestConfigEncoder(unittest.TestCase):
    """Tests extensions to JSON serializer for dumping configs"""

    def encode_test(self, obj, expected):
        """Parametrized test for JSON encoder"""
        self.assertEqual(json.dumps(obj, sort_keys=True, cls=policy.ConfigEncoder), expected)

parametrize_over(TestConfigEncoder, TestConfigEncoder.encode_test,
                 [
                    param("encode_none", None, "null"),
                    param("encode_empty_list", [], "[]"),
                    param("encode_empty_dict", {}, "{}"),
                    param("encode_empty_string", "", '""'),
                    param("encode_integer", 0, "0"),
                    param("encode_true", True, "true"),
                    param("encode_false", False, "false"),
                    param("encode_float", .1, "0.1"),
                    param("encode_datetime",
                          datetime.datetime.fromtimestamp(0, tz=dateutil.tz.tzutc()),
                          '"1970-01-01T00:00:00+0000"'),
                 ])

class TestConfig(unittest.TestCase):
    # pylint: disable=too-many-public-methods
    """Testing configuration
    """

    maxDiff = None

    def setUp(self):
        self.conf = policy.Config()
        self.conf.author = "EFF"
        self.conf.expires = datetime.datetime.now()
        self.conf.timestamp = datetime.datetime.now()
        self.sample_policy = {
            'mxs': ['eff.org', '.eff.org'],
            'mode': 'testing'
        }
        self.other_policy = {
            'mxs': ['example.com', '.example.com'],
            'mode': 'enforce'
        }
        self.conf.policies = {'eff.org': self.sample_policy}

    def test_flush(self):
        with mock.patch("starttls_policy_cli.policy.open", mock.mock_open()) as m:
            self.conf.flush("lol.txt")
            m.assert_called_with("lol.txt", "w")
            m().write.assert_called_once()

    def test_flush_default(self):
        with mock.patch("starttls_policy_cli.policy.open", mock.mock_open()) as m:
            self.conf.flush()
            m.assert_called_with(self.conf.filename, "w")
            m().write.assert_called_once()

    def test_merge_keeps_old_settings(self):
        conf2 = policy.Config()
        conf2.author = "EFF"
        updated_timestamp = self.conf.expires - datetime.timedelta(days=1)
        conf2.timestamp = updated_timestamp
        new_conf = self.conf.merge(conf2)
        self.assertEqual(new_conf.author, "EFF")
        self.assertTrue(new_conf.timestamp is not None)
        self.assertEqual(new_conf.timestamp, updated_timestamp)

    def test_update_drops_old_settings(self):
        conf2 = policy.Config()
        conf2.author = "EFF"
        new_conf = self.conf.update(conf2)
        self.assertEqual(new_conf.author, "EFF")
        self.assertEqual(new_conf.timestamp, None)

    def test_merge_keeps_old_policies(self):
        conf2 = policy.Config()
        conf2.policies = {'example.com': self.other_policy}
        new_conf = self.conf.merge(conf2)
        self.assertTrue('example.com' in new_conf.policies)
        self.assertTrue('eff.org' in new_conf.policies)

    def test_update_drops_old_policies(self):
        conf2 = policy.Config()
        conf2.policies = {'example.com': self.other_policy}
        new_conf = self.conf.update(conf2)
        self.assertTrue('example.com' in new_conf.policies)
        self.assertFalse('eff.org' in new_conf.policies)

    def test_should_update_invalid_type(self):
        self.assertFalse(self.conf.should_update(None))

    def test_should_update_valid_type(self):
        conf2 = policy.Config()
        conf2.policies = {'example.com': self.other_policy}
        self.assertTrue(self.conf.should_update(conf2))

    def test_updateble_checks_types(self):
        with assertRaisesRegex(self, util.ConfigError,
                                     "Attempting to update a "
                                     "<class 'starttls_policy_cli.policy.Config'> "
                                     "with a <(type|class) 'NoneType'>"):
            self.conf.update(None)

    def test_len_method(self):
        conf2 = policy.Config()
        conf2.policies = {'example.com': self.other_policy}
        self.assertEqual(len(conf2), 1)

    def test_keys_empty_policy(self):
        conf2 = policy.Config()
        with self.assertRaises(StopIteration):
            next(iter(conf2.keys()))

    def test_basic_parsing(self):
        obj = json.loads(test_json)
        self.conf.load_from_dict(obj)
        self.assertEqual(self.conf.author, "Electronic Frontier Foundation")
        self.assertEqual(json.loads(self.conf.dump()), obj)

    def test_timestamp_and_expires_required(self):
        with self.assertRaises(util.ConfigError):
            policy.Config().load_from_dict({'expires': datetime.datetime.now()})
        with self.assertRaises(util.ConfigError):
            policy.Config().load_from_dict({'timestamp': datetime.datetime.now()})
        conf = policy.Config()
        conf.load_from_dict({'timestamp': datetime.datetime.now(),
                             'expires': datetime.datetime.now()})
        self.assertTrue(isinstance(conf.timestamp, datetime.datetime))
        self.assertTrue(isinstance(conf.expires, datetime.datetime))

    def test_set_author(self):
        with self.assertRaises(util.ConfigError):
            policy.Config().load_from_dict({'author': 0})
        with self.assertRaises(util.ConfigError):
            policy.Config().author = 0
        conf = policy.Config()
        conf.author = "me"
        self.assertEqual(conf.author, "me")

    def test_set_policies(self):
        invalid_policy = {'mode': 'none'}
        with self.assertRaises(util.ConfigError):
            policy.Config().policies = {'invalid': invalid_policy}
        conf = policy.Config()
        conf.policies = {'valid': {'mxs': ['example.com']}}
        self.assertEqual(conf.get_policy_for('valid').mxs, ['example.com'])

    def test_set_aliased_policy(self):
        conf = policy.Config()
        conf.policy_aliases = {'valid': {}}
        with self.assertRaises(util.ConfigError):
            conf.policies = {'invalid': {'policy-alias': 'invalid'}}
        conf.policies = {'valid': {'policy-alias': 'valid'}}
        self.assertEqual(conf.get_policy_for('valid').mode, 'testing')

    def test_iter_policies_aliased(self):
        conf = policy.Config()
        conf.policy_aliases = {'valid': {'mode': 'enforce'}}
        conf.policies = {'valid1': {'policy-alias': 'valid'},
                         'valid2': {'policy-alias': 'valid'}}
        for val in conf:
            self.assertEqual(conf[val].mode, 'enforce')

    def test_iter_policies(self):
        conf = policy.Config()
        sample_policy = {'mode': 'enforce'}
        conf.policies = {'valid1': sample_policy,
                         'valid2': sample_policy}
        for val in conf:
            self.assertEqual(conf[val].mode, 'enforce')

    def test_no_aliasing_in_alias(self):
        conf = policy.Config()
        with self.assertRaises(util.ConfigError):
            conf.policy_aliases = {'valid': {},
                                   'valid2': {'policy-alias': 'valid'}}

    def test_bad_schema(self):
        conf = policy.Config(schema={"author": "Author field here"})
        with self.assertRaises(util.ConfigError):
            conf.author = "Me"

class TestPolicy(unittest.TestCase):
    """Testing policy configuration
    """

    def setUp(self):
        self.sample_policy = {
            'mxs': ['eff.org', '.eff.org'],
            'mode': 'testing'
        }

    def test_merge_keeps_old_mxs(self):
        p = policy.Policy({'mxs': ['eff.org']})
        new_conf = p.merge(policy.Policy({'mxs': ['example.com']}))
        self.assertTrue('eff.org' in new_conf.mxs)
        self.assertTrue('example.com' in new_conf.mxs)

    def test_update_drops_old_mxs(self):
        p = policy.Policy({'mxs': ['eff.org']})
        new_conf = p.update(policy.Policy({'mxs': ['example.com']}))
        self.assertFalse('eff.org' in new_conf.mxs)
        self.assertTrue('example.com' in new_conf.mxs)

    def test_mode_default(self):
        p = policy.Policy({})
        self.assertEqual(p.mode, 'testing')

    def test_mode_valid(self):
        p = policy.Policy({})
        with self.assertRaises(util.ConfigError):
            policy.Policy({'mode': 'none'})
        with self.assertRaises(util.ConfigError):
            p.mode = 'none'
        p.mode = 'enforce'
        self.assertEqual(p.mode, 'enforce')

    def test_alias_valid(self):
        p = policy.Policy({}, aliases={'valid': self.sample_policy})
        with self.assertRaises(util.ConfigError):
            p.policy_alias = 'invalid'
        with self.assertRaises(util.ConfigError):
            policy.Policy({'policy-alias': 'invalid'}, aliases={'valid': self.sample_policy})
        p.policy_alias = 'valid'
        self.assertEqual(p.policy_alias, 'valid')

    def test_mxs(self):
        p = policy.Policy({})
        with self.assertRaises(util.ConfigError):
            p.mxs = [True]
        with self.assertRaises(util.ConfigError):
            p.mxs = True
        self.assertEqual(len(p.mxs), 0)
        p.mxs = ['eff.org', '.eff.org']
        self.assertEqual(p.mxs, ['eff.org', '.eff.org'])

    def test_no_alias_policy_setter(self):
        p = policy.PolicyNoAlias({}, aliases={'valid': self.sample_policy})
        with self.assertRaises(util.ConfigError):
            p.policy_alias = 'valid'

    def test_no_alias_policy_getter(self):
        p = policy.PolicyNoAlias({}, aliases={'valid': self.sample_policy})
        self.assertIsNone(p.policy_alias)

if __name__ == '__main__':
    unittest.main()

""" Tests for policy.py """
import unittest

import datetime
import json
import mock
import dateutil.tz

from starttls_policy_cli import policy
from starttls_policy_cli import util
from starttls_policy_cli.tests.util import assertRaisesRegex, param, parametrize_over

test_json = '{\
    "author": "Electronic Frontier Foundation",\
        "expires": "2014-05-26T01:35:33+0000",\
        "timestamp": "2014-05-26T01:35:33+0000",\
        "policies": {\
            ".valid.example-recipient.com": {\
                    "mode": "enforce",\
                    "mxs": [".valid.example-recipient.com"]\
            }\
        }\
    }'

class TestConfigEncoder(unittest.TestCase):
    """Tests extensions to JSON serializer for dumping configs"""

    def encode_test(self, obj, expected):
        """Parametrized test for JSON encoder"""
        self.assertEqual(json.dumps(obj, sort_keys=True, cls=policy.ConfigEncoder), expected)

parametrize_over(TestConfigEncoder, TestConfigEncoder.encode_test,
                 [
                    param("encode_none", None, "null"),
                    param("encode_empty_list", [], "[]"),
                    param("encode_empty_dict", {}, "{}"),
                    param("encode_empty_string", "", '""'),
                    param("encode_integer", 0, "0"),
                    param("encode_true", True, "true"),
                    param("encode_false", False, "false"),
                    param("encode_float", .1, "0.1"),
                    param("encode_datetime",
                          datetime.datetime.fromtimestamp(0, tz=dateutil.tz.tzutc()),
                          '"1970-01-01T00:00:00+0000"'),
                 ])

class TestConfig(unittest.TestCase):
    # pylint: disable=too-many-public-methods
    """Testing configuration
    """

    maxDiff = None

    def setUp(self):
        self.conf = policy.Config()
        self.conf.author = "EFF"
        self.conf.expires = datetime.datetime.now()
        self.conf.timestamp = datetime.datetime.now()
        self.sample_policy = {
            'mxs': ['eff.org', '.eff.org'],
            'mode': 'testing'
        }
        self.other_policy = {
            'mxs': ['example.com', '.example.com'],
            'mode': 'enforce'
        }
        self.conf.policies = {'eff.org': self.sample_policy}

    def test_flush(self):
        with mock.patch("starttls_policy_cli.policy.open", mock.mock_open()) as m:
            self.conf.flush("lol.txt")
            m.assert_called_with("lol.txt", "w")
            m().write.assert_called_once()

    def test_flush_default(self):
        with mock.patch("starttls_policy_cli.policy.open", mock.mock_open()) as m:
            self.conf.flush()
            m.assert_called_with(self.conf.filename, "w")
            m().write.assert_called_once()

    def test_merge_keeps_old_settings(self):
        conf2 = policy.Config()
        conf2.author = "EFF"
        updated_timestamp = self.conf.expires - datetime.timedelta(days=1)
        conf2.timestamp = updated_timestamp
        new_conf = self.conf.merge(conf2)
        self.assertEqual(new_conf.author, "EFF")
        self.assertTrue(new_conf.timestamp is not None)
        self.assertEqual(new_conf.timestamp, updated_timestamp)

    def test_update_drops_old_settings(self):
        conf2 = policy.Config()
        conf2.author = "EFF"
        new_conf = self.conf.update(conf2)
        self.assertEqual(new_conf.author, "EFF")
        self.assertEqual(new_conf.timestamp, None)

    def test_merge_keeps_old_policies(self):
        conf2 = policy.Config()
        conf2.policies = {'example.com': self.other_policy}
        new_conf = self.conf.merge(conf2)
        self.assertTrue('example.com' in new_conf.policies)
        self.assertTrue('eff.org' in new_conf.policies)

    def test_update_drops_old_policies(self):
        conf2 = policy.Config()
        conf2.policies = {'example.com': self.other_policy}
        new_conf = self.conf.update(conf2)
        self.assertTrue('example.com' in new_conf.policies)
        self.assertFalse('eff.org' in new_conf.policies)

    def test_should_update_invalid_type(self):
        self.assertFalse(self.conf.should_update(None))

    def test_should_update_valid_type(self):
        conf2 = policy.Config()
        conf2.policies = {'example.com': self.other_policy}
        self.assertTrue(self.conf.should_update(conf2))

    def test_updateble_checks_types(self):
        with assertRaisesRegex(self, util.ConfigError,
                                     "Attempting to update a "
                                     "<class 'starttls_policy_cli.policy.Config'> "
                                     "with a <(type|class) 'NoneType'>"):
            self.conf.update(None)

    def test_len_method(self):
        conf2 = policy.Config()
        conf2.policies = {'example.com': self.other_policy}
        self.assertEqual(len(conf2), 1)

    def test_keys_empty_policy(self):
        conf2 = policy.Config()
        with self.assertRaises(StopIteration):
            next(iter(conf2.keys()))

    def test_basic_parsing(self):
        obj = json.loads(test_json)
        self.conf.load_from_dict(obj)
        self.assertEqual(self.conf.author, "Electronic Frontier Foundation")
        self.assertEqual(json.loads(self.conf.dump()), obj)

    def test_timestamp_and_expires_required(self):
        with self.assertRaises(util.ConfigError):
            policy.Config().load_from_dict({'expires': datetime.datetime.now()})
        with self.assertRaises(util.ConfigError):
            policy.Config().load_from_dict({'timestamp': datetime.datetime.now()})
        conf = policy.Config()
        conf.load_from_dict({'timestamp': datetime.datetime.now(),
                             'expires': datetime.datetime.now()})
        self.assertTrue(isinstance(conf.timestamp, datetime.datetime))
        self.assertTrue(isinstance(conf.expires, datetime.datetime))

    def test_set_author(self):
        with self.assertRaises(util.ConfigError):
            policy.Config().load_from_dict({'author': 0})
        with self.assertRaises(util.ConfigError):
            policy.Config().author = 0
        conf = policy.Config()
        conf.author = "me"
        self.assertEqual(conf.author, "me")

    def test_set_policies(self):
        invalid_policy = {'mode': 'none'}
        with self.assertRaises(util.ConfigError):
            policy.Config().policies = {'invalid': invalid_policy}
        conf = policy.Config()
        conf.policies = {'valid': {'mxs': ['example.com']}}
        self.assertEqual(conf.get_policy_for('valid').mxs, ['example.com'])

    def test_set_aliased_policy(self):
        conf = policy.Config()
        conf.policy_aliases = {'valid': {}}
        with self.assertRaises(util.ConfigError):
            conf.policies = {'invalid': {'policy-alias': 'invalid'}}
        conf.policies = {'valid': {'policy-alias': 'valid'}}
        self.assertEqual(conf.get_policy_for('valid').mode, 'testing')

    def test_iter_policies_aliased(self):
        conf = policy.Config()
        conf.policy_aliases = {'valid': {'mode': 'enforce'}}
        conf.policies = {'valid1': {'policy-alias': 'valid'},
                         'valid2': {'policy-alias': 'valid'}}
        for val in conf:
            self.assertEqual(conf[val].mode, 'enforce')

    def test_iter_policies(self):
        conf = policy.Config()
        sample_policy = {'mode': 'enforce'}
        conf.policies = {'valid1': sample_policy,
                         'valid2': sample_policy}
        for val in conf:
            self.assertEqual(conf[val].mode, 'enforce')

    def test_no_aliasing_in_alias(self):
        conf = policy.Config()
        with self.assertRaises(util.ConfigError):
            conf.policy_aliases = {'valid': {},
                                   'valid2': {'policy-alias': 'valid'}}

    def test_bad_schema(self):
        conf = policy.Config(schema={"author": "Author field here"})
        with self.assertRaises(util.ConfigError):
            conf.author = "Me"

class TestPolicy(unittest.TestCase):
    """Testing policy configuration
    """

    def setUp(self):
        self.sample_policy = {
            'mxs': ['eff.org', '.eff.org'],
            'mode': 'testing'
        }

    def test_merge_keeps_old_mxs(self):
        p = policy.Policy({'mxs': ['eff.org']})
        new_conf = p.merge(policy.Policy({'mxs': ['example.com']}))
        self.assertTrue('eff.org' in new_conf.mxs)
        self.assertTrue('example.com' in new_conf.mxs)

    def test_update_drops_old_mxs(self):
        p = policy.Policy({'mxs': ['eff.org']})
        new_conf = p.update(policy.Policy({'mxs': ['example.com']}))
        self.assertFalse('eff.org' in new_conf.mxs)
        self.assertTrue('example.com' in new_conf.mxs)

    def test_mode_default(self):
        p = policy.Policy({})
        self.assertEqual(p.mode, 'testing')

    def test_mode_valid(self):
        p = policy.Policy({})
        with self.assertRaises(util.ConfigError):
            policy.Policy({'mode': 'none'})
        with self.assertRaises(util.ConfigError):
            p.mode = 'none'
        p.mode = 'enforce'
        self.assertEqual(p.mode, 'enforce')

    def test_alias_valid(self):
        p = policy.Policy({}, aliases={'valid': self.sample_policy})
        with self.assertRaises(util.ConfigError):
            p.policy_alias = 'invalid'
        with self.assertRaises(util.ConfigError):
            policy.Policy({'policy-alias': 'invalid'}, aliases={'valid': self.sample_policy})
        p.policy_alias = 'valid'
        self.assertEqual(p.policy_alias, 'valid')

    def test_mxs(self):
        p = policy.Policy({})
        with self.assertRaises(util.ConfigError):
            p.mxs = [True]
        with self.assertRaises(util.ConfigError):
            p.mxs = True
        self.assertEqual(len(p.mxs), 0)
        p.mxs = ['eff.org', '.eff.org']
        self.assertEqual(p.mxs, ['eff.org', '.eff.org'])

    def test_no_alias_policy_setter(self):
        p = policy.PolicyNoAlias({}, aliases={'valid': self.sample_policy})
        with self.assertRaises(util.ConfigError):
            p.policy_alias = 'valid'

    def test_no_alias_policy_getter(self):
        p = policy.PolicyNoAlias({}, aliases={'valid': self.sample_policy})
        self.assertIsNone(p.policy_alias)

if __name__ == '__main__':
    unittest.main()

Tests for policy.py Tests extensions to JSON serializer for dumping configs Parametrized test for JSON encoder # pylint: disable=too-many-public-methods Testing configuration Testing policy configuration