Implemented input data validation
Change-Id: I8407cf4cbb69e60b7def891625522f3ca3c822fe Implements: blueprint unify-input-data
This commit is contained in:
parent
d99d1228ef
commit
9e107cde9a
@ -19,6 +19,7 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import jsonschema
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from packetary.controllers import RepositoryController
|
from packetary.controllers import RepositoryController
|
||||||
@ -28,7 +29,8 @@ from packetary.objects import PackageRelation
|
|||||||
from packetary.objects import PackagesForest
|
from packetary.objects import PackagesForest
|
||||||
from packetary.objects import PackagesTree
|
from packetary.objects import PackagesTree
|
||||||
from packetary.objects.statistics import CopyStatistics
|
from packetary.objects.statistics import CopyStatistics
|
||||||
|
from packetary.schemas import PACKAGE_FILES_SCHEMA
|
||||||
|
from packetary.schemas import PACKAGES_SCHEMA
|
||||||
|
|
||||||
logger = logging.getLogger(__package__)
|
logger = logging.getLogger(__package__)
|
||||||
|
|
||||||
@ -122,6 +124,7 @@ class RepositoryApi(object):
|
|||||||
:param package_files: The list of URLs of packages
|
:param package_files: The list of URLs of packages
|
||||||
"""
|
"""
|
||||||
self._validate_repo_data(repo_data)
|
self._validate_repo_data(repo_data)
|
||||||
|
self._validate_package_files(package_files)
|
||||||
return self.controller.create_repository(repo_data, package_files)
|
return self.controller.create_repository(repo_data, package_files)
|
||||||
|
|
||||||
def get_packages(self, repos_data, requirements_data=None,
|
def get_packages(self, repos_data, requirements_data=None,
|
||||||
@ -215,7 +218,6 @@ class RepositoryApi(object):
|
|||||||
self._validate_requirements_data(requirements_data)
|
self._validate_requirements_data(requirements_data)
|
||||||
result = []
|
result = []
|
||||||
for r in requirements_data:
|
for r in requirements_data:
|
||||||
self._validate_requirements_data(r)
|
|
||||||
versions = r.get('versions', None)
|
versions = r.get('versions', None)
|
||||||
if versions is None:
|
if versions is None:
|
||||||
result.append(PackageRelation.from_args((r['name'],)))
|
result.append(PackageRelation.from_args((r['name'],)))
|
||||||
@ -227,9 +229,34 @@ class RepositoryApi(object):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _validate_repo_data(self, repo_data):
|
def _validate_repo_data(self, repo_data):
|
||||||
# TODO(bgaifullin) implement me
|
schema = self.controller.get_repository_data_schema()
|
||||||
pass
|
self._validate_data(repo_data, schema)
|
||||||
|
|
||||||
def _validate_requirements_data(self, requirements_data):
|
def _validate_requirements_data(self, requirements_data):
|
||||||
# TODO(bgaifullin) implement me
|
self._validate_data(requirements_data, PACKAGES_SCHEMA)
|
||||||
pass
|
|
||||||
|
def _validate_package_files(self, package_files):
|
||||||
|
self._validate_data(package_files, PACKAGE_FILES_SCHEMA)
|
||||||
|
|
||||||
|
def _validate_data(self, data, schema):
|
||||||
|
"""Validate the input data using jsonschema validation.
|
||||||
|
|
||||||
|
:param data: a data to validate represented as a dict
|
||||||
|
:param schema: a schema to validate represented as a dict;
|
||||||
|
must be in JSON Schema Draft 4 format.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
jsonschema.validate(data, schema)
|
||||||
|
except jsonschema.ValidationError as e:
|
||||||
|
self._raise_validation_error("data", e.message, e.absolute_path)
|
||||||
|
except jsonschema.SchemaError as e:
|
||||||
|
self._raise_validation_error(
|
||||||
|
"schema", e.message, e.absolute_schema_path
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _raise_validation_error(what, details, path):
|
||||||
|
message = "Invalid {0}: {1}.".format(what, details)
|
||||||
|
if path:
|
||||||
|
message = "\n".join((message, "Field: {0}".format(".".join(path))))
|
||||||
|
raise ValueError(message)
|
||||||
|
@ -137,6 +137,13 @@ class RepositoryController(object):
|
|||||||
self.assign_packages(repo, packages)
|
self.assign_packages(repo, packages)
|
||||||
return repo
|
return repo
|
||||||
|
|
||||||
|
def get_repository_data_schema(self):
|
||||||
|
"""Return jsonschema to validate data for required driver.
|
||||||
|
|
||||||
|
:return : Return a jsonschema represented as a dict
|
||||||
|
"""
|
||||||
|
return self.driver.repository_data_schema()
|
||||||
|
|
||||||
def _copy_packages(self, target, packages, observer):
|
def _copy_packages(self, target, packages, observer):
|
||||||
with self.context.async_section() as section:
|
with self.context.async_section() as section:
|
||||||
for package in packages:
|
for package in packages:
|
||||||
|
@ -111,3 +111,7 @@ class RepositoryDriverBase(object):
|
|||||||
:return: the integer value that is relevant repository`s priority
|
:return: the integer value that is relevant repository`s priority
|
||||||
less number means greater priority
|
less number means greater priority
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_repository_data_schema(self):
|
||||||
|
"""Gets the json scheme for repository data validation."""
|
||||||
|
@ -36,6 +36,7 @@ from packetary.objects import FileChecksum
|
|||||||
from packetary.objects import Package
|
from packetary.objects import Package
|
||||||
from packetary.objects import PackageRelation
|
from packetary.objects import PackageRelation
|
||||||
from packetary.objects import Repository
|
from packetary.objects import Repository
|
||||||
|
from packetary.schemas import DEB_REPO_SCHEMA
|
||||||
|
|
||||||
|
|
||||||
_OPERATORS_MAPPING = {
|
_OPERATORS_MAPPING = {
|
||||||
@ -83,6 +84,9 @@ _checksum_collector = checksum_composite('md5', 'sha1', 'sha256')
|
|||||||
|
|
||||||
|
|
||||||
class DebRepositoryDriver(RepositoryDriverBase):
|
class DebRepositoryDriver(RepositoryDriverBase):
|
||||||
|
def get_repository_data_schema(self):
|
||||||
|
return DEB_REPO_SCHEMA
|
||||||
|
|
||||||
def priority_sort(self, repo_data):
|
def priority_sort(self, repo_data):
|
||||||
# DEB repository expects general values from 0 to 1000. 0
|
# DEB repository expects general values from 0 to 1000. 0
|
||||||
# to have lowest priority and 1000 -- the highest. Note that a
|
# to have lowest priority and 1000 -- the highest. Note that a
|
||||||
@ -94,7 +98,7 @@ class DebRepositoryDriver(RepositoryDriverBase):
|
|||||||
return -priority
|
return -priority
|
||||||
|
|
||||||
def get_repository(self, connection, repository_data, arch, consumer):
|
def get_repository(self, connection, repository_data, arch, consumer):
|
||||||
url = utils.normalize_repository_url(repository_data['url'])
|
url = utils.normalize_repository_url(repository_data['uri'])
|
||||||
suite = repository_data['suite']
|
suite = repository_data['suite']
|
||||||
components = repository_data.get('section')
|
components = repository_data.get('section')
|
||||||
path = repository_data.get('path')
|
path = repository_data.get('path')
|
||||||
@ -202,7 +206,7 @@ class DebRepositoryDriver(RepositoryDriverBase):
|
|||||||
return new_repo
|
return new_repo
|
||||||
|
|
||||||
def create_repository(self, repository_data, arch):
|
def create_repository(self, repository_data, arch):
|
||||||
url = utils.normalize_repository_url(repository_data['url'])
|
url = utils.normalize_repository_url(repository_data['uri'])
|
||||||
suite = repository_data['suite']
|
suite = repository_data['suite']
|
||||||
component = repository_data.get('section')
|
component = repository_data.get('section')
|
||||||
path = repository_data.get('path')
|
path = repository_data.get('path')
|
||||||
|
@ -36,6 +36,7 @@ from packetary.objects import PackageRelation
|
|||||||
from packetary.objects import PackageVersion
|
from packetary.objects import PackageVersion
|
||||||
from packetary.objects import Repository
|
from packetary.objects import Repository
|
||||||
from packetary.objects import VersionRange
|
from packetary.objects import VersionRange
|
||||||
|
from packetary.schemas import RPM_REPO_SCHEMA
|
||||||
|
|
||||||
|
|
||||||
urljoin = six.moves.urllib.parse.urljoin
|
urljoin = six.moves.urllib.parse.urljoin
|
||||||
@ -86,6 +87,9 @@ class CreaterepoCallBack(object):
|
|||||||
|
|
||||||
|
|
||||||
class RpmRepositoryDriver(RepositoryDriverBase):
|
class RpmRepositoryDriver(RepositoryDriverBase):
|
||||||
|
def get_repository_data_schema(self):
|
||||||
|
return RPM_REPO_SCHEMA
|
||||||
|
|
||||||
def priority_sort(self, repo_data):
|
def priority_sort(self, repo_data):
|
||||||
# DEB repository expects general values from 0 to 1000. 0
|
# DEB repository expects general values from 0 to 1000. 0
|
||||||
# to have lowest priority and 1000 -- the highest. Note that a
|
# to have lowest priority and 1000 -- the highest. Note that a
|
||||||
@ -99,7 +103,7 @@ class RpmRepositoryDriver(RepositoryDriverBase):
|
|||||||
def get_repository(self, connection, repository_data, arch, consumer):
|
def get_repository(self, connection, repository_data, arch, consumer):
|
||||||
consumer(Repository(
|
consumer(Repository(
|
||||||
name=repository_data['name'],
|
name=repository_data['name'],
|
||||||
url=utils.normalize_repository_url(repository_data["url"]),
|
url=utils.normalize_repository_url(repository_data["uri"]),
|
||||||
architecture=arch,
|
architecture=arch,
|
||||||
origin=""
|
origin=""
|
||||||
))
|
))
|
||||||
@ -195,7 +199,7 @@ class RpmRepositoryDriver(RepositoryDriverBase):
|
|||||||
def create_repository(self, repository_data, arch):
|
def create_repository(self, repository_data, arch):
|
||||||
repository = Repository(
|
repository = Repository(
|
||||||
name=repository_data['name'],
|
name=repository_data['name'],
|
||||||
url=utils.normalize_repository_url(repository_data["url"]),
|
url=utils.normalize_repository_url(repository_data["uri"]),
|
||||||
architecture=arch,
|
architecture=arch,
|
||||||
origin=repository_data.get('origin')
|
origin=repository_data.get('origin')
|
||||||
)
|
)
|
||||||
|
29
packetary/schemas/__init__.py
Normal file
29
packetary/schemas/__init__.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2016 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
from packetary.schemas.deb_repo_schema import DEB_REPO_SCHEMA
|
||||||
|
from packetary.schemas.package_files_schema import PACKAGE_FILES_SCHEMA
|
||||||
|
from packetary.schemas.packages_schema import PACKAGES_SCHEMA
|
||||||
|
from packetary.schemas.rpm_repo_schema import RPM_REPO_SCHEMA
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"DEB_REPO_SCHEMA",
|
||||||
|
"PACKAGES_SCHEMA",
|
||||||
|
"RPM_REPO_SCHEMA",
|
||||||
|
"PACKAGE_FILES_SCHEMA"
|
||||||
|
]
|
56
packetary/schemas/deb_repo_schema.py
Normal file
56
packetary/schemas/deb_repo_schema.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2016 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
DEB_REPO_SCHEMA = {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"uri",
|
||||||
|
"suite",
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"suite": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"section": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
packetary/schemas/package_files_schema.py
Normal file
26
packetary/schemas/package_files_schema.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2016 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
PACKAGE_FILES_SCHEMA = {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(\/|file:\/\/|https?:\/\/).+$"
|
||||||
|
}
|
||||||
|
}
|
43
packetary/schemas/packages_schema.py
Normal file
43
packetary/schemas/packages_schema.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2016 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
PACKAGES_SCHEMA = {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"versions"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"versions": {
|
||||||
|
"type": "array",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^([<>]=?|=)\s+.+$"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
packetary/schemas/rpm_repo_schema.py
Normal file
49
packetary/schemas/rpm_repo_schema.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2016 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
RPM_REPO_SCHEMA = {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"uri",
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 99,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,3 +31,9 @@ class TestCase(unittest.TestCase):
|
|||||||
assertion(
|
assertion(
|
||||||
exp, method(*value), "{0} != f({1})".format(exp, value)
|
exp, method(*value), "{0} != f({1})".format(exp, value)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def assertNotRaises(self, exception, method, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
method(*args, **kwargs)
|
||||||
|
except exception as e:
|
||||||
|
self.fail("Unexpected error: {0}".format(e))
|
||||||
|
@ -22,6 +22,7 @@ from packetary import objects
|
|||||||
def gen_repository(name="test", url="file:///test",
|
def gen_repository(name="test", url="file:///test",
|
||||||
architecture="x86_64", origin="Test", **kwargs):
|
architecture="x86_64", origin="Test", **kwargs):
|
||||||
"""Helper to create Repository object with default attributes."""
|
"""Helper to create Repository object with default attributes."""
|
||||||
|
url = kwargs.pop("uri", url)
|
||||||
return objects.Repository(name, url, architecture, origin, **kwargs)
|
return objects.Repository(name, url, architecture, origin, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import os.path as path
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from packetary.drivers import deb_driver
|
from packetary.drivers import deb_driver
|
||||||
|
from packetary.schemas import DEB_REPO_SCHEMA
|
||||||
from packetary.tests import base
|
from packetary.tests import base
|
||||||
from packetary.tests.stubs.generator import gen_package
|
from packetary.tests.stubs.generator import gen_package
|
||||||
from packetary.tests.stubs.generator import gen_repository
|
from packetary.tests.stubs.generator import gen_repository
|
||||||
@ -66,7 +67,7 @@ class TestDebDriver(base.TestCase):
|
|||||||
def test_get_repository(self):
|
def test_get_repository(self):
|
||||||
repos = []
|
repos = []
|
||||||
repo_data = {
|
repo_data = {
|
||||||
"name": "repo1", "url": "http://host", "suite": "trusty",
|
"name": "repo1", "uri": "http://host", "suite": "trusty",
|
||||||
"section": ["main", "universe"], "path": "my_path"
|
"section": ["main", "universe"], "path": "my_path"
|
||||||
}
|
}
|
||||||
self.connection.open_stream.return_value = {"Origin": "Ubuntu"}
|
self.connection.open_stream.return_value = {"Origin": "Ubuntu"}
|
||||||
@ -99,7 +100,7 @@ class TestDebDriver(base.TestCase):
|
|||||||
|
|
||||||
def test_get_repository_if_release_does_not_exist(self):
|
def test_get_repository_if_release_does_not_exist(self):
|
||||||
repo_data = {
|
repo_data = {
|
||||||
"name": "repo1", "url": "http://host", "suite": "trusty",
|
"name": "repo1", "uri": "http://host", "suite": "trusty",
|
||||||
"section": ["main"], "path": "my_path"
|
"section": ["main"], "path": "my_path"
|
||||||
}
|
}
|
||||||
repos = []
|
repos = []
|
||||||
@ -115,7 +116,7 @@ class TestDebDriver(base.TestCase):
|
|||||||
|
|
||||||
def test_get_repository_fail_if_error(self):
|
def test_get_repository_fail_if_error(self):
|
||||||
repo_data = {
|
repo_data = {
|
||||||
"name": "repo1", "url": "http://host", "suite": "trusty",
|
"name": "repo1", "uri": "http://host", "suite": "trusty",
|
||||||
"section": ["main"], "path": "my_path"
|
"section": ["main"], "path": "my_path"
|
||||||
}
|
}
|
||||||
repos = []
|
repos = []
|
||||||
@ -132,7 +133,7 @@ class TestDebDriver(base.TestCase):
|
|||||||
with self.assertRaisesRegexp(ValueError, "does not supported"):
|
with self.assertRaisesRegexp(ValueError, "does not supported"):
|
||||||
self.driver.get_repository(
|
self.driver.get_repository(
|
||||||
self.connection,
|
self.connection,
|
||||||
{"url": "http://host", "suite": "trusty"},
|
{"uri": "http://host", "suite": "trusty"},
|
||||||
"x86_64",
|
"x86_64",
|
||||||
lambda x: None
|
lambda x: None
|
||||||
)
|
)
|
||||||
@ -313,14 +314,14 @@ class TestDebDriver(base.TestCase):
|
|||||||
@mock.patch("packetary.drivers.deb_driver.utils.ensure_dir_exist")
|
@mock.patch("packetary.drivers.deb_driver.utils.ensure_dir_exist")
|
||||||
def test_create_repository(self, mkdir_mock, deb822, gzip, open, os):
|
def test_create_repository(self, mkdir_mock, deb822, gzip, open, os):
|
||||||
repository_data = {
|
repository_data = {
|
||||||
"name": "Test", "url": "file:///repo", "suite": "trusty",
|
"name": "Test", "uri": "file:///repo", "suite": "trusty",
|
||||||
"section": "main", "type": "rpm", "priority": "100",
|
"section": "main", "type": "rpm", "priority": "100",
|
||||||
"origin": "Origin", "path": "/repo"
|
"origin": "Origin", "path": "/repo"
|
||||||
}
|
}
|
||||||
repo = self.driver.create_repository(repository_data, "x86_64")
|
repo = self.driver.create_repository(repository_data, "x86_64")
|
||||||
self.assertEqual(repository_data["name"], repo.name)
|
self.assertEqual(repository_data["name"], repo.name)
|
||||||
self.assertEqual("x86_64", repo.architecture)
|
self.assertEqual("x86_64", repo.architecture)
|
||||||
self.assertEqual(repository_data["url"] + "/", repo.url)
|
self.assertEqual(repository_data["uri"] + "/", repo.url)
|
||||||
self.assertEqual(repository_data["origin"], repo.origin)
|
self.assertEqual(repository_data["origin"], repo.origin)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
(repository_data["suite"], repository_data["section"]),
|
(repository_data["suite"], repository_data["section"]),
|
||||||
@ -340,7 +341,7 @@ class TestDebDriver(base.TestCase):
|
|||||||
|
|
||||||
def test_createrepository_fails_if_invalid_data(self):
|
def test_createrepository_fails_if_invalid_data(self):
|
||||||
repository_data = {
|
repository_data = {
|
||||||
"name": "Test", "url": "file:///repo", "suite": "trusty",
|
"name": "Test", "uri": "file:///repo", "suite": "trusty",
|
||||||
"type": "rpm", "priority": "100",
|
"type": "rpm", "priority": "100",
|
||||||
"origin": "Origin", "path": "/repo"
|
"origin": "Origin", "path": "/repo"
|
||||||
}
|
}
|
||||||
@ -397,3 +398,7 @@ class TestDebDriver(base.TestCase):
|
|||||||
)
|
)
|
||||||
rel_path = self.driver.get_relative_path(repo, "test.pkg")
|
rel_path = self.driver.get_relative_path(repo, "test.pkg")
|
||||||
self.assertEqual("pool/main/t/test.pkg", rel_path)
|
self.assertEqual("pool/main/t/test.pkg", rel_path)
|
||||||
|
|
||||||
|
def test_get_repository_data_scheme(self):
|
||||||
|
schema = self.driver.get_repository_data_schema()
|
||||||
|
self.assertIs(DEB_REPO_SCHEMA, schema)
|
||||||
|
@ -19,21 +19,31 @@
|
|||||||
import copy
|
import copy
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
import jsonschema
|
||||||
|
|
||||||
from packetary.api import Configuration
|
from packetary.api import Configuration
|
||||||
from packetary.api import Context
|
from packetary.api import Context
|
||||||
from packetary.api import RepositoryApi
|
from packetary.api import RepositoryApi
|
||||||
|
from packetary.schemas import PACKAGE_FILES_SCHEMA
|
||||||
|
from packetary.schemas import PACKAGES_SCHEMA
|
||||||
from packetary.tests import base
|
from packetary.tests import base
|
||||||
from packetary.tests.stubs import generator
|
from packetary.tests.stubs import generator
|
||||||
from packetary.tests.stubs.helpers import CallbacksAdapter
|
from packetary.tests.stubs.helpers import CallbacksAdapter
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("packetary.api.jsonschema")
|
||||||
class TestRepositoryApi(base.TestCase):
|
class TestRepositoryApi(base.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.controller = CallbacksAdapter()
|
self.controller = CallbacksAdapter()
|
||||||
self.api = RepositoryApi(self.controller)
|
self.api = RepositoryApi(self.controller)
|
||||||
self.repo_data = {"name": "repo1", "url": "file:///repo1"}
|
self.repo_data = {"name": "repo1", "uri": "file:///repo1"}
|
||||||
|
self.requirements_data = [
|
||||||
|
{"name": "test1"}, {"name": "test2", "versions": ["< 3", "> 1"]}
|
||||||
|
]
|
||||||
|
self.schema = {}
|
||||||
self.repo = generator.gen_repository(**self.repo_data)
|
self.repo = generator.gen_repository(**self.repo_data)
|
||||||
self.controller.load_repositories.return_value = [self.repo]
|
self.controller.load_repositories.return_value = [self.repo]
|
||||||
|
self.controller.get_repository_data_schema.return_value = self.schema
|
||||||
self._generate_packages()
|
self._generate_packages()
|
||||||
|
|
||||||
def _generate_packages(self):
|
def _generate_packages(self):
|
||||||
@ -56,7 +66,8 @@ class TestRepositoryApi(base.TestCase):
|
|||||||
|
|
||||||
@mock.patch("packetary.api.RepositoryController")
|
@mock.patch("packetary.api.RepositoryController")
|
||||||
@mock.patch("packetary.api.ConnectionsManager")
|
@mock.patch("packetary.api.ConnectionsManager")
|
||||||
def test_create_with_config(self, connection_mock, controller_mock):
|
def test_create_with_config(self, connection_mock, controller_mock,
|
||||||
|
jsonschema_mock):
|
||||||
config = Configuration(
|
config = Configuration(
|
||||||
http_proxy="http://localhost", https_proxy="https://localhost",
|
http_proxy="http://localhost", https_proxy="https://localhost",
|
||||||
retries_num=10, retry_interval=1, threads_num=8,
|
retries_num=10, retry_interval=1, threads_num=8,
|
||||||
@ -75,7 +86,8 @@ class TestRepositoryApi(base.TestCase):
|
|||||||
|
|
||||||
@mock.patch("packetary.api.RepositoryController")
|
@mock.patch("packetary.api.RepositoryController")
|
||||||
@mock.patch("packetary.api.ConnectionsManager")
|
@mock.patch("packetary.api.ConnectionsManager")
|
||||||
def test_create_with_context(self, connection_mock, controller_mock):
|
def test_create_with_context(self, connection_mock, controller_mock,
|
||||||
|
jsonschema_mock):
|
||||||
config = Configuration(
|
config = Configuration(
|
||||||
http_proxy="http://localhost", https_proxy="https://localhost",
|
http_proxy="http://localhost", https_proxy="https://localhost",
|
||||||
retries_num=10, retry_interval=1, threads_num=8,
|
retries_num=10, retry_interval=1, threads_num=8,
|
||||||
@ -93,42 +105,67 @@ class TestRepositoryApi(base.TestCase):
|
|||||||
context, "deb", "x86_64"
|
context, "deb", "x86_64"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_repository(self):
|
def test_create_repository(self, jsonschema_mock):
|
||||||
file_urls = ["file://test1.pkg"]
|
file_urls = ["file://test1.pkg"]
|
||||||
self.api.create_repository(self.repo_data, file_urls)
|
self.api.create_repository(self.repo_data, file_urls)
|
||||||
self.controller.create_repository.assert_called_once_with(
|
self.controller.create_repository.assert_called_once_with(
|
||||||
self.repo_data, file_urls
|
self.repo_data, file_urls
|
||||||
)
|
)
|
||||||
|
jsonschema_mock.validate.assert_has_calls(
|
||||||
|
[
|
||||||
|
mock.call(self.repo_data, self.schema),
|
||||||
|
mock.call(file_urls, PACKAGE_FILES_SCHEMA),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def test_get_packages_as_is(self):
|
def test_get_packages_as_is(self, jsonschema_mock):
|
||||||
packages = self.api.get_packages([self.repo_data], None)
|
packages = self.api.get_packages([self.repo_data], None)
|
||||||
self.assertEqual(5, len(packages))
|
self.assertEqual(5, len(packages))
|
||||||
self.assertItemsEqual(
|
self.assertItemsEqual(
|
||||||
self.packages,
|
self.packages,
|
||||||
packages
|
packages
|
||||||
)
|
)
|
||||||
|
jsonschema_mock.validate.assert_called_once_with(
|
||||||
|
self.repo_data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
def test_get_packages_by_requirements_with_mandatory(self):
|
def test_get_packages_by_requirements_with_mandatory(self,
|
||||||
|
jsonschema_mock):
|
||||||
|
requirements = [{"name": "package1"}]
|
||||||
packages = self.api.get_packages(
|
packages = self.api.get_packages(
|
||||||
[self.repo_data], [{"name": "package1"}], True
|
[self.repo_data], requirements, True
|
||||||
)
|
)
|
||||||
self.assertEqual(3, len(packages))
|
self.assertEqual(3, len(packages))
|
||||||
self.assertItemsEqual(
|
self.assertItemsEqual(
|
||||||
["package1", "package2", "package3"],
|
["package1", "package2", "package3"],
|
||||||
(x.name for x in packages)
|
(x.name for x in packages)
|
||||||
)
|
)
|
||||||
|
jsonschema_mock.validate.assert_has_calls(
|
||||||
|
[
|
||||||
|
mock.call(self.repo_data, self.schema),
|
||||||
|
mock.call(requirements, PACKAGES_SCHEMA),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def test_get_packages_by_requirements_without_mandatory(self):
|
def test_get_packages_by_requirements_without_mandatory(self,
|
||||||
|
jsonschema_mock):
|
||||||
|
requirements = [{"name": "package4"}]
|
||||||
packages = self.api.get_packages(
|
packages = self.api.get_packages(
|
||||||
[self.repo_data], [{"name": "package4"}], False
|
[self.repo_data], requirements, False
|
||||||
)
|
)
|
||||||
self.assertEqual(2, len(packages))
|
self.assertEqual(2, len(packages))
|
||||||
self.assertItemsEqual(
|
self.assertItemsEqual(
|
||||||
["package1", "package4"],
|
["package1", "package4"],
|
||||||
(x.name for x in packages)
|
(x.name for x in packages)
|
||||||
)
|
)
|
||||||
|
jsonschema_mock.validate.assert_has_calls(
|
||||||
|
[
|
||||||
|
mock.call(self.repo_data, self.schema),
|
||||||
|
mock.call(requirements, PACKAGES_SCHEMA),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def test_clone_repositories_as_is(self):
|
def test_clone_repositories_as_is(self, jsonschema_mock):
|
||||||
# return value is used as statistics
|
# return value is used as statistics
|
||||||
mirror = copy.copy(self.repo)
|
mirror = copy.copy(self.repo)
|
||||||
mirror.url = "file:///mirror/repo"
|
mirror.url = "file:///mirror/repo"
|
||||||
@ -143,15 +180,19 @@ class TestRepositoryApi(base.TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(6, stats.total)
|
self.assertEqual(6, stats.total)
|
||||||
self.assertEqual(4, stats.copied)
|
self.assertEqual(4, stats.copied)
|
||||||
|
jsonschema_mock.validate.assert_called_once_with(
|
||||||
|
self.repo_data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
def test_clone_by_requirements_with_mandatory(self):
|
def test_clone_by_requirements_with_mandatory(self, jsonschema_mock):
|
||||||
# return value is used as statistics
|
# return value is used as statistics
|
||||||
mirror = copy.copy(self.repo)
|
mirror = copy.copy(self.repo)
|
||||||
mirror.url = "file:///mirror/repo"
|
mirror.url = "file:///mirror/repo"
|
||||||
|
requirements = [{"name": "package1"}]
|
||||||
self.controller.fork_repository.return_value = mirror
|
self.controller.fork_repository.return_value = mirror
|
||||||
self.controller.assign_packages.return_value = [0, 1, 1]
|
self.controller.assign_packages.return_value = [0, 1, 1]
|
||||||
stats = self.api.clone_repositories(
|
stats = self.api.clone_repositories(
|
||||||
[self.repo_data], [{"name": "package1"}],
|
[self.repo_data], requirements,
|
||||||
"/mirror", include_mandatory=True
|
"/mirror", include_mandatory=True
|
||||||
)
|
)
|
||||||
packages = {self.packages[0], self.packages[1], self.packages[2]}
|
packages = {self.packages[0], self.packages[1], self.packages[2]}
|
||||||
@ -163,15 +204,23 @@ class TestRepositoryApi(base.TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(3, stats.total)
|
self.assertEqual(3, stats.total)
|
||||||
self.assertEqual(2, stats.copied)
|
self.assertEqual(2, stats.copied)
|
||||||
|
jsonschema_mock.validate.assert_has_calls(
|
||||||
|
[
|
||||||
|
mock.call(self.repo_data, self.schema),
|
||||||
|
mock.call(requirements, PACKAGES_SCHEMA),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def test_clone_by_requirements_without_mandatory(self):
|
def test_clone_by_requirements_without_mandatory(self,
|
||||||
|
jsonschema_mock):
|
||||||
# return value is used as statistics
|
# return value is used as statistics
|
||||||
mirror = copy.copy(self.repo)
|
mirror = copy.copy(self.repo)
|
||||||
mirror.url = "file:///mirror/repo"
|
mirror.url = "file:///mirror/repo"
|
||||||
|
requirements = [{"name": "package4"}]
|
||||||
self.controller.fork_repository.return_value = mirror
|
self.controller.fork_repository.return_value = mirror
|
||||||
self.controller.assign_packages.return_value = [0, 4]
|
self.controller.assign_packages.return_value = [0, 4]
|
||||||
stats = self.api.clone_repositories(
|
stats = self.api.clone_repositories(
|
||||||
[self.repo_data], [{"name": "package4"}],
|
[self.repo_data], requirements,
|
||||||
"/mirror", include_mandatory=False
|
"/mirror", include_mandatory=False
|
||||||
)
|
)
|
||||||
packages = {self.packages[0], self.packages[3]}
|
packages = {self.packages[0], self.packages[3]}
|
||||||
@ -183,30 +232,65 @@ class TestRepositoryApi(base.TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(2, stats.total)
|
self.assertEqual(2, stats.total)
|
||||||
self.assertEqual(1, stats.copied)
|
self.assertEqual(1, stats.copied)
|
||||||
|
jsonschema_mock.validate.assert_has_calls(
|
||||||
|
[
|
||||||
|
mock.call(self.repo_data, self.schema),
|
||||||
|
mock.call(requirements, PACKAGES_SCHEMA),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def test_get_unresolved(self):
|
def test_get_unresolved(self, jsonschema_mock):
|
||||||
unresolved = self.api.get_unresolved_dependencies([self.repo_data])
|
unresolved = self.api.get_unresolved_dependencies([self.repo_data])
|
||||||
self.assertItemsEqual(["package6"], (x.name for x in unresolved))
|
self.assertItemsEqual(["package6"], (x.name for x in unresolved))
|
||||||
|
jsonschema_mock.validate.assert_called_once_with(
|
||||||
|
self.repo_data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
def test_load_requirements(self):
|
def test_load_requirements(self, jsonschema_mock):
|
||||||
expected = {
|
expected = {
|
||||||
generator.gen_relation("test1"),
|
generator.gen_relation("test1"),
|
||||||
generator.gen_relation("test2", ["<", "3"]),
|
generator.gen_relation("test2", ["<", "3"]),
|
||||||
generator.gen_relation("test2", [">", "1"]),
|
generator.gen_relation("test2", [">", "1"]),
|
||||||
}
|
}
|
||||||
actual = set(self.api._load_requirements(
|
actual = set(self.api._load_requirements(
|
||||||
[{"name": "test1"}, {"name": "test2", "versions": ["< 3", "> 1"]}]
|
self.requirements_data
|
||||||
))
|
))
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
self.assertIsNone(self.api._load_requirements(None))
|
self.assertIsNone(self.api._load_requirements(None))
|
||||||
|
jsonschema_mock.validate.assert_called_once_with(
|
||||||
|
self.requirements_data,
|
||||||
|
PACKAGES_SCHEMA
|
||||||
|
)
|
||||||
|
|
||||||
def test_validate_repo_data(self):
|
def test_validate_data(self, jsonschema_mock):
|
||||||
# TODO(bgaifullin) implement me
|
self.api._validate_data(self.repo_data, self.schema)
|
||||||
pass
|
jsonschema_mock.validate.assert_called_once_with(
|
||||||
|
self.repo_data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
def test_validate_requirements_data(self):
|
def test_validate_invalid_data(self, jschema_m):
|
||||||
# TODO(bgaifullin) implement me
|
jschema_m.ValidationError = jsonschema.ValidationError
|
||||||
pass
|
jschema_m.SchemaError = jsonschema.SchemaError
|
||||||
|
|
||||||
|
paths = [("a", "b"), ()]
|
||||||
|
for path in paths:
|
||||||
|
msg = "Invalid data: error."
|
||||||
|
details = "\nField: {0}".format(".".join(path)) if path else ""
|
||||||
|
with self.assertRaisesRegexp(ValueError, msg + details):
|
||||||
|
jschema_m.validate.side_effect = jsonschema.ValidationError(
|
||||||
|
"error", path=path
|
||||||
|
)
|
||||||
|
self.api._validate_data([], {})
|
||||||
|
jschema_m.validate.assert_called_with([], {})
|
||||||
|
jschema_m.validate.reset_mock()
|
||||||
|
|
||||||
|
msg = "Invalid schema: error."
|
||||||
|
with self.assertRaisesRegexp(ValueError, msg + details):
|
||||||
|
jschema_m.validate.side_effect = jsonschema.SchemaError(
|
||||||
|
"error", schema_path=path
|
||||||
|
)
|
||||||
|
self.api._validate_data([], {})
|
||||||
|
jschema_m.validate.assert_called_with([], {})
|
||||||
|
|
||||||
|
|
||||||
class TestContext(base.TestCase):
|
class TestContext(base.TestCase):
|
||||||
|
@ -53,7 +53,7 @@ class TestRepositoryController(base.TestCase):
|
|||||||
self.assertIs(self.driver, controller.driver)
|
self.assertIs(self.driver, controller.driver)
|
||||||
|
|
||||||
def test_load_repositories(self):
|
def test_load_repositories(self):
|
||||||
repo_data = {"name": "test", "url": "file:///test1"}
|
repo_data = {"name": "test", "uri": "file:///test1"}
|
||||||
repo = gen_repository(**repo_data)
|
repo = gen_repository(**repo_data)
|
||||||
self.driver.get_repository = CallbacksAdapter()
|
self.driver.get_repository = CallbacksAdapter()
|
||||||
self.driver.get_repository.side_effect = [repo]
|
self.driver.get_repository.side_effect = [repo]
|
||||||
@ -150,7 +150,7 @@ class TestRepositoryController(base.TestCase):
|
|||||||
|
|
||||||
def test_create_repository(self):
|
def test_create_repository(self):
|
||||||
repository_data = {
|
repository_data = {
|
||||||
"name": "Test", "url": "file:///repo/",
|
"name": "Test", "uri": "file:///repo/",
|
||||||
"section": ("trusty", "main"), "origin": "Test"
|
"section": ("trusty", "main"), "origin": "Test"
|
||||||
}
|
}
|
||||||
repo = gen_repository(**repository_data)
|
repo = gen_repository(**repository_data)
|
||||||
|
@ -23,6 +23,7 @@ import sys
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from packetary.objects import FileChecksum
|
from packetary.objects import FileChecksum
|
||||||
|
from packetary.schemas import RPM_REPO_SCHEMA
|
||||||
from packetary.tests import base
|
from packetary.tests import base
|
||||||
from packetary.tests.stubs.generator import gen_repository
|
from packetary.tests.stubs.generator import gen_repository
|
||||||
from packetary.tests.stubs.helpers import get_compressed
|
from packetary.tests.stubs.helpers import get_compressed
|
||||||
@ -68,7 +69,7 @@ class TestRpmDriver(base.TestCase):
|
|||||||
|
|
||||||
def test_get_repository(self):
|
def test_get_repository(self):
|
||||||
repos = []
|
repos = []
|
||||||
repo_data = {"name": "os", "url": "http://host/centos/os/x86_64/"}
|
repo_data = {"name": "os", "uri": "http://host/centos/os/x86_64/"}
|
||||||
self.driver.get_repository(
|
self.driver.get_repository(
|
||||||
self.connection,
|
self.connection,
|
||||||
repo_data,
|
repo_data,
|
||||||
@ -220,13 +221,13 @@ class TestRpmDriver(base.TestCase):
|
|||||||
@mock.patch("packetary.drivers.rpm_driver.utils.ensure_dir_exist")
|
@mock.patch("packetary.drivers.rpm_driver.utils.ensure_dir_exist")
|
||||||
def test_create_repository(self, ensure_dir_exists_mock):
|
def test_create_repository(self, ensure_dir_exists_mock):
|
||||||
repository_data = {
|
repository_data = {
|
||||||
"name": "Test", "url": "file:///repo/os/x86_64", "origin": "Test"
|
"name": "Test", "uri": "file:///repo/os/x86_64", "origin": "Test"
|
||||||
}
|
}
|
||||||
repo = self.driver.create_repository(repository_data, "x86_64")
|
repo = self.driver.create_repository(repository_data, "x86_64")
|
||||||
ensure_dir_exists_mock.assert_called_once_with("/repo/os/x86_64/")
|
ensure_dir_exists_mock.assert_called_once_with("/repo/os/x86_64/")
|
||||||
self.assertEqual(repository_data["name"], repo.name)
|
self.assertEqual(repository_data["name"], repo.name)
|
||||||
self.assertEqual("x86_64", repo.architecture)
|
self.assertEqual("x86_64", repo.architecture)
|
||||||
self.assertEqual(repository_data["url"] + "/", repo.url)
|
self.assertEqual(repository_data["uri"] + "/", repo.url)
|
||||||
self.assertEqual(repository_data["origin"], repo.origin)
|
self.assertEqual(repository_data["origin"], repo.origin)
|
||||||
|
|
||||||
@mock.patch("packetary.drivers.rpm_driver.utils")
|
@mock.patch("packetary.drivers.rpm_driver.utils")
|
||||||
@ -278,3 +279,7 @@ class TestRpmDriver(base.TestCase):
|
|||||||
)
|
)
|
||||||
rel_path = self.driver.get_relative_path(repo, "test.pkg")
|
rel_path = self.driver.get_relative_path(repo, "test.pkg")
|
||||||
self.assertEqual("packages/test.pkg", rel_path)
|
self.assertEqual("packages/test.pkg", rel_path)
|
||||||
|
|
||||||
|
def test_get_repository_data_schema(self):
|
||||||
|
schema = self.driver.get_repository_data_schema()
|
||||||
|
self.assertIs(RPM_REPO_SCHEMA, schema)
|
||||||
|
287
packetary/tests/test_schemas.py
Normal file
287
packetary/tests/test_schemas.py
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2016 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
import jsonschema
|
||||||
|
|
||||||
|
from packetary.schemas import DEB_REPO_SCHEMA
|
||||||
|
from packetary.schemas import PACKAGE_FILES_SCHEMA
|
||||||
|
from packetary.schemas import PACKAGES_SCHEMA
|
||||||
|
from packetary.schemas import RPM_REPO_SCHEMA
|
||||||
|
from packetary.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestRepositorySchemaBase(base.TestCase):
|
||||||
|
def check_invalid_name(self):
|
||||||
|
self._check_invalid_type('name')
|
||||||
|
|
||||||
|
def check_invalid_uri(self):
|
||||||
|
self._check_invalid_type('uri')
|
||||||
|
|
||||||
|
def check_invalid_path(self):
|
||||||
|
self._check_invalid_type('path')
|
||||||
|
|
||||||
|
def check_required_properties(self):
|
||||||
|
repos_data = [{"name": "os"}, {"uri": "file:///repo"}]
|
||||||
|
for data in repos_data:
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError,
|
||||||
|
"is a required property",
|
||||||
|
jsonschema.validate, data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_priority(self, min_value=None, max_value=None):
|
||||||
|
if min_value is not None:
|
||||||
|
self._check_invalid_priority(min_value - 1)
|
||||||
|
self._check_valid_priority(min_value)
|
||||||
|
if max_value is not None:
|
||||||
|
self._check_invalid_priority(max_value + 1)
|
||||||
|
self._check_valid_priority(max_value)
|
||||||
|
self._check_valid_priority(None)
|
||||||
|
self._check_invalid_priority("abc")
|
||||||
|
|
||||||
|
def _check_invalid_type(self, key):
|
||||||
|
invalid_data = {key: 123}
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "123 is not of type 'string'",
|
||||||
|
jsonschema.validate, invalid_data[key],
|
||||||
|
self.schema['properties'][key]
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check_valid_priority(self, value):
|
||||||
|
self.assertNotRaises(
|
||||||
|
jsonschema.ValidationError, jsonschema.validate, value,
|
||||||
|
self.schema['properties']['priority']
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check_invalid_priority(self, value):
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError,
|
||||||
|
"is not valid under any of the given schemas",
|
||||||
|
jsonschema.validate, value, self.schema['properties']['priority']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDebRepoSchema(TestRepositorySchemaBase):
|
||||||
|
def setUp(self):
|
||||||
|
self.schema = DEB_REPO_SCHEMA
|
||||||
|
|
||||||
|
def test_valid_repo_data(self):
|
||||||
|
repo_data = {
|
||||||
|
"name": "os", "uri": "file:///repo", "suite": "trusty",
|
||||||
|
"section": ["main", "multiverse"], "path": "/some/path",
|
||||||
|
"priority": 1001
|
||||||
|
}
|
||||||
|
self.assertNotRaises(
|
||||||
|
jsonschema.ValidationError, jsonschema.validate,
|
||||||
|
repo_data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_priority(self):
|
||||||
|
self.check_priority(0)
|
||||||
|
|
||||||
|
def test_validation_fail_for_required_properties(self):
|
||||||
|
self.check_required_properties()
|
||||||
|
|
||||||
|
repo_data = {"name": "os", "uri": "file:///repo"}
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "'suite' is a required property",
|
||||||
|
jsonschema.validate, repo_data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_if_name_is_invalid(self):
|
||||||
|
self.check_invalid_name()
|
||||||
|
|
||||||
|
def test_validation_fail_if_uri_is_invalid(self):
|
||||||
|
self.check_invalid_uri()
|
||||||
|
|
||||||
|
def test_validation_fail_if_path_is_invalid(self):
|
||||||
|
self.check_invalid_path()
|
||||||
|
|
||||||
|
def test_validation_fail_if_suite_is_invalid(self):
|
||||||
|
repo_data = {"name": "os", "uri": "file:///repo", "suite": 123}
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "123 is not of type 'string'",
|
||||||
|
jsonschema.validate, repo_data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_if_section_not_array(self):
|
||||||
|
repo_data = {
|
||||||
|
"name": "os", "uri": "file:///repo", "suite": "trusty",
|
||||||
|
"section": 123
|
||||||
|
}
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "123 is not of type 'array'",
|
||||||
|
jsonschema.validate, repo_data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_if_section_not_string(self):
|
||||||
|
repo_data = {
|
||||||
|
"name": "os", "uri": "file:///repo", "suite": "trusty",
|
||||||
|
"section": [123]
|
||||||
|
}
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "123 is not of type 'string'",
|
||||||
|
jsonschema.validate, repo_data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRpmRepoSchema(TestRepositorySchemaBase):
|
||||||
|
def setUp(self):
|
||||||
|
self.schema = RPM_REPO_SCHEMA
|
||||||
|
|
||||||
|
def test_valid_repo_data(self):
|
||||||
|
repo_data = {
|
||||||
|
"name": "os", "uri": "file:///repo", "path": "/some/path",
|
||||||
|
"priority": 45
|
||||||
|
}
|
||||||
|
self.assertNotRaises(
|
||||||
|
jsonschema.ValidationError, jsonschema.validate, repo_data,
|
||||||
|
self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_priority(self):
|
||||||
|
self.check_priority(1, 99)
|
||||||
|
|
||||||
|
def test_validation_fail_for_required_properties(self):
|
||||||
|
self.check_required_properties()
|
||||||
|
|
||||||
|
def test_validation_fail_if_name_is_invalid(self):
|
||||||
|
self.check_invalid_name()
|
||||||
|
|
||||||
|
def test_validation_fail_if_uri_is_invalid(self):
|
||||||
|
self.check_invalid_uri()
|
||||||
|
|
||||||
|
def test_validation_fail_if_path_is_invalid(self):
|
||||||
|
self.check_invalid_path()
|
||||||
|
|
||||||
|
|
||||||
|
class TestPackagesSchema(base.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.schema = PACKAGES_SCHEMA
|
||||||
|
|
||||||
|
def test_valid_requirements_data(self):
|
||||||
|
requirements_data = [
|
||||||
|
{"name": "test1", "versions": [">= 1.1.2", "<= 3"]},
|
||||||
|
{"name": "test2", "versions": ["< 3", "> 1", ">= 4"]},
|
||||||
|
{"name": "test3", "versions": ["= 3"]},
|
||||||
|
{"name": "test4", "versions": ["= 3"]}
|
||||||
|
]
|
||||||
|
self.assertNotRaises(
|
||||||
|
jsonschema.ValidationError, jsonschema.validate, requirements_data,
|
||||||
|
self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_for_required_properties(self):
|
||||||
|
requirements_data = [
|
||||||
|
[{"name": "test1"}],
|
||||||
|
[{"versions": ["< 3", "> 1"]}]
|
||||||
|
]
|
||||||
|
for data in requirements_data:
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError,
|
||||||
|
"is a required property",
|
||||||
|
jsonschema.validate, data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_if_name_is_invalid(self):
|
||||||
|
requirements_data = [
|
||||||
|
{"name": 123, "versions": [">= 1.1.2", "<= 3"]},
|
||||||
|
]
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "123 is not of type 'string'",
|
||||||
|
jsonschema.validate, requirements_data, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_if_versions_not_array(self):
|
||||||
|
requirements_data = [
|
||||||
|
{"name": "test1", "versions": 123}
|
||||||
|
]
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "123 is not of type 'array'",
|
||||||
|
jsonschema.validate, requirements_data,
|
||||||
|
self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_if_versions_not_string(self):
|
||||||
|
requirements_data = [
|
||||||
|
{"name": "test1", "versions": [123]}
|
||||||
|
]
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "123 is not of type 'string'",
|
||||||
|
jsonschema.validate, requirements_data,
|
||||||
|
self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_if_versions_not_match(self):
|
||||||
|
versions = [
|
||||||
|
["1.1.2"], # relational operator
|
||||||
|
[">=3"], # not whitespace after ro
|
||||||
|
["== 3"] # ==
|
||||||
|
]
|
||||||
|
for version in versions:
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "does not match",
|
||||||
|
jsonschema.validate, version,
|
||||||
|
self.schema['items']['properties']['versions']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPackageFilesSchema(base.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.schema = PACKAGE_FILES_SCHEMA
|
||||||
|
|
||||||
|
def test_valid_file_urls(self):
|
||||||
|
file_urls = [
|
||||||
|
"file://test1.pkg",
|
||||||
|
"file:///test2.pkg",
|
||||||
|
"/test3.pkg",
|
||||||
|
"http://test4.pkg",
|
||||||
|
"https://test5.pkg"
|
||||||
|
]
|
||||||
|
self.assertNotRaises(
|
||||||
|
jsonschema.ValidationError, jsonschema.validate, file_urls,
|
||||||
|
self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_if_urls_not_array(self):
|
||||||
|
file_urls = "/test1.pkg"
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "'/test1.pkg' is not of type 'array'",
|
||||||
|
jsonschema.validate, file_urls, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_if_urls_not_string(self):
|
||||||
|
file_urls = [123]
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "123 is not of type 'string'",
|
||||||
|
jsonschema.validate, file_urls, self.schema
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_validation_fail_if_invalid_file_urls(self):
|
||||||
|
file_urls = [
|
||||||
|
["test1.pkg"], # does not match pattern
|
||||||
|
["./test2.pkg"], # does not match pattern
|
||||||
|
["file//test3.pkg"], # does not match pattern
|
||||||
|
["http//test4.pkg"] # does not match pattern
|
||||||
|
]
|
||||||
|
|
||||||
|
for url in file_urls[2:]:
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
jsonschema.ValidationError, "does not match",
|
||||||
|
jsonschema.validate, url, self.schema
|
||||||
|
)
|
@ -12,3 +12,4 @@ stevedore>=1.1.0
|
|||||||
six>=1.5.2
|
six>=1.5.2
|
||||||
python-debian>=0.1.21
|
python-debian>=0.1.21
|
||||||
lxml>=3.2
|
lxml>=3.2
|
||||||
|
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
|
||||||
|
Loading…
Reference in New Issue
Block a user