# Copyright (c) 2015 Mirantis, Inc.
#
# 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 unittest
import uuid

import mock
from oslo_serialization import jsonutils
import pkg_resources
import requests

from glance.api.glare.v0_1 import glare
from glance.api.glare.v0_1 import router
from glance.common.glare import definitions
from glance.common.glare import loader
from glance.common import wsgi
from glance.tests import functional


class Artifact(definitions.ArtifactType):
    __type_name__ = "WithProps"
    prop1 = definitions.String()
    prop2 = definitions.Integer()
    prop_list = definitions.Array(item_type=definitions.Integer())
    tuple_prop = definitions.Array(item_type=[definitions.Integer(),
                                              definitions.Boolean()])
    dict_prop = definitions.Dict(properties={
        "foo": definitions.String(),
        "bar_list": definitions.Array(definitions.Integer())})
    dict_prop_strval = definitions.Dict(properties=definitions.String())
    depends_on = definitions.ArtifactReference()
    depends_on_list = definitions.ArtifactReferenceList()


class ArtifactNoProps(definitions.ArtifactType):
    __type_name__ = "NoProp"


class ArtifactNoProps1(definitions.ArtifactType):
    __type_name__ = "NoProp"
    __type_version__ = "0.5"


class ArtifactWithBlob(definitions.ArtifactType):
    __type_name__ = "WithBlob"
    blob1 = definitions.BinaryObject()
    blob_list = definitions.BinaryObjectList()


def _create_resource():
    plugins = None
    mock_this = 'stevedore.extension.ExtensionManager._find_entry_points'
    with mock.patch(mock_this) as fep:
        path = 'glance.tests.functional.glare.test_glare'
        fep.return_value = [
            pkg_resources.EntryPoint.parse('WithProps=%s:Artifact' % path),
            pkg_resources.EntryPoint.parse(
                'NoProp=%s:ArtifactNoProps' % path),
            pkg_resources.EntryPoint.parse(
                'NoProp=%s:ArtifactNoProps1' % path),
            pkg_resources.EntryPoint.parse(
                'WithBlob=%s:ArtifactWithBlob' % path)
        ]
        plugins = loader.ArtifactsPluginLoader('glance.artifacts.types')
    deserializer = glare.RequestDeserializer(plugins=plugins)
    serializer = glare.ResponseSerializer()
    controller = glare.ArtifactsController(plugins=plugins)
    return wsgi.Resource(controller, deserializer, serializer)


class TestRouter(router.API):
    def _get_artifacts_resource(self):
        return _create_resource()


