1247 lines
48 KiB
Python
1247 lines
48 KiB
Python
# Copyright 2017 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 copy
|
|
import yaml
|
|
|
|
import mock
|
|
from oslo_config import cfg
|
|
|
|
from deckhand.control import buckets
|
|
from deckhand.engine import document_validation
|
|
from deckhand import factories
|
|
from deckhand.tests import test_utils
|
|
from deckhand.tests.unit.control import base as test_base
|
|
from deckhand import types
|
|
|
|
CONF = cfg.CONF
|
|
|
|
|
|
VALIDATION_FAILURE_RESULT = """
|
|
---
|
|
status: failure
|
|
errors:
|
|
- documents:
|
|
- schema: promenade/Node/v1
|
|
name: node-document-name
|
|
- schema: promenade/Masters/v1
|
|
name: kubernetes-masters
|
|
message: Node has master role, but not included in cluster masters list.
|
|
validator:
|
|
name: promenade
|
|
version: 1.1.2
|
|
"""
|
|
|
|
VALIDATION_SUCCESS_RESULT = """
|
|
---
|
|
status: success
|
|
errors: []
|
|
validator:
|
|
name: promenade
|
|
version: 1.1.2
|
|
"""
|
|
|
|
|
|
class BaseValidationsControllerTest(test_base.BaseControllerTest):
|
|
|
|
def _create_revision(self, payload=None):
|
|
if not payload:
|
|
documents_factory = factories.DocumentFactory(1, [1])
|
|
payload = documents_factory.gen_test({})
|
|
data_schema_factory = factories.DataSchemaFactory()
|
|
data_schema = data_schema_factory.gen_test(
|
|
payload[1]['schema'], data={})
|
|
payload.append(data_schema)
|
|
|
|
resp = self.app.simulate_put(
|
|
'/api/v1.0/buckets/mop/documents',
|
|
headers={'Content-Type': 'application/x-yaml'},
|
|
body=yaml.safe_dump_all(payload))
|
|
self.assertEqual(200, resp.status_code)
|
|
revision_id = list(yaml.safe_load_all(resp.text))[0]['status'][
|
|
'revision']
|
|
return revision_id
|
|
|
|
def _create_validation(self, revision_id, validation_name, policy):
|
|
resp = self.app.simulate_post(
|
|
'/api/v1.0/revisions/%s/validations/%s' % (revision_id,
|
|
validation_name),
|
|
headers={'Content-Type': 'application/x-yaml'}, body=policy)
|
|
return resp
|
|
|
|
def _monkey_patch_document_validation(self):
|
|
"""Workaround for testing complex validation scenarios by forcibly
|
|
passing in `pre_validate=False`.
|
|
"""
|
|
# TODO(felipemonteiro): Remove this workaround by testing these more
|
|
# complex scenarios against the rendered-documents endpoint instead
|
|
# (which performs post-validation).
|
|
original_document_validation = document_validation.DocumentValidation
|
|
|
|
def monkey_patch(*args, **kwargs):
|
|
return original_document_validation(*args, pre_validate=False)
|
|
|
|
mock.patch.object(buckets.document_validation, 'DocumentValidation',
|
|
side_effect=monkey_patch, autospec=True).start()
|
|
self.addCleanup(mock.patch.stopall)
|
|
|
|
|
|
class TestValidationsController(BaseValidationsControllerTest):
|
|
"""Test suite for validating Validations API."""
|
|
|
|
def test_create_validation(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:create_validation': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
revision_id = self._create_revision()
|
|
validation_name = test_utils.rand_name('validation')
|
|
resp = self._create_validation(revision_id, validation_name,
|
|
VALIDATION_FAILURE_RESULT)
|
|
|
|
self.assertEqual(201, resp.status_code)
|
|
expected_body = {
|
|
'status': 'failure',
|
|
'validator': {
|
|
'name': 'promenade',
|
|
'version': '1.1.2'
|
|
}
|
|
}
|
|
self.assertEqual(expected_body, yaml.safe_load(resp.text))
|
|
|
|
def test_list_validations(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
revision_id = self._create_revision()
|
|
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
|
|
# Validate that the internal deckhand validation was created already.
|
|
body = list(yaml.safe_load_all(resp.text))
|
|
expected = {
|
|
'count': 1,
|
|
'results': [
|
|
{
|
|
'status': 'success',
|
|
'name': types.DECKHAND_SCHEMA_VALIDATION
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(1, len(body))
|
|
self.assertEqual(expected, body[0])
|
|
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:create_validation': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Validate that, after creating a validation policy by an external
|
|
# service, it is listed as well.
|
|
validation_name = test_utils.rand_name('validation')
|
|
resp = self._create_validation(revision_id, validation_name,
|
|
VALIDATION_FAILURE_RESULT)
|
|
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 2,
|
|
'results': [
|
|
{
|
|
'name': types.DECKHAND_SCHEMA_VALIDATION,
|
|
'status': 'success'
|
|
},
|
|
{
|
|
'name': validation_name,
|
|
'status': 'failure'
|
|
}
|
|
]
|
|
}
|
|
body['results'] = sorted(body['results'], key=lambda x: x['name'])
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_list_validation_entries(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:create_validation': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
revision_id = self._create_revision()
|
|
|
|
# Validate that 3 entries (1 for each of the 3 documents created)
|
|
# exists for:
|
|
# /api/v1.0/revisions/1/validations/deckhand-schema-validation
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s' % (
|
|
revision_id, types.DECKHAND_SCHEMA_VALIDATION),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 3,
|
|
'results': [{'id': x, 'status': 'success'} for x in range(3)]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
# Add the result of a validation to a revision.
|
|
validation_name = test_utils.rand_name('validation')
|
|
resp = self._create_validation(revision_id, validation_name,
|
|
VALIDATION_FAILURE_RESULT)
|
|
|
|
# Validate that the entry is present.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s' % (revision_id,
|
|
validation_name),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [{'id': 0, 'status': 'failure'}]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_list_validation_entries_after_creating_validation(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:create_validation': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
revision_id = self._create_revision()
|
|
|
|
# Add the result of a validation to a revision.
|
|
validation_name = test_utils.rand_name('validation')
|
|
resp = self._create_validation(revision_id, validation_name,
|
|
VALIDATION_FAILURE_RESULT)
|
|
|
|
# Validate that the entry is present.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s' % (revision_id,
|
|
validation_name),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [{'id': 0, 'status': 'failure'}]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
# Add the result of another validation to the same revision.
|
|
resp = self._create_validation(revision_id, validation_name,
|
|
VALIDATION_SUCCESS_RESULT)
|
|
|
|
# Validate that 2 entries now exist.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s' % (revision_id,
|
|
validation_name),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 2,
|
|
'results': [
|
|
{'id': 0, 'status': 'failure'}, {'id': 1, 'status': 'success'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_list_validation_entries_with_multiple_entries(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:create_validation': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
revision_id = self._create_revision()
|
|
validation_name = test_utils.rand_name('validation')
|
|
self._create_validation(revision_id, validation_name,
|
|
VALIDATION_FAILURE_RESULT)
|
|
self._create_validation(revision_id, validation_name,
|
|
VALIDATION_SUCCESS_RESULT)
|
|
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s' % (revision_id,
|
|
validation_name),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 2,
|
|
'results': [
|
|
{'id': 0, 'status': 'failure'}, {'id': 1, 'status': 'success'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_show_validation_entry(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:create_validation': '@',
|
|
'deckhand:show_validation': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
revision_id = self._create_revision()
|
|
validation_name = test_utils.rand_name('validation')
|
|
resp = self._create_validation(revision_id, validation_name,
|
|
VALIDATION_FAILURE_RESULT)
|
|
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s/entries/0' % (
|
|
revision_id, validation_name),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'name': validation_name,
|
|
'status': 'failure',
|
|
'createdAt': None,
|
|
'expiresAfter': None,
|
|
'errors': [
|
|
{
|
|
'documents': [
|
|
{
|
|
'name': 'node-document-name',
|
|
'schema': 'promenade/Node/v1'
|
|
}, {
|
|
'name': 'kubernetes-masters',
|
|
'schema': 'promenade/Masters/v1'
|
|
}
|
|
],
|
|
'message': 'Node has master role, but not included in '
|
|
'cluster masters list.'
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_show_nonexistent_validation_entry_returns_404(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:create_validation': '@',
|
|
'deckhand:show_validation': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
revision_id = self._create_revision()
|
|
validation_name = test_utils.rand_name('validation')
|
|
resp = self._create_validation(revision_id, validation_name,
|
|
VALIDATION_FAILURE_RESULT)
|
|
self.assertEqual(201, resp.status_code)
|
|
expected_error = ('The requested validation entry 5 was not found for '
|
|
'validation name %s and revision ID %d' % (
|
|
validation_name, revision_id))
|
|
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s/entries/5' % (
|
|
revision_id, validation_name),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(404, resp.status_code)
|
|
self.assertEqual(expected_error, yaml.safe_load(resp.text)['message'])
|
|
|
|
def test_list_validations_details(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
revision_id = self._create_revision()
|
|
|
|
# Validate that 3 entries (1 for each of the 3 documents created)
|
|
# exists for
|
|
# /api/v1.0/revisions/1/validations/deckhand-schema-validation
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/detail' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'results': [{
|
|
'createdAt': None,
|
|
'errors': [],
|
|
'expiresAfter': None,
|
|
'id': idx,
|
|
'name': 'deckhand-schema-validation',
|
|
'status': 'success'
|
|
|
|
} for idx in range(3)],
|
|
'count': 3
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
|
|
class TestValidationsControllerPreValidate(BaseValidationsControllerTest):
|
|
"""Test suite for validating positive scenarios for pre-validations with
|
|
Validations controller.
|
|
"""
|
|
|
|
def test_pre_validate_flag_skips_registered_dataschema_validations(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create a `DataSchema` against which the test document will be
|
|
# validated.
|
|
data_schema_factory = factories.DataSchemaFactory()
|
|
metadata_name = 'example/foo/v1'
|
|
schema_to_use = {
|
|
'$schema': 'http://json-schema.org/schema#',
|
|
'type': 'object',
|
|
'properties': {
|
|
'a': {
|
|
'type': 'integer' # Test doc will fail b/c of wrong type.
|
|
}
|
|
},
|
|
'required': ['a']
|
|
}
|
|
data_schema = data_schema_factory.gen_test(
|
|
metadata_name, data=schema_to_use)
|
|
|
|
# Create a document that passes validation and another that fails it.
|
|
doc_factory = factories.DocumentFactory(1, [1])
|
|
fail_doc = doc_factory.gen_test(
|
|
{'_GLOBAL_DATA_1_': {'data': {'a': 'fail'}}},
|
|
global_abstract=False)[-1]
|
|
fail_doc['schema'] = 'example/foo/v1'
|
|
fail_doc['metadata']['name'] = 'test_doc'
|
|
|
|
revision_id = self._create_revision(payload=[data_schema, fail_doc])
|
|
|
|
# Validate that the validation reports success because `fail_doc`
|
|
# isn't validated by the `DataSchema`.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [
|
|
{'name': types.DECKHAND_SCHEMA_VALIDATION, 'status': 'success'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
|
|
class TestValidationsControllerPostValidate(BaseValidationsControllerTest):
|
|
"""Test suite for validating positive scenarios for post-validations with
|
|
Validations controller.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super(TestValidationsControllerPostValidate, self).setUp()
|
|
self._monkey_patch_document_validation()
|
|
|
|
def test_validation_with_registered_data_schema(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create a `DataSchema` against which the test document will be
|
|
# validated.
|
|
data_schema_factory = factories.DataSchemaFactory()
|
|
metadata_name = 'example/Doc/v1'
|
|
schema_to_use = {
|
|
'$schema': 'http://json-schema.org/schema#',
|
|
'type': 'object',
|
|
'properties': {
|
|
'a': {
|
|
'type': 'string'
|
|
}
|
|
},
|
|
'required': ['a'],
|
|
'additionalProperties': False
|
|
}
|
|
data_schema = data_schema_factory.gen_test(
|
|
metadata_name, data=schema_to_use)
|
|
|
|
# Create the test document whose data section adheres to the
|
|
# `DataSchema` above.
|
|
doc_factory = factories.DocumentFactory(1, [1])
|
|
doc_to_test = doc_factory.gen_test(
|
|
{'_GLOBAL_DATA_1_': {'data': {'a': 'whatever'}}},
|
|
global_abstract=False)[-1]
|
|
doc_to_test['schema'] = 'example/Doc/v1'
|
|
|
|
revision_id = self._create_revision(payload=[doc_to_test, data_schema])
|
|
|
|
# Validate that the validation was created and succeeded.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [
|
|
{'name': types.DECKHAND_SCHEMA_VALIDATION, 'status': 'success'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_validation_data_schema_different_revision_expect_failure(self):
|
|
"""Validates that creating a ``DataSchema`` in one revision and then
|
|
creating a document in another revision that relies on the previously
|
|
created ``DataSchema`` results in an expected failure.
|
|
"""
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@',
|
|
'deckhand:list_cleartext_documents': '@',
|
|
'deckhand:list_encrypted_documents': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create a `DataSchema` against which the test document will be
|
|
# validated.
|
|
data_schema_factory = factories.DataSchemaFactory()
|
|
metadata_name = 'example/foo/v1'
|
|
schema_to_use = {
|
|
'$schema': 'http://json-schema.org/schema#',
|
|
'type': 'object',
|
|
'properties': {
|
|
'a': {
|
|
'type': 'integer' # Test doc will fail b/c of wrong type.
|
|
}
|
|
},
|
|
'required': ['a']
|
|
}
|
|
data_schema = data_schema_factory.gen_test(
|
|
metadata_name, data=schema_to_use)
|
|
revision_id = self._create_revision(payload=[data_schema])
|
|
|
|
# Validate that the internal deckhand validation was created.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [
|
|
{'name': types.DECKHAND_SCHEMA_VALIDATION, 'status': 'success'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
# Create the test document that fails the validation due to the
|
|
# schema defined by the `DataSchema` document.
|
|
doc_factory = factories.DocumentFactory(1, [1])
|
|
docs_to_test = doc_factory.gen_test(
|
|
{'_GLOBAL_DATA_1_': {'data': {'a': 'fail'}}},
|
|
global_abstract=False)
|
|
docs_to_test[1]['schema'] = 'example/foo/v1'
|
|
docs_to_test[1]['metadata']['name'] = 'test_doc'
|
|
|
|
revision_id = self._create_revision(
|
|
payload=docs_to_test + [data_schema])
|
|
|
|
# Validate that the validation was created and reports failure.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [
|
|
{'name': types.DECKHAND_SCHEMA_VALIDATION, 'status': 'failure'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
# Validate that the validation was created and reports failure.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/rendered-documents' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(400, resp.status_code)
|
|
|
|
def test_validation_data_schema_same_revision_expect_failure(self):
|
|
"""Validates that creating a ``DataSchema`` alongside a document
|
|
that relies on it in the same revision results in an expected failure.
|
|
"""
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create a `DataSchema` against which the test document will be
|
|
# validated.
|
|
data_schema_factory = factories.DataSchemaFactory()
|
|
metadata_name = 'example/foo/v1'
|
|
schema_to_use = {
|
|
'$schema': 'http://json-schema.org/schema#',
|
|
'type': 'object',
|
|
'properties': {
|
|
'a': {
|
|
'type': 'integer' # Test doc will fail b/c of wrong type.
|
|
}
|
|
},
|
|
'required': ['a']
|
|
}
|
|
data_schema = data_schema_factory.gen_test(
|
|
metadata_name, data=schema_to_use)
|
|
|
|
# Create the test document that fails the validation due to the
|
|
# schema defined by the `DataSchema` document.
|
|
doc_factory = factories.DocumentFactory(1, [1])
|
|
doc_to_test = doc_factory.gen_test(
|
|
{'_GLOBAL_DATA_1_': {'data': {'a': 'fail'}}},
|
|
global_abstract=False)[-1]
|
|
doc_to_test['schema'] = 'example/foo/v1'
|
|
doc_to_test['metadata']['name'] = 'test_doc'
|
|
|
|
revision_id = self._create_revision(payload=[doc_to_test, data_schema])
|
|
|
|
# Validate that the validation was created and reports failure.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [
|
|
{'name': types.DECKHAND_SCHEMA_VALIDATION, 'status': 'failure'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_validation_with_registered_data_schema_expect_multi_failure(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@',
|
|
'deckhand:show_validation': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create a `DataSchema` against which the test document will be
|
|
# validated.
|
|
data_schema_factory = factories.DataSchemaFactory()
|
|
metadata_name = 'example/foo/v1'
|
|
schema_to_use = {
|
|
'$schema': 'http://json-schema.org/schema#',
|
|
'type': 'object',
|
|
'properties': {
|
|
'a': {
|
|
'type': 'integer' # Test doc will fail b/c of wrong type.
|
|
},
|
|
'b': {
|
|
'type': 'string'
|
|
}
|
|
},
|
|
'required': ['a', 'b']
|
|
}
|
|
data_schema = data_schema_factory.gen_test(
|
|
metadata_name, data=schema_to_use)
|
|
|
|
# Failure #1: Provide wrong type for property "a".
|
|
# Failure #2: Don't include required property "b".
|
|
doc_factory = factories.DocumentFactory(1, [1])
|
|
doc_to_test = doc_factory.gen_test(
|
|
{'_GLOBAL_DATA_1_': {'data': {'a': 'fail'}}},
|
|
global_abstract=False)[-1]
|
|
doc_to_test['schema'] = 'example/foo/v1'
|
|
doc_to_test['metadata']['name'] = 'test_doc'
|
|
|
|
revision_id = self._create_revision(payload=[doc_to_test, data_schema])
|
|
|
|
# Validate that the validation was created and reports failure.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [
|
|
{'name': types.DECKHAND_SCHEMA_VALIDATION, 'status': 'failure'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
# Validate that both expected errors are present for validation.
|
|
expected_errors = [{
|
|
'error_section': {
|
|
'data': {'a': 'fail'},
|
|
'metadata': {'labels': {'global': 'global1'},
|
|
'storagePolicy': 'cleartext',
|
|
'layeringDefinition': {'abstract': False,
|
|
'layer': 'global'},
|
|
'name': doc_to_test['metadata']['name'],
|
|
'schema': doc_to_test['metadata']['schema']},
|
|
'schema': doc_to_test['schema']
|
|
},
|
|
'name': 'test_doc',
|
|
'layer': 'global',
|
|
'path': '.data',
|
|
'schema': 'example/foo/v1',
|
|
'message': "'b' is a required property",
|
|
'validation_schema': schema_to_use,
|
|
'schema_path': '.required'
|
|
}, {
|
|
'error_section': {'a': 'fail'},
|
|
'name': 'test_doc',
|
|
'layer': 'global',
|
|
'path': '.data.a',
|
|
'schema': 'example/foo/v1',
|
|
'message': "'fail' is not of type 'integer'",
|
|
'validation_schema': schema_to_use,
|
|
'schema_path': '.properties.a.type'
|
|
}]
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s/entries/0' % (
|
|
revision_id, types.DECKHAND_SCHEMA_VALIDATION),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
|
|
self.assertEqual('failure', body['status'])
|
|
self.assertEqual(sorted(expected_errors, key=lambda x: x['path']),
|
|
sorted(body['errors'], key=lambda x: x['path']))
|
|
|
|
def test_validation_with_registered_data_schema_expect_mixed(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@',
|
|
'deckhand:show_validation': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create a `DataSchema` against which the test document will be
|
|
# validated.
|
|
data_schema_factory = factories.DataSchemaFactory()
|
|
metadata_name = 'example/foo/v1'
|
|
schema_to_use = {
|
|
'$schema': 'http://json-schema.org/schema#',
|
|
'type': 'object',
|
|
'properties': {
|
|
'a': {
|
|
'type': 'integer' # Test doc will fail b/c of wrong type.
|
|
}
|
|
},
|
|
'required': ['a']
|
|
}
|
|
|
|
expected_errors = [{
|
|
'error_section': {'a': 'fail'},
|
|
'name': 'fail_doc',
|
|
'layer': 'global',
|
|
'path': '.data.a',
|
|
'schema': 'example/foo/v1',
|
|
'message': "'fail' is not of type 'integer'",
|
|
'validation_schema': schema_to_use,
|
|
'schema_path': '.properties.a.type'
|
|
}]
|
|
|
|
data_schema = data_schema_factory.gen_test(
|
|
metadata_name, data=schema_to_use)
|
|
|
|
# Create a document that passes validation and another that fails it.
|
|
doc_factory = factories.DocumentFactory(1, [1])
|
|
fail_doc = doc_factory.gen_test(
|
|
{'_GLOBAL_DATA_1_': {'data': {'a': 'fail'}}},
|
|
global_abstract=False)[-1]
|
|
fail_doc['schema'] = 'example/foo/v1'
|
|
fail_doc['metadata']['name'] = 'fail_doc'
|
|
|
|
pass_doc = copy.deepcopy(fail_doc)
|
|
pass_doc['metadata']['name'] = 'pass_doc'
|
|
pass_doc['data']['a'] = 5
|
|
|
|
revision_id = self._create_revision(
|
|
payload=[fail_doc, pass_doc, data_schema])
|
|
|
|
# Validate that the validation reports failure since `fail_doc`
|
|
# should've failed validation.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [
|
|
{'name': types.DECKHAND_SCHEMA_VALIDATION, 'status': 'failure'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s' % (
|
|
revision_id, types.DECKHAND_SCHEMA_VALIDATION),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 3,
|
|
'results': [{'id': 0, 'status': 'failure'}, # fail_doc failed.
|
|
{'id': 1, 'status': 'success'}, # DataSchema passed.
|
|
{'id': 2, 'status': 'success'}] # pass_doc succeeded.
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
# Validate that fail_doc validation failed for the expected reason.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s/entries/0' % (
|
|
revision_id, types.DECKHAND_SCHEMA_VALIDATION),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
|
|
self.assertIn('errors', body)
|
|
self.assertEqual(expected_errors, body['errors'])
|
|
|
|
def test_document_without_data_section_ingested(self):
|
|
"""Validate that a document without the data section is ingested
|
|
successfully.
|
|
"""
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
documents_factory = factories.DocumentFactory(1, [1])
|
|
document = documents_factory.gen_test({}, global_abstract=False)[-1]
|
|
del document['data']
|
|
|
|
data_schema_factory = factories.DataSchemaFactory()
|
|
data_schema = data_schema_factory.gen_test(document['schema'], {})
|
|
|
|
revision_id = self._create_revision(payload=[document, data_schema])
|
|
|
|
# Validate that the entry is present.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s' % (
|
|
revision_id, types.DECKHAND_SCHEMA_VALIDATION),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 2,
|
|
'results': [{'id': 0, 'status': 'success'}, # Document.
|
|
{'id': 1, 'status': 'success'}] # DataSchema.
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_validation_only_new_data_schema_registered(self):
|
|
"""Validate whether newly created DataSchemas replace old DataSchemas
|
|
when it comes to validation.
|
|
"""
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create 2 DataSchemas that will fail if they're used. These shouldn't
|
|
# be used for validation.
|
|
data_schema_factory = factories.DataSchemaFactory()
|
|
metadata_names = ['exampleA/Doc/v1', 'exampleB/Doc/v1']
|
|
schemas_to_use = [{
|
|
'$schema': 'http://json-schema.org/schema#',
|
|
'type': 'object',
|
|
'properties': {
|
|
'a': {
|
|
'type': 'integer'
|
|
}
|
|
},
|
|
'required': ['a'],
|
|
'additionalProperties': False
|
|
}] * 2
|
|
old_data_schemas = [
|
|
data_schema_factory.gen_test(
|
|
metadata_names[i], data=schemas_to_use[i])
|
|
for i in range(2)
|
|
]
|
|
# Save the DataSchemas in the first revision.
|
|
revision_id = self._create_revision(payload=old_data_schemas)
|
|
|
|
# Create 2 DataSchemas that will pass if they're used. These should
|
|
# be used for validation.
|
|
for schema_to_use in schemas_to_use:
|
|
schema_to_use['properties']['a']['type'] = 'string'
|
|
new_data_schemas = [
|
|
data_schema_factory.gen_test(
|
|
metadata_names[i], data=schemas_to_use[i])
|
|
for i in range(2)
|
|
]
|
|
doc_factory = factories.DocumentFactory(1, [1])
|
|
example1_doc = doc_factory.gen_test(
|
|
{'_GLOBAL_DATA_1_': {'data': {'a': 'whatever'}}},
|
|
global_abstract=False)[-1]
|
|
example1_doc['schema'] = metadata_names[0]
|
|
example2_doc = copy.deepcopy(example1_doc)
|
|
example2_doc['schema'] = metadata_names[1]
|
|
# Save the documents that will be validated alongside the DataSchemas
|
|
# that will be used to validate them.
|
|
revision_id = self._create_revision(
|
|
payload=[example1_doc, example2_doc] + new_data_schemas)
|
|
|
|
# Validate that the validation was created and succeeded: This means
|
|
# that the new DataSchemas were used, not the old ones.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [
|
|
{'name': types.DECKHAND_SCHEMA_VALIDATION, 'status': 'success'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
|
|
class TestValidationsControllerWithValidationPolicy(
|
|
BaseValidationsControllerTest):
|
|
|
|
def setUp(self):
|
|
super(TestValidationsControllerWithValidationPolicy, self).setUp()
|
|
self._monkey_patch_document_validation()
|
|
|
|
def test_validation_with_validation_policy_success(self):
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create a `ValidationPolicy` which is used to check whether a revision
|
|
# passed all the validations.
|
|
validation_policy = yaml.safe_load("""
|
|
---
|
|
schema: deckhand/ValidationPolicy/v1
|
|
metadata:
|
|
schema: metadata/Control/v1
|
|
name: site-deploy-ready
|
|
layeringDefinition:
|
|
abstract: true
|
|
layer: site
|
|
data:
|
|
validations:
|
|
- name: deckhand-schema-validation
|
|
...
|
|
""")
|
|
revision_id = self._create_revision(payload=[validation_policy])
|
|
|
|
# Validate that the validation was created and reports success.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [
|
|
{'name': 'deckhand-schema-validation', 'status': 'success'}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_with_validation_policy_external_validation(self):
|
|
"""Validate that a ValidationPolicy with an externally registered
|
|
validation that is successful passes.
|
|
"""
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:create_validation': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create a `ValidationPolicy` which expects two validations.
|
|
validation_policy = yaml.safe_load("""
|
|
---
|
|
schema: deckhand/ValidationPolicy/v1
|
|
metadata:
|
|
schema: metadata/Control/v1
|
|
name: site-deploy-ready
|
|
layeringDefinition:
|
|
abstract: true
|
|
layer: site
|
|
data:
|
|
validations:
|
|
- name: deckhand-schema-validation
|
|
- name: promenade-schema-validation
|
|
...
|
|
""")
|
|
revision_id = self._create_revision(payload=[validation_policy])
|
|
|
|
# Create the external validation for "promenade-schema-validation".
|
|
resp = self._create_validation(
|
|
revision_id, 'promenade-schema-validation',
|
|
VALIDATION_SUCCESS_RESULT)
|
|
self.assertEqual(201, resp.status_code)
|
|
|
|
# Validate that the validation was created and reports success.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 2,
|
|
'results': [
|
|
{'name': 'deckhand-schema-validation', 'status': 'success'},
|
|
{'name': 'promenade-schema-validation', 'status': 'success'}
|
|
]
|
|
}
|
|
body['results'] = sorted(body['results'], key=lambda x: x['name'])
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_with_multiple_validation_policy_external_validation(self):
|
|
"""Validate that two ValidationPolicy documents, one that references
|
|
the internal deckhand-schema-validation, and the other which requires
|
|
an externally registered validation, produces a successful validation
|
|
result.
|
|
"""
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:create_validation': '@',
|
|
'deckhand:list_validations': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create two `ValidationPolicy` documents.
|
|
validation_policies = yaml.safe_load_all("""
|
|
---
|
|
schema: deckhand/ValidationPolicy/v1
|
|
metadata:
|
|
schema: metadata/Control/v1
|
|
name: vp-1
|
|
layeringDefinition:
|
|
abstract: true
|
|
layer: site
|
|
data:
|
|
validations:
|
|
- name: deckhand-schema-validation
|
|
---
|
|
schema: deckhand/ValidationPolicy/v1
|
|
metadata:
|
|
schema: metadata/Control/v1
|
|
name: vp-2
|
|
layeringDefinition:
|
|
abstract: true
|
|
layer: site
|
|
data:
|
|
validations:
|
|
- name: promenade-schema-validation
|
|
...
|
|
""")
|
|
revision_id = self._create_revision(payload=validation_policies)
|
|
|
|
# Create the external validation for "promenade-schema-validation".
|
|
resp = self._create_validation(
|
|
revision_id, 'promenade-schema-validation',
|
|
VALIDATION_SUCCESS_RESULT)
|
|
self.assertEqual(201, resp.status_code)
|
|
|
|
# Validate that the validation was created and reports success.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 2,
|
|
'results': [
|
|
{'name': 'deckhand-schema-validation', 'status': 'success'},
|
|
{'name': 'promenade-schema-validation', 'status': 'success'}
|
|
]
|
|
}
|
|
body['results'] = sorted(body['results'], key=lambda x: x['name'])
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_with_validation_policy_missing_external_validation(self):
|
|
"""Validate that a ValidationPolicy with a missing externally
|
|
registered validation that is listed under the validations for the
|
|
ValidationPolicy defaults to "failure".
|
|
"""
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:list_validations': '@',
|
|
'deckhand:show_validation': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create a `ValidationPolicy` which expects two validations but do not
|
|
# create the validation for "promenade-schema-validation".
|
|
validation_policy = yaml.safe_load("""
|
|
---
|
|
schema: deckhand/ValidationPolicy/v1
|
|
metadata:
|
|
schema: metadata/Control/v1
|
|
name: site-deploy-ready
|
|
layeringDefinition:
|
|
abstract: true
|
|
layer: site
|
|
data:
|
|
validations:
|
|
- name: deckhand-schema-validation
|
|
- name: promenade-schema-validation
|
|
...
|
|
""")
|
|
revision_id = self._create_revision(payload=[validation_policy])
|
|
|
|
# Validate that the validation was created and that the missing one
|
|
# defaults to "failure".
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 2,
|
|
'results': [
|
|
{'name': 'deckhand-schema-validation', 'status': 'success'},
|
|
{'name': 'promenade-schema-validation', 'status': 'failure'}
|
|
]
|
|
}
|
|
body['results'] = sorted(body['results'], key=lambda x: x['name'])
|
|
self.assertEqual(expected_body, body)
|
|
|
|
# Validate that 'promenade-schema-validation' is 'failure' even though
|
|
# it was never externally registered.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s' % (
|
|
revision_id, 'promenade-schema-validation'),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [{'id': 0, 'status': 'failure'}]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
# Validate information explaining why 'promenade-schema-validation'
|
|
# failed is returned. Note that DH should be smart enough to say that
|
|
# it was never registered externally, which is why it's 'failure'.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s/entries/0' % (
|
|
revision_id, 'promenade-schema-validation'),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
|
|
expected_msg = ('The result for this validation was never externally '
|
|
'registered so its status defaulted to "failure".')
|
|
expected_body = {
|
|
'name': 'promenade-schema-validation',
|
|
'status': 'failure',
|
|
'createdAt': None,
|
|
'expiresAfter': None,
|
|
'errors': [{'message': expected_msg}]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
def test_with_validation_policy_extra_external_validation(self):
|
|
"""Validate that a ValidationPolicy with extra externally registered
|
|
validations that aren't listed under the validations for the
|
|
ValidationPolicy defaults to "ignored [{original_status}]".
|
|
"""
|
|
rules = {'deckhand:create_cleartext_documents': '@',
|
|
'deckhand:create_validation': '@',
|
|
'deckhand:list_validations': '@',
|
|
'deckhand:show_validation': '@'}
|
|
self.policy.set_rules(rules)
|
|
|
|
# Create a `ValidationPolicy` with only 1 validation.
|
|
validation_policy = yaml.safe_load("""
|
|
---
|
|
schema: deckhand/ValidationPolicy/v1
|
|
metadata:
|
|
schema: metadata/Control/v1
|
|
name: site-deploy-ready
|
|
layeringDefinition:
|
|
abstract: true
|
|
layer: site
|
|
data:
|
|
validations:
|
|
- name: deckhand-schema-validation
|
|
...
|
|
""")
|
|
|
|
def _do_test(validation_result, expected_status):
|
|
revision_id = self._create_revision(payload=[validation_policy])
|
|
|
|
# Register an extra validation not in the ValidationPolicy.
|
|
resp = self._create_validation(
|
|
revision_id, 'promenade-schema-validation',
|
|
validation_result)
|
|
self.assertEqual(201, resp.status_code)
|
|
|
|
# Validate that the extra validation is ignored.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations' % revision_id,
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 2,
|
|
'results': [
|
|
{'name': 'deckhand-schema-validation',
|
|
'status': 'success'},
|
|
{'name': 'promenade-schema-validation',
|
|
'status': 'ignored [%s]' % expected_status}
|
|
]
|
|
}
|
|
body['results'] = sorted(body['results'], key=lambda x: x['name'])
|
|
self.assertEqual(expected_body, body)
|
|
|
|
# Validate that 'promenade-schema-validation' is
|
|
# 'ignored [expected_status]'.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s' % (
|
|
revision_id, 'promenade-schema-validation'),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
expected_body = {
|
|
'count': 1,
|
|
'results': [
|
|
{'id': 0, 'status': 'ignored [%s]' % expected_status}
|
|
]
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
# Validate information explaining why 'promenade-schema-validation'
|
|
# is ignored is returned.
|
|
resp = self.app.simulate_get(
|
|
'/api/v1.0/revisions/%s/validations/%s/entries/0' % (
|
|
revision_id, 'promenade-schema-validation'),
|
|
headers={'Content-Type': 'application/x-yaml'})
|
|
self.assertEqual(200, resp.status_code)
|
|
body = yaml.safe_load(resp.text)
|
|
|
|
expected_msg = ('The result for this validation was externally '
|
|
'registered but has been ignored because it is not'
|
|
' found in the validations for ValidationPolicy '
|
|
'[%s, %s] %s: %s.' % (
|
|
validation_policy['schema'],
|
|
validation_policy['metadata'][
|
|
'layeringDefinition']['layer'],
|
|
validation_policy['metadata']['name'],
|
|
types.DECKHAND_SCHEMA_VALIDATION))
|
|
|
|
expected_errors = []
|
|
if expected_status == 'failure':
|
|
expected_errors.extend(
|
|
yaml.safe_load(VALIDATION_FAILURE_RESULT)['errors'])
|
|
expected_errors.append({'message': expected_msg})
|
|
|
|
expected_body = {
|
|
'name': 'promenade-schema-validation',
|
|
'status': 'ignored [%s]' % expected_status,
|
|
'createdAt': None,
|
|
'expiresAfter': None,
|
|
'errors': expected_errors
|
|
}
|
|
self.assertEqual(expected_body, body)
|
|
|
|
_do_test(VALIDATION_SUCCESS_RESULT, 'success')
|
|
_do_test(VALIDATION_FAILURE_RESULT, 'failure')
|