Put, post operations implemented for environments
Implementation of environment operations moved to library package. Code for searching objects by ids and names extracted to the library function. Exceptions propagation added to example config. It is useful for testing and troubleshooting to have error message not only in logs but in the API response too. Tuningbox errors hierarchy added to the project. Change-Id: Ic2fd3c3c17409723bfa3cfff1c0bb18f3a65f0d7
This commit is contained in:
parent
e6d6e58bcf
commit
8f5bf1883d
@ -11,6 +11,7 @@
|
||||
# under the License.
|
||||
|
||||
LOG_LEVEL = 'DEBUG'
|
||||
PROPAGATE_EXCEPTIONS = True
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = \
|
||||
'postgresql://tuningbox:tuningbox@localhost/tuningbox'
|
||||
|
@ -15,24 +15,33 @@ import itertools
|
||||
|
||||
import flask
|
||||
import flask_restful
|
||||
from flask_restful import fields
|
||||
from sqlalchemy import exc as sa_exc
|
||||
from werkzeug import exceptions
|
||||
|
||||
from tuning_box import converters
|
||||
from tuning_box import db
|
||||
from tuning_box import errors
|
||||
from tuning_box.library import components
|
||||
from tuning_box.library import environments
|
||||
from tuning_box import logger
|
||||
from tuning_box.middleware import keystone
|
||||
|
||||
# These handlers work if PROPAGATE_EXCEPTIONS is off (non-Nailgun case)
|
||||
api_errors = {
|
||||
'IntegrityError': {'status': 409}, # sqlalchemy IntegrityError
|
||||
'TuningboxIntegrityError': {'status': 409},
|
||||
'TuningboxNotFound': {'status': 404}
|
||||
}
|
||||
api = flask_restful.Api(errors=api_errors)
|
||||
|
||||
api.add_resource(components.ComponentsCollection, '/components')
|
||||
api.add_resource(components.Component, '/components/<int:component_id>')
|
||||
api.add_resource(environments.EnvironmentsCollection, '/environments')
|
||||
api.add_resource(
|
||||
environments.Environment,
|
||||
'/environments/<int:environment_id>', # Backward compatibility support
|
||||
'/environment/<int:environment_id>'
|
||||
)
|
||||
|
||||
|
||||
def with_transaction(f):
|
||||
@ -44,55 +53,6 @@ def with_transaction(f):
|
||||
return inner
|
||||
|
||||
|
||||
environment_fields = {
|
||||
'id': fields.Integer,
|
||||
'components': fields.List(fields.Integer(attribute='id')),
|
||||
'hierarchy_levels': fields.List(fields.String(attribute='name')),
|
||||
}
|
||||
|
||||
|
||||
@api.resource('/environments')
|
||||
class EnvironmentsCollection(flask_restful.Resource):
|
||||
method_decorators = [flask_restful.marshal_with(environment_fields)]
|
||||
|
||||
def get(self):
|
||||
return db.Environment.query.all()
|
||||
|
||||
@with_transaction
|
||||
def post(self):
|
||||
component_ids = flask.request.json['components']
|
||||
# TODO(yorik-sar): verify that resource names do not clash
|
||||
components = [db.Component.query.get_by_id_or_name(i)
|
||||
for i in component_ids]
|
||||
|
||||
hierarchy_levels = []
|
||||
level = None
|
||||
for name in flask.request.json['hierarchy_levels']:
|
||||
level = db.EnvironmentHierarchyLevel(name=name, parent=level)
|
||||
hierarchy_levels.append(level)
|
||||
|
||||
environment = db.Environment(components=components,
|
||||
hierarchy_levels=hierarchy_levels)
|
||||
if 'id' in flask.request.json:
|
||||
environment.id = flask.request.json['id']
|
||||
db.db.session.add(environment)
|
||||
return environment, 201
|
||||
|
||||
|
||||
@api.resource('/environments/<int:environment_id>')
|
||||
class Environment(flask_restful.Resource):
|
||||
method_decorators = [flask_restful.marshal_with(environment_fields)]
|
||||
|
||||
def get(self, environment_id):
|
||||
return db.Environment.query.get_or_404(environment_id)
|
||||
|
||||
@with_transaction
|
||||
def delete(self, environment_id):
|
||||
environment = db.Environment.query.get_or_404(environment_id)
|
||||
db.db.session.delete(environment)
|
||||
return None, 204
|
||||
|
||||
|
||||
def iter_environment_level_values(environment, levels):
|
||||
env_levels = db.EnvironmentHierarchyLevel.get_for_environment(environment)
|
||||
level_pairs = zip(env_levels, levels)
|
||||
@ -250,6 +210,12 @@ def handle_integrity_error(exc):
|
||||
return response
|
||||
|
||||
|
||||
def handle_object_not_found(exc):
|
||||
response = flask.jsonify(msg=exc.args[0])
|
||||
response.status_code = 404
|
||||
return response
|
||||
|
||||
|
||||
def build_app(configure_logging=True, with_keystone=True):
|
||||
app = flask.Flask(__name__)
|
||||
app.url_map.converters.update(converters.ALL)
|
||||
@ -259,6 +225,10 @@ def build_app(configure_logging=True, with_keystone=True):
|
||||
app.config.from_envvar('TUNINGBOX_SETTINGS', silent=True)
|
||||
# These handlers work if PROPAGATE_EXCEPTIONS is on (Nailgun case)
|
||||
app.register_error_handler(sa_exc.IntegrityError, handle_integrity_error)
|
||||
app.register_error_handler(errors.TuningboxIntegrityError,
|
||||
handle_integrity_error)
|
||||
app.register_error_handler(errors.TuningboxNotFound,
|
||||
handle_object_not_found)
|
||||
db.db.init_app(app)
|
||||
if configure_logging:
|
||||
log_level = app.config.get('LOG_LEVEL', 'INFO')
|
||||
|
@ -47,12 +47,12 @@ def fk(cls, **kwargs):
|
||||
|
||||
|
||||
class BaseQuery(flask_sqlalchemy.BaseQuery):
|
||||
def get_by_id_or_name(self, id_or_name):
|
||||
def get_by_id_or_name(self, id_or_name, fail_on_none=True):
|
||||
if isinstance(id_or_name, int):
|
||||
result = self.get(id_or_name)
|
||||
else:
|
||||
result = self.filter_by(name=id_or_name).one_or_none()
|
||||
if result is None:
|
||||
if fail_on_none and result is None:
|
||||
flask.abort(404)
|
||||
return result
|
||||
|
||||
|
23
tuning_box/errors.py
Normal file
23
tuning_box/errors.py
Normal file
@ -0,0 +1,23 @@
|
||||
# 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.
|
||||
|
||||
|
||||
class BaseTuningboxError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class TuningboxIntegrityError(BaseTuningboxError):
|
||||
pass
|
||||
|
||||
|
||||
class TuningboxNotFound(BaseTuningboxError):
|
||||
pass
|
@ -0,0 +1,44 @@
|
||||
# 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.
|
||||
|
||||
from tuning_box import errors
|
||||
|
||||
|
||||
def load_objects(model, ids):
|
||||
if ids is None:
|
||||
return None
|
||||
result = []
|
||||
for obj_id in ids:
|
||||
obj = model.query.filter_by(id=obj_id).one_or_none()
|
||||
if obj is None:
|
||||
raise errors.TuningboxNotFound(
|
||||
"{0} not found by identifier: "
|
||||
"{1}".format(model.__tablename__, obj_id)
|
||||
)
|
||||
result.append(obj)
|
||||
return result
|
||||
|
||||
|
||||
def load_objects_by_id_or_name(model, identifiers):
|
||||
if identifiers is None:
|
||||
return None
|
||||
result = []
|
||||
for identifier in identifiers:
|
||||
obj = model.query.get_by_id_or_name(
|
||||
identifier, fail_on_none=False)
|
||||
if obj is None:
|
||||
raise errors.TuningboxNotFound(
|
||||
"{0} not found by identifier: "
|
||||
"{1}".format(model.__tablename__, identifier)
|
||||
)
|
||||
result.append(obj)
|
||||
return result
|
@ -16,7 +16,7 @@ import flask_restful
|
||||
from flask_restful import fields
|
||||
|
||||
from tuning_box import db
|
||||
|
||||
from tuning_box import library
|
||||
|
||||
resource_definition_fields = {
|
||||
'id': fields.Integer,
|
||||
@ -64,14 +64,8 @@ class Component(flask_restful.Resource):
|
||||
component.name = update_by.get('name', component.name)
|
||||
resource_definitions = update_by.get('resource_definitions')
|
||||
if resource_definitions is not None:
|
||||
resources = []
|
||||
for resource_data in resource_definitions:
|
||||
resource = db.ResourceDefinition.query.filter_by(
|
||||
id=resource_data.get('id')
|
||||
).one()
|
||||
resource.component_id = component.id
|
||||
db.db.session.add(resource)
|
||||
resources.append(resource)
|
||||
ids = [data['id'] for data in resource_definitions]
|
||||
resources = library.load_objects(db.ResourceDefinition, ids)
|
||||
component.resource_definitions = resources
|
||||
|
||||
def put(self, component_id):
|
||||
|
115
tuning_box/library/environments.py
Normal file
115
tuning_box/library/environments.py
Normal file
@ -0,0 +1,115 @@
|
||||
# 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 flask
|
||||
import flask_restful
|
||||
from flask_restful import fields
|
||||
|
||||
from tuning_box import db
|
||||
from tuning_box import errors
|
||||
from tuning_box import library
|
||||
|
||||
environment_fields = {
|
||||
'id': fields.Integer,
|
||||
'components': fields.List(fields.Integer(attribute='id')),
|
||||
'hierarchy_levels': fields.List(fields.String(attribute='name')),
|
||||
}
|
||||
|
||||
|
||||
class EnvironmentsCollection(flask_restful.Resource):
|
||||
method_decorators = [flask_restful.marshal_with(environment_fields)]
|
||||
|
||||
def get(self):
|
||||
return db.Environment.query.all()
|
||||
|
||||
def _check_components(self, components):
|
||||
identities = set()
|
||||
duplicates = set()
|
||||
id_names = ('id', 'name')
|
||||
for component in components:
|
||||
for id_name in id_names:
|
||||
value = getattr(component, id_name)
|
||||
|
||||
if value not in identities:
|
||||
identities.add(value)
|
||||
else:
|
||||
duplicates.add(value)
|
||||
if duplicates:
|
||||
raise errors.TuningboxIntegrityError(
|
||||
"Components duplicates: {0}".format(duplicates))
|
||||
|
||||
@db.with_transaction
|
||||
def post(self):
|
||||
component_ids = flask.request.json['components']
|
||||
components = [db.Component.query.get_by_id_or_name(i)
|
||||
for i in component_ids]
|
||||
self._check_components(components)
|
||||
|
||||
hierarchy_levels = []
|
||||
level = None
|
||||
for name in flask.request.json['hierarchy_levels']:
|
||||
level = db.EnvironmentHierarchyLevel(name=name, parent=level)
|
||||
hierarchy_levels.append(level)
|
||||
|
||||
environment = db.Environment(components=components,
|
||||
hierarchy_levels=hierarchy_levels)
|
||||
if 'id' in flask.request.json:
|
||||
environment.id = flask.request.json['id']
|
||||
db.db.session.add(environment)
|
||||
return environment, 201
|
||||
|
||||
|
||||
class Environment(flask_restful.Resource):
|
||||
method_decorators = [flask_restful.marshal_with(environment_fields)]
|
||||
|
||||
def get(self, environment_id):
|
||||
return db.Environment.query.get_or_404(environment_id)
|
||||
|
||||
def _update_components(self, environment, components):
|
||||
if components is not None:
|
||||
new_components = library.load_objects_by_id_or_name(
|
||||
db.Component, components)
|
||||
environment.components = new_components
|
||||
|
||||
def _update_hierarchy_levels(self, environment, hierarchy_levels):
|
||||
if hierarchy_levels is not None:
|
||||
new_hierarchy_levels = library.load_objects_by_id_or_name(
|
||||
db.EnvironmentHierarchyLevel, hierarchy_levels)
|
||||
parent = None
|
||||
for level in new_hierarchy_levels:
|
||||
level.parent = parent
|
||||
parent = level
|
||||
environment.hierarchy_levels = new_hierarchy_levels
|
||||
|
||||
@db.with_transaction
|
||||
def _perform_update(self, environment_id):
|
||||
environment = db.Environment.query.get_or_404(environment_id)
|
||||
update_by = flask.request.json
|
||||
|
||||
components = update_by.get('components')
|
||||
self._update_components(environment, components)
|
||||
|
||||
hierarchy_levels = update_by.get('hierarchy_levels')
|
||||
self._update_hierarchy_levels(environment, hierarchy_levels)
|
||||
|
||||
def put(self, environment_id):
|
||||
return self.patch(environment_id)
|
||||
|
||||
def patch(self, env_id):
|
||||
self._perform_update(env_id)
|
||||
return None, 204
|
||||
|
||||
@db.with_transaction
|
||||
def delete(self, environment_id):
|
||||
environment = db.Environment.query.get_or_404(environment_id)
|
||||
db.db.session.delete(environment)
|
||||
return None, 204
|
@ -10,6 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
from tuning_box import db
|
||||
from tuning_box.library import components
|
||||
@ -156,6 +157,21 @@ class TestComponents(BaseTest):
|
||||
self.assertItemsEqual(initial_data['resource_definitions'],
|
||||
actual_component['resource_definitions'])
|
||||
|
||||
def test_put_component_resource_not_found(self):
|
||||
self._fixture()
|
||||
component_url = '/components/7'
|
||||
initial_data = self._component_json
|
||||
|
||||
resource_definition = copy.deepcopy(
|
||||
initial_data['resource_definitions'][0])
|
||||
resource_definition['id'] = None
|
||||
|
||||
res = self.client.put(
|
||||
component_url,
|
||||
data={'resource_definitions': [resource_definition]}
|
||||
)
|
||||
self.assertEqual(404, res.status_code)
|
||||
|
||||
def test_put_component_ignore_changing_id(self):
|
||||
self._fixture()
|
||||
component_url = '/components/7'
|
||||
|
260
tuning_box/tests/library/test_environments.py
Normal file
260
tuning_box/tests/library/test_environments.py
Normal file
@ -0,0 +1,260 @@
|
||||
# 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.
|
||||
|
||||
from tuning_box import db
|
||||
from tuning_box import library
|
||||
from tuning_box.library import environments
|
||||
from tuning_box.tests.test_app import BaseTest
|
||||
|
||||
|
||||
class TestEnvironments(BaseTest):
|
||||
|
||||
def test_get_environments_empty(self):
|
||||
res = self.client.get('/environments')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.json, [])
|
||||
|
||||
def test_get_environments(self):
|
||||
self._fixture()
|
||||
res = self.client.get('/environments')
|
||||
self.assertEqual(200, res.status_code)
|
||||
self.assertEqual(1, len(res.json))
|
||||
self.assertItemsEqual(
|
||||
{'id': 9, 'components': [7], 'hierarchy_levels': ['lvl1', 'lvl2']},
|
||||
res.json[0]
|
||||
)
|
||||
|
||||
def test_get_one_environment(self):
|
||||
self._fixture()
|
||||
res = self.client.get('/environments/9')
|
||||
self.assertEqual(200, res.status_code)
|
||||
self.assertItemsEqual(
|
||||
{'id': 9, 'components': [7], 'hierarchy_levels': ['lvl1', 'lvl2']},
|
||||
res.json
|
||||
)
|
||||
|
||||
def test_get_one_environment_404(self):
|
||||
res = self.client.get('/environments/9')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_post_environment(self):
|
||||
self._fixture()
|
||||
json = {'components': [7], 'hierarchy_levels': ['lvla', 'lvlb']}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
json['id'] = res.json['id']
|
||||
self.assertItemsEqual(json, res.json)
|
||||
self._assert_db_effect(
|
||||
db.Environment, res.json['id'],
|
||||
environments.environment_fields, json)
|
||||
|
||||
def test_post_environment_preserve_id(self):
|
||||
self._fixture()
|
||||
json = {
|
||||
'id': 42,
|
||||
'components': [7],
|
||||
'hierarchy_levels': ['lvla', 'lvlb'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(201, res.status_code)
|
||||
self.assertItemsEqual(json, res.json)
|
||||
self._assert_db_effect(
|
||||
db.Environment, 42, environments.environment_fields, json)
|
||||
|
||||
def test_post_environment_preserve_id_conflict(self):
|
||||
self._fixture()
|
||||
json = {
|
||||
'id': 9,
|
||||
'components': [7],
|
||||
'hierarchy_levels': ['lvla', 'lvlb'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 409)
|
||||
|
||||
def test_post_environment_preserve_id_conflict_propagate_exc(self):
|
||||
self.app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||
self._fixture()
|
||||
json = {
|
||||
'id': 9,
|
||||
'components': [7],
|
||||
'hierarchy_levels': ['lvla', 'lvlb'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 409)
|
||||
|
||||
def test_post_environment_by_component_name(self):
|
||||
self._fixture()
|
||||
json = {
|
||||
'components': ['component1'],
|
||||
'hierarchy_levels': ['lvla', 'lvlb'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
json['id'] = res.json['id']
|
||||
json['components'] = [7]
|
||||
self.assertItemsEqual(json, res.json)
|
||||
self._assert_db_effect(
|
||||
db.Environment, res.json['id'],
|
||||
environments.environment_fields, json)
|
||||
|
||||
def test_post_components_duplication(self):
|
||||
self._fixture()
|
||||
json = {
|
||||
'components': ['component1', 7],
|
||||
'hierarchy_levels': ['lvl'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(409, res.status_code)
|
||||
|
||||
def test_post_components_no_duplication(self):
|
||||
self._fixture()
|
||||
components_url = '/components'
|
||||
res = self.client.get(components_url)
|
||||
self.assertEqual(200, res.status_code)
|
||||
component = res.json[0]
|
||||
|
||||
# Creating component with name equal to id of existed component
|
||||
res = self.client.post(
|
||||
components_url,
|
||||
data={
|
||||
'name': component['id'],
|
||||
'resource_definitions': []
|
||||
}
|
||||
)
|
||||
self.assertEqual(201, res.status_code)
|
||||
new_component = res.json
|
||||
|
||||
# Checking no components duplication detected
|
||||
json = {
|
||||
'components': [component['id'], new_component['name']],
|
||||
'hierarchy_levels': ['lvl'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(201, res.status_code)
|
||||
|
||||
def test_post_environment_404(self):
|
||||
self._fixture()
|
||||
json = {'components': [8], 'hierarchy_levels': ['lvla', 'lvlb']}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 404)
|
||||
self._assert_not_in_db(db.Environment, 10)
|
||||
|
||||
def test_post_environment_by_component_name_404(self):
|
||||
self._fixture()
|
||||
json = {
|
||||
'components': ['component2'],
|
||||
'hierarchy_levels': ['lvla', 'lvlb'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 404)
|
||||
self._assert_not_in_db(db.Environment, 10)
|
||||
|
||||
def test_delete_environment(self):
|
||||
self._fixture()
|
||||
res = self.client.delete('/environments/9')
|
||||
self.assertEqual(res.status_code, 204)
|
||||
self.assertEqual(res.data, b'')
|
||||
self._assert_not_in_db(db.Environment, 9)
|
||||
|
||||
def test_delete_environment_404(self):
|
||||
res = self.client.delete('/environments/9')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_put_environment_404(self):
|
||||
res = self.client.put('/environments/7')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_put_environment_components(self):
|
||||
self._fixture()
|
||||
environment_url = '/environment/9'
|
||||
initial = self.client.get(environment_url).json
|
||||
|
||||
# Updating components
|
||||
res = self.client.put(environment_url,
|
||||
data={'components': []})
|
||||
self.assertEqual(204, res.status_code)
|
||||
actual = self.client.get(environment_url).json
|
||||
self.assertEqual([], actual['components'])
|
||||
|
||||
# Restoring components
|
||||
res = self.client.put(
|
||||
environment_url,
|
||||
data={'components': initial['components']}
|
||||
)
|
||||
self.assertEqual(204, res.status_code)
|
||||
actual = self.client.get(environment_url).json
|
||||
self.assertItemsEqual(initial, actual)
|
||||
|
||||
def test_put_environment_component_not_found(self):
|
||||
self._fixture()
|
||||
environment_url = '/environment/9'
|
||||
res = self.client.put(
|
||||
environment_url,
|
||||
data={'components': [None]}
|
||||
)
|
||||
self.assertEqual(404, res.status_code)
|
||||
|
||||
def check_hierarchy_levels(self, hierarchy_levels_names):
|
||||
with self.app.app_context():
|
||||
hierarchy_levels = library.load_objects_by_id_or_name(
|
||||
db.EnvironmentHierarchyLevel, hierarchy_levels_names)
|
||||
parent_id = None
|
||||
for level in hierarchy_levels:
|
||||
self.assertEqual(parent_id, level.parent_id)
|
||||
parent_id = level.id
|
||||
|
||||
def test_put_environment_hierarchy_levels(self):
|
||||
self._fixture()
|
||||
environment_url = '/environment/9'
|
||||
initial = self.client.get(environment_url).json
|
||||
|
||||
# Updating hierarchy levels
|
||||
res = self.client.put(environment_url,
|
||||
data={'hierarchy_levels': []})
|
||||
self.assertEqual(204, res.status_code)
|
||||
actual = self.client.get(environment_url).json
|
||||
self.assertEqual([], actual['hierarchy_levels'])
|
||||
|
||||
# Restoring levels
|
||||
res = self.client.put(
|
||||
environment_url,
|
||||
data={'hierarchy_levels': initial['hierarchy_levels']}
|
||||
)
|
||||
self.assertEqual(204, res.status_code)
|
||||
actual = self.client.get(environment_url).json
|
||||
self.assertItemsEqual(initial, actual)
|
||||
self.check_hierarchy_levels(actual['hierarchy_levels'])
|
||||
|
||||
def test_put_environment_hierarchy_levels_remove_level(self):
|
||||
self._fixture()
|
||||
environment_url = '/environment/9'
|
||||
initial = self.client.get(environment_url).json
|
||||
expected_levels = initial['hierarchy_levels'][1:]
|
||||
|
||||
# Updating hierarchy levels
|
||||
res = self.client.put(
|
||||
environment_url,
|
||||
data={'hierarchy_levels': expected_levels}
|
||||
)
|
||||
self.assertEqual(204, res.status_code)
|
||||
actual = self.client.get(environment_url).json
|
||||
self.assertEqual(expected_levels, actual['hierarchy_levels'])
|
||||
self.check_hierarchy_levels(actual['hierarchy_levels'])
|
||||
|
||||
def test_put_environment_level_not_found(self):
|
||||
self._fixture()
|
||||
environment_url = '/environment/9'
|
||||
res = self.client.put(
|
||||
environment_url,
|
||||
data={'hierarchy_levels': [None]}
|
||||
)
|
||||
self.assertEqual(404, res.status_code)
|
@ -88,120 +88,6 @@ class BaseTest(base.TestCase):
|
||||
|
||||
class TestApp(BaseTest):
|
||||
|
||||
def test_get_environments_empty(self):
|
||||
res = self.client.get('/environments')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.json, [])
|
||||
|
||||
def test_get_environments(self):
|
||||
self._fixture()
|
||||
res = self.client.get('/environments')
|
||||
self.assertEqual(200, res.status_code)
|
||||
self.assertEqual(1, len(res.json))
|
||||
self.assertItemsEqual(
|
||||
{'id': 9, 'components': [7], 'hierarchy_levels': ['lvl1', 'lvl2']},
|
||||
res.json[0]
|
||||
)
|
||||
|
||||
def test_get_one_environment(self):
|
||||
self._fixture()
|
||||
res = self.client.get('/environments/9')
|
||||
self.assertEqual(200, res.status_code)
|
||||
self.assertItemsEqual(
|
||||
{'id': 9, 'components': [7], 'hierarchy_levels': ['lvl1', 'lvl2']},
|
||||
res.json
|
||||
)
|
||||
|
||||
def test_get_one_environment_404(self):
|
||||
res = self.client.get('/environments/9')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_post_environment(self):
|
||||
self._fixture()
|
||||
json = {'components': [7], 'hierarchy_levels': ['lvla', 'lvlb']}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
json['id'] = res.json['id']
|
||||
self.assertItemsEqual(json, res.json)
|
||||
self._assert_db_effect(
|
||||
db.Environment, res.json['id'], app.environment_fields, json)
|
||||
|
||||
def test_post_environment_preserve_id(self):
|
||||
self._fixture()
|
||||
json = {
|
||||
'id': 42,
|
||||
'components': [7],
|
||||
'hierarchy_levels': ['lvla', 'lvlb'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(201, res.status_code)
|
||||
self.assertItemsEqual(json, res.json)
|
||||
self._assert_db_effect(
|
||||
db.Environment, 42, app.environment_fields, json)
|
||||
|
||||
def test_post_environment_preserve_id_conflict(self):
|
||||
self._fixture()
|
||||
json = {
|
||||
'id': 9,
|
||||
'components': [7],
|
||||
'hierarchy_levels': ['lvla', 'lvlb'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 409)
|
||||
|
||||
def test_post_environment_preserve_id_conflict_propagate_exc(self):
|
||||
self.app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||
self._fixture()
|
||||
json = {
|
||||
'id': 9,
|
||||
'components': [7],
|
||||
'hierarchy_levels': ['lvla', 'lvlb'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 409)
|
||||
|
||||
def test_post_environment_by_component_name(self):
|
||||
self._fixture()
|
||||
json = {
|
||||
'components': ['component1'],
|
||||
'hierarchy_levels': ['lvla', 'lvlb'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
json['id'] = res.json['id']
|
||||
json['components'] = [7]
|
||||
self.assertItemsEqual(json, res.json)
|
||||
self._assert_db_effect(
|
||||
db.Environment, res.json['id'], app.environment_fields, json)
|
||||
|
||||
def test_post_environment_404(self):
|
||||
self._fixture()
|
||||
json = {'components': [8], 'hierarchy_levels': ['lvla', 'lvlb']}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 404)
|
||||
self._assert_not_in_db(db.Environment, 10)
|
||||
|
||||
def test_post_environment_by_component_name_404(self):
|
||||
self._fixture()
|
||||
json = {
|
||||
'components': ['component2'],
|
||||
'hierarchy_levels': ['lvla', 'lvlb'],
|
||||
}
|
||||
res = self.client.post('/environments', data=json)
|
||||
self.assertEqual(res.status_code, 404)
|
||||
self._assert_not_in_db(db.Environment, 10)
|
||||
|
||||
def test_delete_environment(self):
|
||||
self._fixture()
|
||||
res = self.client.delete('/environments/9')
|
||||
self.assertEqual(res.status_code, 204)
|
||||
self.assertEqual(res.data, b'')
|
||||
self._assert_not_in_db(db.Environment, 9)
|
||||
|
||||
def test_delete_environment_404(self):
|
||||
res = self.client.delete('/environments/9')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_get_environment_level_value_root(self):
|
||||
self._fixture()
|
||||
with self.app.app_context(), db.db.session.begin():
|
||||
|
Loading…
x
Reference in New Issue
Block a user