diff --git a/lbrynet/core/Wallet.py b/lbrynet/core/Wallet.py index 0ce5c66d3..896d71de8 100644 --- a/lbrynet/core/Wallet.py +++ b/lbrynet/core/Wallet.py @@ -13,6 +13,7 @@ from twisted.python.failure import Failure from twisted.enterprise import adbapi from collections import defaultdict, deque from zope.interface import implements +from jsonschema import ValidationError from decimal import Decimal from lbryum import SimpleConfig, Network @@ -338,7 +339,7 @@ class Wallet(object): try: metadata = Metadata(json.loads(result['value'])) - except (ValueError, TypeError): + except ValidationError: return Failure(InvalidStreamInfoError(name)) txid = result['txid'] @@ -421,7 +422,7 @@ class Wallet(object): meta_ver = metadata.version sd_hash = metadata['sources']['lbry_sd_hash'] d = self._save_name_metadata(name, txid, sd_hash) - except AssertionError: + except ValidationError: metadata = claim['value'] meta_ver = "Non-compliant" d = defer.succeed(None) diff --git a/lbrynet/lbrynet_daemon/Daemon.py b/lbrynet/lbrynet_daemon/Daemon.py index 2ca513023..86d5ff3ca 100644 --- a/lbrynet/lbrynet_daemon/Daemon.py +++ b/lbrynet/lbrynet_daemon/Daemon.py @@ -23,6 +23,7 @@ from twisted.internet.task import LoopingCall from txjsonrpc import jsonrpclib from txjsonrpc.web import jsonrpc from txjsonrpc.web.jsonrpc import Handler +from jsonschema import ValidationError from lbrynet import __version__ as lbrynet_version from lbryum.version import LBRYUM_VERSION as lbryum_version @@ -2009,7 +2010,7 @@ class Daemon(jsonrpc.JSONRPC): metadata = Metadata(p['metadata']) make_lbry_file = False sd_hash = metadata['sources']['lbry_sd_hash'] - except AssertionError: + except ValidationError: make_lbry_file = True sd_hash = None metadata = p['metadata'] diff --git a/lbrynet/lbrynet_daemon/Publisher.py b/lbrynet/lbrynet_daemon/Publisher.py index 7a6e73ee3..1836befbf 100644 --- a/lbrynet/lbrynet_daemon/Publisher.py +++ b/lbrynet/lbrynet_daemon/Publisher.py @@ -160,4 +160,4 @@ class Publisher(object): def get_content_type(filename): - return mimetypes.guess_type(filename)[0] + return mimetypes.guess_type(filename)[0] or 'application/octet-stream' diff --git a/lbrynet/metadata/Fee.py b/lbrynet/metadata/Fee.py index 870d5f3b1..0e77b3cfe 100644 --- a/lbrynet/metadata/Fee.py +++ b/lbrynet/metadata/Fee.py @@ -1,116 +1,39 @@ import logging +import fee_schemas -from lbrynet.metadata.Validator import Validator, skip_validate -from lbrynet.conf import CURRENCIES +from lbrynet.metadata.StructuredDict import StructuredDict log = logging.getLogger(__name__) -def verify_supported_currency(fee): - assert len(fee) == 1 - for c in fee: - assert c in CURRENCIES - return True - - -def verify_amount(x): - return isinstance(x, float) or isinstance(x, int) and x > 0 - - -class LBCFeeValidator(Validator): - FV001 = "0.0.1" - CURRENT_FEE_VERSION = FV001 - - FEE_REVISIONS = {} - - FEE_REVISIONS[FV001] = [ - (Validator.REQUIRE, 'amount', verify_amount), - (Validator.REQUIRE, 'address', skip_validate), - ] - - FEE_MIGRATIONS = [] - - current_version = CURRENT_FEE_VERSION - versions = FEE_REVISIONS - migrations = FEE_MIGRATIONS - +class FeeValidator(StructuredDict): def __init__(self, fee): - Validator.__init__(self, fee) + self._versions = [ + ('0.0.1', fee_schemas.VER_001, None) + ] + StructuredDict.__init__(self, fee, fee.get('ver', '0.0.1')) -class BTCFeeValidator(Validator): - FV001 = "0.0.1" - CURRENT_FEE_VERSION = FV001 - - FEE_REVISIONS = {} - - FEE_REVISIONS[FV001] = [ - (Validator.REQUIRE, 'amount',verify_amount), - (Validator.REQUIRE, 'address', skip_validate), - ] - - FEE_MIGRATIONS = [] - - current_version = CURRENT_FEE_VERSION - versions = FEE_REVISIONS - migrations = FEE_MIGRATIONS - - def __init__(self, fee): - Validator.__init__(self, fee) - - -class USDFeeValidator(Validator): - FV001 = "0.0.1" - CURRENT_FEE_VERSION = FV001 - - FEE_REVISIONS = {} - - FEE_REVISIONS[FV001] = [ - (Validator.REQUIRE, 'amount',verify_amount), - (Validator.REQUIRE, 'address', skip_validate), - ] - - FEE_MIGRATIONS = [] - - current_version = CURRENT_FEE_VERSION - versions = FEE_REVISIONS - migrations = FEE_MIGRATIONS - - def __init__(self, fee): - Validator.__init__(self, fee) - - -class FeeValidator(Validator): - CV001 = "0.0.1" - CURRENT_CURRENCY_VERSION = CV001 - - CURRENCY_REVISIONS = {} - - CURRENCY_REVISIONS[CV001] = [ - (Validator.OPTIONAL, 'BTC', BTCFeeValidator.validate), - (Validator.OPTIONAL, 'USD', USDFeeValidator.validate), - (Validator.OPTIONAL, 'LBC', LBCFeeValidator.validate), - ] - - CURRENCY_MIGRATIONS = [] - - current_version = CURRENT_CURRENCY_VERSION - versions = CURRENCY_REVISIONS - migrations = CURRENCY_MIGRATIONS - - def __init__(self, fee_dict): - Validator.__init__(self, fee_dict) self.currency_symbol = self.keys()[0] self.amount = self._get_amount() self.address = self[self.currency_symbol]['address'] def _get_amount(self): amt = self[self.currency_symbol]['amount'] - if isinstance(amt, float): - return amt - else: - try: - return float(amt) - except TypeError: - log.error('Failed to convert %s to float', amt) - raise + try: + return float(amt) + except TypeError: + log.error('Failed to convert fee amount %s to float', amt) + raise + + +class LBCFeeValidator(StructuredDict): + pass + + +class BTCFeeValidator(StructuredDict): + pass + + +class USDFeeValidator(StructuredDict): + pass diff --git a/lbrynet/metadata/Metadata.py b/lbrynet/metadata/Metadata.py index f1294a95e..39522022f 100644 --- a/lbrynet/metadata/Metadata.py +++ b/lbrynet/metadata/Metadata.py @@ -1,8 +1,7 @@ import logging -from lbrynet.metadata.Validator import Validator, skip_validate -from lbrynet.metadata.Fee import FeeValidator, verify_supported_currency -from lbrynet.conf import SOURCE_TYPES +from lbrynet.metadata.StructuredDict import StructuredDict +import metadata_schemas log = logging.getLogger(__name__) NAME_ALLOWED_CHARSET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0987654321-' @@ -13,74 +12,27 @@ def verify_name_characters(name): assert c in NAME_ALLOWED_CHARSET, "Invalid character" return True +def migrate_001_to_002(metadata): + metadata['ver'] = '0.0.2' + metadata['nsfw'] = False -def validate_sources(sources): - for source in sources: - assert source in SOURCE_TYPES, "Unknown source type: %s" % str(source) - return True +def migrate_002_to_003(metadata): + metadata['ver'] = '0.0.3' + if 'content-type' in metadata: + metadata['content_type'] = metadata['content-type'] + del metadata['content-type'] -class Metadata(Validator): - MV001 = "0.0.1" - MV002 = "0.0.2" - MV003 = "0.0.3" - CURRENT_METADATA_VERSION = MV003 +class Metadata(StructuredDict): + current_version = '0.0.3' - METADATA_REVISIONS = {} - - METADATA_REVISIONS[MV001] = [ - (Validator.REQUIRE, 'title', skip_validate), - (Validator.REQUIRE, 'description', skip_validate), - (Validator.REQUIRE, 'author', skip_validate), - (Validator.REQUIRE, 'language', skip_validate), - (Validator.REQUIRE, 'license', skip_validate), - (Validator.REQUIRE, 'content-type', skip_validate), - (Validator.REQUIRE, 'sources', validate_sources), - (Validator.OPTIONAL, 'thumbnail', skip_validate), - (Validator.OPTIONAL, 'preview', skip_validate), - (Validator.OPTIONAL, 'fee', verify_supported_currency), - (Validator.OPTIONAL, 'contact', skip_validate), - (Validator.OPTIONAL, 'pubkey', skip_validate), + _versions = [ + ('0.0.1', metadata_schemas.VER_001, None), + ('0.0.2', metadata_schemas.VER_002, migrate_001_to_002), + ('0.0.3', metadata_schemas.VER_003, migrate_002_to_003) ] - METADATA_REVISIONS[MV002] = [ - (Validator.REQUIRE, 'nsfw', skip_validate), - (Validator.REQUIRE, 'ver', skip_validate), - (Validator.OPTIONAL, 'license_url', skip_validate), - ] + def __init__(self, metadata, migrate=True, target_version=None): + starting_version = metadata.get('ver', '0.0.1') - METADATA_REVISIONS[MV003] = [ - (Validator.REQUIRE, 'content_type', skip_validate), - (Validator.SKIP, 'content-type'), - (Validator.OPTIONAL, 'sig', skip_validate), - (Validator.IF_KEY, 'sig', (Validator.REQUIRE, 'pubkey', skip_validate), Validator.DO_NOTHING), - (Validator.IF_KEY, 'pubkey', (Validator.REQUIRE, 'sig', skip_validate), Validator.DO_NOTHING), - ] - - MIGRATE_MV001_TO_MV002 = [ - (Validator.IF_KEY, 'nsfw', Validator.DO_NOTHING, (Validator.LOAD, 'nsfw', False)), - (Validator.IF_KEY, 'ver', Validator.DO_NOTHING, (Validator.LOAD, 'ver', MV002)), - ] - - MIGRATE_MV002_TO_MV003 = [ - (Validator.IF_KEY, 'content-type', (Validator.UPDATE, 'content-type', 'content_type'), Validator.DO_NOTHING), - (Validator.IF_VAL, 'ver', MV002, (Validator.LOAD, 'ver', MV003), Validator.DO_NOTHING), - ] - - METADATA_MIGRATIONS = [ - MIGRATE_MV001_TO_MV002, - MIGRATE_MV002_TO_MV003, - ] - - current_version = CURRENT_METADATA_VERSION - versions = METADATA_REVISIONS - migrations = METADATA_MIGRATIONS - - def __init__(self, metadata, process_now=True): - Validator.__init__(self, metadata, process_now) - self.meta_version = self.get('ver', Metadata.MV001) - self._load_fee() - - def _load_fee(self): - if 'fee' in self: - self.update({'fee': FeeValidator(self['fee'])}) + StructuredDict.__init__(self, metadata, starting_version, migrate, target_version) \ No newline at end of file diff --git a/lbrynet/metadata/StructuredDict.py b/lbrynet/metadata/StructuredDict.py new file mode 100644 index 000000000..0c9db594c --- /dev/null +++ b/lbrynet/metadata/StructuredDict.py @@ -0,0 +1,62 @@ +import jsonschema +import logging + +from jsonschema import ValidationError + +log = logging.getLogger(__name__) + + +class StructuredDict(dict): + """ + A dictionary that enforces a structure specified by a schema, and supports + migration between different versions of the schema. + """ + + # To be specified in sub-classes, an array in the format + # [(version, schema, migration), ...] + _versions = [] + + # Used internally to allow schema lookups by version number + _schemas = {} + + version = None + + def __init__(self, value, starting_version, migrate=True, target_version=None): + dict.__init__(self, value) + + self.version = starting_version + self._schemas = dict([(version, schema) for (version, schema, _) in self._versions]) + + self.validate(starting_version) + + if migrate: + self.migrate(target_version) + + def _upgrade_version_range(self, start_version, end_version): + after_starting_version = False + for version, schema, migration in self._versions: + if not after_starting_version: + if version == self.version: + after_starting_version = True + continue + + yield version, schema, migration + + if end_version and version == end_version: + break + + def validate(self, version): + jsonschema.validate(self, self._schemas[version]) + + def migrate(self, target_version=None): + if target_version: + assert self._versions.index(target_version) > self.versions.index(self.version), "Current version is above target version" + + for version, schema, migration in self._upgrade_version_range(self.version, target_version): + migration(self) + try: + self.validate(version) + except ValidationError as e: + raise ValidationError, "Could not migrate to version %s due to validation error: %s" % (version, e.message) + + self.version = version diff --git a/lbrynet/metadata/Validator.py b/lbrynet/metadata/Validator.py deleted file mode 100644 index b08ed64e2..000000000 --- a/lbrynet/metadata/Validator.py +++ /dev/null @@ -1,155 +0,0 @@ -import json -import logging -from copy import deepcopy -from distutils.version import StrictVersion -from lbrynet.core.utils import version_is_greater_than - -log = logging.getLogger(__name__) - - -def skip_validate(value): - return True - - -def processor(cls): - for methodname in dir(cls): - method = getattr(cls, methodname) - if hasattr(method, 'cmd_name'): - cls.commands.update({method.cmd_name: methodname}) - return cls - - -def cmd(cmd_name): - def wrapper(func): - func.cmd_name = cmd_name - return func - return wrapper - - -@processor -class Validator(dict): - """ - Base class for validated dictionaries - """ - - # override these - current_version = None - versions = {} - migrations = [] - - # built in commands - DO_NOTHING = "do_nothing" - UPDATE = "update_key" - IF_KEY = "if_key" - REQUIRE = "require" - SKIP = "skip" - OPTIONAL = "optional" - LOAD = "load" - IF_VAL = "if_val" - - commands = {} - - @classmethod - def load_from_hex(cls, hex_val): - return cls(json.loads(hex_val.decode('hex'))) - - @classmethod - def validate(cls, value): - if cls(value): - return True - else: - return False - - def __init__(self, value, process_now=False): - dict.__init__(self) - self._skip = [] - value_to_load = deepcopy(value) - if process_now: - self.process(value_to_load) - self._verify_value(value_to_load) - self.version = self.get('ver', "0.0.1") - - def process(self, value): - if self.migrations is not None: - self._migrate_value(value) - - @cmd(DO_NOTHING) - def _do_nothing(self): - pass - - @cmd(SKIP) - def _add_to_skipped(self, rx_value, key): - if key not in self._skip: - self._skip.append(key) - - @cmd(UPDATE) - def _update(self, rx_value, old_key, new_key): - rx_value.update({new_key: rx_value.pop(old_key)}) - - @cmd(IF_KEY) - def _if_key(self, rx_value, key, if_true, if_else): - if key in rx_value: - return self._handle(if_true, rx_value) - return self._handle(if_else, rx_value) - - @cmd(IF_VAL) - def _if_val(self, rx_value, key, val, if_true, if_else): - if key in rx_value: - if rx_value[key] == val: - return self._handle(if_true, rx_value) - return self._handle(if_else, rx_value) - - @cmd(LOAD) - def _load(self, rx_value, key, value): - rx_value.update({key: value}) - - @cmd(REQUIRE) - def _require(self, rx_value, key, validator=None): - if key not in self._skip: - assert key in rx_value, "Key is missing: %s" % key - if isinstance(validator, type): - assert isinstance(rx_value[key], validator), "%s: %s isn't required %s" % (key, type(rx_value[key]), validator) - elif callable(validator): - assert validator(rx_value[key]), "Failed to validate %s" % key - self.update({key: rx_value.pop(key)}) - - @cmd(OPTIONAL) - def _optional(self, rx_value, key, validator=None): - if key in rx_value and key not in self._skip: - if isinstance(validator, type): - assert isinstance(rx_value[key], validator), "%s type %s isn't required %s" % (key, type(rx_value[key]), validator) - elif callable(validator): - assert validator(rx_value[key]), "Failed to validate %s" % key - self.update({key: rx_value.pop(key)}) - - def _handle(self, cmd_tpl, value): - if cmd_tpl == Validator.DO_NOTHING: - return - command = cmd_tpl[0] - f = getattr(self, self.commands[command]) - if len(cmd_tpl) > 1: - args = (value,) + cmd_tpl[1:] - f(*args) - else: - f() - - def _load_revision(self, version, value): - for k in self.versions[version]: - self._handle(k, value) - - def _verify_value(self, value): - val_ver = value.get('ver', "0.0.1") - # verify version requirements in reverse order starting from the version asserted in the value - versions = sorted([v for v in self.versions if not version_is_greater_than(v, val_ver)], key=StrictVersion, reverse=True) - for version in versions: - self._load_revision(version, value) - assert value == {} or value.keys() == self._skip, "Unknown keys: %s" % json.dumps(value) - - def _migrate_value(self, value): - for migration in self.migrations: - self._run_migration(migration, value) - - def _run_migration(self, commands, value): - for cmd in commands: - self._handle(cmd, value) - diff --git a/lbrynet/metadata/fee_schemas.py b/lbrynet/metadata/fee_schemas.py new file mode 100644 index 000000000..18efd64b6 --- /dev/null +++ b/lbrynet/metadata/fee_schemas.py @@ -0,0 +1,16 @@ +VER_001 = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'LBRY fee schema 0.0.1', + 'type': 'object', + + 'properties': { + 'amount': { + 'type': 'number', + 'minimum': 0, + 'exclusiveMinimum': True, + }, + 'address': { + 'type': 'string' + } + }, +} diff --git a/lbrynet/metadata/metadata_schemas.py b/lbrynet/metadata/metadata_schemas.py new file mode 100644 index 000000000..2f66a3c74 --- /dev/null +++ b/lbrynet/metadata/metadata_schemas.py @@ -0,0 +1,269 @@ +VER_001 = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'LBRY metadata schema 0.0.1', + 'definitions': { + 'fee_info': { + 'type': 'object', + 'properties': { + 'amount': { + 'type': 'number', + 'minimum': 0, + 'exclusiveMinimum': True, + }, + 'address': { + 'type': 'string' + } + }, + } + }, + 'type': 'object', + + 'properties': { + 'ver': { + 'type': 'string', + 'default': '0.0.1' + }, + 'title': { + 'type': 'string' + }, + 'description': { + 'type': 'string' + }, + 'author': { + 'type': 'string' + }, + 'language': { + 'type': 'string' + }, + 'license': { + 'type': 'string' + }, + 'content-type': { + 'type': 'string' + }, + 'sources': { + 'type': 'object', + 'properties': { + 'lbry_sd_hash': { + 'type': 'string' + }, + 'btih': { + 'type': 'string' + }, + 'url': { + 'type': 'string' + } + }, + 'additionalProperties': False + }, + 'thumbnail': { + 'type': 'string' + }, + 'preview': { + 'type': 'string' + }, + 'fee': { + 'properties': { + 'LBC': { '$ref': '#/definitions/fee_info' }, + 'BTC': { '$ref': '#/definitions/fee_info' }, + 'USD': { '$ref': '#/definitions/fee_info' } + } + }, + 'contact': { + 'type': 'number' + }, + 'pubkey': { + 'type': 'string' + }, + }, + 'required': ['title', 'description', 'author', 'language', 'license', 'content-type', 'sources'], + 'additionalProperties': False +} + + +VER_002 = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'LBRY metadata schema 0.0.2', + 'definitions': { + 'fee_info': { + 'type': 'object', + 'properties': { + 'amount': { + 'type': 'number', + 'minimum': 0, + 'exclusiveMinimum': True, + }, + 'address': { + 'type': 'string' + } + }, + } + }, + 'type': 'object', + + 'properties': { + 'ver': { + 'type': 'string', + 'enum': ['0.0.2'], + }, + 'title': { + 'type': 'string' + }, + 'description': { + 'type': 'string' + }, + 'author': { + 'type': 'string' + }, + 'language': { + 'type': 'string' + }, + 'license': { + 'type': 'string' + }, + 'content-type': { + 'type': 'string' + }, + 'sources': { + 'type': 'object', + 'properties': { + 'lbry_sd_hash': { + 'type': 'string' + }, + 'btih': { + 'type': 'string' + }, + 'url': { + 'type': 'string' + } + }, + 'additionalProperties': False + }, + 'thumbnail': { + 'type': 'string' + }, + 'preview': { + 'type': 'string' + }, + 'fee': { + 'properties': { + 'LBC': { '$ref': '#/definitions/fee_info' }, + 'BTC': { '$ref': '#/definitions/fee_info' }, + 'USD': { '$ref': '#/definitions/fee_info' } + } + }, + 'contact': { + 'type': 'number' + }, + 'pubkey': { + 'type': 'string' + }, + 'license_url': { + 'type': 'string' + }, + 'nsfw': { + 'type': 'boolean', + 'default': False + }, + + }, + 'required': ['ver', 'title', 'description', 'author', 'language', 'license', 'content-type', 'sources', 'nsfw'], + 'additionalProperties': False +} + + +VER_003 = { + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'LBRY metadata schema 0.0.3', + 'definitions': { + 'fee_info': { + 'type': 'object', + 'properties': { + 'amount': { + 'type': 'number', + 'minimum': 0, + 'exclusiveMinimum': True, + }, + 'address': { + 'type': 'string' + } + }, + } + }, + 'type': 'object', + + 'properties': { + 'ver': { + 'type': 'string', + 'enum': ['0.0.3'], + }, + 'title': { + 'type': 'string' + }, + 'description': { + 'type': 'string' + }, + 'author': { + 'type': 'string' + }, + 'language': { + 'type': 'string' + }, + 'license': { + 'type': 'string' + }, + 'content_type': { + 'type': 'string' + }, + 'sources': { + 'type': 'object', + 'properties': { + 'lbry_sd_hash': { + 'type': 'string' + }, + 'btih': { + 'type': 'string' + }, + 'url': { + 'type': 'string' + } + }, + 'additionalProperties': False + }, + 'thumbnail': { + 'type': 'string' + }, + 'preview': { + 'type': 'string' + }, + 'fee': { + 'properties': { + 'LBC': { '$ref': '#/definitions/fee_info' }, + 'BTC': { '$ref': '#/definitions/fee_info' }, + 'USD': { '$ref': '#/definitions/fee_info' } + } + }, + 'contact': { + 'type': 'number' + }, + 'pubkey': { + 'type': 'string' + }, + 'license_url': { + 'type': 'string' + }, + 'nsfw': { + 'type': 'boolean', + 'default': False + }, + 'sig': { + 'type': 'string' + } + }, + 'required': ['ver', 'title', 'description', 'author', 'language', 'license', 'content_type', 'sources', 'nsfw'], + 'additionalProperties': False, + 'dependencies': { + 'pubkey': ['sig'], + 'sig': ['pubkey'] + } +} diff --git a/requirements.txt b/requirements.txt index 3a723866c..276b82289 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ ecdsa==0.13 gmpy==1.17 jsonrpc==1.2 jsonrpclib==0.1.7 +jsonschema==2.5.1 https://github.com/lbryio/lbryum/tarball/master/#egg=lbryum loggly-python-handler==1.0.0 miniupnpc==1.9 diff --git a/setup.py b/setup.py index 87b68a60c..c93a67812 100644 --- a/setup.py +++ b/setup.py @@ -40,6 +40,7 @@ requires = [ 'lbryum', 'jsonrpc', 'simplejson', + 'jsonschema', 'appdirs', 'six==1.9.0', 'base58', diff --git a/tests/unit/core/test_ExchangeRateManager.py b/tests/unit/core/test_ExchangeRateManager.py index d6a0f92a6..96827c263 100644 --- a/tests/unit/core/test_ExchangeRateManager.py +++ b/tests/unit/core/test_ExchangeRateManager.py @@ -1,5 +1,5 @@ import mock -from lbrynet.metadata import Metadata +from lbrynet.metadata import Fee from lbrynet.lbrynet_daemon import ExchangeRateManager from twisted.trial import unittest @@ -13,7 +13,7 @@ class FeeFormatTest(unittest.TestCase): 'address': "bRcHraa8bYJZL7vkh5sNmGwPDERFUjGPP9" } } - fee = Metadata.FeeValidator(fee_dict) + fee = Fee.FeeValidator(fee_dict) self.assertEqual(10.0, fee['USD']['amount']) diff --git a/tests/unit/core/test_Metadata.py b/tests/unit/core/test_Metadata.py index c86af5708..b13456968 100644 --- a/tests/unit/core/test_Metadata.py +++ b/tests/unit/core/test_Metadata.py @@ -1,14 +1,14 @@ from lbrynet.metadata import Metadata from twisted.trial import unittest - +from jsonschema import ValidationError class MetadataTest(unittest.TestCase): - def test_assertion_if_no_metadata(self): + def test_validation_error_if_no_metadata(self): metadata = {} - with self.assertRaises(AssertionError): + with self.assertRaises(ValidationError): Metadata.Metadata(metadata) - def test_assertion_if_source_is_missing(self): + def test_validation_error_if_source_is_missing(self): metadata = { 'license': 'Oscilloscope Laboratories', 'description': 'Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.', @@ -18,7 +18,7 @@ class MetadataTest(unittest.TestCase): 'content-type': 'audio/mpeg', 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', } - with self.assertRaises(AssertionError): + with self.assertRaises(ValidationError): Metadata.Metadata(metadata) def test_metadata_works_without_fee(self): @@ -36,7 +36,7 @@ class MetadataTest(unittest.TestCase): m = Metadata.Metadata(metadata) self.assertFalse('fee' in m) - def test_assertion_if_invalid_source(self): + def test_validation_error_if_invalid_source(self): metadata = { 'license': 'Oscilloscope Laboratories', 'description': 'Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.', @@ -48,10 +48,10 @@ class MetadataTest(unittest.TestCase): 'content-type': 'audio/mpeg', 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', } - with self.assertRaises(AssertionError): + with self.assertRaises(ValidationError): Metadata.Metadata(metadata) - def test_assertion_if_missing_v001_field(self): + def test_validation_error_if_missing_v001_field(self): metadata = { 'license': 'Oscilloscope Laboratories', 'fee': {'LBC': {'amount': 50.0, 'address': 'bRQJASJrDbFZVAvcpv3NoNWoH74LQd5JNV'}}, @@ -63,7 +63,7 @@ class MetadataTest(unittest.TestCase): 'content-type': 'audio/mpeg', 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg' } - with self.assertRaises(AssertionError): + with self.assertRaises(ValidationError): Metadata.Metadata(metadata) def test_version_is_001_if_all_fields_are_present(self): @@ -78,10 +78,10 @@ class MetadataTest(unittest.TestCase): 'content-type': 'audio/mpeg', 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', } - m = Metadata.Metadata(metadata, process_now=False) + m = Metadata.Metadata(metadata, migrate=False) self.assertEquals('0.0.1', m.version) - def test_assertion_if_there_is_an_extra_field(self): + def test_validation_error_if_there_is_an_extra_field(self): metadata = { 'license': 'Oscilloscope Laboratories', 'description': 'Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.', @@ -94,8 +94,8 @@ class MetadataTest(unittest.TestCase): 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', 'MYSTERYFIELD': '?' } - with self.assertRaises(AssertionError): - Metadata.Metadata(metadata, process_now=False) + with self.assertRaises(ValidationError): + Metadata.Metadata(metadata, migrate=False) def test_version_is_002_if_all_fields_are_present(self): metadata = { @@ -112,7 +112,7 @@ class MetadataTest(unittest.TestCase): 'content-type': 'video/mp4', 'thumbnail': 'https://svs.gsfc.nasa.gov/vis/a010000/a012000/a012034/Combined.00_08_16_17.Still004.jpg' } - m = Metadata.Metadata(metadata, process_now=False) + m = Metadata.Metadata(metadata, migrate=False) self.assertEquals('0.0.2', m.version) def test_version_is_003_if_all_fields_are_present(self): @@ -130,7 +130,7 @@ class MetadataTest(unittest.TestCase): 'content_type': 'video/mp4', 'thumbnail': 'https://svs.gsfc.nasa.gov/vis/a010000/a012000/a012034/Combined.00_08_16_17.Still004.jpg' } - m = Metadata.Metadata(metadata, process_now=False) + m = Metadata.Metadata(metadata, migrate=False) self.assertEquals('0.0.3', m.version) def test_version_claimed_is_001_but_version_is_002(self): @@ -148,8 +148,8 @@ class MetadataTest(unittest.TestCase): 'content-type': 'video/mp4', 'thumbnail': 'https://svs.gsfc.nasa.gov/vis/a010000/a012000/a012034/Combined.00_08_16_17.Still004.jpg' } - with self.assertRaises(AssertionError): - Metadata.Metadata(metadata, process_now=False) + with self.assertRaises(ValidationError): + Metadata.Metadata(metadata, migrate=False) def test_version_claimed_is_002_but_version_is_003(self): metadata = { @@ -166,8 +166,8 @@ class MetadataTest(unittest.TestCase): 'content_type': 'video/mp4', 'thumbnail': 'https://svs.gsfc.nasa.gov/vis/a010000/a012000/a012034/Combined.00_08_16_17.Still004.jpg' } - with self.assertRaises(AssertionError): - Metadata.Metadata(metadata, process_now=False) + with self.assertRaises(ValidationError): + Metadata.Metadata(metadata, migrate=False) def test_version_001_ports_to_003(self): metadata = { @@ -181,7 +181,7 @@ class MetadataTest(unittest.TestCase): 'content-type': 'audio/mpeg', 'thumbnail': 'http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg', } - m = Metadata.Metadata(metadata, process_now=True) + m = Metadata.Metadata(metadata, migrate=True) self.assertEquals('0.0.3', m.version) def test_version_002_ports_to_003(self): @@ -199,5 +199,5 @@ class MetadataTest(unittest.TestCase): 'content-type': 'video/mp4', 'thumbnail': 'https://svs.gsfc.nasa.gov/vis/a010000/a012000/a012034/Combined.00_08_16_17.Still004.jpg' } - m = Metadata.Metadata(metadata, process_now=True) + m = Metadata.Metadata(metadata, migrate=True) self.assertEquals('0.0.3', m.version) \ No newline at end of file