class TestArtifacts(functional.FunctionalTest):

    users = {
        'user1': {
            'id': str(uuid.uuid4()),
            'tenant_id': str(uuid.uuid4()),
            'token': str(uuid.uuid4()),
            'role': 'member'
        },
        'user2': {
            'id': str(uuid.uuid4()),
            'tenant_id': str(uuid.uuid4()),
            'token': str(uuid.uuid4()),
            'role': 'member'
        },
        'admin': {
            'id': str(uuid.uuid4()),
            'tenant_id': str(uuid.uuid4()),
            'token': str(uuid.uuid4()),
            'role': 'admin'
        }
    }

    def setUp(self):
        super(TestArtifacts, self).setUp()
        self._set_user('user1')
        self.api_server.server_name = 'glare'
        self.api_server.server_module = 'glance.cmd.glare'
        self.api_server.deployment_flavor = 'noauth'
        self.start_servers(**self.__dict__.copy())

    def tearDown(self):
        self.stop_servers()
        self._reset_database(self.api_server.sql_connection)
        super(TestArtifacts, self).tearDown()

    def _url(self, path):
        return 'http://127.0.0.1:%d/v0.1/artifacts%s' % (self.api_port, path)

    def _set_user(self, username):
        if username not in self.users:
            raise KeyError
        self.current_user = username

    def _headers(self, custom_headers=None):
        base_headers = {
            'X-Identity-Status': 'Confirmed',
            'X-Auth-Token': self.users[self.current_user]['token'],
            'X-User-Id': self.users[self.current_user]['id'],
            'X-Tenant-Id': self.users[self.current_user]['tenant_id'],
            'X-Roles': self.users[self.current_user]['role'],
        }
        base_headers.update(custom_headers or {})
        return base_headers

    def start_servers(self, **kwargs):
        # noqa
        new_paste_conf_base = """[pipeline:glare-api]
pipeline = versionnegotiation unauthenticated-context rootapp

[pipeline:glare-api-fakeauth]
pipeline = versionnegotiation fakeauth context rootapp

[pipeline:glare-api-noauth]
pipeline = versionnegotiation context rootapp

[composite:rootapp]
paste.composite_factory = glance.api:root_app_factory
/: apiversions
/v0.1: glareapi

[app:apiversions]
paste.app_factory = glance.api.glare.versions:create_resource

[app:glareapi]
paste.app_factory =
   glance.tests.functional.glare.test_glare:TestRouter.factory

[filter:versionnegotiation]
paste.filter_factory =
 glance.api.middleware.version_negotiation:
   GlareVersionNegotiationFilter.factory

[filter:context]
paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory

[filter:unauthenticated-context]
paste.filter_factory =
 glance.api.middleware.context:UnauthenticatedContextMiddleware.factory

[filter:fakeauth]
paste.filter_factory = glance.tests.utils:FakeAuthMiddleware.factory
"""
        self.cleanup()
        self.api_server.paste_conf_base = new_paste_conf_base
        super(TestArtifacts, self).start_servers(**kwargs)

    def _create_artifact(self, type_name, type_version='1.0', data=None,
                         status=201):
        # create an artifact first
        artifact_data = data or {'name': 'artifact-1',
                                 'version': '12'}
        return self._check_artifact_post('/%s/v%s/drafts' % (type_name,
                                                             type_version),
                                         artifact_data, status=status)

    def _check_artifact_method(self, method, url, data=None, status=200,
                               headers=None):
        if not headers:
            headers = self._headers()
        else:
            headers = self._headers(headers)
        headers.setdefault("Content-Type", "application/json")
        if 'application/json' in headers['Content-Type']:
            data = jsonutils.dumps(data)
        response = getattr(requests, method)(self._url(url), headers=headers,
                                             data=data)
        self.assertEqual(status, response.status_code)
        if status >= 400:
            return response.text
        if "application/json" in response.headers["content-type"]:
            return jsonutils.loads(response.text)
        return response.text

    def _check_artifact_post(self, url, data, status=201,
                             headers=None):
        if headers is None:
            headers = {'Content-Type': 'application/json'}

        return self._check_artifact_method("post", url, data, status=status,
                                           headers=headers)

    def _check_artifact_get(self, url, status=200):
        return self._check_artifact_method("get", url, status=status)

    def _check_artifact_delete(self, url, status=204):
        response = requests.delete(self._url(url), headers=self._headers())
        self.assertEqual(status, response.status_code)
        return response.text

    def _check_artifact_patch(self, url, data, status=200,
                              headers={'Content-Type': 'application/json'}):
        return self._check_artifact_method("patch", url, data, status=status,
                                           headers=headers)

    def _check_artifact_put(self, url, data, status=200,
                            headers={'Content-Type': 'application/json'}):
        return self._check_artifact_method("put", url, data, status=status,
                                           headers=headers)

    def test_list_any_artifacts(self):
        """Returns information about all draft artifacts with given endpoint"""
        self._create_artifact('noprop')
        artifacts = self._check_artifact_get('/noprop/drafts')["artifacts"]
        self.assertEqual(1, len(artifacts))

    def test_list_last_version(self):
        """/artifacts/endpoint == /artifacts/endpoint/all-versions"""
        self._create_artifact('noprop')
        artifacts = self._check_artifact_get('/noprop/drafts')["artifacts"]
        self.assertEqual(1, len(artifacts))
        # the same result can be achieved if asked for artifact with
        # type_version=last version
        artifacts_precise = self._check_artifact_get(
            '/noprop/v1.0/drafts')["artifacts"]
        self.assertEqual(artifacts, artifacts_precise)

    def test_list_artifacts_by_state(self):
        """Returns last version of artifacts with given state"""
        self._create_artifact('noprop')
        creating_state = self._check_artifact_get(
            '/noprop/drafts')["artifacts"]
        self.assertEqual(1, len(creating_state))
        # no active [/type_name/active == /type_name]
        active_state = self._check_artifact_get('/noprop')["artifacts"]
        self.assertEqual(0, len(active_state))

    def test_list_artifacts_with_version(self):
        """Supplying precise artifact version does not break anything"""
        self._create_artifact('noprop')
        list_creating = self._check_artifact_get(
            '/noprop/v1.0/drafts')["artifacts"]
        self.assertEqual(1, len(list_creating))
        bad_version = self._check_artifact_get('/noprop/v1.0bad',
                                               status=400)
        self.assertIn("Invalid version string: u'1.0bad'", bad_version)

    def test_list_artifacts_with_pagination(self):
        """List artifacts with pagination"""
        # create artifacts
        art1 = {'name': 'artifact-1',
                'version': '12'}
        art2 = {'name': 'artifact-2',
                'version': '12'}
        self._create_artifact('noprop', data=art1)
        self._create_artifact('noprop', data=art2)
        # sorting is desc by default
        first_page = self._check_artifact_get(
            '/noprop/drafts?limit=1&sort=name')
        # check the first artifacts has returned correctly
        self.assertEqual(1, len(first_page["artifacts"]))
        self.assertEqual("artifact-2", first_page["artifacts"][0]["name"])
        self.assertIn("next", first_page)
        # check the second page
        second_page_url = first_page["next"].split("artifacts", 1)[1]
        second_page = self._check_artifact_get(second_page_url)
        self.assertIn("next", second_page)
        self.assertEqual(1, len(second_page["artifacts"]))
        self.assertEqual("artifact-1", second_page["artifacts"][0]["name"])
        # check that the latest item is empty
        last_page_url = second_page["next"].split("artifacts", 1)[1]
        last_page = self._check_artifact_get(last_page_url)
        self.assertEqual(0, len(last_page["artifacts"]))
        self.assertNotIn("next", last_page)

    def test_get_artifact_by_id_any_version(self):
        data = self._create_artifact('noprop')
        artifact_id = data['id']
        artifacts = self._check_artifact_get(
            '/noprop/%s' % artifact_id)
        self.assertEqual(artifact_id, artifacts['id'])

    def test_list_artifact_no_such_version(self):
        """Version filtering should be applied for existing plugins.

        An attempt to retrieve an artifact out of existing plugin but with
        a wrong version should result in
        400 BadRequest 'No such plugin has been loaded'
        """
        msg = self._check_artifact_get('/noprop/v0.0.9', 400)
        self.assertIn("No plugin for 'noprop v 0.0.9' has been loaded",
                      msg)

    def test_get_artifact_by_id(self):
        data = self._create_artifact('noprop')
        artifact_id = data['id']
        artifacts = self._check_artifact_get(
            '/noprop/%s' % artifact_id)
        self.assertEqual(artifact_id, artifacts['id'])
        # the same result can be achieved if asked for artifact with
        # type_version=last version
        artifacts_precise = self._check_artifact_get(
            '/noprop/v1.0/%s' % artifact_id)
        self.assertEqual(artifacts, artifacts_precise)

    def test_get_artifact_basic_show_level(self):
        no_prop_art = self._create_artifact('noprop')
        art = self._create_artifact(
            'withprops',
            data={"name": "name", "version": "42",
                  "depends_on": no_prop_art['id']})
        self.assertEqual(no_prop_art['id'], art['depends_on']['id'])
        self.assertEqual(no_prop_art['name'], art['depends_on']['name'])

        artifact_id = art['id']
        artifact = self._check_artifact_get(
            '/withprops/%s?show_level=basic' % artifact_id)
        self.assertEqual(artifact_id, artifact['id'])
        self.assertIsNone(artifact['depends_on'])

    def test_get_artifact_none_show_level(self):
        """Create an artifact (with two deployer-defined properties)"""
        artifact_data = {'name': 'artifact-1',
                         'version': '12',
                         'tags': ['gagaga', 'sesese'],
                         'prop1': 'Arthur Dent',
                         'prop2': 42}
        art = self._check_artifact_post('/withprops/v1.0/drafts',
                                        artifact_data)
        expected_artifact = {
            'state': 'creating',
            'name': 'artifact-1',
            'version': '12.0.0',
            'tags': ['gagaga', 'sesese'],
            'visibility': 'private',
            'type_name': 'WithProps',
            'type_version': '1.0',
            'prop1': 'Arthur Dent',
            'prop2': 42
        }
        for key, value in expected_artifact.items():
            self.assertEqual(art[key], value, key)

        artifact_id = art['id']
        artifact = self._check_artifact_get(
            '/withprops/%s?show_level=none' % artifact_id)
        self.assertEqual(artifact_id, artifact['id'])
        self.assertIsNone(artifact['prop1'])
        self.assertIsNone(artifact['prop2'])

    def test_get_artifact_invalid_show_level(self):
        no_prop_art = self._create_artifact('noprop')
        art = self._create_artifact(
            'withprops',
            data={"name": "name", "version": "42",
                  "depends_on": no_prop_art['id']})
        self.assertEqual(no_prop_art['id'], art['depends_on']['id'])
        self.assertEqual(no_prop_art['name'], art['depends_on']['name'])

        artifact_id = art['id']
        # 'hui' is invalid show level
        self._check_artifact_get(
            '/noprop/%s?show_level=yoba' % artifact_id, status=400)

    def test_get_artifact_no_such_id(self):
        msg = self._check_artifact_get(
            '/noprop/%s' % str(uuid.uuid4()), status=404)
        self.assertIn('No artifact found with ID', msg)

    def test_get_artifact_present_id_wrong_type(self):
        artifact_data = {'name': 'artifact-1',
                         'version': '12',
                         'prop1': '12',
                         'prop2': 12}
        art1 = self._create_artifact('withprops', data=artifact_data)
        art2 = self._create_artifact('noprop')
        # ok id and type_name but bad type_version should result in 404
        self._check_artifact_get('/noprop/v0.5/%s' % str(art2['id']),
                                 status=404)
        # try to access art2 by supplying art1.type and art2.id
        self._check_artifact_get('/withprops/%s' % str(art2['id']),
                                 status=404)
        self._check_artifact_get('/noprop/%s' % str(art1['id']), status=404)

    def test_delete_artifact(self):
        artifact_data = {'name': 'artifact-1',
                         'version': '12',
                         'prop1': '12',
                         'prop2': 12}
        art1 = self._create_artifact('withprops', data=artifact_data)
        self._check_artifact_delete('/withprops/v1.0/%s' % art1['id'])
        art1_deleted = self._check_artifact_get('/withprops/%s' % art1['id'],
                                                status=404)
        self.assertIn('No artifact found with ID', art1_deleted)

    def test_delete_artifact_no_such_id(self):
        self._check_artifact_delete('/noprop/v1/%s' % str(uuid.uuid4()),
                                    status=404)

    @unittest.skip("Test is unstable")
    def test_delete_artifact_with_dependency(self):
        # make sure that artifact can't be deleted if it has some dependencies
        # still not deleted
        art = self._create_artifact('withprops')
        no_prop_art = self._create_artifact('noprop')
        art_updated = self._check_artifact_patch(
            '/withprops/v1/%s' % art['id'],
            data=[{'value': no_prop_art['id'],
                   'op': 'replace',
                   'path': '/depends_on'},
                  {'value': [no_prop_art['id']],
                   'op': 'add',
                   'path': '/depends_on_list'}])
        self.assertEqual(no_prop_art['id'], art_updated['depends_on']['id'])
        self.assertEqual(1, len(art_updated['depends_on_list']))
        # try to delete an artifact prior to its dependency
        res = self._check_artifact_delete('/withprops/v1/%s' % art['id'],
                                          status=400)
        self.assertIn(
            "Dependency property 'depends_on' has to be deleted first", res)
        # delete a dependency
        art_updated = self._check_artifact_patch(
            '/withprops/v1/%s' % art['id'],
            data=[{'op': 'remove', 'path': '/depends_on'}])
        # try to delete prior to deleting artifact_list dependencies
        res = self._check_artifact_delete('/withprops/v1/%s' % art['id'],
                                          status=400)
        self.assertIn(
            "Dependency property 'depends_on_list' has to be deleted first",
            res)
        art_updated = self._check_artifact_patch(
            '/withprops/v1/%s' % art['id'],
            data=[{'op': 'remove', 'path': '/depends_on_list'}])
        # delete dependency list
        self._check_artifact_delete('/withprops/v1/%s' % art['id'])

    def test_delete_artifact_with_blob(self):
        # Upload some data to an artifact
        art = self._create_artifact('withblob')
        headers = self._headers({'Content-Type': 'application/octet-stream'})
        self._check_artifact_post('/withblob/v1/%s/blob1' % art['id'],
                                  headers=headers,
                                  data='ZZZZZ', status=200)
        self._check_artifact_delete('/withblob/v1/%s' % art['id'])

    def test_update_nonexistent_property_by_replace_op(self):
        art = self._create_artifact('withprops', data={'name': 'some art',
                                                       'version': '4.2'})
        data = [{'op': 'replace', 'value': 'some value',
                 'path': '/nonexistent_property'}]
        result = self._check_artifact_patch('/withprops/v1/%s' %
                                            art['id'],
                                            data=data,
                                            status=400)
        self.assertIn('400 Bad Request', result)
        self.assertIn('Artifact has no property nonexistent_property', result)

    def test_update_nonexistent_property_by_remove_op(self):
        art = self._create_artifact('withprops', data={'name': 'some art',
                                                       'version': '4.2'})
        data = [{'op': 'replace', 'value': 'some value',
                 'path': '/nonexistent_property'}]
        result = self._check_artifact_patch('/withprops/v1/%s' %
                                            art['id'],
                                            data=data,
                                            status=400)
        self.assertIn('400 Bad Request', result)
        self.assertIn('Artifact has no property nonexistent_property', result)

    def test_update_nonexistent_property_by_add_op(self):
        art = self._create_artifact('withprops', data={'name': 'some art',
                                                       'version': '4.2'})
        data = [{'op': 'replace', 'value': 'some value',
                 'path': '/nonexistent_property'}]
        result = self._check_artifact_patch('/withprops/v1/%s' %
                                            art['id'],
                                            data=data,
                                            status=400)
        self.assertIn('400 Bad Request', result)
        self.assertIn('Artifact has no property nonexistent_property', result)

    def test_update_array_property_by_replace_op(self):
        art = self._create_artifact('withprops', data={'name': 'some art',
                                                       'version': '4.2'})
        self.assertEqual('some art', art['name'])
        data = [{'op': 'replace', 'value': [1, 2, 3], 'path': '/prop_list'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s' %
                                                 art['id'],
                                                 data=data)
        self.assertEqual([1, 2, 3], art_updated['prop_list'])
        # now try to change first element of the list
        data_change_first = [{'op': 'replace', 'value': 42,
                              'path': '/prop_list/1'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s' %
                                                 art['id'],
                                                 data=data_change_first)
        self.assertEqual([1, 42, 3], art_updated['prop_list'])
        # replace last element
        data_change_last = [{'op': 'replace', 'value': 24,
                             'path': '/prop_list/-'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s' %
                                                 art['id'],
                                                 data=data_change_last)
        self.assertEqual([1, 42, 24], art_updated['prop_list'])

    def test_update_dict_property_by_replace_op(self):
        art = self._create_artifact(
            'withprops',
            data={'name': 'some art',
                  'version': '4.2',
                  'dict_prop': {'foo': "Fenchurch", 'bar_list': [42, 42]}})
        self.assertEqual({'foo': "Fenchurch", 'bar_list': [42, 42]},
                         art['dict_prop'])
        data = [{'op': 'replace', 'value': 24,
                 'path': '/dict_prop/bar_list/0'},
                {'op': 'replace', 'value': 'cello lesson',
                 'path': '/dict_prop/foo'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data)
        self.assertEqual({'foo': 'cello lesson', 'bar_list': [24, 42]},
                         art_updated['dict_prop'])

    def test_update_empty_dict_property_by_replace_op(self):
        art = self._create_artifact('withprops')
        self.assertIsNone(art['dict_prop'])
        data = [{'op': 'replace', 'value': "don't panic",
                 'path': '/dict_prop/foo'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data, status=400)
        self.assertIn("The provided path 'dict_prop/foo' is invalid",
                      art_updated)

    def test_update_empty_dict_property_by_remove_op(self):
        art = self._create_artifact('withprops')
        self.assertIsNone(art['dict_prop'])
        data = [{'op': 'remove', 'path': '/dict_prop/bar_list'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data, status=400)
        self.assertIn("The provided path 'dict_prop/bar_list' is invalid",
                      art_updated)

    def test_update_dict_property_by_remove_op(self):
        art = self._create_artifact(
            'withprops',
            data={'name': 'some art', 'version': '4.2',
                  'dict_prop': {'foo': "Fenchurch", 'bar_list': [42, 42]}})
        self.assertEqual({'foo': 'Fenchurch', 'bar_list': [42, 42]},
                         art['dict_prop'])
        data = [{'op': 'remove', 'path': '/dict_prop/foo'},
                {'op': 'remove', 'path': '/dict_prop/bar_list/1'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data)
        self.assertEqual({'bar_list': [42]}, art_updated['dict_prop'])
        # now delete the whole dict
        data = [{'op': 'remove', 'path': '/dict_prop'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data)
        self.assertIsNone(art_updated['dict_prop'])

    @unittest.skip("Skipping due to a know bug")
    def test_update_dict_property_change_values(self):
        art = self._create_artifact(
            'withprops', data={'name': 'some art', 'version': '4.2',
                               'dict_prop_strval':
                               {'foo': 'Fenchurch', 'bar': 'no value'}})
        self.assertEqual({'foo': 'Fenchurch', 'bar': 'no value'},
                         art['dict_prop_strval'])
        new_data = [{'op': 'replace', 'path': '/dict_prop_strval',
                     'value': {'new-foo': 'Arthur Dent'}}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=new_data)
        self.assertEqual({'new-foo': 'Arthur Dent'},
                         art_updated['dict_prop_strval'])

    def test_update_array_property_by_remove_op(self):
        art = self._create_artifact(
            'withprops', data={'name': 'some art',
                               'version': '4.2',
                               'prop_list': [1, 2, 3]})
        self.assertEqual([1, 2, 3], art['prop_list'])
        data = [{'op': 'remove', 'path': '/prop_list/0'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data)
        self.assertEqual([2, 3], art_updated['prop_list'])
        # remove last element
        data = [{'op': 'remove', 'path': '/prop_list/-'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data)
        self.assertEqual([2], art_updated['prop_list'])
        # now delete the whole array
        data = [{'op': 'remove', 'path': '/prop_list'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data)
        self.assertIsNone(art_updated['prop_list'])

    def test_update_array_property_by_add_op(self):
        art = self._create_artifact(
            'withprops', data={'name': 'some art',
                               'version': '4.2'})
        self.assertIsNone(art['prop_list'])
        data = [{'op': 'add', 'path': '/prop_list', 'value': [2, 12, 0, 6]}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'], data=data)
        self.assertEqual([2, 12, 0, 6], art_updated['prop_list'])
        data = [{'op': 'add', 'path': '/prop_list/2', 'value': 85}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'], data=data)
        self.assertEqual([2, 12, 85, 0, 6], art_updated['prop_list'])
        # add where path='/array/-' means append to the end
        data = [{'op': 'add', 'path': '/prop_list/-', 'value': 7}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'], data=data)
        self.assertEqual([2, 12, 85, 0, 6, 7], art_updated['prop_list'])
        # an attempt to add an element to nonexistent position should
        # result in 400
        self.assertEqual(6, len(art_updated['prop_list']))
        bad_index_data = [{'op': 'add', 'path': '/prop_list/11',
                           'value': 42}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=bad_index_data,
                                                 status=400)
        self.assertIn("The provided path 'prop_list/11' is invalid",
                      art_updated)

    def test_update_dict_property_by_add_op(self):
        art = self._create_artifact("withprops")
        self.assertIsNone(art['dict_prop'])
        data = [{'op': 'add', 'path': '/dict_prop/foo', 'value': "some value"}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data)
        self.assertEqual({"foo": "some value"}, art_updated['dict_prop'])

    def test_update_empty_array_property_by_add_op(self):
        """Test jsonpatch add.

        According to RFC 6902:
        * if the array is empty, '/array/0' is a valid path
        """
        create_data = {'name': 'new artifact',
                       'version': '4.2'}
        art = self._create_artifact('withprops', data=create_data)
        self.assertIsNone(art['prop_list'])
        data = [{'op': 'add', 'path': '/prop_list/0', 'value': 3}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data)
        self.assertEqual([3], art_updated['prop_list'])

    def test_update_tuple_property_by_index(self):
        art = self._create_artifact(
            'withprops', data={'name': 'some art',
                               'version': '4.2',
                               'tuple_prop': [1, False]})
        self.assertEqual([1, False], art['tuple_prop'])
        data = [{'op': 'replace', 'value': True,
                 'path': '/tuple_prop/1'},
                {'op': 'replace', 'value': 2,
                 'path': '/tuple_prop/0'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data)
        self.assertEqual([2, True], art_updated['tuple_prop'])

    def test_update_artifact(self):
        art = self._create_artifact('noprop')
        self.assertEqual('artifact-1', art['name'])
        art_updated = self._check_artifact_patch(
            '/noprop/v1/%s' % art['id'],
            data=[{'op': 'replace', 'value': '0.0.9', 'path': '/version'}])
        self.assertEqual('0.0.9', art_updated['version'])

    def test_update_artifact_properties(self):
        art = self._create_artifact('withprops')
        for prop in ['prop1', 'prop2']:
            self.assertIsNone(art[prop])
            data = [{'op': 'replace', 'value': 'some value',
                     'path': '/prop1'}]
            art_updated = self._check_artifact_patch(
                '/withprops/v1/%s' % art['id'], data=data)
            self.assertEqual('some value', art_updated['prop1'])

    def test_update_remove_non_existent_artifact_properties(self):
        art = self._create_artifact('withprops')
        for prop in ['prop1', 'prop2']:
            self.assertIsNone(art[prop])
            data = [{'op': 'remove', 'value': 'some value',
                     'path': '/non-existent-path/and-another'}]
            art_updated = self._check_artifact_patch(
                '/withprops/v1/%s' % art['id'], data=data, status=400)
            self.assertIn('Artifact has no property', art_updated)

    def test_update_replace_non_existent_artifact_properties(self):
        art = self._create_artifact('withprops')
        for prop in ['prop1', 'prop2']:
            self.assertIsNone(art[prop])
            data = [{'op': 'replace', 'value': 'some value',
                     'path': '/non-existent-path/and-another'}]
            art_updated = self._check_artifact_patch(
                '/withprops/v1/%s' % art['id'], data=data, status=400)
            self.assertIn('Artifact has no property', art_updated)

    def test_update_artifact_remove_property(self):
        artifact_data = {'name': 'artifact-1',
                         'version': '12',
                         'tags': ['gagaga', 'sesese'],
                         'prop1': 'Arthur Dent',
                         'prop2': 42}
        art = self._create_artifact('withprops', data=artifact_data)
        data = [{'op': 'remove', 'path': '/prop1'}]
        art_updated = self._check_artifact_patch('/withprops/v1/%s'
                                                 % art['id'],
                                                 data=data)
        self.assertIsNone(art_updated['prop1'])
        self.assertEqual(42, art_updated['prop2'])

    def test_update_wrong_property_type(self):
        art = self._create_artifact('withprops')
        for prop in ['prop2', 'prop2']:
            self.assertIsNone(art[prop])
        data = [{'op': 'replace', 'value': 123, 'path': '/prop1'}]
        art_updated = self._check_artifact_patch(
            '/withprops/v1/%s' % art['id'], data=data, status=400)
        self.assertIn("Property 'prop1' may not have value '123'", art_updated)

    def test_update_multiple_properties(self):
        with_prop_art = self._create_artifact('withprops')
        data = [{'op': 'replace',
                 'path': '/prop1',
                 'value': 'some value'},
                {'op': 'replace',
                 'path': '/prop2',
                 'value': 42}]
        updated = self._check_artifact_patch(
            '/withprops/v1/%s' % with_prop_art['id'], data=data)
        self.assertEqual('some value', updated['prop1'])
        self.assertEqual(42, updated['prop2'])

    def test_create_artifact_with_dependency(self):
        no_prop_art = self._create_artifact('noprop')
        art = self._create_artifact(
            'withprops',
            data={"name": "name", "version": "42",
                  "depends_on": no_prop_art['id']})
        self.assertEqual(no_prop_art['id'], art['depends_on']['id'])
        self.assertEqual(no_prop_art['name'], art['depends_on']['name'])

    def test_create_artifact_dependency_list(self):
        no_prop_art1 = self._create_artifact('noprop')
        no_prop_art2 = self._create_artifact('noprop')
        art = self._create_artifact(
            'withprops',
            data={"name": "name", "version": "42",
                  "depends_on_list": [no_prop_art1['id'], no_prop_art2['id']]})
        self.assertEqual(2, len(art['depends_on_list']))
        self.assertEqual([no_prop_art1['id'], no_prop_art2['id']],
                         map(lambda x: x['id'], art['depends_on_list']))

    def test_create_dependency_list_same_id(self):
        no_prop_art = self._create_artifact('noprop')
        res = self._create_artifact(
            'withprops',
            data={"name": "name", "version": "42",
                  "depends_on_list": [no_prop_art['id'],
                                      no_prop_art['id']]}, status=400)
        self.assertIn("Items have to be unique", res)

    def test_create_artifact_bad_dependency_format(self):
        """Invalid dependencies creation.

        Dependencies should be passed:
        * as a list of ids if param is an ArtifactReferenceList
        * as an id if param is an ArtifactReference
        """
        no_prop_art = self._create_artifact('noprop')
        art = self._check_artifact_post(
            '/withprops/v1/drafts',
            {"name": "name", "version": "42",
             "depends_on": [no_prop_art['id']]}, status=400)
        self.assertIn('Not a valid value type', art)
        art = self._check_artifact_post(
            '/withprops/v1.0/drafts',
            {"name": "name", "version": "42",
             "depends_on_list": no_prop_art['id']}, status=400)
        self.assertIn('object is not iterable', art)

    def test_update_dependency(self):
        no_prop_art = self._create_artifact('noprop')
        no_prop_art1 = self._create_artifact('noprop')
        with_prop_art = self._create_artifact('withprops')
        data = [{'op': 'replace',
                 'path': '/depends_on',
                 'value': no_prop_art['id']}]
        updated = self._check_artifact_patch(
            '/withprops/v1/%s' % with_prop_art['id'], data=data)
        self.assertEqual(no_prop_art['id'], updated['depends_on']['id'])
        self.assertEqual(no_prop_art['name'], updated['depends_on']['name'])
        data = [{'op': 'replace',
                 'path': '/depends_on',
                 'value': no_prop_art1['id']}]
        # update again and make sure it changes
        updated = self._check_artifact_patch(
            '/withprops/v1/%s' % with_prop_art['id'], data=data)
        self.assertEqual(no_prop_art1['id'], updated['depends_on']['id'])
        self.assertEqual(no_prop_art1['name'], updated['depends_on']['name'])

    def test_update_dependency_circular_reference(self):
        with_prop_art = self._create_artifact('withprops')
        data = [{'op': 'replace',
                 'path': '/depends_on',
                 'value': [with_prop_art['id']]}]
        not_updated = self._check_artifact_patch(
            '/withprops/v1/%s' % with_prop_art['id'], data=data, status=400)
        self.assertIn('Artifact with a circular dependency can not be created',
                      not_updated)

    def test_publish_artifact(self):
        art = self._create_artifact('withprops')
        # now create dependency
        no_prop_art = self._create_artifact('noprop')
        art_updated = self._check_artifact_patch(
            '/withprops/v1/%s' % art['id'],
            data=[{'value': no_prop_art['id'],
                   'op': 'replace',
                   'path': '/depends_on'}])
        self.assertNotEqual(0, len(art_updated['depends_on']))
        # artifact can't be published if any dependency is in non-active state
        res = self._check_artifact_post(
            '/withprops/v1/%s/publish' % art['id'], {}, status=400)
        self.assertIn("Not all dependencies are in 'active' state", res)
        # after you publish the dependency -> artifact can be published
        dep_published = self._check_artifact_post(
            '/noprop/v1/%s/publish' % no_prop_art['id'], {}, status=200)
        self.assertEqual('active', dep_published['state'])
        art_published = self._check_artifact_post(
            '/withprops/v1.0/%s/publish' % art['id'], {}, status=200)
        self.assertEqual('active', art_published['state'])

    def test_no_mutable_change_in_published_state(self):
        art = self._create_artifact('withprops')
        no_prop_art = self._create_artifact('noprop')
        no_prop_other = self._create_artifact('noprop')
        art_updated = self._check_artifact_patch(
            '/withprops/v1/%s' % art['id'],
            data=[{'value': no_prop_art['id'],
                   'op': 'replace',
                   'path': '/depends_on'}])
        self.assertEqual(no_prop_art['id'], art_updated['depends_on']['id'])
        # now change dependency to some other artifact
        art_updated = self._check_artifact_patch(
            '/withprops/v1/%s' % art['id'],
            data=[{'value': no_prop_other['id'],
                   'op': 'replace',
                   'path': '/depends_on'}])
        self.assertEqual(no_prop_other['id'], art_updated['depends_on']['id'])
        # publish dependency
        dep_published = self._check_artifact_post(
            '/noprop/v1/%s/publish' % no_prop_other['id'], {}, status=200)
        self.assertEqual('active', dep_published['state'])
        # publish artifact
        art_published = self._check_artifact_post(
            '/withprops/v1.0/%s/publish' % art['id'], {}, status=200)
        self.assertEqual('active', art_published['state'])
        # try to change dependency, should fail as already published
        res = self._check_artifact_patch(
            '/withprops/v1/%s' % art_published['id'],
            data=[{'op': 'remove', 'path': '/depends_on'}], status=400)
        self.assertIn('Attempt to set value of immutable property', res)

    def test_create_artifact_empty_body(self):
        self._check_artifact_post('/noprop/v1.0/drafts', {}, 400)

    def test_create_artifact_insufficient_arguments(self):
        self._check_artifact_post('/noprop/v1.0/drafts',
                                  {'name': 'some name, no version'},
                                  status=400)

    def test_create_artifact_no_such_version(self):
        """Creation impossible without specifying a correct version.

        An attempt to create an artifact out of existing plugin but with
        a wrong version should result in
        400 BadRequest 'No such plugin has been loaded'
        """
        # make sure there is no such artifact noprop
        self._check_artifact_get('/noprop/v0.0.9', 400)
        artifact_data = {'name': 'artifact-1',
                         'version': '12'}
        msg = self._check_artifact_post('/noprop/v0.0.9/drafts',
                                        artifact_data,
                                        status=400)
        self.assertIn("No plugin for 'noprop v 0.0.9' has been loaded",
                      msg)

    def test_create_artifact_no_type_version_specified(self):
        """Creation impossible without specifying a version.

        It should not be possible to create an artifact out of existing plugin
        without specifying any version
        """
        artifact_data = {'name': 'artifact-1',
                         'version': '12'}
        self._check_artifact_post('/noprop/drafts', artifact_data, 404)

    def test_create_artifact_no_properties(self):
        """Create an artifact with minimum parameters"""
        artifact_data = {'name': 'artifact-1',
                         'version': '12'}
        artifact = self._check_artifact_post('/withprops/v1.0/drafts',
                                             artifact_data)
        # verify that all fields have the values expected
        expected_artifact = {
            'state': 'creating',
            'name': 'artifact-1',
            'version': '12.0.0',
            'tags': [],
            'visibility': 'private',
            'type_name': 'WithProps',
            'type_version': '1.0',
            'prop1': None,
            'prop2': None
        }
        for key, value in expected_artifact.items():
            self.assertEqual(artifact[key], value, key)

    def test_create_artifact_with_properties(self):
        """Create an artifact (with two deployer-defined properties)"""
        artifact_data = {'name': 'artifact-1',
                         'version': '12',
                         'tags': ['gagaga', 'sesese'],
                         'prop1': 'Arthur Dent',
                         'prop2': 42}
        artifact = self._check_artifact_post('/withprops/v1.0/drafts',
                                             artifact_data)
        expected_artifact = {
            'state': 'creating',
            'name': 'artifact-1',
            'version': '12.0.0',
            'tags': ['gagaga', 'sesese'],
            'visibility': 'private',
            'type_name': 'WithProps',
            'type_version': '1.0',
            'prop1': 'Arthur Dent',
            'prop2': 42
        }
        for key, value in expected_artifact.items():
            self.assertEqual(artifact[key], value, key)

    def test_create_artifact_not_all_properties(self):
        """Create artifact with minimal properties.

        Checks that it is possible to create an artifact by passing all
        required properties but omitting some not required
        """
        artifact_data = {'name': 'artifact-1',
                         'version': '12',
                         'visibility': 'private',
                         'tags': ['gagaga', 'sesese'],
                         'prop1': 'i am ok'}
        artifact = self._check_artifact_post('/withprops/v1.0/drafts',
                                             artifact_data)
        expected_artifact = {
            'state': 'creating',
            'name': 'artifact-1',
            'version': '12.0.0',
            'tags': ['gagaga', 'sesese'],
            'visibility': 'private',
            'type_name': 'WithProps',
            'type_version': '1.0',
            'prop1': 'i am ok',
            'prop2': None}
        for key, value in expected_artifact.items():
            self.assertEqual(artifact[key], value, key)
        # now check creation with no properties specified
        for prop in ['prop1', 'prop2']:
            artifact_data.pop(prop, '')
        artifact = self._check_artifact_post('/withprops/v1.0/drafts',
                                             artifact_data)
        for prop in ['prop1', 'prop2']:
            self.assertIsNone(artifact[prop])

    def test_create_artifact_invalid_properties(self):
        """Any attempt to pass invalid properties should result in 400"""
        artifact_data = {'name': 'artifact-1',
                         'version': '12',
                         'prop1': 1}
        res = self._check_artifact_post('/withprops/v1.0/drafts',
                                        artifact_data,
                                        status=400)
        self.assertIn("Property 'prop1' may not have value '1'", res)
        artifact_data.pop('prop1')
        artifact_data['nosuchprop'] = "Random"
        res = self._check_artifact_post('/withprops/v1.0/drafts',
                                        artifact_data,
                                        status=400)
        self.assertIn("Artifact has no property nosuchprop", res)

    def test_create_public_artifact(self):
        """Create an artifact with visibility set to public"""
        artifact_data = {'name': 'artifact-1',
                         'version': '12',
                         'visibility': 'public'}
        artifact = self._check_artifact_post('/withprops/v1.0/drafts',
                                             artifact_data)
        # verify that all fields have the values expected
        expected_artifact = {
            'state': 'creating',
            'name': 'artifact-1',
            'version': '12.0.0',
            'tags': [],
            'visibility': 'public',
            'type_name': 'WithProps',
            'type_version': '1.0',
            'prop1': None,
            'prop2': None
        }
        for key, value in expected_artifact.items():
            self.assertEqual(artifact[key], value, key)

    def test_upload_file(self):
        # Upload some data to an artifact
        art = self._create_artifact('withblob')
        headers = self._headers({'Content-Type': 'application/octet-stream'})
        self._check_artifact_post('/withblob/v1/%s/blob1' % art['id'],
                                  headers=headers,
                                  data='ZZZZZ', status=200)

    def test_upload_file_with_invalid_content_type(self):
        art = self._create_artifact('withblob')
        data = {'data': 'jjjjjj'}
        res = self._check_artifact_post('/withblob/v1/%s/blob1' % art['id'],
                                        data=data, status=400)
        self.assertIn('Invalid Content-Type for work with blob1', res)

        res = self._check_artifact_post('/withblob/v1/%s/blob_list'
                                        % art['id'],
                                        data=data, status=400)
        self.assertIn('Invalid Content-Type for work with blob_list', res)

    def test_upload_list_files(self):
        art = self._create_artifact('withblob')
        headers = self._headers({'Content-Type': 'application/octet-stream'})
        self._check_artifact_post('/withblob/v1/%s/blob_list' % art['id'],
                                  headers=headers,
                                  data='ZZZZZ', status=200)
        self._check_artifact_post('/withblob/v1/%s/blob_list' % art['id'],
                                  headers=headers,
                                  data='YYYYY', status=200)

    def test_download_file(self):
        # Download some data from an artifact
        art = self._create_artifact('withblob')
        artifact_id = art['id']
        headers = self._headers({'Content-Type': 'application/octet-stream'})
        self._check_artifact_post('/withblob/v1/%s/blob1' % art['id'],
                                  headers=headers,
                                  data='ZZZZZ', status=200)

        art = self._check_artifact_get('/withblob/%s' % artifact_id)
        self.assertEqual(artifact_id, art['id'])
        self.assertIn('download_link', art['blob1'])

        data = self._check_artifact_get(
            '/withblob/%s/blob1/download' % art['id'])
        self.assertEqual('ZZZZZ', data)

    def test_file_w_unknown_size(self):
        # Upload and download data provided by an iterator, thus without
        # knowing the length in advance
        art = self._create_artifact('withblob')
        artifact_id = art['id']

        def iterate_string(val):
            for char in val:
                yield char

        headers = self._headers({'Content-Type': 'application/octet-stream'})
        self._check_artifact_post('/withblob/v1/%s/blob1' % art['id'],
                                  headers=headers,
                                  data=iterate_string('ZZZZZ'), status=200)

        art = self._check_artifact_get('/withblob/%s' % artifact_id)
        self.assertEqual(artifact_id, art['id'])
        self.assertIn('download_link', art['blob1'])

        data = self._check_artifact_get(
            '/withblob/%s/blob1/download' % art['id'])
        self.assertEqual('ZZZZZ', data)

    def test_limit(self):
        artifact_data = {'name': 'artifact-1',
                         'version': '12'}
        self._check_artifact_post('/withprops/v1/drafts',
                                  artifact_data)
        artifact_data = {'name': 'artifact-1',
                         'version': '13'}
        self._check_artifact_post('/withprops/v1/drafts',
                                  artifact_data)
        result = self._check_artifact_get('/withprops/v1/drafts')
        self.assertEqual(2, len(result["artifacts"]))
        result = self._check_artifact_get('/withprops/v1/drafts?limit=1')
        self.assertEqual(1, len(result["artifacts"]))

    def _check_sorting_order(self, expected, actual):
        for e, a in zip(expected, actual):
            self.assertEqual(e['name'], a['name'])
            self.assertEqual(e['version'], a['version'])
            self.assertEqual(e['prop1'], a['prop1'])

    def test_sort(self):
        artifact_data = {'name': 'artifact-1',
                         'version': '12',
                         'prop1': 'lala'}
        art1 = self._check_artifact_post('/withprops/v1.0/drafts',
                                         artifact_data)
        artifact_data = {'name': 'artifact-2',
                         'version': '13',
                         'prop1': 'lala'}
        art2 = self._check_artifact_post('/withprops/v1.0/drafts',
                                         artifact_data)
        artifact_data = {'name': 'artifact-3',
                         'version': '13',
                         'prop1': 'tutu'}
        art3 = self._check_artifact_post('/withprops/v1.0/drafts',
                                         artifact_data)
        artifact_data = {'name': 'artifact-4',
                         'version': '13',
                         'prop1': 'hyhy'}
        art4 = self._check_artifact_post('/withprops/v1.0/drafts',
                                         artifact_data)
        artifact_data = {'name': 'artifact-5',
                         'version': '13',
                         'prop1': 'bebe'}
        art5 = self._check_artifact_post('/withprops/v1.0/drafts',
                                         artifact_data)

        result = self._check_artifact_get(
            '/withprops/v1.0/drafts?sort=name')["artifacts"]
        self.assertEqual(5, len(result))

        # default direction is 'desc'
        expected = [art5, art4, art3, art2, art1]
        self._check_sorting_order(expected, result)

        result = self._check_artifact_get(
            '/withprops/v1.0/drafts?sort=name:asc')["artifacts"]
        self.assertEqual(5, len(result))

        expected = [art1, art2, art3, art4, art5]
        self._check_sorting_order(expected, result)

        result = self._check_artifact_get(
            '/withprops/v1.0/drafts?sort=version:asc,prop1')["artifacts"]
        self.assertEqual(5, len(result))

        expected = [art1, art3, art2, art4, art5]
        self._check_sorting_order(expected, result)

    def test_update_property(self):
        data = {'name': 'an artifact',
                'version': '42'}
        art = self._create_artifact('withprops', data=data)
        # update single integer property via PUT
        upd = self._check_artifact_put('/withprops/v1.0/%s/prop2' % art['id'],
                                       data={'data': 15})
        self.assertEqual(15, upd['prop2'])
        # create list property via PUT
        upd = self._check_artifact_put(
            '/withprops/v1.0/%s/tuple_prop' % art['id'],
            data={'data': [42, True]})
        self.assertEqual([42, True], upd['tuple_prop'])
        # change list property via PUT
        upd = self._check_artifact_put(
            '/withprops/v1.0/%s/tuple_prop/0' % art['id'], data={'data': 24})
        self.assertEqual([24, True], upd['tuple_prop'])
        # append to list property via POST
        upd = self._check_artifact_post(
            '/withprops/v1.0/%s/prop_list' % art['id'], data={'data': [11]},
            status=200)
        self.assertEqual([11], upd['prop_list'])
        # append to list property via POST
        upd = self._check_artifact_post(
            '/withprops/v1.0/%s/prop_list/-' % art['id'],
            status=200, data={'data': 10})
        self.assertEqual([11, 10], upd['prop_list'])

    def test_bad_update_property(self):
        data = {'name': 'an artifact',
                'version': '42'}
        art = self._create_artifact('withprops', data=data)
        # try to update nonexistent property
        upd = self._check_artifact_put(
            '/withprops/v1.0/%s/nosuchprop' % art['id'],
            data={'data': 'wont be set'}, status=400)
        self.assertIn('Artifact has no property nosuchprop', upd)
        # try to pass wrong property value
        upd = self._check_artifact_put(
            '/withprops/v1.0/%s/tuple_prop' % art['id'],
            data={'data': ['should be an int', False]}, status=400)
        self.assertIn("Property 'tuple_prop[0]' may not have value", upd)
        # try to pass bad body (not a valid json)
        upd = self._check_artifact_put(
            '/withprops/v1.0/%s/tuple_prop' % art['id'], data="not a json",
            status=400)
        self.assertIn("Invalid json body", upd)
        # try to pass json body invalid under schema
        upd = self._check_artifact_put(
            '/withprops/v1.0/%s/tuple_prop' % art['id'],
            data={"bad": "schema"}, status=400)
        self.assertIn("Invalid json body", upd)

    def test_update_different_depths_levels(self):
        data = {'name': 'an artifact',
                'version': '42'}
        art = self._create_artifact('withprops', data=data)
        upd = self._check_artifact_post(
            '/withprops/v1.0/%s/dict_prop' % art['id'],
            data={'data': {'foo': 'some value'}}, status=200)
        self.assertEqual({'foo': 'some value'}, upd['dict_prop'])
        upd = self._check_artifact_post(
            '/withprops/v1.0/%s/dict_prop/bar_list' % art['id'],
            data={'data': [5]}, status=200)
        self.assertEqual({'foo': 'some value', 'bar_list': [5]},
                         upd['dict_prop'])
        upd = self._check_artifact_post(
            '/withprops/v1.0/%s/dict_prop/bar_list/0' % art['id'],
            data={'data': 15}, status=200)
        self.assertEqual({'foo': 'some value', 'bar_list': [5, 15]},
                         upd['dict_prop'])
        # try to attempt dict_property by nonexistent path
        upd = self._check_artifact_post(
            '/withprops/v1.0/%s/dict_prop/bar_list/nosuchkey' % art['id'],
            data={'data': 15}, status=400)

    def test_artifact_inaccessible_by_different_user(self):
        data = {'name': 'an artifact',
                'version': '42'}
        art = self._create_artifact('withprops', data=data)
        self._set_user('user2')
        self._check_artifact_get('/withprops/%s' % art['id'], 404)

    def test_artifact_accessible_by_admin(self):
        data = {'name': 'an artifact',
                'version': '42'}
        art = self._create_artifact('withprops', data=data)
        self._set_user('admin')
        self._check_artifact_get('/withprops/%s' % art['id'], 200)

    def test_public_artifact_accessible_by_different_user(self):
        data = {'name': 'an artifact',
                'version': '42'}
        art = self._create_artifact('withprops', data=data)
        self._check_artifact_patch(
            '/withprops/v1.0/%s' % art['id'],
            data=[{'op': 'replace', 'value': 'public', 'path': '/visibility'}])
        self._set_user('user2')
        self._check_artifact_get('/withprops/%s' % art['id'], 200)

    def test_public_artifact_not_editable_by_different_user(self):
        data = {'name': 'an artifact',
                'version': '42'}
        art = self._create_artifact('withprops', data=data)
        self._check_artifact_patch(
            '/withprops/v1.0/%s' % art['id'],
            data=[{'op': 'replace', 'value': 'public', 'path': '/visibility'}])
        self._set_user('user2')
        self._check_artifact_patch(
            '/withprops/v1.0/%s' % art['id'],
            data=[{'op': 'replace', 'value': 'private',
                   'path': '/visibility'}], status=403)

    def test_public_artifact_editable_by_admin(self):
        data = {'name': 'an artifact',
                'version': '42'}
        art = self._create_artifact('withprops', data=data)
        self._check_artifact_patch(
            '/withprops/v1.0/%s' % art['id'],
            data=[{'op': 'replace', 'value': 'public', 'path': '/visibility'}])
        self._set_user('admin')
        self._check_artifact_patch(
            '/withprops/v1.0/%s' % art['id'],
            data=[{'op': 'replace', 'value': 'private',
                   'path': '/visibility'}], status=200)

    def test_list_artifact_types(self):
        actual = {
            u'artifact_types': [
                {u'displayed_name': u'NoProp',
                 u'type_name': u'NoProp',
                 u'versions':
                     [{u'id': u'v0.5',
                       u'link': u'http://127.0.0.1:%d/v0.1/'
                                u'artifacts/noprop/v0.5'
                                % self.api_port},
                      {u'id': u'v1.0',
                       u'link': u'http://127.0.0.1:%d/v0.1/'
                                u'artifacts/noprop/v1.0'
                                % self.api_port}]},
                {u'displayed_name': u'WithBlob',
                 u'type_name': u'WithBlob',
                 u'versions':
                     [{u'id': u'v1.0',
                       u'link':
                           u'http://127.0.0.1:%d/v0.1/artifacts/withblob/v1.0'
                           % self.api_port}]},
                {u'displayed_name': u'WithProps',
                 u'type_name': u'WithProps',
                 u'versions':
                     [{u'id': u'v1.0',
                       u'link':
                           u'http://127.0.0.1:%d/v0.1/artifacts/withprops/v1.0'
                           % self.api_port}]}]}

        response = self._check_artifact_get("", status=200)
        response[u'artifact_types'].sort(key=lambda x: x[u'type_name'])
        for artifact_type in response[u'artifact_types']:
            artifact_type[u'versions'].sort(key=lambda x: x[u'id'])

        self.assertEqual(actual, response)

    def test_invalid_content_type(self):
        data = {'name': 'name1', 'version': '2.2'}
        self._check_artifact_post('/withprops/v1.0/drafts',
                                  data=data,
                                  status=400,
                                  headers={'Content-Type': 'lalala'})

    def test_filter_by_non_dict_props(self):
        data = {'name': 'art1',
                'version': '4.2',
                'prop2': 12
                }
        self._create_artifact('withprops', data=data)

        data = {'name': 'art2',
                'version': '4.2',
                'prop2': 10
                }
        self._create_artifact('withprops', data=data)

        data = {'name': 'art3',
                'version': '4.2',
                'prop2': 10
                }
        self._create_artifact('withprops', data=data)

        data = {'name': 'art4',
                'version': '4.3',
                'prop2': 33
                }
        self._create_artifact('withprops', data=data)

        result = self._check_artifact_get(
            '/withprops/v1.0/drafts?name=art2')['artifacts']
        self.assertEqual(1, len(result))

        result = self._check_artifact_get(
            '/withprops/v1.0/drafts?prop2=10')['artifacts']
        self.assertEqual(2, len(result))

    def test_filter_by_dict_props(self):
        data = {'name': 'art1',
                'version': '4.2',
                'dict_prop':
                    {'foo': 'Moscow',
                     'bar_list': [42, 44]}
                }
        self._create_artifact('withprops', data=data)
        data = {'name': 'art2',
                'version': '4.2',
                'dict_prop':
                    {'foo': 'Saratov',
                     'bar_list': [42, 42]}
                }
        self._create_artifact('withprops', data=data)

        data = {'name': 'art3',
                'version': '4.2',
                'dict_prop':
                    {'foo': 'Saratov',
                     'bar_list': [42, 44]}
                }
        self._create_artifact('withprops', data=data)

        url = '/withprops/v1.0/drafts?dict_prop.foo=Saratov'
        result = self._check_artifact_get(url=url)

        self.assertEqual(2, len(result))

        url = '/withprops/v1.0/drafts?dict_prop.bar_list=44'
        result = self._check_artifact_get(url=url)

        self.assertEqual(2, len(result))

    def test_transformation_versions(self):
        data = {'name': 'art1',
                'version': '1'}
        art1 = self._create_artifact('noprop', data=data)

        data = {'name': 'art2',
                'version': '1.0'}
        art2 = self._create_artifact('noprop', data=data)

        v1 = art1.get("version")
        v2 = art2.get("version")

        self.assertEqual('1.0.0', v1)
        self.assertEqual('1.0.0', v2)

    def test_filter_by_ge_version(self):
        data = {'name': 'art1',
                'version': '4.0.0'}
        art1 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.0.1'}
        art2 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-1'}
        art3 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-2'}
        art4 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0'}
        art5 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '5.0.0'}
        art6 = self._create_artifact('noprop', data=data)

        url = '/noprop/v1.0/drafts?name=art1&version=ge:4.0.0'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3, art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ge:4.0.1'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art2, art3, art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ge:4.2.0-1'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art3, art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ge:4.2.0-2'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ge:4.2.0'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ge:5.0.0'
        result = self._check_artifact_get(url=url)['artifacts']
        actual = [art6]
        self.assertEqual(actual, result)

    def test_filter_by_gt_version(self):
        data = {'name': 'art1',
                'version': '4.0.0'}
        self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.0.1'}
        art2 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-1'}
        art3 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-2'}
        art4 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0'}
        art5 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '5.0.0'}
        art6 = self._create_artifact('noprop', data=data)

        url = '/noprop/v1.0/drafts?name=art1&version=gt:4.0.0'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art2, art3, art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=gt:4.0.1'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art3, art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=gt:4.2.0-1'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=gt:4.2.0-2'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=gt:4.2.0'
        result = self._check_artifact_get(url=url)['artifacts']
        actual = [art6]
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=gt:5.0.0'
        result = self._check_artifact_get(url=url)['artifacts']
        actual = []
        self.assertEqual(actual, result)

    def test_filter_by_le_version(self):
        data = {'name': 'art1',
                'version': '4.0.0'}
        art1 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.0.1'}
        art2 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-1'}
        art3 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-2'}
        art4 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0'}
        art5 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '5.0.0'}
        art6 = self._create_artifact('noprop', data=data)

        url = '/noprop/v1.0/drafts?name=art1&version=le:4.0.0'
        result = self._check_artifact_get(url=url)['artifacts']
        actual = [art1]
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=le:4.0.1'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=le:4.2.0-1'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=le:4.2.0-2'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3, art4]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=le:4.2.0'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3, art4, art5]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=le:5.0.0'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3, art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

    def test_filter_by_lt_version(self):
        data = {'name': 'art1',
                'version': '4.0.0'}
        art1 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.0.1'}
        art2 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-1'}
        art3 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-2'}
        art4 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0'}
        art5 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '5.0.0'}
        self._create_artifact('noprop', data=data)

        url = '/noprop/v1.0/drafts?name=art1&version=lt:4.0.0'
        result = self._check_artifact_get(url=url)['artifacts']
        actual = []
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=lt:4.0.1'
        result = self._check_artifact_get(url=url)['artifacts']
        actual = [art1]
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=lt:4.2.0-1'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=lt:4.2.0-2'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=lt:4.2.0'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3, art4]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=lt:5.0.0'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3, art4, art5]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

    def test_filter_by_ne_version(self):
        data = {'name': 'art1',
                'version': '4.0.0'}
        art1 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.0.1'}
        art2 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-1'}
        art3 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-2'}
        art4 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0'}
        art5 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '5.0.0'}
        art6 = self._create_artifact('noprop', data=data)

        url = '/noprop/v1.0/drafts?name=art1&version=ne:4.0.0'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art2, art3, art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ne:4.0.1'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art3, art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ne:4.2.0-1'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art4, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ne:4.2.0-2'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3, art5, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ne:4.2.0'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3, art4, art6]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ne:5.0.0'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2, art3, art4, art5]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

    def test_filter_by_pre_release_version(self):
        data = {'name': 'art1',
                'version': '4.2.0-1'}
        art1 = self._create_artifact('noprop', data=data)

        data = {'name': 'art1',
                'version': '4.2.0-2'}
        art2 = self._create_artifact('noprop', data=data)

        url = '/noprop/v1.0/drafts?name=art1&version=ge:4.2.0-2'
        result = self._check_artifact_get(url=url)['artifacts']
        actual = [art2]
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=le:4.2.0-2'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=ge:4.2.0-1'
        result = self._check_artifact_get(url=url)['artifacts']
        result.sort(key=lambda x: x['id'])

        actual = [art1, art2]
        actual.sort(key=lambda x: x['id'])
        self.assertEqual(actual, result)

        url = '/noprop/v1.0/drafts?name=art1&version=le:4.2.0-1'
        result = self._check_artifact_get(url=url)['artifacts']
        actual = [art1]
        self.assertEqual(actual, result)

    def test_filter_by_range_props(self):
        data = {'name': 'art1',
                'version': '4.2',
                'prop2': 10
                }
        self._create_artifact('withprops', data=data)
        data = {'name': 'art2',
                'version': '4.2',
                'prop2': 100
                }
        self._create_artifact('withprops', data=data)

        data = {'name': 'art3',
                'version': '4.2',
                'prop2': 1000
                }
        self._create_artifact('withprops', data=data)

        url = '/withprops/v1.0/drafts?prop2=gt:99&prop2=lt:101'
        result = self._check_artifact_get(url=url)['artifacts']

        self.assertEqual(1, len(result))

        url = '/withprops/v1.0/drafts?prop2=gt:99&prop2=lt:2000'
        result = self._check_artifact_get(url=url)['artifacts']

        self.assertEqual(2, len(result))

    def test_filter_by_tags(self):
        data = {'name': 'art1',
                'version': '4.2',
                'tags': ['hyhyhy', 'tytyty']
                }
        self._create_artifact('withprops', data=data)
        data = {'name': 'art2',
                'version': '4.2',
                'tags': ['hyhyhy', 'cicici']
                }
        self._create_artifact('withprops', data=data)

        data = {'name': 'art3',
                'version': '4.2',
                'tags': ['ededed', 'bobobo']
                }
        self._create_artifact('withprops', data=data)

        url = '/withprops/v1.0/drafts?tags=hyhyhy'
        result = self._check_artifact_get(url=url)['artifacts']

        self.assertEqual(2, len(result))

        url = '/withprops/v1.0/drafts?tags=cicici&tags=hyhyhy'
        result = self._check_artifact_get(url=url)['artifacts']

        self.assertEqual(1, len(result))

    def test_filter_by_latest_version(self):
        data = {'name': 'art1',
                'version': '1.2',
                'tags': ['hyhyhy', 'tytyty']
                }
        self._create_artifact('withprops', data=data)
        data = {'name': 'latest_artifact',
                'version': '3.2',
                'tags': ['hyhyhy', 'cicici']
                }
        self._create_artifact('withprops', data=data)

        data = {'name': 'latest_artifact',
                'version': '3.2',
                'tags': ['ededed', 'bobobo']
                }
        self._create_artifact('withprops', data=data)

        url = '/withprops/v1.0/drafts?version=latest&name=latest_artifact'
        result = self._check_artifact_get(url=url)

        self.assertEqual(2, len(result))

        url = '/withprops/v1.0/drafts?version=latest'
        self._check_artifact_get(url=url, status=400)

    def test_filter_by_version_only(self):
        data = {'name': 'art1',
                'version': '3.2'
                }
        self._create_artifact('withprops', data=data)
        data = {'name': 'art2',
                'version': '4.2'
                }
        self._create_artifact('withprops', data=data)

        data = {'name': 'art3',
                'version': '4.3'
                }
        self._create_artifact('withprops', data=data)

        url = '/withprops/v1.0/drafts?version=gt:4.0&version=lt:10.1'
        result = self._check_artifact_get(url=url)['artifacts']

        self.assertEqual(2, len(result))

        url = '/withprops/v1.0/drafts?version=gt:4.0&version=ne:4.3'
        result = self._check_artifact_get(url=url)['artifacts']

        self.assertEqual(1, len(result))

    def test_operation_patch_with_blob(self):
        data = {'name': 'art1',
                'version': '3.2'
                }
        art = self._create_artifact('withblob', data=data)

        msg = 'Invalid request PATCH for work with blob'

        result = self._check_artifact_patch(
            '/withblob/v1.0/%s' % art['id'],
            status=400,
            data=[{'op': 'replace',
                   'value': 'public',
                   'path': '/blob1'}])
        self.assertIn(msg, result)

        result = self._check_artifact_patch(
            '/withblob/v1.0/%s' % art['id'],
            status=400,
            data=[{'op': 'remove',
                   'value': 'public',
                   'path': '/blob1'}])
        self.assertIn(msg, result)

        result = self._check_artifact_patch(
            '/withblob/v1.0/%s' % art['id'],
            status=400,
            data=[{'op': 'add',
                   'value': 'public',
                   'path': '/blob1'}])
        self.assertIn(msg, result)

    def test_filter_by_bad_version(self):
        bad_versions = ['kkk', '1.k', 'h.0', '1.3.hf', 's.9.2s2']
        response_string = ('The format of the version %s is not valid. '
                           'Use semver notation')
        for bad_version in bad_versions:
            url = '/withprops/v1.0/drafts?version=gt:%s' % bad_version
            result = self._check_artifact_get(url=url, status=400)
            self.assertIn(response_string % bad_version, result)

    def test_circular_dependency(self):
        data = {'name': 'artifact',
                'version': '12'}
        art = self._create_artifact('withprops', data=data)

        upd = self._check_artifact_post(
            '/withprops/v1.0/%s/depends_on' % art['id'],
            data={'data': art['id']}, status=400)
        self.assertIn(
            'Artifact with a circular dependency can not be created', upd)
