4480ab5574
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
242 lines
9.4 KiB
Python
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)
|