diff --git a/.travis.yml b/.travis.yml index 289b5b0..e2fb880 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,20 @@ language: python install: pip install tox python-coveralls script: tox -v after_success: coveralls + +matrix: + include: + - env: TOXENV=flake + + - env: TOXENV=py27,coverage + python: '2.7' + - env: TOXENV=py34,coverage + python: '3.4' + - env: TOXENV=py35,coverage + python: '3.5' + - env: TOXENV=py36,coverage + python: '3.6' + deploy: provider: pypi user: jonls diff --git a/s3_deploy/config.py b/s3_deploy/config.py index 02e45b5..8635fdd 100644 --- a/s3_deploy/config.py +++ b/s3_deploy/config.py @@ -13,6 +13,11 @@ logger = logging.getLogger(__name__) +def _load_config_from_path(path): + with open(path, 'r') as f: + return yaml.safe_load(f) + + def load_config_file(path): """Load configuration settings from file. @@ -22,8 +27,7 @@ def load_config_file(path): for filename in ('.s3_website.yaml', '.s3_website.yml'): filepath = os.path.join(path, filename) try: - with open(filepath, 'r') as f: - config = yaml.safe_load(f) + config = _load_config_from_path(filepath) except Exception: logger.debug('Unable to load config from {}'.format(filepath), exc_info=True) @@ -34,8 +38,7 @@ def load_config_file(path): raise ValueError( 'Unable to find .s3_website.yaml in {}'.format(path)) else: - with open(path, 'r') as f: - config = yaml.safe_load(f) + config = _load_config_from_path(path) base_path = os.path.dirname(path) return config, base_path diff --git a/s3_deploy/tests/test_config.py b/s3_deploy/tests/test_config.py index 9fc7a29..cb448fc 100644 --- a/s3_deploy/tests/test_config.py +++ b/s3_deploy/tests/test_config.py @@ -1,6 +1,11 @@ -import unittest from datetime import timedelta +import os +import shutil +import tempfile +import unittest + +from mock import patch, call from s3_deploy import config @@ -97,3 +102,101 @@ def test_resolve_second_rule(self): {'match': '*', 'maxage': 300} ]) self.assertEqual(cache, 'maxage=200') + + +class LoadConfigFileTest(unittest.TestCase): + def setUp(self): + self.tmp_dir = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.tmp_dir) + + def test_load_config_from_path(self): + path = os.path.join(self.tmp_dir, 'some_file_name') + with open(path, 'w') as f: + f.write('\n'.join([ + 'site: _site', + 's3_bucket: example.com', + 'cloudfront_distribution_id: ABCDEFGHI', + '', + 'cache_rules:', + '- match: "/assets/*"', + ' maxage: 30 days', + '', + '- match: "/css/*"', + ' maxage: 30 days', + '', + '- match: "*"', + ' maxage: 1 hour', + '', + ])) + + self.assertEqual(config._load_config_from_path(path), { + 'site': '_site', + 's3_bucket': 'example.com', + 'cloudfront_distribution_id': 'ABCDEFGHI', + 'cache_rules': [ + { + 'match': '/assets/*', + 'maxage': '30 days', + }, + { + 'match': '/css/*', + 'maxage': '30 days', + }, + { + 'match': '*', + 'maxage': '1 hour', + }, + ] + }) + + @patch('os.path.isdir', return_value=False) + @patch('s3_deploy.config._load_config_from_path') + def test_load_config_file_from_absolute_path(self, mock_load, mock_is_dir): + mocked_config_dict = {} + mock_load.return_value = mocked_config_dict + fake_path = os.path.join('path', 'to', 'some_file.ext') + config_dict, base_path = config.load_config_file(fake_path) + + mock_load.assert_called_with(fake_path) + self.assertEqual(config_dict, mocked_config_dict) + self.assertEqual(base_path, os.path.join('path', 'to')) + + @patch('os.path.isdir', return_value=False) + @patch('s3_deploy.config._load_config_from_path') + def test_load_config_file_from_nonexisting_file( + self, mock_load, mock_is_dir): + mock_load.side_effect = IOError('Failed to load fake file') + fake_path = os.path.join('path', 'to', 'some_file.ext') + with self.assertRaises(IOError): + config.load_config_file(fake_path) + + mock_load.assert_called_with(fake_path) + + @patch('os.path.isdir', return_value=True) + @patch('s3_deploy.config._load_config_from_path') + def test_load_config_file_from_directory(self, mock_load, mock_is_dir): + mocked_config_dict = {} + mock_load.return_value = mocked_config_dict + fake_path = os.path.join('path', 'to', 'some', 'directory') + config_dict, base_path = config.load_config_file(fake_path) + + mock_load.assert_called_once_with( + os.path.join(fake_path, '.s3_website.yaml')) + self.assertEqual(config_dict, mocked_config_dict) + self.assertEqual(base_path, fake_path) + + @patch('os.path.isdir', return_value=True) + @patch('s3_deploy.config._load_config_from_path') + def test_load_config_file_from_directory_nonexisting( + self, mock_load, mock_is_dir): + mock_load.side_effect = IOError('Failed to load fake file') + fake_path = os.path.join('path', 'to', 'directory') + with self.assertRaises(ValueError): + config.load_config_file(fake_path) + + mock_load.assert_has_calls([ + call(os.path.join(fake_path, '.s3_website.yaml')), + call(os.path.join(fake_path, '.s3_website.yml')) + ]) diff --git a/setup.py b/setup.py index a97f768..0a521e4 100755 --- a/setup.py +++ b/setup.py @@ -24,6 +24,7 @@ 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP :: Site Management', ], @@ -33,7 +34,11 @@ 's3-deploy-website = s3_deploy.deploy:main' ] }, - test_suite='s3_deploy.tests', install_requires=[ 'boto3', 'PyYAML', 'six' - ]) + ], + test_suite='s3_deploy.tests', + tests_require=[ + 'mock', + ], +) diff --git a/tox.ini b/tox.ini index fa726d5..7f2b7ad 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,8 @@ envlist = py27, py34, + py35, + py36, coverage, flake @@ -11,7 +13,7 @@ ignore = E226,D101,D102,D103,D104,D203 [testenv] deps = coverage commands = - coverage run -p --branch --omit={envdir}/*,s3_deploy/tests/*,setup.py \ + coverage run -p --branch --include=s3_deploy/* --omit=s3_deploy/tests/* \ ./setup.py test [testenv:coverage]