pegleg/tests/unit/engine/test_selectable_linting.py
Ian H. Pittwood 4480ab5574 Restructure usage of test fixtures
Pytest includes a fixture that can be used to generate temporary
directories. Previously Pegleg had implemented a hombrewed version of a
temporary directory fixture. This change removes the homebrewed version
and replaces it with the tmpdir fixture.

Implement tmpdir fixture in tests

Upgrade all testing packages to use the latest features

Removes unused imports and organizes import lists

Removes mock package requirement and uses unittest.mock, included in
python >3.3

Implements a slightly cleaner method to get proxy info

Change-Id: If66e1cfba858d5fb8948529deb8fb2d32345f630
2019-07-29 11:37:36 -05:00

242 lines
9.4 KiB
Python

# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from unittest import mock
import click
import pytest
from deckhand import errors as dh_errors
from pegleg import config
from pegleg.engine import errorcodes
from pegleg.engine import lint
_SKIP_P003_REASON = """Currently broken with revisioned repositories
directory layout changes. The old pseudo-revision folders like 'v4.0' are
no longer relevant and so the lint logic for this rule needs to be updated.
For more information, see: https://storyboard.openstack.org/#!/story/2003762
"""
class TestSelectableLinting(object):
def setup(self):
self.site_yaml_path = os.path.join(os.getcwd(), 'site_yamls')
def _exclude_all(self, except_code):
"""Helper to generate list of all error codes to exclude except for
``except_code`` in order to isolate tests that focus on validating
``warn_lint``: Just check that the expected code issues a warning and
effectively ignore all other errors.
"""
return [code for code in errorcodes.ALL_CODES if code != except_code]
@mock.patch.object(lint, '_verify_deckhand_render', return_value=[])
@mock.patch.object(lint, '_verify_no_unexpected_files', return_value=[])
def test_lint_excludes_P001(self, *args):
"""Test exclude flag for P001 - Document has storagePolicy cleartext
(expected is encrypted) yet its schema is a mandatory encrypted type.
"""
exclude_lint = ['P001']
config.set_site_repo(self.site_yaml_path)
code_1 = 'X001'
msg_1 = 'is a secret, but has unexpected storagePolicy: "cleartext"'
code_2 = 'X002'
msg_2 = 'test msg'
msgs = [(code_1, msg_1), (code_2, msg_2)]
with mock.patch.object(lint, '_verify_file_contents',
return_value=msgs) as mock_methed:
with pytest.raises(click.ClickException) as expected_exc:
lint.full(False, exclude_lint, [])
assert msg_1 in expected_exc
assert msg_2 in expected_exc
@pytest.mark.skip(reason=_SKIP_P003_REASON)
@mock.patch.object(lint, '_verify_deckhand_render', return_value=[])
def test_lint_excludes_P003(self, *args):
"""Test exclude flag for P003 - All repos contain expected
directories.
"""
exclude_lint = ['P003']
with mock.patch.object(lint, '_verify_no_unexpected_files',
return_value=[('P003', 'test message')
]) as mock_method:
result = lint.full(False, exclude_lint, [])
mock_method.assert_called()
assert not result # Exclude doesn't return anything.
@mock.patch.object(lint, '_verify_deckhand_render', return_value=[])
@mock.patch.object(lint, '_verify_no_unexpected_files', return_value=[])
def test_lint_warns_P001(self, *args):
"""Test lint flag for P001 - Document has storagePolicy cleartext
(expected is encrypted) yet its schema is a mandatory encrypted type.
"""
exclude_lint = self._exclude_all(except_code='P001')
warn_lint = ['P001']
config.set_site_repo(self.site_yaml_path)
code_1 = 'X001'
msg_1 = 'is a secret, but has unexpected storagePolicy: "cleartext"'
code_2 = 'X002'
msg_2 = 'test msg'
msgs = [(code_1, msg_1), (code_2, msg_2)]
with mock.patch.object(lint, '_verify_file_contents',
return_value=msgs) as mock_methed:
with pytest.raises(click.ClickException) as expected_exc:
lint.full(
False, exclude_lint=exclude_lint, warn_lint=warn_lint)
assert msg_1 not in expected_exc
assert msg_2 in expected_exc
@pytest.mark.skip(reason=_SKIP_P003_REASON)
@mock.patch.object(lint, '_verify_deckhand_render', return_value=[])
def test_lint_warns_P003(self, *args):
"""Test warn flag for P003 - All repos contain expected directories."""
exclude_lint = self._exclude_all(except_code='P003')
warn_lint = ['P003']
config.set_site_repo(self.site_yaml_path)
with mock.patch.object(lint,
'_verify_no_unexpected_files') as mock_method:
result = lint.full(
False, exclude_lint=exclude_lint, warn_lint=warn_lint)
mock_method.assert_called()
assert len(result) == 1
assert result[0].startswith(warn_lint[0])
@mock.patch('pegleg.engine.util.deckhand.layering', autospec=True)
def test_lint_warns_P004(self, mock_layering):
"""Test warn flag for P004 - Duplicate Deckhand DataSchema document
detected.
"""
# Stub out Deckhand render logic.
mock_layering.DocumentLayering.return_value.render.return_value = []
exclude_lint = self._exclude_all(except_code='P004')
warn_lint = ['P004']
config.set_site_repo(self.site_yaml_path)
documents = {
mock.sentinel.site: [
{
# Create 2 duplicate DataSchema documents.
"schema": "deckhand/DataSchema/v1",
"metadata": {
"name": mock.sentinel.document_name
},
"data": {}
}
] * 2
}
with mock.patch(
'pegleg.engine.util.definition.documents_for_each_site',
autospec=True, return_value=documents):
result = lint.full(
False, exclude_lint=exclude_lint, warn_lint=warn_lint)
assert len(result) == 1
assert result[0].startswith(warn_lint[0])
@mock.patch('pegleg.engine.util.deckhand.layering', autospec=True)
def test_lint_warns_P005(self, mock_layering):
"""Test warn flag for P005 - Deckhand rendering exception."""
# Make Deckhand render expected exception to trigger error code.
mock_layering.DocumentLayering.return_value.render.side_effect = (
dh_errors.DeckhandException)
exclude_lint = self._exclude_all(except_code='P005')
warn_lint = ['P005']
config.set_site_repo(self.site_yaml_path)
documents = {
mock.sentinel.site: [
{
"schema": "deckhand/DataSchema/v1",
"metadata": {
"name": mock.sentinel.document_name
},
"data": {}
}
]
}
with mock.patch(
'pegleg.engine.util.definition.documents_for_each_site',
autospec=True, return_value=documents):
result = lint.full(
False, exclude_lint=exclude_lint, warn_lint=warn_lint)
assert len(result) == 1
assert result[0].startswith(warn_lint[0])
def test_lint_warns_P006(self, tmpdir):
"""Test warn flag for P006 - YAML file missing document header."""
exclude_lint = self._exclude_all(except_code='P006')
warn_lint = ['P006']
config.set_site_repo(self.site_yaml_path)
p = tmpdir.mkdir(self.__class__.__name__).join("test.yaml")
p.write("foo: bar")
with mock.patch('pegleg.engine.util.files.all', autospec=True,
return_value=[p.strpath]):
result = lint.full(
False, exclude_lint=exclude_lint, warn_lint=warn_lint)
assert len(result) == 1
assert result[0].startswith(warn_lint[0])
def test_lint_warns_P007(self, tmpdir):
"""Test warn flag for P007 - YAML file is not valid YAML."""
exclude_lint = self._exclude_all(except_code='P007')
warn_lint = ['P007']
config.set_site_repo(self.site_yaml_path)
p = tmpdir.mkdir(self.__class__.__name__).join("test.yaml")
# Invalid YAML - will trigger error.
p.write("---\nfoo: bar: baz")
with mock.patch('pegleg.engine.util.files.all', autospec=True,
return_value=[p.strpath]):
result = lint.full(
False, exclude_lint=exclude_lint, warn_lint=warn_lint)
assert len(result) == 1
assert result[0].startswith(warn_lint[0])
# TODO(felipemonteiro): Add tests for P008, P009.
class TestSelectableLintingHelperFunctions(object):
"""The fixture ``temp_deployment_files`` produces many linting errors
by default.
"""
def test_verify_file_contents(self, temp_deployment_files):
"""Validate that linting by a specific site ("cicd") produces a subset
of all the linting errors produced for all sites.
"""
all_lint_errors = lint._verify_file_contents()
assert all_lint_errors
cicd_lint_errors = lint._verify_file_contents(sitename="cicd")
assert cicd_lint_errors
assert len(cicd_lint_errors) < len(all_lint_errors)