diff --git a/acoustid-test.conf b/acoustid-test.conf index 98633de60043a118d4dcf8b4ba97a97ae1f98b4a..08f4e5a3f0126b651ec95f7787c0bc094d276eb4 100644 --- a/acoustid-test.conf +++ b/acoustid-test.conf @@ -1,19 +1,33 @@ [database] +two_phase_commit=yes + +[database:default] host=127.0.0.1 -port=5432 +port=15432 user=acoustid name=acoustid_test password=acoustid +[database:slow] +host=127.0.0.1 +port=15432 +user=acoustid +name=acoustid_slow_test +password=acoustid + [logging] level=WARNING level.sqlalchemy=WARNING syslog=yes syslog_facility=local1 +[index] +host=127.0.0.1 +port=16080 + [redis] host=127.0.0.1 -port=6379 +port=16379 [website] base_url=http://acoustid.org/ diff --git a/acoustid.conf.dist b/acoustid.conf.dist index caa52cca71998971c2a3f9ea14d0f8ccb959921c..2bb3d7eedd9e27e7d52ddda715397ea6b18356be 100644 --- a/acoustid.conf.dist +++ b/acoustid.conf.dist @@ -1,8 +1,14 @@ [database] +name=acoustid user=acoustid password=XXX -superuser=postgres +host=localhost +port=5432 + +[database_slow] name=acoustid +user=acoustid +password=XXX host=localhost port=5432 diff --git a/acoustid/api/v2/__init__.py b/acoustid/api/v2/__init__.py index fc6a760703f5428ac8fc20b98daeea64b81e49f2..42ce8c72486e383d1e886758246908aaba5dc692 100644 --- a/acoustid/api/v2/__init__.py +++ b/acoustid/api/v2/__init__.py @@ -8,18 +8,15 @@ import time import operator from typing import Type from acoustid import const +from acoustid.db import DatabaseContext from acoustid.const import MAX_REQUESTS_PER_SECOND from acoustid.handler import Handler +from acoustid.models import Application, Account, Submission, SubmissionResult, Track from acoustid.data.track import lookup_mbids, resolve_track_gid, lookup_meta_ids from acoustid.data.musicbrainz import lookup_metadata -from acoustid.data.submission import insert_submission, lookup_submission_status from acoustid.data.fingerprint import decode_fingerprint, FingerprintSearcher -from acoustid.data.format import find_or_insert_format from acoustid.data.application import lookup_application_id_by_apikey -from acoustid.data.account import lookup_account_id_by_apikey -from acoustid.data.source import find_or_insert_source -from acoustid.data.meta import insert_meta, lookup_meta -from acoustid.data.foreignid import find_or_insert_foreignid +from acoustid.data.meta import lookup_meta from acoustid.data.stats import update_lookup_counter, update_user_agent_counter, update_lookup_avg_time from acoustid.ratelimiter import RateLimiter from werkzeug.utils import cached_property @@ -103,6 +100,7 @@ class APIHandler(Handler): else: connect = server.engine.connect handler = cls(connect=connect) + handler.server = server handler.index = server.index handler.redis = server.redis handler.config = server.config @@ -564,6 +562,7 @@ class LookupHandler(APIHandler): matches = [(0, track_id, p['track_gid'], 1.0)] else: matches = searcher.search(p['fingerprint'], p['duration']) + print(repr(matches)) all_matches.append(matches) response = {} if params.batch: @@ -589,37 +588,121 @@ class LookupHandler(APIHandler): return response +class APIHandlerWithORM(APIHandler): + + params_class = None # type: Type[APIHandlerParams] + + def __init__(self, server): + self.server = server + + @property + def index(self): + return self.server.index + + @property + def redis(self): + return self.server.redis + + @property + def config(self): + return self.server.config + + @classmethod + def create_from_server(cls, server): + return cls(server=server) + + def _rate_limit(self, user_ip, application_id): + ip_rate_limit = self.config.rate_limiter.ips.get(user_ip, MAX_REQUESTS_PER_SECOND) + if self.rate_limiter.limit('ip', user_ip, ip_rate_limit): + if application_id == DEMO_APPLICATION_ID: + raise errors.TooManyRequests(ip_rate_limit) + if application_id is not None: + application_rate_limit = self.config.rate_limiter.applications.get(application_id) + if application_rate_limit is not None: + if self.rate_limiter.limit('app', application_id, application_rate_limit): + if application_id == DEMO_APPLICATION_ID: + raise errors.TooManyRequests(application_rate_limit) + + def handle(self, req): + params = self.params_class(self.config) + if req.access_route: + self.user_ip = req.access_route[0] + else: + self.user_ip = req.remote_addr + self.is_secure = req.is_secure + self.user_agent = req.user_agent + self.rate_limiter = RateLimiter(self.redis, 'rl') + try: + with DatabaseContext(self.server) as db: + self.db = db + try: + try: + params.parse(req.values, db) + self._rate_limit(self.user_ip, getattr(params, 'application_id', None)) + return self._ok(self._handle_internal(params), params.format) + except errors.WebServiceError: + raise + except Exception: + logger.exception('Error while handling API request') + raise errors.InternalError() + finally: + self.db = None + except errors.WebServiceError as e: + logger.warning("WS error: %s", e.message) + return self._error(e.code, e.message, params.format, status=e.status) + + +def get_submission_status(db, submission_ids): + submissions = ( + db.session.query(SubmissionResult.submission_id, SubmissionResult.track_id) + .filter(SubmissionResult.submission_id.in_(submission_ids)) + ) + track_ids = {submission_id: track_id for (submission_id, track_id) in submissions} + + tracks = ( + db.session.query(Track.id, Track.gid) + .filter(Track.id.in_(track_ids.values())) + ) + track_gids = {track_id: track_gid for (track_id, track_gid) in tracks} + + response = {'submissions': []} + for submission_id in submission_ids: + submission_response = {'id': submission_id, 'status': 'pending'} + track_id = track_ids.get(submission_id) + if track_id is not None: + track_gid = track_gids.get(track_id) + if track_gid is not None: + submission_response.update({ + 'status': 'imported', + 'response': {'id': track_gid}, + }) + response['submissions'].append(submission_response) + return response + + class SubmissionStatusHandlerParams(APIHandlerParams): - def parse(self, values, conn): - super(SubmissionStatusHandlerParams, self).parse(values, conn) - self._parse_client(values, conn) + def parse(self, values, db): + super(SubmissionStatusHandlerParams, self).parse(values, db) + self._parse_client(values, db.session.connection(mapper=Application)) self.ids = values.getlist('id', type=int) -class SubmissionStatusHandler(APIHandler): +class SubmissionStatusHandler(APIHandlerWithORM): params_class = SubmissionStatusHandlerParams def _handle_internal(self, params): - response = {'submissions': [{'id': id, 'status': 'pending'} for id in params.ids]} - tracks = lookup_submission_status(self.conn, params.ids) - for submission in response['submissions']: - id = submission['id'] - track_gid = tracks.get(id) - if track_gid is not None: - submission['status'] = 'imported' - submission['result'] = {'id': track_gid} - return response + return get_submission_status(self.db, params.ids) class SubmitHandlerParams(APIHandlerParams): - def _parse_user(self, values, conn): + def _parse_user(self, values, db): account_apikey = values.get('user') if not account_apikey: raise errors.MissingParameterError('user') - self.account_id = lookup_account_id_by_apikey(conn, account_apikey) + self.account_id = db.session.query(Account.id).filter(Account.apikey == account_apikey).scalar() if not self.account_id: raise errors.InvalidUserAPIKeyError() @@ -640,8 +723,8 @@ class SubmitHandlerParams(APIHandlerParams): p['foreignid'] = values.get('foreignid' + suffix) if p['foreignid'] and not is_foreignid(p['foreignid']): raise errors.InvalidForeignIDError('foreignid' + suffix) - p['mbids'] = values.getlist('mbid' + suffix) - if p['mbids'] and not all(map(is_uuid, p['mbids'])): + p['mbid'] = values.get('mbid' + suffix) + if p['mbid'] and not is_uuid(p['mbid']): raise errors.InvalidUUIDError('mbid' + suffix) self._parse_duration_and_format(p, values, suffix) fingerprint_string = values.get('fingerprint' + suffix) @@ -662,10 +745,10 @@ class SubmitHandlerParams(APIHandlerParams): p['year'] = values.get('year' + suffix, type=int) self.submissions.append(p) - def parse(self, values, conn): - super(SubmitHandlerParams, self).parse(values, conn) - self._parse_client(values, conn) - self._parse_user(values, conn) + def parse(self, values, db): + super(SubmitHandlerParams, self).parse(values, db) + self._parse_client(values, db.session.connection(mapper=Application)) + self._parse_user(values, db) self.wait = values.get('wait', type=int, default=0) self.submissions = [] suffixes = list(iter_args_suffixes(values, 'fingerprint')) @@ -679,73 +762,45 @@ class SubmitHandlerParams(APIHandlerParams): raise -class SubmitHandler(APIHandler): +class SubmitHandler(APIHandlerWithORM): params_class = SubmitHandlerParams meta_fields = ('track', 'artist', 'album', 'album_artist', 'track_no', 'disc_no', 'year') def _handle_internal(self, params): - response = {'submissions': []} - ids = set() - with self.conn.begin(): - source_id = find_or_insert_source(self.conn, params.application_id, params.account_id, params.application_version) - format_ids = {} - for p in params.submissions: - if p['format']: - if p['format'] not in format_ids: - format_ids[p['format']] = find_or_insert_format(self.conn, p['format']) - p['format_id'] = format_ids[p['format']] - for p in params.submissions: - mbids = p['mbids'] or [None] - for mbid in mbids: - values = { - 'mbid': mbid or None, - 'puid': p['puid'] or None, - 'bitrate': p['bitrate'] or None, - 'fingerprint': p['fingerprint'], - 'length': p['duration'], - 'format_id': p.get('format_id'), - 'source_id': source_id - } - meta_values = dict((n, p[n] or None) for n in self.meta_fields) - if any(meta_values.itervalues()): - values['meta_id'] = insert_meta(self.conn, meta_values) - if p['foreignid']: - values['foreignid_id'] = find_or_insert_foreignid(self.conn, p['foreignid']) - id = insert_submission(self.conn, values) - ids.add(id) - submission = {'id': id, 'status': 'pending'} - if p['index']: - submission['index'] = p['index'] - response['submissions'].append(submission) - - if self.redis is not None: - self.redis.publish('channel.submissions', json.dumps(list(ids))) - - clients_waiting_key = 'submission.waiting' - clients_waiting = self.redis.incr(clients_waiting_key) - 1 - try: - max_wait = 10 - self.redis.expire(clients_waiting_key, max_wait) - tracks = {} - remaining = min(max(0, max_wait - 2 ** clients_waiting), params.wait) - logger.debug('starting to wait at %f %d', remaining, clients_waiting) - while remaining > 0 and ids: - logger.debug('waiting %f seconds', remaining) - time.sleep(0.5) # XXX replace with LISTEN/NOTIFY - remaining -= 0.5 - tracks = lookup_submission_status(self.conn, ids) - if not tracks: - continue - for submission in response['submissions']: - id = submission['id'] - track_gid = tracks.get(id) - if track_gid is not None: - submission['status'] = 'imported' - submission['result'] = {'id': track_gid} - ids.remove(id) - finally: - self.redis.decr(clients_waiting_key) - + ids = {} + for p in params.submissions: + submission = Submission() + submission.account_id = params.account_id + submission.application_id = params.application_id + submission.application_version = params.application_version + submission.fingerprint = p['fingerprint'] + submission.duration = p['duration'] + submission.mbid = p['mbid'] or None + submission.puid = p['puid'] or None + submission.foreignid = p['foreignid'] or None + submission.bitrate = p['bitrate'] or None + submission.format = p['format'] or None + submission.track = p['track'] or None + submission.artist = p['artist'] or None + submission.album = p['album'] or None + submission.album_artist = p['album_artist'] or None + submission.track_no = p['track_no'] or None + submission.disc_no = p['disc_no'] or None + submission.year = p['year'] or None + self.db.session.add(submission) + self.db.session.flush() + ids[submission.id] = p['index'] + + self.db.session.commit() + + self.redis.publish('channel.submissions', json.dumps(list(ids.keys()))) + + response = get_submission_status(self.db, list(ids.keys())) + for submission_response in response['submissions']: + submission_id = submission_response['id'] + index = ids[submission_id] + if index: + submission_response['index'] = index return response diff --git a/acoustid/config.py b/acoustid/config.py index fc75fb43990c8b51ad9860d33973456cde4d7204..a00316f41b88f93783fb18a887bc0db8c4ae946b 100644 --- a/acoustid/config.py +++ b/acoustid/config.py @@ -18,8 +18,10 @@ def read_env_item(obj, key, name, convert=None): value = None if name in os.environ: value = os.environ[name] - if name + '_FILE' in os.environ: + logger.info('Reading config value from environment variable %s', name) + elif name + '_FILE' in os.environ: value = open(os.environ[name + '_FILE']).read().strip() + logger.info('Reading config value from environment variable %s', name + '_FILE') if value is not None: if convert is not None: value = convert(value) @@ -39,6 +41,35 @@ class BaseConfig(object): pass +class DatabasesConfig(BaseConfig): + + def __init__(self): + self.databases = { + 'default': DatabaseConfig(), + 'slow': DatabaseConfig(), + } + self.use_two_phase_commit = False + + def create_engines(self, **kwargs): + engines = {} + for name, db_config in self.databases.items(): + engines[name] = db_config.create_engine(**kwargs) + return engines + + def read_section(self, parser, section): + if parser.has_option(section, 'two_phase_commit'): + self.use_two_phase_commit = parser.getboolean(section, 'two_phase_commit') + for name, sub_config in self.databases.items(): + sub_section = '{}:{}'.format(section, name) + sub_config.read_section(parser, sub_section) + + def read_env(self, prefix): + read_env_item(self, 'use_two_phase_commit', prefix + 'TWO_PHASE_COMMIT', convert=str_to_bool) + for name, sub_config in self.databases.items(): + sub_prefix = prefix + name.upper() + '_' + sub_config.read_env(sub_prefix) + + class DatabaseConfig(BaseConfig): def __init__(self): @@ -103,21 +134,21 @@ class DatabaseConfig(BaseConfig): if parser.has_option(section, 'password'): self.password = parser.get(section, 'password') if parser.has_option(section, 'pool_size'): - self.password = parser.getint(section, 'pool_size') + self.pool_size = parser.getint(section, 'pool_size') if parser.has_option(section, 'pool_recycle'): - self.password = parser.getint(section, 'pool_recycle') + self.pool_recycle = parser.getint(section, 'pool_recycle') if parser.has_option(section, 'pool_pre_ping'): - self.password = parser.getboolean(section, 'pool_pre_ping') + self.pool_pre_ping = parser.getboolean(section, 'pool_pre_ping') def read_env(self, prefix): - read_env_item(self, 'name', prefix + 'POSTGRES_DB') - read_env_item(self, 'host', prefix + 'POSTGRES_HOST') - read_env_item(self, 'port', prefix + 'POSTGRES_PORT', convert=int) - read_env_item(self, 'user', prefix + 'POSTGRES_USER') - read_env_item(self, 'password', prefix + 'POSTGRES_PASSWORD') - read_env_item(self, 'pool_size', prefix + 'POSTGRES_POOL_SIZE', convert=int) - read_env_item(self, 'pool_recycle', prefix + 'POSTGRES_POOL_RECYCLE', convert=int) - read_env_item(self, 'pool_pre_ping', prefix + 'POSTGRES_POOL_PRE_PING', convert=str_to_bool) + read_env_item(self, 'name', prefix + 'NAME') + read_env_item(self, 'host', prefix + 'HOST') + read_env_item(self, 'port', prefix + 'PORT', convert=int) + read_env_item(self, 'user', prefix + 'USER') + read_env_item(self, 'password', prefix + 'PASSWORD') + read_env_item(self, 'pool_size', prefix + 'POOL_SIZE', convert=int) + read_env_item(self, 'pool_recycle', prefix + 'POOL_RECYCLE', convert=int) + read_env_item(self, 'pool_pre_ping', prefix + 'POOL_PRE_PING', convert=str_to_bool) class IndexConfig(BaseConfig): @@ -342,7 +373,7 @@ class RateLimiterConfig(BaseConfig): class Config(object): def __init__(self): - self.database = DatabaseConfig() + self.databases = DatabasesConfig() self.logging = LoggingConfig() self.website = WebSiteConfig() self.index = IndexConfig() @@ -357,7 +388,7 @@ class Config(object): logger.info("Loading configuration file %s", path) parser = ConfigParser.RawConfigParser() parser.read(path) - self.database.read(parser, 'database') + self.databases.read(parser, 'database') self.logging.read(parser, 'logging') self.website.read(parser, 'website') self.index.read(parser, 'index') @@ -373,7 +404,7 @@ class Config(object): prefix = 'ACOUSTID_TEST_' else: prefix = 'ACOUSTID_' - self.database.read_env(prefix) + self.databases.read_env(prefix) self.logging.read_env(prefix) self.website.read_env(prefix) self.index.read_env(prefix) diff --git a/acoustid/const.py b/acoustid/const.py index c4d6a15a6a10acd1b4582af77610ce122ac4315c..f96a679d78bbc2ab2dc2e7dcb04838a9b960c1c4 100644 --- a/acoustid/const.py +++ b/acoustid/const.py @@ -21,3 +21,6 @@ FINGERPRINT_MAX_LENGTH_DIFF = 7 FINGERPRINT_MAX_ALLOWED_LENGTH_DIFF = 30 MAX_REQUESTS_PER_SECOND = 3 + +MAX_FOREIGNID_NAMESPACE_LENGTH = 10 +MAX_FOREIGNID_VALUE_LENGTH = 64 diff --git a/acoustid/data/submission.py b/acoustid/data/submission.py index 2605624f3e89f645b4de87ea50573aa646988f13..7bfde1c93882a274e105435fab3bfc4564291e2e 100644 --- a/acoustid/data/submission.py +++ b/acoustid/data/submission.py @@ -28,6 +28,7 @@ def insert_submission(conn, data): 'source_id': data.get('source_id'), 'format_id': data.get('format_id'), 'meta_id': data.get('meta_id'), + 'foreignid': data.get('foreignid'), 'foreignid_id': data.get('foreignid_id'), }) id = conn.execute(insert_stmt).inserted_primary_key[0] diff --git a/acoustid/db.py b/acoustid/db.py index 8765092db874fb9967fb3f719383dbe4a2f17711..6d43da09f6f179c80cd3b78209904b488535d322 100644 --- a/acoustid/db.py +++ b/acoustid/db.py @@ -1,13 +1,29 @@ from sqlalchemy.orm import sessionmaker +from acoustid.tables import metadata Session = sessionmaker() +def get_bind_args(engines): + binds = {} + for table in metadata.sorted_tables: + bind_key = table.info.get('bind_key', 'default') + if bind_key != 'default': + binds[table] = engines[bind_key] + return {'bind': engines['default'], 'binds': binds} + + +def get_session_args(script): + kwargs = {'twophase': script.config.databases.use_two_phase_commit} + kwargs.update(get_bind_args(script.db_engines)) + return kwargs + + class DatabaseContext(object): - def __init__(self, bind): - self.session = Session(bind=bind) + def __init__(self, script): + self.session = Session(**get_session_args(script)) def __enter__(self): return self diff --git a/acoustid/models.py b/acoustid/models.py index d190c624c24a800abc663405d5d9f1185aae8a6e..d95a07d64ea44b9e6194b17b826f9af2b07a625c 100644 --- a/acoustid/models.py +++ b/acoustid/models.py @@ -79,3 +79,11 @@ class StatsLookups(Base): __table__ = tables.stats_lookups application = relationship('Application') + + +class Submission(Base): + __table__ = tables.submission + + +class SubmissionResult(Base): + __table__ = tables.submission_result diff --git a/acoustid/script.py b/acoustid/script.py index 501d7d18c915d3439463fe3aa647eac4eedfeefd..d0272cffea373c875e059350215b1c4e5df9f643 100644 --- a/acoustid/script.py +++ b/acoustid/script.py @@ -11,11 +11,26 @@ from optparse import OptionParser from acoustid.config import Config from acoustid.indexclient import IndexClientPool from acoustid.utils import LocalSysLogHandler +from acoustid.db import DatabaseContext from acoustid._release import GIT_RELEASE logger = logging.getLogger(__name__) +class ScriptContext(object): + + def __init__(self, db, redis, index): + self.db = db + self.redis = redis + self.index = index + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.db.close() + + class Script(object): def __init__(self, config_path, tests=False): @@ -23,25 +38,30 @@ class Script(object): if config_path: self.config.read(config_path) self.config.read_env(tests=tests) - if tests: - self.engine = sqlalchemy.create_engine(self.config.database.create_url(), - poolclass=sqlalchemy.pool.AssertionPool) - else: - self.engine = sqlalchemy.create_engine(self.config.database.create_url()) + + create_engine_kwargs = {'poolclass': sqlalchemy.pool.AssertionPool} if tests else {} + self.db_engines = self.config.databases.create_engines(**create_engine_kwargs) + if not self.config.index.host: self.index = None else: self.index = IndexClientPool(host=self.config.index.host, port=self.config.index.port, recycle=60) + if not self.config.redis.host: self.redis = None else: self.redis = Redis(host=self.config.redis.host, port=self.config.redis.port) + self._console_logging_configured = False self.setup_logging() + @property + def engine(self): + return self.db_engines['default'] + def setup_logging(self): for logger_name, level in sorted(self.config.logging.levels.items()): logging.getLogger(logger_name).setLevel(level) @@ -66,6 +86,10 @@ class Script(object): def setup_sentry(self): sentry_sdk.init(self.config.sentry.script_dsn, release=GIT_RELEASE) + def context(self): + db = DatabaseContext(self).session + return ScriptContext(db=db, redis=self.redis, index=self.index) + def run_script(func, option_cb=None, master_only=False): parser = OptionParser() diff --git a/acoustid/server.py b/acoustid/server.py index 41aab52b2d984337b598b1e22d007a083efcccf0..b2b8a34f736520bffc02c3f404a20531a5b687a8 100644 --- a/acoustid/server.py +++ b/acoustid/server.py @@ -9,7 +9,7 @@ from cStringIO import StringIO from werkzeug.exceptions import HTTPException from werkzeug.routing import Map, Rule, Submount from werkzeug.wrappers import Request -from werkzeug.contrib.fixers import ProxyFix +from werkzeug.middleware.proxy_fix import ProxyFix from acoustid.script import Script from acoustid._release import GIT_RELEASE import acoustid.api.v1 @@ -53,12 +53,15 @@ admin_url_rules = [ class Server(Script): - def __init__(self, config_path): - super(Server, self).__init__(config_path) + def __init__(self, config_path, **kwargs): + super(Server, self).__init__(config_path, **kwargs) url_rules = api_url_rules + admin_url_rules self.url_map = Map(url_rules, strict_slashes=False) def __call__(self, environ, start_response): + return self.wsgi_app(environ, start_response) + + def wsgi_app(self, environ, start_response): urls = self.url_map.bind_to_environ(environ) handler = None try: @@ -112,16 +115,16 @@ def add_cors_headers(app): return wrapped_app -def make_application(config_path): +def make_application(config_path, **kwargs): """Construct a WSGI application for the AcoustID server :param config_path: path to the server configuration file """ - server = Server(config_path) + server = Server(config_path, **kwargs) server.setup_sentry() - app = GzipRequestMiddleware(server) - app = ProxyFix(app) - app = SentryWsgiMiddleware(app) - app = replace_double_slashes(app) - app = add_cors_headers(app) - return server, app + server.wsgi_app = GzipRequestMiddleware(server.wsgi_app) + server.wsgi_app = ProxyFix(server.wsgi_app) + server.wsgi_app = SentryWsgiMiddleware(server.wsgi_app) + server.wsgi_app = replace_double_slashes(server.wsgi_app) + server.wsgi_app = add_cors_headers(server.wsgi_app) + return server diff --git a/acoustid/tables.py b/acoustid/tables.py index abe1b329a1cefa7bd16a0166d956236e8dfc5887..d70b1c853b81160c42a1484a56d4b5a6f7988575 100644 --- a/acoustid/tables.py +++ b/acoustid/tables.py @@ -1,6 +1,7 @@ +import sqlalchemy import sqlalchemy.event from sqlalchemy import ( - MetaData, Table, Column, Index, + MetaData, Table, Column, Index, Sequence, ForeignKey, CheckConstraint, Integer, String, DateTime, Boolean, Date, Text, SmallInteger, BigInteger, CHAR, DDL, sql, @@ -103,7 +104,7 @@ source = Table('source', metadata, Index('source_idx_uniq', 'application_id', 'account_id', 'version', unique=True), ) -submission = Table('submission', metadata, +submission_old = Table('submission_old', metadata, Column('id', Integer, primary_key=True), Column('fingerprint', ARRAY(Integer), nullable=False), Column('length', SmallInteger, CheckConstraint('length>0'), nullable=False), @@ -118,7 +119,55 @@ submission = Table('submission', metadata, Column('foreignid_id', Integer, ForeignKey('foreignid.id')), ) -Index('submission_idx_handled', submission.c.id, postgresql_where=submission.c.handled == False) # noqa: E712 +Index('submission_idx_handled', submission_old.c.id, postgresql_where=submission_old.c.handled == False) # noqa: E712 + +submission_id_seq = Sequence('submission_id_seq', metadata=metadata) + +submission = Table('submission', metadata, + Column('id', Integer, submission_id_seq, server_default=submission_id_seq.next_value(), primary_key=True), + Column('created', DateTime(timezone=True), server_default=sql.func.current_timestamp(), nullable=False), + Column('handled', Boolean, default=False, server_default=sql.false()), + + Column('account_id', Integer, nullable=False), # ForeignKey('account.id') + Column('application_id', Integer, nullable=False), # ForeignKey('application.id') + Column('application_version', String), + + Column('fingerprint', ARRAY(Integer), nullable=False), + Column('duration', Integer, CheckConstraint('duration>0'), nullable=False), + Column('bitrate', Integer, CheckConstraint('bitrate>0')), + Column('format', String), + Column('mbid', UUID), + Column('puid', UUID), + Column('foreignid', String), + + Column('track', String), + Column('artist', String), + Column('album', String), + Column('album_artist', String), + Column('track_no', Integer), + Column('disc_no', Integer), + Column('year', Integer), + + info={'bind_key': 'slow'}, +) + +submission_result = Table('submission_result', metadata, + Column('submission_id', Integer, primary_key=True, autoincrement=False), + Column('created', DateTime(timezone=True), server_default=sql.func.current_timestamp(), nullable=False), + + Column('account_id', Integer, nullable=False), # ForeignKey('account.id') + Column('application_id', Integer, nullable=False), # ForeignKey('application.id') + Column('application_version', String), + + Column('fingerprint_id', Integer, nullable=False), # ForeignKey('fingerprint.id') + Column('track_id', Integer, nullable=False), # ForeignKey('track.id') + Column('meta_id', Integer), # ForeignKey('meta.id') + Column('mbid', UUID), + Column('puid', UUID), + Column('foreignid', String), + + info={'bind_key': 'slow'}, +) stats = Table('stats', metadata, Column('id', Integer, primary_key=True), @@ -193,6 +242,8 @@ fingerprint = Table('fingerprint', metadata, Index('fingerprint_idx_track_id', 'track_id'), ) +fingerprint.add_is_dependent_on(track) + fingerprint_source = Table('fingerprint_source', metadata, Column('id', Integer, primary_key=True), Column('fingerprint_id', Integer, ForeignKey('fingerprint.id'), nullable=False), diff --git a/acoustid/utils.py b/acoustid/utils.py index ba6a5dfb4e5ee0e27023d1adb0c4f95421849aad..2c31323a7e7d32d54485d542fed686bf957470e1 100644 --- a/acoustid/utils.py +++ b/acoustid/utils.py @@ -9,6 +9,7 @@ import datetime import hmac import base64 import six +from acoustid.const import MAX_FOREIGNID_NAMESPACE_LENGTH, MAX_FOREIGNID_VALUE_LENGTH from six.moves.urllib.request import urlopen from six.moves.urllib.parse import urlencode from logging import Handler @@ -54,7 +55,15 @@ def is_int(s): def is_foreignid(s): - return bool(re.match(r'^[0-9a-z]+:.+$', s)) + match = re.match(r'^([0-9a-z]+):(.+)$', s) + if match is None: + return False + namespace, value = match.groups() + if len(namespace) > MAX_FOREIGNID_NAMESPACE_LENGTH: + return False + if len(value) > MAX_FOREIGNID_VALUE_LENGTH: + return False + return True def singular(plural): diff --git a/acoustid/web/app.py b/acoustid/web/app.py index 8d72bb8588cb061c136c51af06d0b211d662cb34..8287961257b8563344c355c651e541f1b3fd9cb5 100644 --- a/acoustid/web/app.py +++ b/acoustid/web/app.py @@ -5,9 +5,10 @@ import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration from flask import Flask, request, session from flask.sessions import SecureCookieSessionInterface -from werkzeug.contrib.fixers import ProxyFix +from werkzeug.middleware.proxy_fix import ProxyFix from sqlalchemy.orm import scoped_session from acoustid.script import Script +from acoustid.db import get_session_args from acoustid.web import db from acoustid.web.views.general import general_page from acoustid.web.views.user import user_page @@ -36,6 +37,8 @@ def make_application(config_filename=None, tests=False): GOOGLE_OAUTH_CLIENT_ID=config.website.google_oauth_client_id, GOOGLE_OAUTH_CLIENT_SECRET=config.website.google_oauth_client_secret, ) + if tests: + app.config['TESTING'] = True app.acoustid_script = script app.acoustid_config = config @@ -88,7 +91,7 @@ def make_application(config_filename=None, tests=False): from acoustid.api import get_health_response return get_health_response(script, request) - db.session_factory.configure(bind=config.database.create_engine()) + db.session_factory.configure(**get_session_args(script)) db.session = scoped_session(db.session_factory, scopefunc=get_flask_request_scope) app.register_blueprint(general_page) diff --git a/acoustid/web/views/general.py b/acoustid/web/views/general.py index 5119e61261e53a18fd9d7ba0f59796312d4c000b..5b8c1e561130e84e785e57c2074e9706dbb18970 100644 --- a/acoustid/web/views/general.py +++ b/acoustid/web/views/general.py @@ -33,7 +33,7 @@ def render_page(name, **context): text = file.read().decode('utf8') text = render_template_string(text, **context) md = Markdown(extensions=['meta']) - md.treeprocessors["flask_links"] = MarkdownFlaskUrlProcessor(md) + md.treeprocessors.register(MarkdownFlaskUrlProcessor(md), 'flask_links', 50) html = md.convert(text) title = ' '.join(md.Meta.get('title', [])) return render_template('page.html', content=html, title=title) diff --git a/admin/ci/create_db.sql b/admin/ci/create_db.sql index bf0c6a00e3a4e0d0b253f57eb0906758aa1c2c66..f95388d6509ff4f8056578b167985fab7a9d289f 100644 --- a/admin/ci/create_db.sql +++ b/admin/ci/create_db.sql @@ -1,6 +1,9 @@ CREATE DATABASE "acoustid"; CREATE DATABASE "acoustid_test"; +CREATE DATABASE "acoustid_slow"; +CREATE DATABASE "acoustid_slow_test"; + \c acoustid create extension intarray; create extension pgcrypto; @@ -12,3 +15,15 @@ create extension intarray; create extension pgcrypto; create extension acoustid; create extension cube; + +\c acoustid_slow +create extension intarray; +create extension pgcrypto; +create extension acoustid; +create extension cube; + +\c acoustid_slow_test +create extension intarray; +create extension pgcrypto; +create extension acoustid; +create extension cube; diff --git a/alembic.ini b/alembic.ini index 69c69c57c197ee46efe335bb750e497c1c9e7c70..2a13ffafa65cc69cae294906e0df333abc50e517 100644 --- a/alembic.ini +++ b/alembic.ini @@ -29,6 +29,8 @@ script_location = alembic # are written from script.py.mako # output_encoding = utf-8 +databases = default, slow + # Logging configuration [loggers] keys = root,sqlalchemy,alembic diff --git a/alembic/README b/alembic/README index 98e4f9c44effe479ed38c66ba922e7bcc672916f..bcf10863a43339af6fc52d2822bf1bc6a6e679c9 100644 --- a/alembic/README +++ b/alembic/README @@ -1 +1 @@ -Generic single-database configuration. \ No newline at end of file +AcoustID's multi-database configuration. diff --git a/alembic/env.py b/alembic/env.py index 3c9d9c4c0741792e4a03c23612bd094c8b19c476..f23e465d8dcaf04a663bc838d69a856dab2c94a7 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -1,11 +1,13 @@ from __future__ import with_statement import os +import logging from alembic import context from sqlalchemy import engine_from_config, pool from logging.config import fileConfig config = context.config fileConfig(config.config_file_name) +logger = logging.getLogger("alembic.env") import acoustid.tables target_metadata = acoustid.tables.metadata @@ -17,12 +19,25 @@ acoustid_config = acoustid.config.Config() acoustid_config.read(acoustid_config_filename) acoustid_config.read_env() -def include_object(obj, name, type, reflected, compare_to): - if type == "table" and obj.schema == "musicbrainz": - return False - if type == "column" and not obj.table.schema == "musicbrainz": - return False - return True +use_two_phase_commit = acoustid_config.databases.use_two_phase_commit + + +def include_object(db_name): + def inner(obj, name, obj_type, reflected, compare_to): + if obj_type == "table": + if obj.schema == "musicbrainz": + return False + bind_key = obj.info.get('bind_key', 'default') + if bind_key != db_name: + return False + if obj_type == "column": + if obj.table.schema == "musicbrainz": + return False + bind_key = obj.table.info.get('bind_key', 'default') + if bind_key != db_name: + return False + return True + return inner def run_migrations_offline(): @@ -37,13 +52,18 @@ def run_migrations_offline(): script output. """ - url = acoustid_config.database.create_url() - context.configure( - url=url, target_metadata=target_metadata, literal_binds=True, - include_object=include_object) + for name, db_config in acoustid_config.databases.databases.items(): + logger.info("Migrating database %s" % name) + + context.configure( + url=db_config.create_url(), + target_metadata=target_metadata, + literal_binds=True, + include_object=include_object(name), + ) - with context.begin_transaction(): - context.run_migrations() + with context.begin_transaction(): + context.run_migrations() def run_migrations_online(): @@ -53,17 +73,45 @@ def run_migrations_online(): and associate a connection with the context. """ - connectable = acoustid_config.database.create_engine(poolclass=pool.NullPool) - - with connectable.connect() as connection: - context.configure( - connection=connection, - target_metadata=target_metadata, - include_object=include_object, - ) - - with context.begin_transaction(): - context.run_migrations() + engines = {} + for name, db_config in acoustid_config.databases.databases.items(): + engines[name] = rec = {} + rec["engine"] = db_config.create_engine(poolclass=pool.NullPool) + + for name, rec in engines.items(): + engine = rec["engine"] + rec["connection"] = conn = engine.connect() + + if use_two_phase_commit: + rec["transaction"] = conn.begin_twophase() + else: + rec["transaction"] = conn.begin() + + try: + for name, rec in engines.items(): + logger.info("Migrating database %s" % name) + context.configure( + connection=rec["connection"], + upgrade_token="%s_upgrades" % name, + downgrade_token="%s_downgrades" % name, + target_metadata=target_metadata, + include_object=include_object(name), + ) + context.run_migrations(engine_name=name) + + if use_two_phase_commit: + for rec in engines.values(): + rec["transaction"].prepare() + + for rec in engines.values(): + rec["transaction"].commit() + except: + for rec in engines.values(): + rec["transaction"].rollback() + raise + finally: + for rec in engines.values(): + rec["connection"].close() if context.is_offline_mode(): diff --git a/alembic/script.py.mako b/alembic/script.py.mako index 43c09401bc839adc93c2bbb41fb1407b09ce7718..5d43608eb4fca3dcb06d120028309256f7dbcb81 100644 --- a/alembic/script.py.mako +++ b/alembic/script.py.mako @@ -1,10 +1,16 @@ -"""${message} +<%! +import re + +%>"""${message} Revision ID: ${up_revision} Revises: ${down_revision | comma,n} Create Date: ${create_date} """ +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} # revision identifiers, used by Alembic. revision = ${repr(up_revision)} @@ -12,13 +18,20 @@ down_revision = ${repr(down_revision)} branch_labels = ${repr(branch_labels)} depends_on = ${repr(depends_on)} -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} -def upgrade(): - ${upgrades if upgrades else "pass"} +def upgrade(engine_name): + globals()["upgrade_%s" % engine_name]() + + +def downgrade(engine_name): + globals()["downgrade_%s" % engine_name]() +% for db_name in re.split(r',\s*', config.get_main_option("databases")): + + +def upgrade_${db_name}(): + ${context.get("%s_upgrades" % db_name, "pass")} -def downgrade(): - ${downgrades if downgrades else "pass"} +def downgrade_${db_name}(): + ${context.get("%s_downgrades" % db_name, "pass")} +% endfor diff --git a/alembic/versions/3b48d5f44110_fixes.py b/alembic/versions/3b48d5f44110_fixes.py index dbe3d082f7740962fef63099647e945d8ef18c46..a1a46b0ad30f82f55880f8d917f155da0c8aa59b 100644 --- a/alembic/versions/3b48d5f44110_fixes.py +++ b/alembic/versions/3b48d5f44110_fixes.py @@ -16,7 +16,15 @@ from alembic import op import sqlalchemy as sa -def upgrade(): +def upgrade(engine_name): + globals()["upgrade_%s" % engine_name]() + + +def downgrade(engine_name): + globals()["downgrade_%s" % engine_name]() + + +def upgrade_default(): op.create_foreign_key(op.f('account_google_fk_account_id'), 'account_google', 'account', ['account_id'], ['id']) op.create_foreign_key(op.f('application_fk_account_id'), 'application', 'account', ['account_id'], ['id']) op.create_foreign_key(op.f('stats_lookups_fk_application_id'), 'stats_lookups', 'application', ['application_id'], ['id']) @@ -46,7 +54,7 @@ def upgrade(): op.alter_column('recording_acoustid', 'created', nullable=False) -def downgrade(): +def downgrade_default(): op.alter_column('recording_acoustid', 'created', nullable=True) op.alter_column('track_foreignid_source', 'created', nullable=True) op.alter_column('track_foreignid', 'created', nullable=True) @@ -74,3 +82,11 @@ def downgrade(): op.drop_constraint(op.f('stats_lookups_fk_application_id'), 'stats_lookups', type_='foreignkey') op.drop_constraint(op.f('application_fk_account_id'), 'application', type_='foreignkey') op.drop_constraint(op.f('account_google_fk_account_id'), 'account_google', type_='foreignkey') + + +def upgrade_slow(): + pass + + +def downgrade_slow(): + pass diff --git a/alembic/versions/57c4d22c87b8_initial.py b/alembic/versions/57c4d22c87b8_initial.py index d1e9615c2498662b28fa8efbcccf9a5ab193a6a2..1f110445ac87d48e7f417606a7819cecebb6d63a 100644 --- a/alembic/versions/57c4d22c87b8_initial.py +++ b/alembic/versions/57c4d22c87b8_initial.py @@ -16,7 +16,16 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql -def upgrade(): + +def upgrade(engine_name): + globals()["upgrade_%s" % engine_name]() + + +def downgrade(engine_name): + globals()["downgrade_%s" % engine_name]() + + +def upgrade_default(): op.create_table('account_google', sa.Column('google_user_id', sa.String(), nullable=False), sa.Column('account_id', sa.Integer(), nullable=False), @@ -356,7 +365,7 @@ def upgrade(): ) -def downgrade(): +def downgrade_default(): op.drop_table('track_puid_source') op.drop_table('track_meta_source') op.drop_index(op.f('track_mbid_source_idx_track_mbid_id'), table_name='track_mbid_source') @@ -422,3 +431,11 @@ def downgrade(): op.drop_table('account_stats_control') op.drop_index('account_google_idx_account_id', table_name='account_google') op.drop_table('account_google') + + +def upgrade_slow(): + pass + + +def downgrade_slow(): + pass diff --git a/alembic/versions/ae7e1e5763ef_remove_submission_id_constraints.py b/alembic/versions/ae7e1e5763ef_remove_submission_id_constraints.py index 7560895742284497e428b49134abe93e76dee1ce..7abfe32a928688da575cc9398b4f27ab80adaac8 100644 --- a/alembic/versions/ae7e1e5763ef_remove_submission_id_constraints.py +++ b/alembic/versions/ae7e1e5763ef_remove_submission_id_constraints.py @@ -16,7 +16,15 @@ from alembic import op import sqlalchemy as sa -def upgrade(): +def upgrade(engine_name): + globals()["upgrade_%s" % engine_name]() + + +def downgrade(engine_name): + globals()["downgrade_%s" % engine_name]() + + +def upgrade_default(): op.drop_constraint(u'fingerprint_source_fk_submission_id', 'fingerprint_source', type_='foreignkey') op.drop_constraint(u'track_foreignid_source_fk_submission_id', 'track_foreignid_source', type_='foreignkey') op.drop_constraint(u'track_mbid_source_fk_submission_id', 'track_mbid_source', type_='foreignkey') @@ -24,9 +32,17 @@ def upgrade(): op.drop_constraint(u'track_puid_source_fk_submission_id', 'track_puid_source', type_='foreignkey') -def downgrade(): +def downgrade_default(): op.create_foreign_key(u'track_puid_source_fk_submission_id', 'track_puid_source', 'submission', ['submission_id'], ['id']) op.create_foreign_key(u'track_meta_source_fk_submission_id', 'track_meta_source', 'submission', ['submission_id'], ['id']) op.create_foreign_key(u'track_mbid_source_fk_submission_id', 'track_mbid_source', 'submission', ['submission_id'], ['id']) op.create_foreign_key(u'track_foreignid_source_fk_submission_id', 'track_foreignid_source', 'submission', ['submission_id'], ['id']) op.create_foreign_key(u'fingerprint_source_fk_submission_id', 'fingerprint_source', 'submission', ['submission_id'], ['id']) + + +def upgrade_slow(): + pass + + +def downgrade_slow(): + pass diff --git a/alembic/versions/d52d50968cf7_move_submissions.py b/alembic/versions/d52d50968cf7_move_submissions.py new file mode 100644 index 0000000000000000000000000000000000000000..35905833fb5308df8d78f2ef212a6c5a62bf3e45 --- /dev/null +++ b/alembic/versions/d52d50968cf7_move_submissions.py @@ -0,0 +1,89 @@ +"""move submissions + +Revision ID: d52d50968cf7 +Revises: ae7e1e5763ef +Create Date: 2019-06-08 11:04:53.563059 + +""" +import datetime +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql +from sqlalchemy.schema import Sequence, CreateSequence, DropSequence +from dateutil.relativedelta import relativedelta + + +# revision identifiers, used by Alembic. +revision = 'd52d50968cf7' +down_revision = 'ae7e1e5763ef' +branch_labels = None +depends_on = None + + +def upgrade(engine_name): + globals()["upgrade_%s" % engine_name]() + + +def downgrade(engine_name): + globals()["downgrade_%s" % engine_name]() + + +def upgrade_default(): + op.rename_table('submission', 'submission_old') + + +def downgrade_default(): + op.rename_table('submission_old', 'submission') + + +def upgrade_slow(): + op.execute(CreateSequence(Sequence('submission_id_seq'))) + op.create_table('submission', + sa.Column('id', sa.Integer(), server_default=sa.text("nextval('submission_id_seq')"), nullable=False), + sa.Column('created', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), + sa.Column('handled', sa.Boolean(), server_default=sa.text('false'), nullable=True), + sa.Column('account_id', sa.Integer(), nullable=False), + sa.Column('application_id', sa.Integer(), nullable=False), + sa.Column('application_version', sa.String(), nullable=True), + sa.Column('fingerprint', postgresql.ARRAY(sa.Integer()), nullable=False), + sa.Column('duration', sa.Integer(), nullable=False), + sa.Column('bitrate', sa.Integer(), nullable=True), + sa.Column('format', sa.String(), nullable=True), + sa.Column('mbid', postgresql.UUID(), nullable=True), + sa.Column('puid', postgresql.UUID(), nullable=True), + sa.Column('foreignid', sa.String(), nullable=True), + sa.Column('track', sa.String(), nullable=True), + sa.Column('artist', sa.String(), nullable=True), + sa.Column('album', sa.String(), nullable=True), + sa.Column('album_artist', sa.String(), nullable=True), + sa.Column('track_no', sa.Integer(), nullable=True), + sa.Column('disc_no', sa.Integer(), nullable=True), + sa.Column('year', sa.Integer(), nullable=True), + postgresql_partition_by='RANGE (created)', + ) + + one_month = relativedelta(months=1) + partitions_from = datetime.date(2010, 1, 1) + partitions_to = datetime.date.today().replace(month=1, day=1) + relativedelta(months=12) + range_from = partitions_from + while range_from < partitions_to: + range_to = range_from + one_month + op.execute(""" + + CREATE TABLE IF NOT EXISTS submission_y{range_from.year:04d}m{range_to.month:02d} + PARTITION OF submission + FOR VALUES FROM ('{range_from}') TO ('{range_to}'); + + ALTER TABLE submission_y{range_from.year:04d}m{range_to.month:02d} + ADD PRIMARY KEY (id); + + CREATE INDEX submission_y{range_from.year:04d}m{range_to.month:02d}_idx_handled + ON submission_y{range_from.year:04d}m{range_to.month:02d} (handled); + + """.format(range_from=range_from, range_to=range_to)) + range_from = range_to + + +def downgrade_slow(): + op.drop_table('submission') + op.execute(DropSequence(Sequence('submission_id_seq'))) diff --git a/alembic/versions/d5c0520500a6_account_is_admin.py b/alembic/versions/d5c0520500a6_account_is_admin.py index 81e8cb23cd89dbdf175234cafd66a6f78db5b812..06df9efae49351dab1c257d18fe39f7904c60cf9 100644 --- a/alembic/versions/d5c0520500a6_account_is_admin.py +++ b/alembic/versions/d5c0520500a6_account_is_admin.py @@ -16,11 +16,27 @@ from alembic import op import sqlalchemy as sa -def upgrade(): +def upgrade(engine_name): + globals()["upgrade_%s" % engine_name]() + + +def downgrade(engine_name): + globals()["downgrade_%s" % engine_name]() + + +def upgrade_default(): op.add_column('account', sa.Column('is_admin', sa.Boolean(), server_default=sa.text('false'), nullable=False) ) -def downgrade(): +def downgrade_default(): op.drop_column('account', 'is_admin') + + +def upgrade_slow(): + pass + + +def downgrade_slow(): + pass diff --git a/docker-compose.yml b/docker-compose.yml index 985bb699139373ccbe4b99ef757ed78abca4e8bc..5143558f62fef640a595975d20ef09c4f32b5c51 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,12 +4,18 @@ services: redis: image: redis ports: - - "127.0.0.1:6379:6379" + - "127.0.0.1:16379:6379" + + index: + image: quay.io/acoustid/acoustid-index:master + ports: + - "127.0.0.1:16080:6080" postgres: image: quay.io/acoustid/postgresql:master + command: ["-c", "max_connections=100", "-c", "max_prepared_transactions=110"] ports: - - "127.0.0.1:5432:5432" + - "127.0.0.1:15432:5432" volumes: - ./admin/ci/create_db.sql:/docker-entrypoint-initdb.d/10_create_db.sql environment: diff --git a/old_tests/__init__.py b/old_tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a32f92b201cd4dbb25b4c090ff7a0a67e0b8f494 --- /dev/null +++ b/old_tests/__init__.py @@ -0,0 +1,208 @@ +# Copyright (C) 2011 Lukas Lalinsky +# Distributed under the MIT license, see the LICENSE file for details. + +import os +import json +import pprint +import difflib +from contextlib import closing +from nose.tools import make_decorator +from acoustid.script import Script +from acoustid.tables import metadata +from acoustid.db import DatabaseContext + +TEST_2_LENGTH = 320 +TEST_2_FP = 'AQABVtuUZFGShAqO-h9OHD96SvhwBVNCKQnOIYmiIc-ENwF7TDe8Hr0W_AjhvRCP2sfT4DTS7zjyOYeqaI-RSxee5RmaWzhOHnlcaB6HnPgpdE-DkWIH2ysYG_Eh9zJCyfCXGOdw-EGoD2p69IavWOhzMD-a9tBx9FgPVz2qNDvQH3744ISIXRKeHto5_MhyeMtxc-COnYJ_lHLwRAgPvShz_Hga4zd8HD9UKXWOPP3xRLmGnlbQHKfxGPeRvAt6UngMvcF-gkpRi0bUZjGaH6FUHb_xGDt6aHmM__ghfkmH70B4fWiuCj8y8uj3oImZY8d3NFWWHuGF-3hCPEd_uEOyE_nw4w8ueXi24znCHOHxSWtw9BnSBzrSHF2Y4S0e_EioZoh9XMGfo2dqNMeP80aQPM5xGT9efMeTYL-KIqmHdDraHs-P8IcYjoj0I7_Q43iJ9BF64nSKKth2SjG-cvCHH-2OL8txHsUt9HhF4LiK5j16lAf1FkjvQiN55FSOkkOPkmj4GK-OH80eIeyh98HhE_qhPwjzKAV-HJ2OZkd4Q_vhp0d_6Id-_IeWW9CKoP3RKM-Bo3mOfvhxND_6HMgZ6EfXHB-8Q8-iok1znOi-ozmx54P2Dg5V_PCgLxy8KiH6C0cbHU3Ebtiho9Rxw8er47tw7jgRNxl84ziPJ-B1_DiNNClzaGSCvMGPGxePMD5qZYEuAwdTXYSYcIkmodc2nMqg_WgqBk_yBdVx0vCjQD8uhNRxXTgvVFSOSOmx61C1KMaNsFwM93h-PBdmFm8o45nxDabx48cTbGl4hHuhasjSwPtxPvAV1A7yQMukREERR-nxL8j-EbWYQ8sj4joABQQmjQhkjLFCKSAo4QoxYiQwQhgmkGjCKGAIMMA4BIwQwjhAFMBUCCUAYEIxpYxUCDlEjJYOScSMgsIIAgADwjKEFBAUCkMEMYAagoARzAAHCDCIISKANkgYYBiQwgDDjHEMIGWZFUBQLhgohBGkhECOMEAMIYghogTgQghgiSLCYUegAsJApIQjxABNDFWCa6AIAQ4Q4KgAgIABgGDCMNGIMgQJRQAQTACpgBNIJkUcBMkpoKAgXCjAgAAGKIcYIVAYbZgwggkEmKLEiYGYAYQQShFAAAQBFEEAEuEIgwYRQoARnBkAmAGMEAGFGIgQBigCwAkABEIA' +TEST_2_FP_RAW = [-772063964, -772066012, -1007079116, -1011011276, -1027788492, -1029889740, -1031261916, -1031269084, -1031270620, -1031021744, -1031079600, -1031078425, -1031032346, -1018580506, -1018646290, -1022775046, -1056337446, -1056261686, -1073039030, -1073039013, -1068976005, -1001867175, -733365175, -733302711, -737435575, -603332504, -603365272, -737516311, -188076117, -175490390, -745993558, -758642022, -767030630, -1034347878, -1038412133, -1039584631, -1039654264, -1034345784, -1030086056, -1011141092, -1045873092, -1045939676, -1045947803, -2132276505, -2132259914, -2140415082, -2140472938, -2140481130, -2140546634, -2140603929, -2144802523, -2144536267, -2123540203, -2115147515, -2081855203, -2098598611, -2098606801, -2098606737, -2106995329, -2090217989, -2123638309, -2140477173, -2140477173, -2123634405, -2106992325, -2107061957, -2107061991, -2107007715, -1033140963, -1023769329, -1025864433, -1026913002, -1010133962, -1017409482, -1017540482, -1022848902, -1056337830, -1056271030, -1056261302, -1073038501, -1068975271, -1060587447, -993477559, -1001672631, -737435575, -737566615, -737550231, -737419031, 1422585259, 1955228586, 1367940010, 1388841914, 1380453258, 1376328586, 1376458634, 1107956618, 1107899017, 1113072281, 1121657497, 1119494811, 1135224479, 1135226615, 1139489511, 1130891874, 1126713938, 1109977859, 1114237187, 1122691331, 1122695431, 1122687255, 1114233125, 1130944869, 1126746469, 1097554247, 1105885511, 1105885511, 1135270230, 1122523494, 1114135910, 1109939695, 1093236223, 1076520335, 1080714635, 1089107851, 11092923, 11010986, 15209450, 15492074, 7103274, 2913082, 2905882, 2940681, 2947848, 7138056, 32303368, 61716744, 44932552, 1118668232, 1118406137, 1122600424, 1110167912, 1110167848, 1110106424, 1122689305, 1118495003, 1118478714, 1118540010, 1122599146, 1110016234, 1110147562, 1110094153, 1076535560, 1076538376, -1058363384, -794183656, -794249176, -790063064, -261519320, -261519319, -529562582, -529628886, -530153430, -530280406, -534465494, -534459350, -517027794, -517027778, -517056387, 1630428508, 1634606924, 1643060940, -508616995, -508740929, -475252054, -487834709, -496223301, -496231493, -496092485, -488752486, -489735542, -494125366, -494125542, 1641889598, 1627335998, 1617898782, 1613703454, 1614756622, 537664270, 541854222, 541854238, 541874782, 558651982, 558168910, 558168910, 558168398, 566360398, 1636039038, 1669593454, 1938028670, 1942087766, 1942087766, 1665394807, 1631779173, 1640192868, 1640221300, 1640483428, 1640487796, 1631902020, 1627682884, 553932868, 554654068, 554589029, 567179879, 562985575, 562846279, 562879301, 558684487, 554678646, 554678646, 558873462, 567262070, 563067366, 562936022, 567064775, 558692565, 1628436725, 1661925605, 1661893095, 1666087909, 592329573, 567032663, 567032133, 567032132, 1640840020, 1909340900, 1909340900, -238142748, -775079212, -775152956, -1043580220, -1047774524, -2121450764, -2138162460, -2138162460, -2138232091, -2121520409, -2117330313, -2124670345, -2124604585, -2092205227, -2083848891, -2083787451, -2117489195, -2117550619, -2124902943, -2139517453, -2139383405, -2122597997, -2122598221, -2093090639, -2095162991, -2107737727, -2107754111, -2108805231, -2099495199, -2099499291, -2097402137, -2098451465, -2098709513, -2098717737, -2081859113, -2123773481, -2140434985, -2140496425, -2140428906, -2132171338, -2132236874, -2065124170, -2023181130, -2039964482, -2044162882, -2044098306, -2111137761, -2111117043, -2111165684, -2144720372, -2140460532, -2132132340, -2136328643, -2136407507, -2136471761, -2136471761, -2132277457, -2114187977, -2091268651, -2083809851, -2083872379, -2117361257, -2117552729, -2141681241, -2139584009, -2143708937, -2143618857, -2126874411, -2126894891, -2093339196, -2105923644, -2099628348, -2103836012, -2103966047, -2099751259, -2097639449, -2031579161, -2039763466, -2031375914, -2014728234, -2056675370, -2056548654, -2073127278, -2077251950, -2077233262, -2077266510, -2077332302, -2073154382, -2014434126, -2031143722, -2039794617, -2039792379, -2039792868, -2107033028, -2081936836, -2136462820, -2136265204, -2136263155, -2136456385, -2136456401, -2132228626, -2122791506, -2091070041, -2107647561, -2108769915, -2100384379] + +# MP3, 320kbps +TEST_1A_LENGTH = 260 +TEST_1A_FP = 'AQABVkmYaEmkJEsiaGvhH33xXNDyol2Pz-jhW2h1_Bb6w4d2dIbPo8dzVA_Es0E1C9_RCGH1DDmP3hX8DD2-Bdq0ojmPa9CeS_CPdi0-9EFz4bqE02iOQzs6w-fR7-gP9R0ct2ipoEeqD60u5EyFPoSPfjF0NNKOXsMP7TkcMxyq3bjQ_GiP05TQw4d24ejQfDl-VD7EN8JdtFTQHWH8DN8RPhXsED_6BdoOv0df_IJ2FuZ69MaF5hba47dQ-OihC42MvjmeQPthvSRqaR3-HfUFH33YoC-UHddRWSL6Gr6gHd0DO-i0Hj1-WMJjXAdeqoN5_MfhrbgOVzyeGRfOCpbIauh7IY8evEGYo0_woIePyxDtFTgJt8d96BM64UZ1mLfQFw96-EW77Oh__Eb64WLwz_DxZXgHU6HRfbjQozlxhOfgH_rRHz0D60eF6-gPvw6Ow0-G_tBzNKxy9BZyoWOI5smR78HDQtMP-0X1Pfjwo9GPfslRiHksoVWIo1zRrMtxo-dhkviPwzo0PniDXsdOWGYLHfZ0_Ef_oGcK0aHxB7uIT7BwGeqP1_CmA_9hnDizGSd-WIfXo8c7VDZ8_Hgo_McP_3ha_Cl8utCD4xS8B8LhzUc9tAcabT1-aBXM4xBx2DrywyS-Z_A5XMVhK0f7ICf6wxb048R15BfOI3eHE0F-4AzM5dCNH4cPO-iOD7_QPELf4egDpzp6dM0g2jh-CSdEEj_yHt6O_ijSG8cDmSSMG3cAn-hxaNE0BbeO_tPQH_5RizGeAwWAQgg4hIwwgBkmgGKKAieMAgBYI4ACBAmHqDECSyYAIQIZDY0CQggFnAIIOCSYABg5AZxCCFBjkHJEGACAUgIoh5QwmCkAFBIEAKQsEEgRYYAU2CHrAABEAMuQcBwAwgBDgmIsjUBEKSKEIgAIAQwACgBrLHEYGKEEBMQAg5wBFAAGlEBGKAYEQcwQBACkigABjBNAKEEAtYIIYhwBgBAjABCSGMEUMIIJZ6QwnFgGJUDCACKYIEBRRcRAACBIrqECOCGtcEwRZpgEijiGjDKCSsYcIApAwQwimDlhhRHGMaGceFZwBxwhDAjCCA' +TEST_1A_FP_RAW = [-1124735545, -1124600491, -1107954315, -1108017819, -1082858139, -1084889769, -1101531321, -1105799353, -1105832105, -1084795017, -1082696345, -1107993113, -1124772361, -1124739625, -1124608571, -1124600491, -1107962507, -1108028059, -1074469531, -1099635403, -1105857257, -1105758393, -1089054905, -1089054729, -1116316186, -1107866137, -1128970761, -1129024041, -1124829753, -1124735033, -1124600491, -1107962523, -1108017819, -1084955273, -1101666985, -1105725625, -1105799353, -1105766569, -1084795033, -1107862169, -1107995161, -1124772393, -1124739625, -1124608571, -1124600491, -1107962507, -1074465435, -1082858123, -1101667017, -1105726121, -1105832121, -1089054889, -1118414858, -1107927578, -1128968729, -1129032201, -1129024041, -1124735545, -1124735035, -1107954315, -1108026011, -1074463387, -1082858121, -1101531881, -1105791161, -1105832121, -1088989321, -1084795545, -1107993113, -1107995145, -1124772393, -1124608571, -1124600379, -1124600491, -1108028059, -1074465435, -1082858187, -1105861353, -1105726121, -1105832121, -1089054761, -1116315674, -1107931674, -1128968713, -1129032233, -1124829737, -1124735545, -1124604587, -1107954315, -1108017819, -1082854043, -1084889737, -1101531305, -1105791161, -1105832121, -1084795017, -1082698393, -1107995161, -1107995145, -1124772393, -1124641337, -1124600379, -1124608651, -1108026011, -1108023963, -1116445321, -1621793321, -1659424313, -1793715833, -1806297705, -1797909065, -1799942745, -1800069705, -1800135279, -1795934831, -1796905535, -1805158959, -1805257231, -1805255197, -1780089485, -1780095661, -1746405437, -1754794029, -1771579437, -1771580045, -1755196045, -1759414925, -1759414925, -1788609229, -1788674285, -1780220397, -1780351469, -1780418765, -1746884813, -1763593453, -1771842797, -1774988525, -1775151277, -1619823117, -1787658781, -1796006430, -1795879454, -1795945005, -1795916349, -1795719741, -1804108333, -1787331085, -1779040781, -1746405935, -1746405935, -1746544687, -1754967055, -1788585999, -1788774413, -1788775069, -1788641949, -1788609229, -1746796781, -1746669821, -1746667773, -1746675949, -1746811085, -1755253469, -1619991245, -1624119501, -1624148206, -1623761134, -1619624110, -1611235470, -1611182734, -1649062542, -1644931246, -1644341310, -1644300350, -1652688942, -1652758558, -1652759070, -1653213709, -1619528237, -1611205167, -1619601983, -1619567151, -1888133775, -1888133855, -1921647327, -1653213903, -1661475567, -1661475503, -1661471407, -1661631119, -1644919455, -1653295773, -1619527853, -1640564925, -1640605886, -1640542254, -1636352030, -1611190813, -1615520285, -1649140303, -1779165807, -1804257855, -1804110399, -2072611391, -2064157327, -2047548063, -2047548047, -1887967981, -1888026877, -1888035069, -1887971821, -1921714397, -1921718429, -1653213325, -1653090989, -1644767789, -1644702253, -1611147917, -1611344799, -1611352735, -1619729039, -1619659403, -1757948459, -1757981225, -1791615497, -1787413065, -1795809881, -1800073801, -1795943019, -1795918379, -1795857967, -1797135919, -1797136015, -1797200783, -1780358093, -1788611533, -1755063021, -1755063021, -1771905709, -1771905677, -1771844237, -1754835469, -1754958349, -1788512269, -1788479661, -1780099469, -1746545053, -1746602141, -1746868429, -1755257069, -1621035245, -1624148205, -1657571501, -1657637549, -1653459597, -1644821133, -1649080845, -1779165741, -1779141167, -1779075631, -1778909743, -1779303183, -1779303181, -1787691789, -1754008109, -1754017341, -1755065389, -1755065389, -1788348573, -1788479709, -1788478159, -1805254895, -1788477679, -1788477645, -1788346573, -1779968237, -1746544878, -1746537166, -1637484686, -1636313262, -1640671406, -1640540334, -1636352142, -1611186846, -1611326109, -1644946061, -1644939949, -1661700783, -1661639343, -1644698287, -1655179952, -581577392, -552084143, -552075439, -552075440, -547885231, -547885231, -547883055, -1621637647, -1613380109, -1646930445, -1646922253, -1646926381, -1646920205, -1646985885, -1647004317, -1653295837, -1653222125, -1623730941, -1623730429, -1623828717, -1619624077, -1611235485, -1611182093, -1611315247, -1611311151, -1655343151, -1653114927, -1653123597, -1653254797, -1644858254, -1611301550, -1644724926, -1644724910, -1653244557, -1653252749, -1661641357, -1661086223, -1661086255, -1661602351, -1653082671, -1653082671, -1653221903, -1619667487, -1619659295, -1619828397, -1888276205, -986509053, -1003417325, -999214797, -990763741, -994962141] + +# WMA, 160kbps +TEST_1B_LENGTH = TEST_1A_LENGTH +TEST_1B_FP = 'AQABVkmYaEmkJEsiaDv8Hr0GXRd6FuZ69DP6wBdaHb-FHj60CzeaH33wHNUD8WxQPTiF7ghDPcN5hHcFP0OPb4HWFY3M4xr0XGjeoV2LD33QPMKP_2iOQzs6w-fRa-hz4R_EmClaKjjCCK2eCDkt9GHgo18MoVF79At-aD-cd6g2prjQHw2P05TQw4d2AZ3h8-i1o2oO_1BdBi0V9Ej1DK1y5KnQh_CPfoF2WO3Ra_gF7TnMMUdvXGhuoT1OW-jho4cudEbj5ngCbT_8kri0DtV31Bd8oc_RM1B2XEdliegNf0Ih80HjoNOP_vhh4j-uEXipDuZx_IG34jpc8XhmXMVZwSSPjvWQ5xnOoLkQXviCHj4uQ7SP4xl8GT_0ohOO6oJ5C33xoIdftOPR58dvpB8uBv8MH1_wdLCkEP2GCz18tEf4w8-hox965rB-VLiO_vDr4Dj8DD2P59By5WhvIRc6hmie49ODPCw0_bBfVA8eBUf3F_6SExDzS0GrMDjKFV55VObREyaJ_zisQ-ODN-h17IT1Frpg9_hzoT96BnYI9cEu4hMs_FDp4zW86cCPwyfObMaJH9bh9dAOvUN1-MePh8J_HGZJPMefwqfxHDpOwXsgXPBm1EN7wHKP-tA04YchHoetIz98_Bl8Dj9gK0f7ICf644L448R15BfOIy8uHUF-4AzM5dCNH4cPO-iOD7_QPELf4egDpzp6dM0g2jh-CSdEEj_yHt6O_ihyw8cDmYePG3cAH_1xaJsGx1qOfkXPD_7R17hoAAMAMEYAgqgRBmAkBFJMUWAMNQwAYA1QBiGHpHHCCCyZAAAgAMlRQAhAnBLIOCWYABgJAAhCQjskgSCCAeAMIEA5pATATAElBEIAKQmMQIowoIQQyCFhgANAFMGQcBwARhgSDmMpuDEIKkWEUAQAAYABQAFgDVUOAyAAEp4AYJAzgALAhBDMEAEYIIYoQxB1lAAADHBCCUGAKYIIYowiAAhiBBCSCMEUMCQIZ6QwHCiAGJQACcqMIMAxqIhIABiIraFCACokEI4poohhEijiGDVKGUElYw4QBaBgBhHMnLDCCOOYoEIM46xgwhlLgCACAQ' +TEST_1B_FP_RAW = [-1124735545, -1124604587, -1107954315, -1108026011, -1082854041, -1084889737, -1101531305, -1105799353, -1105766569, -1084795017, -1082696345, -1107993113, -1107995145, -1124772393, -1124608571, -1124600507, -1107831435, -1108028059, -1074469531, -1099635403, -1105857257, -1105791673, -1105832121, -1089054761, -1116316186, -1107866138, -1128970761, -1129024041, -1124829753, -1124735033, -1124600363, -1107962523, -1108017819, -1082858137, -1101666985, -1105725625, -1105799353, -1105766569, -1082697881, -1074309785, -1107995161, -1124772393, -1124739625, -1124608571, -1124600491, -1107962507, -1108017819, -1082858123, -1101732555, -1105726121, -1105832121, -1089054905, -1089054730, -1107927578, -1128837657, -1129032201, -1129024041, -1124735545, -1124735547, -1107823243, -1108028059, -1074465435, -1082858121, -1101667049, -1105791161, -1105832121, -1088989353, -1084795545, -1107993113, -1107995145, -1124772393, -1124608571, -1124608571, -1124600491, -1107962507, -1074463387, -1082858187, -1099569899, -1105726121, -1105832121, -1089054889, -1116317706, -1107931674, -1128968729, -1129032201, -1124829737, -1124735545, -1124604459, -1107954315, -1108026011, -1082851995, -1084955273, -1101531305, -1105791161, -1105832121, -1084795017, -1082698393, -1107993113, -1107995145, -1124772393, -1124641337, -1124600379, -1124608683, -1108026011, -1108023963, -1116445193, -1621793321, -1659424297, -1793715833, -1806297705, -1795811913, -1800008281, -1800069705, -1800135279, -1795934831, -1796905535, -1805163055, -1805257231, -1805257247, -1780089501, -1780095661, -1746409533, -1754794029, -1771579437, -1771579917, -1754933901, -1759414925, -1759414925, -1788609229, -1788674285, -1780285677, -1780351469, -1780418765, -1746884813, -1763593453, -1771842797, -1640770797, -1640933549, -1624017549, -1653441053, -1796071966, -1796141598, -1795945006, -1795924541, -1795719741, -1804108333, -1787331085, -1779040781, -1745357359, -1746405935, -1746414639, -1754967055, -1788520463, -1788774413, -1788775069, -1788773021, -1788609229, -1746796781, -1746669821, -1746667773, -1746675949, -1746811085, -1755261661, -1619991245, -1624119501, -1624148206, -1623761134, -1619624110, -1611235470, -1611248270, -1649062542, -1649125422, -1644341310, -1644300350, -1652688942, -1652693006, -1652759070, -1653279245, -1619528237, -1611139631, -1611213375, -1619567151, -1888133775, -1888133855, -1921647327, -1653213903, -1661475567, -1661475503, -1661471407, -1661627023, -1644919455, -1653295775, -1619528365, -1640564925, -1640605886, -1640542254, -1636335646, -1611190814, -1615520285, -1649140303, -1779165807, -1804331583, -1804110399, -1804175935, -2064157359, -1779112591, -1779112591, -1888099053, -1888026877, -1888035069, -1887971821, -1921648845, -1653282973, -1653213325, -1653090989, -1644767789, -1611213357, -1611147917, -1611344799, -1611352735, -1619729039, -1619659403, -1757940267, -1757981225, -1791599113, -1804190281, -1795809881, -1795879497, -1795943017, -1795918379, -1795857967, -1797135919, -1797136015, -1797201807, -1780358093, -1788611533, -1755063021, -1755063021, -1755128493, -1771905709, -1771844237, -1754835469, -1754958349, -1788512269, -1788479661, -1780099469, -1746545055, -1746602141, -1746864333, -1755257069, -1621035245, -1624148205, -1657571501, -1657637037, -1653197453, -1644821133, -1649080845, -1644948013, -1779141165, -1779075631, -1778909743, -1779303183, -1779303181, -1754137357, -1754008109, -1754017341, -1755065389, -1755065389, -1788349581, -1788479645, -1788478159, -1805254863, -1805254895, -1788477645, -1788346573, -1779968237, -1746544878, -1746536654, -1637484686, -1636313262, -1640540334, -1640540334, -1636352142, -1611186846, -1611326109, -1644946061, -1644939917, -1661700783, -1661639343, -1644698287, -1655179952, -581577392, -585640623, -552075439, -552075440, -547885231, -547885231, -547883055, -1621637647, -1613380109, -1646930445, -1646922253, -1646926381, -1646920205, -1646985885, -1647004317, -1653295837, -1653222125, -1623730941, -1623730429, -1623828717, -1619624077, -1611235485, -1611182093, -1611315247, -1611311151, -1655343151, -1653114927, -1653123597, -1653254797, -1644858254, -1611301550, -1644724926, -1644724910, -1653244589, -1653252749, -1661641357, -1661118991, -1661086255, -1661602351, -1653082671, -1653082671, -1653090831, -1619667487, -1619659295, -1619697293, -1888276205, -852283133, -1003417325, -999214797, -990699229, -994953949] + +# Vorbis, 64kbps +TEST_1C_LENGTH = TEST_1A_LENGTH +TEST_1C_FP = 'AQABYpESSUkiJYmSCD30C81RbeXwG7zQvBFa4bfQg_rRQ5vh8-jxHJoe-AyD6sF5NEJYPUPOo3cFP0N7fCm0F40k8fgOPUfzo12LDz0a3sIv4TSa44KGDv7Ra-hz4e8gxi1aBz8aHWF1CXlooWfgg_mO9hDVg6nwQ3sOnx2YvriC_mioBxc6Gj566ASzwyf6Hf0h5h3uFqeCw5I-9Bf6VLBD_OgXaD0srRz64tB-wdmYozp6IX2EVsgtdISPHjoaHX3woPshviQurUP1HfUFH30b9LCLH5oscXgx5YJ2dIedo12P7vhh3MJ9nHipDuZx_IG34jpcEc8cXMVZwRJZoe_xsMGZo3ExXbgWFNsf6Bvs43gG3_ihr-g0vCl6mLfQFw_RD36PdjzRLid848PF4J_h48uQv4OpEP2GS-gNn2gRnsN_iPvRC_1hyUL1D5eOwn9w3PCJXof2w1eO9hZyoWPRPEe-Bw8LTT_so_oefMePRj_6JUcPMW0stCOOckWzLsd99IRJCh9x-OgDPeh17IQos_jhtMdzoT-0M7BD_MFF7BNh4Tp0HTe86Tj-4_CJM5txwjxuCcp2lMe-Q5MN8_g-PMZ_HOaJJ8efwqeh5zhOwTt0XPBm1EN7oNHW4zu04zAMHbYuHOmP34F_XMUJ_2j14CHCh4cPXcdxHXmp48iPXDqOHDgDc4FuFcdh2OguPLgvNI_wdyj6wKmOHl0ziDZ-_NApWCKB_IKHcsYR3vDxBzoJ48YdwOdRHFq04l-Ofuj5Df5RizGeC-FD-AkeTLrxJsePUP1x7dCxc0Z8-DvCdcEvAIUQAsYgYYAhTADFFAXEIKMAAIwJ4IBhSDgElBFYKgAAAsYhaJAQAgBFCUBAOCWMyEhRQYhABhiSBFGOCKHMIEASSTFzVDEhEAJIWcAcYQZQJKRDRiAMgCiCIccBII4ZoYyQzholDQFICEUAAAAxBYA1wCjHgCBECAAUIMZAgCwQRhBDBGBCEMQMQQBAQxUBAoAngBTUJKUEMVIRJ4gRACAGJFFWOWCEA8JJAQki1ggJkKCEEUGAYlQRKhIAAhhprHAACSAdEYoBIphgEijgqCAELCGZAwoQYQQimDEnJDOmCSGNs0I4IIwlQBAGAGJGEEkAYEAJIASgAA' +TEST_1C_FP_RAW = [-1107954315, -1108019867, -1082858137, -1082792617, -1101531385, -1105791161, -1105832113, -1082697857, -1082696337, -1107993105, -1107995137, -1124739625, -1124608569, -1124600491, -1107962507, -1108028059, -1074469531, -1099635401, -1105726185, -1105824441, -1089054905, -1089054729, -1116316186, -1107866137, -1128970761, -1129024041, -1124829241, -1126832185, -1126697003, -1110057627, -1118503579, -1084955289, -1101666985, -1105725625, -1105799353, -1105832105, -1084795545, -1107862169, -1107995161, -1124772393, -1124608553, -1124608571, -1124608683, -1107962507, -1108017819, -1082858123, -1101667019, -1105726121, -1105823929, -1089054905, -1084860425, -1107927578, -1128839705, -1129032201, -1129024041, -1124801073, -1124735009, -1107953795, -1107952283, -1074463387, -1082858121, -1101662889, -1105725617, -1105832113, -1088989345, -1082696337, -1107862161, -1107995137, -1124772385, -1124739633, -1124600371, -1107823275, -1107962507, -1074465435, -1082858123, -1101667049, -1105726121, -1105832105, -1089054889, -1118413321, -1107931673, -1128968713, -1129032233, -1124829737, -1124735545, -1124603947, -1110051467, -1108019867, -1074465435, -1084955273, -1101531369, -1105725625, -1105766569, -1082697866, -1082696346, -1107993241, -1107995145, -1124772393, -1124641337, -1124608571, -1107831435, -1108028059, -1108023963, -1116445195, -1621793321, -1659424297, -1793715833, -1806297705, -1797909065, -1799938649, -1800069705, -1800137321, -1795934825, -1805294139, -1805158971, -1805257231, -1805255197, -1788478093, -1780095661, -1746405565, -1754794029, -1771579437, -1771579405, -1771743757, -1754958477, -1759414925, -1788609229, -1788674285, -1780285677, -1780351469, -1780418765, -1746884813, -1746816237, -1771842797, -1640770797, -1640933549, -1619823117, -1653441053, -1661592093, -1795879453, -1795947053, -1795926585, -1795719737, -1795719721, -1787298317, -1780089359, -1746537007, -1746405935, -1746414639, -1754934287, -1788521487, -1788774413, -1788779165, -1788642013, -1788609229, -1746796781, -1746669821, -1746733309, -1746675949, -1763588301, -1637821149, -1619990733, -1624119533, -1624148205, -1623761133, -1619624109, -1611235469, -1611182733, -1649062414, -1649125422, -1644341310, -1644169278, -1652688942, -1652758542, -1652759070, -1653213709, -1619528461, -1611205167, -1611211327, -1619534383, -1619697807, -1921655007, -1653213919, -1653213903, -1661606639, -1661475567, -1661471407, -1661610639, -1644915359, -1653295757, -1619528365, -1640564925, -1640605886, -1640538158, -1636352030, -1611190813, -1615520285, -1649140303, -1644948079, -1670040127, -1804110399, -2072611391, -2064157327, -2047548063, -1913330319, -1887965933, -1888026877, -1888035069, -1887971821, -1921714397, -1653282973, -1653213325, -1652566701, -1644767917, -1611213485, -1611147917, -1611344541, -1611352735, -1619729039, -1619659403, -1623730729, -1757981225, -1791599177, -1804190281, -1795809881, -1795879499, -1795943019, -1795918379, -1795857963, -1780358703, -1780358799, -1797136335, -1780353997, -1788611533, -1755063021, -1755054829, -1771897517, -1771905709, -1771844237, -1754835469, -1754958349, -1788512301, -1788356781, -1779968395, -1780099227, -1746602143, -1746868429, -1612651245, -1619986669, -1624148205, -1657571501, -1657637037, -1653181069, -1644821133, -1649080845, -1644948013, -1779141167, -1779075631, -1778909743, -1779041039, -1779303181, -1754137357, -1754008109, -1754017341, -1755065389, -1755065389, -1788348573, -1788480221, -1788478159, -1788477647, -1788477679, -1788477647, -1788346573, -1746413805, -1746544877, -1612318926, -1637353614, -1636313262, -1640671406, -1640532142, -1636354190, -1611186846, -1611330206, -1644948109, -1644939949, -1661635247, -1661639343, -1644698287, -581569200, -581577392, -547891887, -552075440, -547881136, -547885231, -547885231, -547883055, -547895823, -1646934541, -1646930445, -1646922253, -1646922285, -1646920205, -1647002269, -1647008413, -1653295837, -1653222125, -1623730413, -1623730429, -1623828717, -1619624077, -1611235485, -1611182093, -1611315247, -1644865583, -1646954543, -1653114925, -1653254797, -1653254797, -1611303566, -1611303854, -1611170494, -1644724926, -1653244589, -1653252749, -1661641229, -1661086223, -1661086255, -1661602351, -1653082671, -1653082671, -1619667471, -1619667487, -1619659295, -1619693229, -1888276205, -1926024957, -1003417325, -999214797, -990763741, -996010717, -995947214, -995881710, -995869422, -991609530, -999993914, -983183930, -949893737, -966552169, -966543979, -958271087, -958279216, -941559312, -941530783, -983473887] + +# WMA, 32kbps +TEST_1D_LENGTH = TEST_1A_LENGTH +TEST_1D_FP = 'AQABVkmYaEkkZUkE7bDao4e-C9VhHtWLHtQFphJ-CT3sQ7vQGT76Bc_RB-LZ4Bb6HR3CUB9yVugr2MnQH_qITieah8eL_oIYs8O3FkUvNK6Fv0L3wz4O7egMn0e_oz_UD45pnBJ-pPLRShdyFn1awZ9xfIQorUdPfIf2C455tC-OXhcaqngldId99IKODs2X40d_OG8E1UXrox9SSR_6C3kq9CH8o1-gHVaPfsUvaD-cjTmq40JjXQjLIxc6Gj56XNAi9MvxBtotwSe-FtVP9BZ89HnQB3aIHz2Jn0Vz9DyOxjmm3sKPHy189MdR7R1s_PBRrofWHmL94Bl-4azQyDzySR725MGZojn65MiJ32g-6Me2_fjxDL7xHM_moWunQzVaUYePkniPcjr8oucR_vCP7_jxJsR_mAv-dIIdyWiPoz_RPPiQH_2h5ceLe0dzVDe-oRfSuA5w-BnC83gOLVc6hP1xoWPRPMf3DHlYNDEPPUf_4Ffwo_mDrkugwXmMVuFxlOvhdUGPmzu0koePH97RB3M__OglwzLxQ_SO6rPwoyfsMHhxSjiPY1Nw6sFhGzmQ74aD91Cm48R9eLfQ4x2gaTv8o9qN6-hhHk-OzylOuMuh4xSMH_phH6VGoEWjFyd2QouFAzquw3yOHM6-BdeL4_AZon_wZTm60sgvGLqE_0beEaeL_BtO_Phx4xrhBz9eEuKD5xDRszNOqEcfHeaL8OhVOKbRH-G0TDC1FdczHDd-I5WOw84u_MKRGz70bDhTIe_hGzcOoz9yaFsVw_mFfuh5pD96xrhyAAaIAgYiRIVjDGHCFAaGCKQYAkIAQIkBxAAjmGQCECIQNFQQo4CQBhBHBBECEEANQ0oApwQyhCBBJBBEUEWcEQRIRwTIzBEgEIBAKQuIckQAAQAUSjDljCKEDAIVkcAZBAAhhiEhFCFAIQWcMko6I4RlgAIBCACGKSoUEEQQ4AQwBgsFlFACKAkCE0IwQ4BwQhikiDHEAEEdAoY4IoBgFiKkEENCEgScEQ5QIwEipAkljBGKMIKAAFIoxohgAipClHEMICyVEYAJBywUChjBCDICAIeEs4gZJQAFTCglBCVEICAQA846IZywglHhhTLCkibQEJwAQQQiAA' +TEST_1D_FP_RAW = [-1126832697, -1126701611, -1110051467, -1110059675, -1084951193, -1084885705, -1101662441, -1105725625, -1105758377, -1084786817, -1084793497, -1107862041, -1107993097, -1124772393, -1124608571, -1124600491, -1107823243, -1108028059, -1074469531, -1099635339, -1105857193, -1105758377, -1089054905, -1089054761, -1118413338, -1107866137, -1128968713, -1129024041, -1124796985, -1124735035, -1126701739, -1110059675, -1108028059, -1082854027, -1101667049, -1105725609, -1105725625, -1105766569, -1084858521, -1074307737, -1107993097, -1124772393, -1124739625, -1124608571, -1124600491, -1107962507, -1074465435, -1082858123, -1101732553, -1105857193, -1105766569, -1089054889, -1084852234, -1110024730, -1107866137, -1128966665, -1124829737, -1124796969, -1124735017, -1107823243, -1107962523, -1074465435, -1084955273, -1101662953, -1105725689, -1105725625, -1084795049, -1084859033, -1107862169, -1107993097, -1124772393, -1124608569, -1124600379, -1124600491, -1108028059, -1074465435, -1082858123, -1105861355, -1105726121, -1088981177, -1089054889, -1118413322, -1107931674, -1128968729, -1129032201, -1124829737, -1124735545, -1126832683, -1110051467, -1108028059, -1082854043, -1084955273, -1101531369, -1105725625, -1105766585, -1084784777, -1082696346, -1107862169, -1107995145, -1124772393, -1124641337, -1124600377, -1124600459, -1108028059, -1108023835, -1084987913, -1084918313, -1223215657, -1793649273, -1806297705, -1797909065, -1800008281, -1800073801, -1800137321, -1795934825, -1796921977, -1805556281, -1805552137, -731775513, -731775641, -715004585, -672929981, -689703101, -698099901, -698100397, -698231437, -702417565, -685640349, -714868941, -714866925, -706478573, -706609613, -673122525, -1746881231, -1771982573, -1771843309, -1774988525, -1775151277, -1758234653, -1787681310, -1796071962, -1800335898, -1800401450, -1796178490, -1796117049, -1804374841, -1804337929, -1787568907, -705440303, -671877679, -671886383, -680406031, -1754246159, -1791920655, -714906271, -714902237, -681443533, -673054957, -673059325, -689701373, -1763453165, -1763588301, -1772038365, -1770981582, -1775180014, -1640990958, -1624148206, -1619624174, -1611251886, -1611264654, -1783296526, -1779149358, -1779083326, -1779075134, -1786943534, -1786976286, -1786972702, -1653279261, -1644825101, -1611139647, -1611203135, -1619534527, -1619665551, -1892295391, -1653220063, -1653213903, -1661475504, -1661475504, -1661471408, -1661627024, -1644919455, -1653295775, -1623722669, -1640564926, -1640605886, -1640538158, -1636352030, -1745408542, -1749738013, -1783362157, -1779165807, -1804323455, -1804110399, -730434111, -705202703, -705370783, -705370765, -713562861, -680067325, -680075517, -713566701, -713754829, -713758941, -578947229, -1652566671, -1644178095, -1610640047, -1611164559, -1611361183, -1762347675, -1770740379, -1774848651, -1774725675, -1757981227, -1791549961, -1804190297, -1795809881, -1795748425, -1795943017, -1796196969, -1796185641, -723426857, -723394089, -723394445, -715000781, -715000525, -714867437, -681313006, -681313006, -1771905773, -1771905677, -1755064973, -1755195947, -1755187243, -1788749995, -1746676107, -1746807195, -1746864283, -1746864283, -1755257545, -1754204905, -1758333113, -1791789241, -1791854761, -1787660937, -1779301001, -1783561737, -1783623177, -1779403305, -1779337771, -1779171883, -1779171851, -1779303179, -1787699977, -1754147625, -1754009145, -1754009145, -1754008617, -1788611737, -1788610777, -1788750045, -1788739823, -1788477679, -1788477679, -1788346605, -1779968237, -1746807501, -1746799310, -1770784942, -1640802478, -1640933550, -1640949934, -1636354190, -1611253918, -1644886686, -1649142286, -1661717005, -1661717037, -1661639215, -570988591, -570427568, -847390896, -818553007, -818413743, -550109359, -545919151, -545790127, -545851951, -545864207, -537410063, -573188623, -573196845, -589976109, -589976079, -1644904991, -1644907167, -1653295837, -1619667661, -1623730413, -1623763198, -1624015086, -1619890318, -1611497630, -1611182605, -1615525933, -1646962733, -1646954541, -1655212077, -1787340845, -1787472526, -1787464334, -1745490606, -1644696238, -1644692142, -1653211821, -1653219981, -722084495, -721435151, -587344400, -587860528, -596118064, -579340848, -579340816, -545917472, -680135199, -697077389, -684510957, -986500861, -1003417325, -999214798, -990764766, -994962142] + +TEST_1_LENGTH = TEST_1A_LENGTH +TEST_1_FP = TEST_1A_FP +TEST_1_FP_RAW = TEST_1A_FP_RAW + +script = None + +SEQUENCES = [ + ('account', 'id'), + ('application', 'id'), + ('format', 'id'), + ('foreignid', 'id'), + ('foreignid_vendor', 'id'), + ('source', 'id'), + ('submission', 'id'), + ('track', 'id'), + ('track_mbid', 'id'), + ('track_mbid_source', 'id'), + ('track_mbid_change', 'id'), + ('track_mbid_flag', 'id'), + ('track_puid', 'id'), + ('track_puid_source', 'id'), + ('track_meta', 'id'), + ('track_meta_source', 'id'), + ('track_foreignid', 'id'), + ('track_foreignid_source', 'id'), + ('fingerprint', 'id'), + ('fingerprint_source', 'id'), + ('meta', 'id'), +] + +TABLES = [ + 'account', + 'application', + 'format', + 'foreignid', + 'foreignid_vendor', + 'source', + 'stats', + 'track', + 'track_mbid', + 'track_mbid_source', + 'track_mbid_change', + 'track_mbid_flag', + 'track_puid', + 'track_puid_source', + 'track_meta', + 'track_meta_source', + 'track_foreignid', + 'track_foreignid_source', + 'meta', + 'musicbrainz.artist_credit_name', + 'musicbrainz.artist_credit', + 'musicbrainz.artist_name', + 'musicbrainz.artist', + 'musicbrainz.track_name', + 'musicbrainz.track', + 'musicbrainz.release', + 'musicbrainz.medium', + 'musicbrainz.medium_format', + 'musicbrainz.recording', + 'musicbrainz.release_name', + 'musicbrainz.release_group', + 'musicbrainz.clientversion', + 'musicbrainz.tracklist', +] + +BASE_SQL = ''' +INSERT INTO account (name, apikey, lastlogin) VALUES ('User 1', 'user1key', now() - INTERVAL '2 day'); +INSERT INTO account (name, apikey, lastlogin) VALUES ('User 2', 'user2key', now() - INTERVAL '5 day'); +INSERT INTO application (name, apikey, version, account_id) VALUES ('App 1', 'app1key', '0.1', 1); +INSERT INTO application (name, apikey, version, account_id) VALUES ('App 2', 'app2key', '0.1', 2); +INSERT INTO format (name) VALUES ('FLAC'); +INSERT INTO source (account_id, application_id) VALUES (1, 1); +INSERT INTO source (account_id, application_id) VALUES (2, 2); +INSERT INTO track (id, gid) VALUES + (1, 'eb31d1c3-950e-468b-9e36-e46fa75b1291'), + (2, '92732e4b-97c6-4250-b237-1636384d466f'), + (3, '30e66c45-f761-490a-b1bd-55763e8b59be'), + (4, '014e973b-368e-42bf-b619-84cab14c4af6'); +INSERT INTO track_mbid (track_id, mbid, submission_count) VALUES (1, 'b81f83ee-4da4-11e0-9ed8-0025225356f3', 1); +INSERT INTO meta (id, track, artist) VALUES (1, 'Custom Track', 'Custom Artist'); +INSERT INTO meta (id, track, artist) VALUES (2, 'Custom Track', 'Custom Artist'); +INSERT INTO track_meta (track_id, meta_id, submission_count) VALUES (1, 1, 1); +INSERT INTO track_meta (track_id, meta_id, submission_count) VALUES (1, 2, 10); +''' + + +def prepare_sequences(conn): + with conn.begin(): + for table, column in SEQUENCES: + conn.execute(""" + SELECT setval('%(table)s_%(column)s_seq', + coalesce((SELECT max(%(column)s) FROM %(table)s), 0) + 1, false) + """ % {'table': table, 'column': column}) + + +def prepare_database(conn, sql, params=None): + with conn.begin(): + prepare_sequences(conn) + conn.execute(sql, params) + prepare_sequences(conn) + + +def with_database(func): + def wrapper(*args, **kwargs): + with closing(script.engine.connect()) as conn: + prepare_sequences(conn) + trans = conn.begin() + try: + func(conn, *args, **kwargs) + finally: + trans.rollback() + wrapper = make_decorator(func)(wrapper) + return wrapper + + +def with_database_context(cleanup=False): + def inner(func): + def wrapper(*args, **kwargs): + with DatabaseContext(script) as db: + prepare_sequences(conn) + try: + func(db, *args, **kwargs) + finally: + if cleanup: + pass + wrapper = make_decorator(func)(wrapper) + return wrapper + return inner + + +def cleanup_database(): + create_all(script.db) + + +def setup(): + global script + config_path = os.path.dirname(os.path.abspath(__file__)) + '/../acoustid-test.conf' + script = Script(config_path, tests=True) + if not os.environ.get('SKIP_DB_SETUP'): + with DatabaseContext(script) as db: + conn = db.session.connection() + if os.environ.get('ACOUSTID_TEST_FULL_DB_SETUP'): + conn.execute('CREATE EXTENSION intarray') + conn.execute('CREATE EXTENSION pgcrypto') + conn.execute('CREATE EXTENSION cube') + conn.execute('CREATE EXTENSION acoustid') + metadata.create_all(conn) + for table in reversed(metadata.sorted_tables): + conn.execute(table.delete()) + prepare_database(conn, BASE_SQL) + db.session.commit() + + +def make_web_application(): + from acoustid.web.app import make_application + config_path = os.path.dirname(os.path.abspath(__file__)) + '/../acoustid-test.conf' + return make_application(config_path, tests=True) + + +def assert_dict_equals(d1, d2, msg=None): + if d1 != d2: + standardMsg = '%s != %s' % (repr(d1), repr(d2)) + diff = ('\n' + '\n'.join(difflib.ndiff( + pprint.pformat(d1).splitlines(), + pprint.pformat(d2).splitlines()))) + assert d1 == d2, standardMsg + '\n' + diff + + +def assert_json_equals(expected, actual): + assert_dict_equals(expected, json.loads(actual)) diff --git a/old_tests/test_api.py b/old_tests/test_api.py new file mode 100644 index 0000000000000000000000000000000000000000..d3e2bd0d5840626b3ae1d82a7dc10d826ee39f17 --- /dev/null +++ b/old_tests/test_api.py @@ -0,0 +1,40 @@ +# Copyright (C) 2011 Lukas Lalinsky +# Distributed under the MIT license, see the LICENSE file for details. + +from nose.tools import assert_equals +from acoustid.api import serialize_response + + +def test_serialize_json(): + data = {'status': 'ok', 'artists': [{'name': 'Jean Michel Jarre', 'year': 1948, 'cities': ['Paris', 'Lyon']}]} + resp = serialize_response(data, 'json') + assert_equals('application/json; charset=UTF-8', resp.content_type) + expected = b'''{"artists": [{"cities": ["Paris", "Lyon"], "name": "Jean Michel Jarre", "year": 1948}], "status": "ok"}''' + assert_equals(expected, resp.data) + + +def test_serialize_jsonp(): + data = {'status': 'ok', 'artists': [{'name': 'Jean Michel Jarre', 'year': 1948, 'cities': ['Paris', 'Lyon']}]} + resp = serialize_response(data, 'jsonp:getData') + assert_equals('application/javascript; charset=UTF-8', resp.content_type) + expected = b'''getData({"artists": [{"cities": ["Paris", "Lyon"], "name": "Jean Michel Jarre", "year": 1948}], "status": "ok"})''' + assert_equals(expected, resp.data) + + +def test_serialize_xml(): + data = {'status': 'ok', 'artists': [{'name': 'Jean Michel Jarre', 'year': 1948, 'cities': ['Paris', 'Lyon']}]} + resp = serialize_response(data, 'xml') + assert_equals('text/xml; charset=UTF-8', resp.content_type) + expected = ( + '''\nokParisLyon''' + '''Jean Michel Jarre1948''' + ) + assert_equals(expected, resp.data) + + +def test_serialize_xml_attribute(): + data = {'@status': 'ok'} + resp = serialize_response(data, 'xml') + assert_equals('text/xml; charset=UTF-8', resp.content_type) + expected = '''\n''' + assert_equals(expected, resp.data) diff --git a/tests/test_api_v1.py b/old_tests/test_api_v1.py similarity index 100% rename from tests/test_api_v1.py rename to old_tests/test_api_v1.py diff --git a/tests/test_api_v2.py b/old_tests/test_api_v2.py similarity index 100% rename from tests/test_api_v2.py rename to old_tests/test_api_v2.py diff --git a/tests/test_data_account.py b/old_tests/test_data_account.py similarity index 100% rename from tests/test_data_account.py rename to old_tests/test_data_account.py diff --git a/tests/test_data_application.py b/old_tests/test_data_application.py similarity index 100% rename from tests/test_data_application.py rename to old_tests/test_data_application.py diff --git a/tests/test_data_fingerprint.py b/old_tests/test_data_fingerprint.py similarity index 100% rename from tests/test_data_fingerprint.py rename to old_tests/test_data_fingerprint.py diff --git a/tests/test_data_format.py b/old_tests/test_data_format.py similarity index 100% rename from tests/test_data_format.py rename to old_tests/test_data_format.py diff --git a/tests/test_data_meta.py b/old_tests/test_data_meta.py similarity index 100% rename from tests/test_data_meta.py rename to old_tests/test_data_meta.py diff --git a/tests/test_data_source.py b/old_tests/test_data_source.py similarity index 100% rename from tests/test_data_source.py rename to old_tests/test_data_source.py diff --git a/tests/test_data_stats.py b/old_tests/test_data_stats.py similarity index 100% rename from tests/test_data_stats.py rename to old_tests/test_data_stats.py diff --git a/tests/test_data_submission.py b/old_tests/test_data_submission.py similarity index 100% rename from tests/test_data_submission.py rename to old_tests/test_data_submission.py diff --git a/tests/test_data_track.py b/old_tests/test_data_track.py similarity index 100% rename from tests/test_data_track.py rename to old_tests/test_data_track.py diff --git a/tests/test_pg.py b/old_tests/test_pg.py similarity index 100% rename from tests/test_pg.py rename to old_tests/test_pg.py diff --git a/tests/test_server.py b/old_tests/test_server.py similarity index 100% rename from tests/test_server.py rename to old_tests/test_server.py diff --git a/tests/test_utils.py b/old_tests/test_utils.py similarity index 100% rename from tests/test_utils.py rename to old_tests/test_utils.py diff --git a/old_tests/test_web.py b/old_tests/test_web.py new file mode 100644 index 0000000000000000000000000000000000000000..bd0a2913636a0e801e3e19a8f9d4deb42d6cfdb8 --- /dev/null +++ b/old_tests/test_web.py @@ -0,0 +1,21 @@ +from nose.tools import assert_equal +from acoustid.web import db +from tests import make_web_application + +app = None + + +def setup(): + global app + app = make_web_application() + app.config['TESTING'] = True + + +def test_track_page(): + client = app.test_client() + + rv = client.get('/track/eb31d1c3-950e-468b-9e36-e46fa75b1291') + assert_equal(rv.status_code, 200) + assert_equal(rv.data.count('Custom Track'), 1) + assert_equal(rv.data.count('Custom Artist'), 1) + assert not db.session.registry.has() diff --git a/requirements_dev.in b/requirements_dev.in index f3c7e8e6ffb905f7de8b597eb22213a7dc20bfb3..53d3adf976b5dc89d0f3b4731b89f9010602d149 100644 --- a/requirements_dev.in +++ b/requirements_dev.in @@ -1 +1,2 @@ -nose +pytest +WebTest diff --git a/requirements_dev.txt b/requirements_dev.txt index 2e0f2dbf1490238ceb5fdc5bf5b27442136aa047..f55fa4c7b4ac672bb0c1209741c965728104251d 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -4,4 +4,26 @@ # # pip-compile --output-file requirements_dev.txt requirements_dev.in # -nose==1.3.7 +atomicwrites==1.3.0 # via pytest +attrs==19.1.0 # via pytest +backports.functools-lru-cache==1.5 # via soupsieve +beautifulsoup4==4.7.1 # via webtest +configparser==3.7.4 # via importlib-metadata +contextlib2==0.5.5 # via importlib-metadata +funcsigs==1.0.2 # via pytest +importlib-metadata==0.17 # via pluggy, pytest +more-itertools==5.0.0 # via pytest +packaging==19.0 # via pytest +pathlib2==2.3.3 # via importlib-metadata, pytest +pluggy==0.12.0 # via pytest +py==1.8.0 # via pytest +pyparsing==2.4.0 # via packaging +pytest==4.6.2 +scandir==1.10.0 # via pathlib2 +six==1.12.0 # via more-itertools, packaging, pathlib2, pytest, webtest +soupsieve==1.9.1 # via beautifulsoup4 +waitress==1.3.0 # via webtest +wcwidth==0.1.7 # via pytest +webob==1.8.5 # via webtest +webtest==2.0.33 +zipp==0.5.1 # via importlib-metadata diff --git a/scripts/disable_bad_submissions.py b/scripts/disable_bad_submissions.py index 5a524b36faafcdfe490d5220b1d0250bcb551c6a..11e4d33b06c0138c5324d2bbb113232bf33843f6 100755 --- a/scripts/disable_bad_submissions.py +++ b/scripts/disable_bad_submissions.py @@ -160,7 +160,7 @@ def disable_track_mbid(db, track_mbid, note): def main(script, opts, args): - db = DatabaseContext(script.engine) + db = DatabaseContext(script.db) min_track_id = db.session.query(sql.func.min(Track.id)).scalar() max_track_id = db.session.query(sql.func.max(Track.id)).scalar() diff --git a/scripts/run_psql.py b/scripts/run_psql.py index 0e578f9cad3bd74f2abc1893b39d5ffc4e4f002d..6d764dd56566cb224bbc1b6d5ce54f3bd22df42a 100755 --- a/scripts/run_psql.py +++ b/scripts/run_psql.py @@ -8,7 +8,7 @@ import os def main(script, opts, args): - os.execlp('psql', 'psql', *(script.config.database.create_psql_args() + args)) + os.execlp('psql', 'psql', *(script.config.databases.databases['default'].create_psql_args() + args)) run_script(main) diff --git a/tests/__init__.py b/tests/__init__.py index fa615ec77713edb2601dbf3def605bd98143fd40..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,187 +0,0 @@ -# Copyright (C) 2011 Lukas Lalinsky -# Distributed under the MIT license, see the LICENSE file for details. - -import os -import json -import pprint -import difflib -from contextlib import closing -from nose.tools import make_decorator -from acoustid.script import Script -from acoustid.tables import metadata - -TEST_2_LENGTH = 320 -TEST_2_FP = 'AQABVtuUZFGShAqO-h9OHD96SvhwBVNCKQnOIYmiIc-ENwF7TDe8Hr0W_AjhvRCP2sfT4DTS7zjyOYeqaI-RSxee5RmaWzhOHnlcaB6HnPgpdE-DkWIH2ysYG_Eh9zJCyfCXGOdw-EGoD2p69IavWOhzMD-a9tBx9FgPVz2qNDvQH3744ISIXRKeHto5_MhyeMtxc-COnYJ_lHLwRAgPvShz_Hga4zd8HD9UKXWOPP3xRLmGnlbQHKfxGPeRvAt6UngMvcF-gkpRi0bUZjGaH6FUHb_xGDt6aHmM__ghfkmH70B4fWiuCj8y8uj3oImZY8d3NFWWHuGF-3hCPEd_uEOyE_nw4w8ueXi24znCHOHxSWtw9BnSBzrSHF2Y4S0e_EioZoh9XMGfo2dqNMeP80aQPM5xGT9efMeTYL-KIqmHdDraHs-P8IcYjoj0I7_Q43iJ9BF64nSKKth2SjG-cvCHH-2OL8txHsUt9HhF4LiK5j16lAf1FkjvQiN55FSOkkOPkmj4GK-OH80eIeyh98HhE_qhPwjzKAV-HJ2OZkd4Q_vhp0d_6Id-_IeWW9CKoP3RKM-Bo3mOfvhxND_6HMgZ6EfXHB-8Q8-iok1znOi-ozmx54P2Dg5V_PCgLxy8KiH6C0cbHU3Ebtiho9Rxw8er47tw7jgRNxl84ziPJ-B1_DiNNClzaGSCvMGPGxePMD5qZYEuAwdTXYSYcIkmodc2nMqg_WgqBk_yBdVx0vCjQD8uhNRxXTgvVFSOSOmx61C1KMaNsFwM93h-PBdmFm8o45nxDabx48cTbGl4hHuhasjSwPtxPvAV1A7yQMukREERR-nxL8j-EbWYQ8sj4joABQQmjQhkjLFCKSAo4QoxYiQwQhgmkGjCKGAIMMA4BIwQwjhAFMBUCCUAYEIxpYxUCDlEjJYOScSMgsIIAgADwjKEFBAUCkMEMYAagoARzAAHCDCIISKANkgYYBiQwgDDjHEMIGWZFUBQLhgohBGkhECOMEAMIYghogTgQghgiSLCYUegAsJApIQjxABNDFWCa6AIAQ4Q4KgAgIABgGDCMNGIMgQJRQAQTACpgBNIJkUcBMkpoKAgXCjAgAAGKIcYIVAYbZgwggkEmKLEiYGYAYQQShFAAAQBFEEAEuEIgwYRQoARnBkAmAGMEAGFGIgQBigCwAkABEIA' -TEST_2_FP_RAW = [-772063964, -772066012, -1007079116, -1011011276, -1027788492, -1029889740, -1031261916, -1031269084, -1031270620, -1031021744, -1031079600, -1031078425, -1031032346, -1018580506, -1018646290, -1022775046, -1056337446, -1056261686, -1073039030, -1073039013, -1068976005, -1001867175, -733365175, -733302711, -737435575, -603332504, -603365272, -737516311, -188076117, -175490390, -745993558, -758642022, -767030630, -1034347878, -1038412133, -1039584631, -1039654264, -1034345784, -1030086056, -1011141092, -1045873092, -1045939676, -1045947803, -2132276505, -2132259914, -2140415082, -2140472938, -2140481130, -2140546634, -2140603929, -2144802523, -2144536267, -2123540203, -2115147515, -2081855203, -2098598611, -2098606801, -2098606737, -2106995329, -2090217989, -2123638309, -2140477173, -2140477173, -2123634405, -2106992325, -2107061957, -2107061991, -2107007715, -1033140963, -1023769329, -1025864433, -1026913002, -1010133962, -1017409482, -1017540482, -1022848902, -1056337830, -1056271030, -1056261302, -1073038501, -1068975271, -1060587447, -993477559, -1001672631, -737435575, -737566615, -737550231, -737419031, 1422585259, 1955228586, 1367940010, 1388841914, 1380453258, 1376328586, 1376458634, 1107956618, 1107899017, 1113072281, 1121657497, 1119494811, 1135224479, 1135226615, 1139489511, 1130891874, 1126713938, 1109977859, 1114237187, 1122691331, 1122695431, 1122687255, 1114233125, 1130944869, 1126746469, 1097554247, 1105885511, 1105885511, 1135270230, 1122523494, 1114135910, 1109939695, 1093236223, 1076520335, 1080714635, 1089107851, 11092923, 11010986, 15209450, 15492074, 7103274, 2913082, 2905882, 2940681, 2947848, 7138056, 32303368, 61716744, 44932552, 1118668232, 1118406137, 1122600424, 1110167912, 1110167848, 1110106424, 1122689305, 1118495003, 1118478714, 1118540010, 1122599146, 1110016234, 1110147562, 1110094153, 1076535560, 1076538376, -1058363384, -794183656, -794249176, -790063064, -261519320, -261519319, -529562582, -529628886, -530153430, -530280406, -534465494, -534459350, -517027794, -517027778, -517056387, 1630428508, 1634606924, 1643060940, -508616995, -508740929, -475252054, -487834709, -496223301, -496231493, -496092485, -488752486, -489735542, -494125366, -494125542, 1641889598, 1627335998, 1617898782, 1613703454, 1614756622, 537664270, 541854222, 541854238, 541874782, 558651982, 558168910, 558168910, 558168398, 566360398, 1636039038, 1669593454, 1938028670, 1942087766, 1942087766, 1665394807, 1631779173, 1640192868, 1640221300, 1640483428, 1640487796, 1631902020, 1627682884, 553932868, 554654068, 554589029, 567179879, 562985575, 562846279, 562879301, 558684487, 554678646, 554678646, 558873462, 567262070, 563067366, 562936022, 567064775, 558692565, 1628436725, 1661925605, 1661893095, 1666087909, 592329573, 567032663, 567032133, 567032132, 1640840020, 1909340900, 1909340900, -238142748, -775079212, -775152956, -1043580220, -1047774524, -2121450764, -2138162460, -2138162460, -2138232091, -2121520409, -2117330313, -2124670345, -2124604585, -2092205227, -2083848891, -2083787451, -2117489195, -2117550619, -2124902943, -2139517453, -2139383405, -2122597997, -2122598221, -2093090639, -2095162991, -2107737727, -2107754111, -2108805231, -2099495199, -2099499291, -2097402137, -2098451465, -2098709513, -2098717737, -2081859113, -2123773481, -2140434985, -2140496425, -2140428906, -2132171338, -2132236874, -2065124170, -2023181130, -2039964482, -2044162882, -2044098306, -2111137761, -2111117043, -2111165684, -2144720372, -2140460532, -2132132340, -2136328643, -2136407507, -2136471761, -2136471761, -2132277457, -2114187977, -2091268651, -2083809851, -2083872379, -2117361257, -2117552729, -2141681241, -2139584009, -2143708937, -2143618857, -2126874411, -2126894891, -2093339196, -2105923644, -2099628348, -2103836012, -2103966047, -2099751259, -2097639449, -2031579161, -2039763466, -2031375914, -2014728234, -2056675370, -2056548654, -2073127278, -2077251950, -2077233262, -2077266510, -2077332302, -2073154382, -2014434126, -2031143722, -2039794617, -2039792379, -2039792868, -2107033028, -2081936836, -2136462820, -2136265204, -2136263155, -2136456385, -2136456401, -2132228626, -2122791506, -2091070041, -2107647561, -2108769915, -2100384379] - -# MP3, 320kbps -TEST_1A_LENGTH = 260 -TEST_1A_FP = 'AQABVkmYaEmkJEsiaGvhH33xXNDyol2Pz-jhW2h1_Bb6w4d2dIbPo8dzVA_Es0E1C9_RCGH1DDmP3hX8DD2-Bdq0ojmPa9CeS_CPdi0-9EFz4bqE02iOQzs6w-fR7-gP9R0ct2ipoEeqD60u5EyFPoSPfjF0NNKOXsMP7TkcMxyq3bjQ_GiP05TQw4d24ejQfDl-VD7EN8JdtFTQHWH8DN8RPhXsED_6BdoOv0df_IJ2FuZ69MaF5hba47dQ-OihC42MvjmeQPthvSRqaR3-HfUFH33YoC-UHddRWSL6Gr6gHd0DO-i0Hj1-WMJjXAdeqoN5_MfhrbgOVzyeGRfOCpbIauh7IY8evEGYo0_woIePyxDtFTgJt8d96BM64UZ1mLfQFw96-EW77Oh__Eb64WLwz_DxZXgHU6HRfbjQozlxhOfgH_rRHz0D60eF6-gPvw6Ow0-G_tBzNKxy9BZyoWOI5smR78HDQtMP-0X1Pfjwo9GPfslRiHksoVWIo1zRrMtxo-dhkviPwzo0PniDXsdOWGYLHfZ0_Ef_oGcK0aHxB7uIT7BwGeqP1_CmA_9hnDizGSd-WIfXo8c7VDZ8_Hgo_McP_3ha_Cl8utCD4xS8B8LhzUc9tAcabT1-aBXM4xBx2DrywyS-Z_A5XMVhK0f7ICf6wxb048R15BfOI3eHE0F-4AzM5dCNH4cPO-iOD7_QPELf4egDpzp6dM0g2jh-CSdEEj_yHt6O_ijSG8cDmSSMG3cAn-hxaNE0BbeO_tPQH_5RizGeAwWAQgg4hIwwgBkmgGKKAieMAgBYI4ACBAmHqDECSyYAIQIZDY0CQggFnAIIOCSYABg5AZxCCFBjkHJEGACAUgIoh5QwmCkAFBIEAKQsEEgRYYAU2CHrAABEAMuQcBwAwgBDgmIsjUBEKSKEIgAIAQwACgBrLHEYGKEEBMQAg5wBFAAGlEBGKAYEQcwQBACkigABjBNAKEEAtYIIYhwBgBAjABCSGMEUMIIJZ6QwnFgGJUDCACKYIEBRRcRAACBIrqECOCGtcEwRZpgEijiGjDKCSsYcIApAwQwimDlhhRHGMaGceFZwBxwhDAjCCA' -TEST_1A_FP_RAW = [-1124735545, -1124600491, -1107954315, -1108017819, -1082858139, -1084889769, -1101531321, -1105799353, -1105832105, -1084795017, -1082696345, -1107993113, -1124772361, -1124739625, -1124608571, -1124600491, -1107962507, -1108028059, -1074469531, -1099635403, -1105857257, -1105758393, -1089054905, -1089054729, -1116316186, -1107866137, -1128970761, -1129024041, -1124829753, -1124735033, -1124600491, -1107962523, -1108017819, -1084955273, -1101666985, -1105725625, -1105799353, -1105766569, -1084795033, -1107862169, -1107995161, -1124772393, -1124739625, -1124608571, -1124600491, -1107962507, -1074465435, -1082858123, -1101667017, -1105726121, -1105832121, -1089054889, -1118414858, -1107927578, -1128968729, -1129032201, -1129024041, -1124735545, -1124735035, -1107954315, -1108026011, -1074463387, -1082858121, -1101531881, -1105791161, -1105832121, -1088989321, -1084795545, -1107993113, -1107995145, -1124772393, -1124608571, -1124600379, -1124600491, -1108028059, -1074465435, -1082858187, -1105861353, -1105726121, -1105832121, -1089054761, -1116315674, -1107931674, -1128968713, -1129032233, -1124829737, -1124735545, -1124604587, -1107954315, -1108017819, -1082854043, -1084889737, -1101531305, -1105791161, -1105832121, -1084795017, -1082698393, -1107995161, -1107995145, -1124772393, -1124641337, -1124600379, -1124608651, -1108026011, -1108023963, -1116445321, -1621793321, -1659424313, -1793715833, -1806297705, -1797909065, -1799942745, -1800069705, -1800135279, -1795934831, -1796905535, -1805158959, -1805257231, -1805255197, -1780089485, -1780095661, -1746405437, -1754794029, -1771579437, -1771580045, -1755196045, -1759414925, -1759414925, -1788609229, -1788674285, -1780220397, -1780351469, -1780418765, -1746884813, -1763593453, -1771842797, -1774988525, -1775151277, -1619823117, -1787658781, -1796006430, -1795879454, -1795945005, -1795916349, -1795719741, -1804108333, -1787331085, -1779040781, -1746405935, -1746405935, -1746544687, -1754967055, -1788585999, -1788774413, -1788775069, -1788641949, -1788609229, -1746796781, -1746669821, -1746667773, -1746675949, -1746811085, -1755253469, -1619991245, -1624119501, -1624148206, -1623761134, -1619624110, -1611235470, -1611182734, -1649062542, -1644931246, -1644341310, -1644300350, -1652688942, -1652758558, -1652759070, -1653213709, -1619528237, -1611205167, -1619601983, -1619567151, -1888133775, -1888133855, -1921647327, -1653213903, -1661475567, -1661475503, -1661471407, -1661631119, -1644919455, -1653295773, -1619527853, -1640564925, -1640605886, -1640542254, -1636352030, -1611190813, -1615520285, -1649140303, -1779165807, -1804257855, -1804110399, -2072611391, -2064157327, -2047548063, -2047548047, -1887967981, -1888026877, -1888035069, -1887971821, -1921714397, -1921718429, -1653213325, -1653090989, -1644767789, -1644702253, -1611147917, -1611344799, -1611352735, -1619729039, -1619659403, -1757948459, -1757981225, -1791615497, -1787413065, -1795809881, -1800073801, -1795943019, -1795918379, -1795857967, -1797135919, -1797136015, -1797200783, -1780358093, -1788611533, -1755063021, -1755063021, -1771905709, -1771905677, -1771844237, -1754835469, -1754958349, -1788512269, -1788479661, -1780099469, -1746545053, -1746602141, -1746868429, -1755257069, -1621035245, -1624148205, -1657571501, -1657637549, -1653459597, -1644821133, -1649080845, -1779165741, -1779141167, -1779075631, -1778909743, -1779303183, -1779303181, -1787691789, -1754008109, -1754017341, -1755065389, -1755065389, -1788348573, -1788479709, -1788478159, -1805254895, -1788477679, -1788477645, -1788346573, -1779968237, -1746544878, -1746537166, -1637484686, -1636313262, -1640671406, -1640540334, -1636352142, -1611186846, -1611326109, -1644946061, -1644939949, -1661700783, -1661639343, -1644698287, -1655179952, -581577392, -552084143, -552075439, -552075440, -547885231, -547885231, -547883055, -1621637647, -1613380109, -1646930445, -1646922253, -1646926381, -1646920205, -1646985885, -1647004317, -1653295837, -1653222125, -1623730941, -1623730429, -1623828717, -1619624077, -1611235485, -1611182093, -1611315247, -1611311151, -1655343151, -1653114927, -1653123597, -1653254797, -1644858254, -1611301550, -1644724926, -1644724910, -1653244557, -1653252749, -1661641357, -1661086223, -1661086255, -1661602351, -1653082671, -1653082671, -1653221903, -1619667487, -1619659295, -1619828397, -1888276205, -986509053, -1003417325, -999214797, -990763741, -994962141] - -# WMA, 160kbps -TEST_1B_LENGTH = TEST_1A_LENGTH -TEST_1B_FP = 'AQABVkmYaEmkJEsiaDv8Hr0GXRd6FuZ69DP6wBdaHb-FHj60CzeaH33wHNUD8WxQPTiF7ghDPcN5hHcFP0OPb4HWFY3M4xr0XGjeoV2LD33QPMKP_2iOQzs6w-fRa-hz4R_EmClaKjjCCK2eCDkt9GHgo18MoVF79At-aD-cd6g2prjQHw2P05TQw4d2AZ3h8-i1o2oO_1BdBi0V9Ej1DK1y5KnQh_CPfoF2WO3Ra_gF7TnMMUdvXGhuoT1OW-jho4cudEbj5ngCbT_8kri0DtV31Bd8oc_RM1B2XEdliegNf0Ih80HjoNOP_vhh4j-uEXipDuZx_IG34jpc8XhmXMVZwSSPjvWQ5xnOoLkQXviCHj4uQ7SP4xl8GT_0ohOO6oJ5C33xoIdftOPR58dvpB8uBv8MH1_wdLCkEP2GCz18tEf4w8-hox965rB-VLiO_vDr4Dj8DD2P59By5WhvIRc6hmie49ODPCw0_bBfVA8eBUf3F_6SExDzS0GrMDjKFV55VObREyaJ_zisQ-ODN-h17IT1Frpg9_hzoT96BnYI9cEu4hMs_FDp4zW86cCPwyfObMaJH9bh9dAOvUN1-MePh8J_HGZJPMefwqfxHDpOwXsgXPBm1EN7wHKP-tA04YchHoetIz98_Bl8Dj9gK0f7ICf644L448R15BfOIy8uHUF-4AzM5dCNH4cPO-iOD7_QPELf4egDpzp6dM0g2jh-CSdEEj_yHt6O_ihyw8cDmYePG3cAH_1xaJsGx1qOfkXPD_7R17hoAAMAMEYAgqgRBmAkBFJMUWAMNQwAYA1QBiGHpHHCCCyZAAAgAMlRQAhAnBLIOCWYABgJAAhCQjskgSCCAeAMIEA5pATATAElBEIAKQmMQIowoIQQyCFhgANAFMGQcBwARhgSDmMpuDEIKkWEUAQAAYABQAFgDVUOAyAAEp4AYJAzgALAhBDMEAEYIIYoQxB1lAAADHBCCUGAKYIIYowiAAhiBBCSCMEUMCQIZ6QwHCiAGJQACcqMIMAxqIhIABiIraFCACokEI4poohhEijiGDVKGUElYw4QBaBgBhHMnLDCCOOYoEIM46xgwhlLgCACAQ' -TEST_1B_FP_RAW = [-1124735545, -1124604587, -1107954315, -1108026011, -1082854041, -1084889737, -1101531305, -1105799353, -1105766569, -1084795017, -1082696345, -1107993113, -1107995145, -1124772393, -1124608571, -1124600507, -1107831435, -1108028059, -1074469531, -1099635403, -1105857257, -1105791673, -1105832121, -1089054761, -1116316186, -1107866138, -1128970761, -1129024041, -1124829753, -1124735033, -1124600363, -1107962523, -1108017819, -1082858137, -1101666985, -1105725625, -1105799353, -1105766569, -1082697881, -1074309785, -1107995161, -1124772393, -1124739625, -1124608571, -1124600491, -1107962507, -1108017819, -1082858123, -1101732555, -1105726121, -1105832121, -1089054905, -1089054730, -1107927578, -1128837657, -1129032201, -1129024041, -1124735545, -1124735547, -1107823243, -1108028059, -1074465435, -1082858121, -1101667049, -1105791161, -1105832121, -1088989353, -1084795545, -1107993113, -1107995145, -1124772393, -1124608571, -1124608571, -1124600491, -1107962507, -1074463387, -1082858187, -1099569899, -1105726121, -1105832121, -1089054889, -1116317706, -1107931674, -1128968729, -1129032201, -1124829737, -1124735545, -1124604459, -1107954315, -1108026011, -1082851995, -1084955273, -1101531305, -1105791161, -1105832121, -1084795017, -1082698393, -1107993113, -1107995145, -1124772393, -1124641337, -1124600379, -1124608683, -1108026011, -1108023963, -1116445193, -1621793321, -1659424297, -1793715833, -1806297705, -1795811913, -1800008281, -1800069705, -1800135279, -1795934831, -1796905535, -1805163055, -1805257231, -1805257247, -1780089501, -1780095661, -1746409533, -1754794029, -1771579437, -1771579917, -1754933901, -1759414925, -1759414925, -1788609229, -1788674285, -1780285677, -1780351469, -1780418765, -1746884813, -1763593453, -1771842797, -1640770797, -1640933549, -1624017549, -1653441053, -1796071966, -1796141598, -1795945006, -1795924541, -1795719741, -1804108333, -1787331085, -1779040781, -1745357359, -1746405935, -1746414639, -1754967055, -1788520463, -1788774413, -1788775069, -1788773021, -1788609229, -1746796781, -1746669821, -1746667773, -1746675949, -1746811085, -1755261661, -1619991245, -1624119501, -1624148206, -1623761134, -1619624110, -1611235470, -1611248270, -1649062542, -1649125422, -1644341310, -1644300350, -1652688942, -1652693006, -1652759070, -1653279245, -1619528237, -1611139631, -1611213375, -1619567151, -1888133775, -1888133855, -1921647327, -1653213903, -1661475567, -1661475503, -1661471407, -1661627023, -1644919455, -1653295775, -1619528365, -1640564925, -1640605886, -1640542254, -1636335646, -1611190814, -1615520285, -1649140303, -1779165807, -1804331583, -1804110399, -1804175935, -2064157359, -1779112591, -1779112591, -1888099053, -1888026877, -1888035069, -1887971821, -1921648845, -1653282973, -1653213325, -1653090989, -1644767789, -1611213357, -1611147917, -1611344799, -1611352735, -1619729039, -1619659403, -1757940267, -1757981225, -1791599113, -1804190281, -1795809881, -1795879497, -1795943017, -1795918379, -1795857967, -1797135919, -1797136015, -1797201807, -1780358093, -1788611533, -1755063021, -1755063021, -1755128493, -1771905709, -1771844237, -1754835469, -1754958349, -1788512269, -1788479661, -1780099469, -1746545055, -1746602141, -1746864333, -1755257069, -1621035245, -1624148205, -1657571501, -1657637037, -1653197453, -1644821133, -1649080845, -1644948013, -1779141165, -1779075631, -1778909743, -1779303183, -1779303181, -1754137357, -1754008109, -1754017341, -1755065389, -1755065389, -1788349581, -1788479645, -1788478159, -1805254863, -1805254895, -1788477645, -1788346573, -1779968237, -1746544878, -1746536654, -1637484686, -1636313262, -1640540334, -1640540334, -1636352142, -1611186846, -1611326109, -1644946061, -1644939917, -1661700783, -1661639343, -1644698287, -1655179952, -581577392, -585640623, -552075439, -552075440, -547885231, -547885231, -547883055, -1621637647, -1613380109, -1646930445, -1646922253, -1646926381, -1646920205, -1646985885, -1647004317, -1653295837, -1653222125, -1623730941, -1623730429, -1623828717, -1619624077, -1611235485, -1611182093, -1611315247, -1611311151, -1655343151, -1653114927, -1653123597, -1653254797, -1644858254, -1611301550, -1644724926, -1644724910, -1653244589, -1653252749, -1661641357, -1661118991, -1661086255, -1661602351, -1653082671, -1653082671, -1653090831, -1619667487, -1619659295, -1619697293, -1888276205, -852283133, -1003417325, -999214797, -990699229, -994953949] - -# Vorbis, 64kbps -TEST_1C_LENGTH = TEST_1A_LENGTH -TEST_1C_FP = 'AQABYpESSUkiJYmSCD30C81RbeXwG7zQvBFa4bfQg_rRQ5vh8-jxHJoe-AyD6sF5NEJYPUPOo3cFP0N7fCm0F40k8fgOPUfzo12LDz0a3sIv4TSa44KGDv7Ra-hz4e8gxi1aBz8aHWF1CXlooWfgg_mO9hDVg6nwQ3sOnx2YvriC_mioBxc6Gj566ASzwyf6Hf0h5h3uFqeCw5I-9Bf6VLBD_OgXaD0srRz64tB-wdmYozp6IX2EVsgtdISPHjoaHX3woPshviQurUP1HfUFH30b9LCLH5oscXgx5YJ2dIedo12P7vhh3MJ9nHipDuZx_IG34jpcEc8cXMVZwRJZoe_xsMGZo3ExXbgWFNsf6Bvs43gG3_ihr-g0vCl6mLfQFw_RD36PdjzRLid848PF4J_h48uQv4OpEP2GS-gNn2gRnsN_iPvRC_1hyUL1D5eOwn9w3PCJXof2w1eO9hZyoWPRPEe-Bw8LTT_so_oefMePRj_6JUcPMW0stCOOckWzLsd99IRJCh9x-OgDPeh17IQos_jhtMdzoT-0M7BD_MFF7BNh4Tp0HTe86Tj-4_CJM5txwjxuCcp2lMe-Q5MN8_g-PMZ_HOaJJ8efwqeh5zhOwTt0XPBm1EN7oNHW4zu04zAMHbYuHOmP34F_XMUJ_2j14CHCh4cPXcdxHXmp48iPXDqOHDgDc4FuFcdh2OguPLgvNI_wdyj6wKmOHl0ziDZ-_NApWCKB_IKHcsYR3vDxBzoJ48YdwOdRHFq04l-Ofuj5Df5RizGeC-FD-AkeTLrxJsePUP1x7dCxc0Z8-DvCdcEvAIUQAsYgYYAhTADFFAXEIKMAAIwJ4IBhSDgElBFYKgAAAsYhaJAQAgBFCUBAOCWMyEhRQYhABhiSBFGOCKHMIEASSTFzVDEhEAJIWcAcYQZQJKRDRiAMgCiCIccBII4ZoYyQzholDQFICEUAAAAxBYA1wCjHgCBECAAUIMZAgCwQRhBDBGBCEMQMQQBAQxUBAoAngBTUJKUEMVIRJ4gRACAGJFFWOWCEA8JJAQki1ggJkKCEEUGAYlQRKhIAAhhprHAACSAdEYoBIphgEijgqCAELCGZAwoQYQQimDEnJDOmCSGNs0I4IIwlQBAGAGJGEEkAYEAJIASgAA' -TEST_1C_FP_RAW = [-1107954315, -1108019867, -1082858137, -1082792617, -1101531385, -1105791161, -1105832113, -1082697857, -1082696337, -1107993105, -1107995137, -1124739625, -1124608569, -1124600491, -1107962507, -1108028059, -1074469531, -1099635401, -1105726185, -1105824441, -1089054905, -1089054729, -1116316186, -1107866137, -1128970761, -1129024041, -1124829241, -1126832185, -1126697003, -1110057627, -1118503579, -1084955289, -1101666985, -1105725625, -1105799353, -1105832105, -1084795545, -1107862169, -1107995161, -1124772393, -1124608553, -1124608571, -1124608683, -1107962507, -1108017819, -1082858123, -1101667019, -1105726121, -1105823929, -1089054905, -1084860425, -1107927578, -1128839705, -1129032201, -1129024041, -1124801073, -1124735009, -1107953795, -1107952283, -1074463387, -1082858121, -1101662889, -1105725617, -1105832113, -1088989345, -1082696337, -1107862161, -1107995137, -1124772385, -1124739633, -1124600371, -1107823275, -1107962507, -1074465435, -1082858123, -1101667049, -1105726121, -1105832105, -1089054889, -1118413321, -1107931673, -1128968713, -1129032233, -1124829737, -1124735545, -1124603947, -1110051467, -1108019867, -1074465435, -1084955273, -1101531369, -1105725625, -1105766569, -1082697866, -1082696346, -1107993241, -1107995145, -1124772393, -1124641337, -1124608571, -1107831435, -1108028059, -1108023963, -1116445195, -1621793321, -1659424297, -1793715833, -1806297705, -1797909065, -1799938649, -1800069705, -1800137321, -1795934825, -1805294139, -1805158971, -1805257231, -1805255197, -1788478093, -1780095661, -1746405565, -1754794029, -1771579437, -1771579405, -1771743757, -1754958477, -1759414925, -1788609229, -1788674285, -1780285677, -1780351469, -1780418765, -1746884813, -1746816237, -1771842797, -1640770797, -1640933549, -1619823117, -1653441053, -1661592093, -1795879453, -1795947053, -1795926585, -1795719737, -1795719721, -1787298317, -1780089359, -1746537007, -1746405935, -1746414639, -1754934287, -1788521487, -1788774413, -1788779165, -1788642013, -1788609229, -1746796781, -1746669821, -1746733309, -1746675949, -1763588301, -1637821149, -1619990733, -1624119533, -1624148205, -1623761133, -1619624109, -1611235469, -1611182733, -1649062414, -1649125422, -1644341310, -1644169278, -1652688942, -1652758542, -1652759070, -1653213709, -1619528461, -1611205167, -1611211327, -1619534383, -1619697807, -1921655007, -1653213919, -1653213903, -1661606639, -1661475567, -1661471407, -1661610639, -1644915359, -1653295757, -1619528365, -1640564925, -1640605886, -1640538158, -1636352030, -1611190813, -1615520285, -1649140303, -1644948079, -1670040127, -1804110399, -2072611391, -2064157327, -2047548063, -1913330319, -1887965933, -1888026877, -1888035069, -1887971821, -1921714397, -1653282973, -1653213325, -1652566701, -1644767917, -1611213485, -1611147917, -1611344541, -1611352735, -1619729039, -1619659403, -1623730729, -1757981225, -1791599177, -1804190281, -1795809881, -1795879499, -1795943019, -1795918379, -1795857963, -1780358703, -1780358799, -1797136335, -1780353997, -1788611533, -1755063021, -1755054829, -1771897517, -1771905709, -1771844237, -1754835469, -1754958349, -1788512301, -1788356781, -1779968395, -1780099227, -1746602143, -1746868429, -1612651245, -1619986669, -1624148205, -1657571501, -1657637037, -1653181069, -1644821133, -1649080845, -1644948013, -1779141167, -1779075631, -1778909743, -1779041039, -1779303181, -1754137357, -1754008109, -1754017341, -1755065389, -1755065389, -1788348573, -1788480221, -1788478159, -1788477647, -1788477679, -1788477647, -1788346573, -1746413805, -1746544877, -1612318926, -1637353614, -1636313262, -1640671406, -1640532142, -1636354190, -1611186846, -1611330206, -1644948109, -1644939949, -1661635247, -1661639343, -1644698287, -581569200, -581577392, -547891887, -552075440, -547881136, -547885231, -547885231, -547883055, -547895823, -1646934541, -1646930445, -1646922253, -1646922285, -1646920205, -1647002269, -1647008413, -1653295837, -1653222125, -1623730413, -1623730429, -1623828717, -1619624077, -1611235485, -1611182093, -1611315247, -1644865583, -1646954543, -1653114925, -1653254797, -1653254797, -1611303566, -1611303854, -1611170494, -1644724926, -1653244589, -1653252749, -1661641229, -1661086223, -1661086255, -1661602351, -1653082671, -1653082671, -1619667471, -1619667487, -1619659295, -1619693229, -1888276205, -1926024957, -1003417325, -999214797, -990763741, -996010717, -995947214, -995881710, -995869422, -991609530, -999993914, -983183930, -949893737, -966552169, -966543979, -958271087, -958279216, -941559312, -941530783, -983473887] - -# WMA, 32kbps -TEST_1D_LENGTH = TEST_1A_LENGTH -TEST_1D_FP = 'AQABVkmYaEkkZUkE7bDao4e-C9VhHtWLHtQFphJ-CT3sQ7vQGT76Bc_RB-LZ4Bb6HR3CUB9yVugr2MnQH_qITieah8eL_oIYs8O3FkUvNK6Fv0L3wz4O7egMn0e_oz_UD45pnBJ-pPLRShdyFn1awZ9xfIQorUdPfIf2C455tC-OXhcaqngldId99IKODs2X40d_OG8E1UXrox9SSR_6C3kq9CH8o1-gHVaPfsUvaD-cjTmq40JjXQjLIxc6Gj56XNAi9MvxBtotwSe-FtVP9BZ89HnQB3aIHz2Jn0Vz9DyOxjmm3sKPHy189MdR7R1s_PBRrofWHmL94Bl-4azQyDzySR725MGZojn65MiJ32g-6Me2_fjxDL7xHM_moWunQzVaUYePkniPcjr8oucR_vCP7_jxJsR_mAv-dIIdyWiPoz_RPPiQH_2h5ceLe0dzVDe-oRfSuA5w-BnC83gOLVc6hP1xoWPRPMf3DHlYNDEPPUf_4Ffwo_mDrkugwXmMVuFxlOvhdUGPmzu0koePH97RB3M__OglwzLxQ_SO6rPwoyfsMHhxSjiPY1Nw6sFhGzmQ74aD91Cm48R9eLfQ4x2gaTv8o9qN6-hhHk-OzylOuMuh4xSMH_phH6VGoEWjFyd2QouFAzquw3yOHM6-BdeL4_AZon_wZTm60sgvGLqE_0beEaeL_BtO_Phx4xrhBz9eEuKD5xDRszNOqEcfHeaL8OhVOKbRH-G0TDC1FdczHDd-I5WOw84u_MKRGz70bDhTIe_hGzcOoz9yaFsVw_mFfuh5pD96xrhyAAaIAgYiRIVjDGHCFAaGCKQYAkIAQIkBxAAjmGQCECIQNFQQo4CQBhBHBBECEEANQ0oApwQyhCBBJBBEUEWcEQRIRwTIzBEgEIBAKQuIckQAAQAUSjDljCKEDAIVkcAZBAAhhiEhFCFAIQWcMko6I4RlgAIBCACGKSoUEEQQ4AQwBgsFlFACKAkCE0IwQ4BwQhikiDHEAEEdAoY4IoBgFiKkEENCEgScEQ5QIwEipAkljBGKMIKAAFIoxohgAipClHEMICyVEYAJBywUChjBCDICAIeEs4gZJQAFTCglBCVEICAQA846IZywglHhhTLCkibQEJwAQQQiAA' -TEST_1D_FP_RAW = [-1126832697, -1126701611, -1110051467, -1110059675, -1084951193, -1084885705, -1101662441, -1105725625, -1105758377, -1084786817, -1084793497, -1107862041, -1107993097, -1124772393, -1124608571, -1124600491, -1107823243, -1108028059, -1074469531, -1099635339, -1105857193, -1105758377, -1089054905, -1089054761, -1118413338, -1107866137, -1128968713, -1129024041, -1124796985, -1124735035, -1126701739, -1110059675, -1108028059, -1082854027, -1101667049, -1105725609, -1105725625, -1105766569, -1084858521, -1074307737, -1107993097, -1124772393, -1124739625, -1124608571, -1124600491, -1107962507, -1074465435, -1082858123, -1101732553, -1105857193, -1105766569, -1089054889, -1084852234, -1110024730, -1107866137, -1128966665, -1124829737, -1124796969, -1124735017, -1107823243, -1107962523, -1074465435, -1084955273, -1101662953, -1105725689, -1105725625, -1084795049, -1084859033, -1107862169, -1107993097, -1124772393, -1124608569, -1124600379, -1124600491, -1108028059, -1074465435, -1082858123, -1105861355, -1105726121, -1088981177, -1089054889, -1118413322, -1107931674, -1128968729, -1129032201, -1124829737, -1124735545, -1126832683, -1110051467, -1108028059, -1082854043, -1084955273, -1101531369, -1105725625, -1105766585, -1084784777, -1082696346, -1107862169, -1107995145, -1124772393, -1124641337, -1124600377, -1124600459, -1108028059, -1108023835, -1084987913, -1084918313, -1223215657, -1793649273, -1806297705, -1797909065, -1800008281, -1800073801, -1800137321, -1795934825, -1796921977, -1805556281, -1805552137, -731775513, -731775641, -715004585, -672929981, -689703101, -698099901, -698100397, -698231437, -702417565, -685640349, -714868941, -714866925, -706478573, -706609613, -673122525, -1746881231, -1771982573, -1771843309, -1774988525, -1775151277, -1758234653, -1787681310, -1796071962, -1800335898, -1800401450, -1796178490, -1796117049, -1804374841, -1804337929, -1787568907, -705440303, -671877679, -671886383, -680406031, -1754246159, -1791920655, -714906271, -714902237, -681443533, -673054957, -673059325, -689701373, -1763453165, -1763588301, -1772038365, -1770981582, -1775180014, -1640990958, -1624148206, -1619624174, -1611251886, -1611264654, -1783296526, -1779149358, -1779083326, -1779075134, -1786943534, -1786976286, -1786972702, -1653279261, -1644825101, -1611139647, -1611203135, -1619534527, -1619665551, -1892295391, -1653220063, -1653213903, -1661475504, -1661475504, -1661471408, -1661627024, -1644919455, -1653295775, -1623722669, -1640564926, -1640605886, -1640538158, -1636352030, -1745408542, -1749738013, -1783362157, -1779165807, -1804323455, -1804110399, -730434111, -705202703, -705370783, -705370765, -713562861, -680067325, -680075517, -713566701, -713754829, -713758941, -578947229, -1652566671, -1644178095, -1610640047, -1611164559, -1611361183, -1762347675, -1770740379, -1774848651, -1774725675, -1757981227, -1791549961, -1804190297, -1795809881, -1795748425, -1795943017, -1796196969, -1796185641, -723426857, -723394089, -723394445, -715000781, -715000525, -714867437, -681313006, -681313006, -1771905773, -1771905677, -1755064973, -1755195947, -1755187243, -1788749995, -1746676107, -1746807195, -1746864283, -1746864283, -1755257545, -1754204905, -1758333113, -1791789241, -1791854761, -1787660937, -1779301001, -1783561737, -1783623177, -1779403305, -1779337771, -1779171883, -1779171851, -1779303179, -1787699977, -1754147625, -1754009145, -1754009145, -1754008617, -1788611737, -1788610777, -1788750045, -1788739823, -1788477679, -1788477679, -1788346605, -1779968237, -1746807501, -1746799310, -1770784942, -1640802478, -1640933550, -1640949934, -1636354190, -1611253918, -1644886686, -1649142286, -1661717005, -1661717037, -1661639215, -570988591, -570427568, -847390896, -818553007, -818413743, -550109359, -545919151, -545790127, -545851951, -545864207, -537410063, -573188623, -573196845, -589976109, -589976079, -1644904991, -1644907167, -1653295837, -1619667661, -1623730413, -1623763198, -1624015086, -1619890318, -1611497630, -1611182605, -1615525933, -1646962733, -1646954541, -1655212077, -1787340845, -1787472526, -1787464334, -1745490606, -1644696238, -1644692142, -1653211821, -1653219981, -722084495, -721435151, -587344400, -587860528, -596118064, -579340848, -579340816, -545917472, -680135199, -697077389, -684510957, -986500861, -1003417325, -999214798, -990764766, -994962142] - -TEST_1_LENGTH = TEST_1A_LENGTH -TEST_1_FP = TEST_1A_FP -TEST_1_FP_RAW = TEST_1A_FP_RAW - -script = None - -SEQUENCES = [ - ('account', 'id'), - ('application', 'id'), - ('format', 'id'), - ('foreignid', 'id'), - ('foreignid_vendor', 'id'), - ('source', 'id'), - ('submission', 'id'), - ('track', 'id'), - ('track_mbid', 'id'), - ('track_mbid_source', 'id'), - ('track_mbid_change', 'id'), - ('track_mbid_flag', 'id'), - ('track_puid', 'id'), - ('track_puid_source', 'id'), - ('track_meta', 'id'), - ('track_meta_source', 'id'), - ('track_foreignid', 'id'), - ('track_foreignid_source', 'id'), - ('fingerprint', 'id'), - ('fingerprint_source', 'id'), - ('meta', 'id'), -] - -TABLES = [ - 'account', - 'application', - 'format', - 'foreignid', - 'foreignid_vendor', - 'source', - 'stats', - 'track', - 'track_mbid', - 'track_mbid_source', - 'track_mbid_change', - 'track_mbid_flag', - 'track_puid', - 'track_puid_source', - 'track_meta', - 'track_meta_source', - 'track_foreignid', - 'track_foreignid_source', - 'meta', - 'musicbrainz.artist_credit_name', - 'musicbrainz.artist_credit', - 'musicbrainz.artist_name', - 'musicbrainz.artist', - 'musicbrainz.track_name', - 'musicbrainz.track', - 'musicbrainz.release', - 'musicbrainz.medium', - 'musicbrainz.medium_format', - 'musicbrainz.recording', - 'musicbrainz.release_name', - 'musicbrainz.release_group', - 'musicbrainz.clientversion', - 'musicbrainz.tracklist', -] - -BASE_SQL = ''' -INSERT INTO account (name, apikey, lastlogin) VALUES ('User 1', 'user1key', now() - INTERVAL '2 day'); -INSERT INTO account (name, apikey, lastlogin) VALUES ('User 2', 'user2key', now() - INTERVAL '5 day'); -INSERT INTO application (name, apikey, version, account_id) VALUES ('App 1', 'app1key', '0.1', 1); -INSERT INTO application (name, apikey, version, account_id) VALUES ('App 2', 'app2key', '0.1', 2); -INSERT INTO format (name) VALUES ('FLAC'); -INSERT INTO source (account_id, application_id) VALUES (1, 1); -INSERT INTO source (account_id, application_id) VALUES (2, 2); -INSERT INTO track (id, gid) VALUES - (1, 'eb31d1c3-950e-468b-9e36-e46fa75b1291'), - (2, '92732e4b-97c6-4250-b237-1636384d466f'), - (3, '30e66c45-f761-490a-b1bd-55763e8b59be'), - (4, '014e973b-368e-42bf-b619-84cab14c4af6'); -INSERT INTO track_mbid (track_id, mbid, submission_count) VALUES (1, 'b81f83ee-4da4-11e0-9ed8-0025225356f3', 1); -INSERT INTO meta (id, track, artist) VALUES (1, 'Custom Track', 'Custom Artist'); -INSERT INTO meta (id, track, artist) VALUES (2, 'Custom Track', 'Custom Artist'); -INSERT INTO track_meta (track_id, meta_id, submission_count) VALUES (1, 1, 1); -INSERT INTO track_meta (track_id, meta_id, submission_count) VALUES (1, 2, 10); -''' - - -def prepare_sequences(conn): - with conn.begin(): - for table, column in SEQUENCES: - conn.execute(""" - SELECT setval('%(table)s_%(column)s_seq', - coalesce((SELECT max(%(column)s) FROM %(table)s), 0) + 1, false) - """ % {'table': table, 'column': column}) - - -def prepare_database(conn, sql, params=None): - with conn.begin(): - prepare_sequences(conn) - conn.execute(sql, params) - prepare_sequences(conn) - - -def with_database(func): - def wrapper(*args, **kwargs): - with closing(script.engine.connect()) as conn: - prepare_sequences(conn) - trans = conn.begin() - try: - func(conn, *args, **kwargs) - finally: - trans.rollback() - wrapper = make_decorator(func)(wrapper) - return wrapper - - -def setup(): - global script - config_path = os.path.dirname(os.path.abspath(__file__)) + '/../acoustid-test.conf' - script = Script(config_path, tests=True) - if not os.environ.get('SKIP_DB_SETUP'): - with closing(script.engine.connect()) as conn: - with conn.begin(): - if os.environ.get('ACOUSTID_TEST_FULL_DB_SETUP'): - conn.execute('CREATE EXTENSION intarray') - conn.execute('CREATE EXTENSION pgcrypto') - conn.execute('CREATE EXTENSION cube') - conn.execute('CREATE EXTENSION acoustid') - metadata.create_all(conn) - for table in reversed(metadata.sorted_tables): - conn.execute(table.delete()) - prepare_database(conn, BASE_SQL) - - -def make_web_application(): - from acoustid.web.app import make_application - config_path = os.path.dirname(os.path.abspath(__file__)) + '/../acoustid-test.conf' - return make_application(config_path, tests=True) - - -def assert_dict_equals(d1, d2, msg=None): - if d1 != d2: - standardMsg = '%s != %s' % (repr(d1), repr(d2)) - diff = ('\n' + '\n'.join(difflib.ndiff( - pprint.pformat(d1).splitlines(), - pprint.pformat(d2).splitlines()))) - assert d1 == d2, standardMsg + '\n' + diff - - -def assert_json_equals(expected, actual): - assert_dict_equals(expected, json.loads(actual)) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..1d6593e34ab7a9098c8e41a485b762c785727f82 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,115 @@ +import os +import pytest +from acoustid.config import str_to_bool +from acoustid.tables import metadata +from acoustid.data.fingerprint import update_fingerprint_index +from tests.data import create_sample_data + + +RECREATE_DB = True + + +def get_tables_for_bind(metadata, bind_key): + tables = [] + for table in metadata.sorted_tables: + if table.info.get('bind_key', 'default') == bind_key: + tables.append(table) + return tables + + +def create_all(metadata, engines): + for bind_key, engine in engines.items(): + tables = get_tables_for_bind(metadata, bind_key) + with engine.connect() as conn: + with conn.begin(): + metadata.create_all(bind=conn, tables=tables) + + +def drop_all(metadata, engines): + for bind_key, engine in engines.items(): + tables = get_tables_for_bind(metadata, bind_key) + with engine.connect() as conn: + with conn.begin(): + metadata.drop_all(bind=conn, tables=tables) + + +def delete_from_all_tables(metadata, engines): + for bind_key, engine in engines.items(): + tables = get_tables_for_bind(metadata, bind_key) + with engine.connect() as conn: + with conn.begin(): + for table in reversed(tables): + conn.execute(table.delete()) + + +def delete_shutdown_file(config): + if os.path.exists(config.website.shutdown_file_path): + os.remove(config.website.shutdown_file_path) + + +def prepare_database(script): + delete_from_all_tables(metadata, script.db_engines) + with script.context() as ctx: + create_sample_data(ctx) + ctx.db.flush() + update_fingerprint_index(ctx.db.connection(), ctx.index) + ctx.db.commit() + + +@pytest.fixture(scope='session') +def config_path(): + return os.path.dirname(os.path.abspath(__file__)) + '/../acoustid-test.conf' + + +@pytest.fixture(scope='session') +def script_global(config_path): + from acoustid.script import Script + + script = Script(config_path, tests=True) + + recreate_db = str_to_bool(os.environ.get('ACOUSTID_TEST_RECREATE', '')) + if recreate_db: + drop_all(metadata, script.db_engines) + + create_all(metadata, script.db_engines) + prepare_database(script) + + yield script + + +@pytest.fixture(scope='session') +def server_global(script_global, config_path): + from acoustid.server import make_application + + server = make_application(config_path, tests=True) + + delete_shutdown_file(server.config) + + yield server + + +@pytest.fixture(scope='session') +def web_app_global(script_global, config_path): + from acoustid.web.app import make_application + + server = make_application(config_path, tests=True) + + delete_shutdown_file(server.acoustid_config) + + yield server + + +@pytest.fixture() +def server(server_global): + yield server_global + + +@pytest.fixture() +def web_app(web_app_global): + yield web_app_global + + +@pytest.fixture() +def cleanup(script_global): + yield + prepare_database(script_global) diff --git a/tests/data.py b/tests/data.py new file mode 100644 index 0000000000000000000000000000000000000000..c13b817fad4a1f1189c3f36a8ebe031e2a1f6cdd --- /dev/null +++ b/tests/data.py @@ -0,0 +1,24 @@ +from acoustid.models import Account, Application, Fingerprint, Meta, Track, TrackMeta + +TEST_2_LENGTH = 320 +TEST_2_FP = 'AQABVtuUZFGShAqO-h9OHD96SvhwBVNCKQnOIYmiIc-ENwF7TDe8Hr0W_AjhvRCP2sfT4DTS7zjyOYeqaI-RSxee5RmaWzhOHnlcaB6HnPgpdE-DkWIH2ysYG_Eh9zJCyfCXGOdw-EGoD2p69IavWOhzMD-a9tBx9FgPVz2qNDvQH3744ISIXRKeHto5_MhyeMtxc-COnYJ_lHLwRAgPvShz_Hga4zd8HD9UKXWOPP3xRLmGnlbQHKfxGPeRvAt6UngMvcF-gkpRi0bUZjGaH6FUHb_xGDt6aHmM__ghfkmH70B4fWiuCj8y8uj3oImZY8d3NFWWHuGF-3hCPEd_uEOyE_nw4w8ueXi24znCHOHxSWtw9BnSBzrSHF2Y4S0e_EioZoh9XMGfo2dqNMeP80aQPM5xGT9efMeTYL-KIqmHdDraHs-P8IcYjoj0I7_Q43iJ9BF64nSKKth2SjG-cvCHH-2OL8txHsUt9HhF4LiK5j16lAf1FkjvQiN55FSOkkOPkmj4GK-OH80eIeyh98HhE_qhPwjzKAV-HJ2OZkd4Q_vhp0d_6Id-_IeWW9CKoP3RKM-Bo3mOfvhxND_6HMgZ6EfXHB-8Q8-iok1znOi-ozmx54P2Dg5V_PCgLxy8KiH6C0cbHU3Ebtiho9Rxw8er47tw7jgRNxl84ziPJ-B1_DiNNClzaGSCvMGPGxePMD5qZYEuAwdTXYSYcIkmodc2nMqg_WgqBk_yBdVx0vCjQD8uhNRxXTgvVFSOSOmx61C1KMaNsFwM93h-PBdmFm8o45nxDabx48cTbGl4hHuhasjSwPtxPvAV1A7yQMukREERR-nxL8j-EbWYQ8sj4joABQQmjQhkjLFCKSAo4QoxYiQwQhgmkGjCKGAIMMA4BIwQwjhAFMBUCCUAYEIxpYxUCDlEjJYOScSMgsIIAgADwjKEFBAUCkMEMYAagoARzAAHCDCIISKANkgYYBiQwgDDjHEMIGWZFUBQLhgohBGkhECOMEAMIYghogTgQghgiSLCYUegAsJApIQjxABNDFWCa6AIAQ4Q4KgAgIABgGDCMNGIMgQJRQAQTACpgBNIJkUcBMkpoKAgXCjAgAAGKIcYIVAYbZgwggkEmKLEiYGYAYQQShFAAAQBFEEAEuEIgwYRQoARnBkAmAGMEAGFGIgQBigCwAkABEIA' +TEST_2_FP_RAW = [-772063964, -772066012, -1007079116, -1011011276, -1027788492, -1029889740, -1031261916, -1031269084, -1031270620, -1031021744, -1031079600, -1031078425, -1031032346, -1018580506, -1018646290, -1022775046, -1056337446, -1056261686, -1073039030, -1073039013, -1068976005, -1001867175, -733365175, -733302711, -737435575, -603332504, -603365272, -737516311, -188076117, -175490390, -745993558, -758642022, -767030630, -1034347878, -1038412133, -1039584631, -1039654264, -1034345784, -1030086056, -1011141092, -1045873092, -1045939676, -1045947803, -2132276505, -2132259914, -2140415082, -2140472938, -2140481130, -2140546634, -2140603929, -2144802523, -2144536267, -2123540203, -2115147515, -2081855203, -2098598611, -2098606801, -2098606737, -2106995329, -2090217989, -2123638309, -2140477173, -2140477173, -2123634405, -2106992325, -2107061957, -2107061991, -2107007715, -1033140963, -1023769329, -1025864433, -1026913002, -1010133962, -1017409482, -1017540482, -1022848902, -1056337830, -1056271030, -1056261302, -1073038501, -1068975271, -1060587447, -993477559, -1001672631, -737435575, -737566615, -737550231, -737419031, 1422585259, 1955228586, 1367940010, 1388841914, 1380453258, 1376328586, 1376458634, 1107956618, 1107899017, 1113072281, 1121657497, 1119494811, 1135224479, 1135226615, 1139489511, 1130891874, 1126713938, 1109977859, 1114237187, 1122691331, 1122695431, 1122687255, 1114233125, 1130944869, 1126746469, 1097554247, 1105885511, 1105885511, 1135270230, 1122523494, 1114135910, 1109939695, 1093236223, 1076520335, 1080714635, 1089107851, 11092923, 11010986, 15209450, 15492074, 7103274, 2913082, 2905882, 2940681, 2947848, 7138056, 32303368, 61716744, 44932552, 1118668232, 1118406137, 1122600424, 1110167912, 1110167848, 1110106424, 1122689305, 1118495003, 1118478714, 1118540010, 1122599146, 1110016234, 1110147562, 1110094153, 1076535560, 1076538376, -1058363384, -794183656, -794249176, -790063064, -261519320, -261519319, -529562582, -529628886, -530153430, -530280406, -534465494, -534459350, -517027794, -517027778, -517056387, 1630428508, 1634606924, 1643060940, -508616995, -508740929, -475252054, -487834709, -496223301, -496231493, -496092485, -488752486, -489735542, -494125366, -494125542, 1641889598, 1627335998, 1617898782, 1613703454, 1614756622, 537664270, 541854222, 541854238, 541874782, 558651982, 558168910, 558168910, 558168398, 566360398, 1636039038, 1669593454, 1938028670, 1942087766, 1942087766, 1665394807, 1631779173, 1640192868, 1640221300, 1640483428, 1640487796, 1631902020, 1627682884, 553932868, 554654068, 554589029, 567179879, 562985575, 562846279, 562879301, 558684487, 554678646, 554678646, 558873462, 567262070, 563067366, 562936022, 567064775, 558692565, 1628436725, 1661925605, 1661893095, 1666087909, 592329573, 567032663, 567032133, 567032132, 1640840020, 1909340900, 1909340900, -238142748, -775079212, -775152956, -1043580220, -1047774524, -2121450764, -2138162460, -2138162460, -2138232091, -2121520409, -2117330313, -2124670345, -2124604585, -2092205227, -2083848891, -2083787451, -2117489195, -2117550619, -2124902943, -2139517453, -2139383405, -2122597997, -2122598221, -2093090639, -2095162991, -2107737727, -2107754111, -2108805231, -2099495199, -2099499291, -2097402137, -2098451465, -2098709513, -2098717737, -2081859113, -2123773481, -2140434985, -2140496425, -2140428906, -2132171338, -2132236874, -2065124170, -2023181130, -2039964482, -2044162882, -2044098306, -2111137761, -2111117043, -2111165684, -2144720372, -2140460532, -2132132340, -2136328643, -2136407507, -2136471761, -2136471761, -2132277457, -2114187977, -2091268651, -2083809851, -2083872379, -2117361257, -2117552729, -2141681241, -2139584009, -2143708937, -2143618857, -2126874411, -2126894891, -2093339196, -2105923644, -2099628348, -2103836012, -2103966047, -2099751259, -2097639449, -2031579161, -2039763466, -2031375914, -2014728234, -2056675370, -2056548654, -2073127278, -2077251950, -2077233262, -2077266510, -2077332302, -2073154382, -2014434126, -2031143722, -2039794617, -2039792379, -2039792868, -2107033028, -2081936836, -2136462820, -2136265204, -2136263155, -2136456385, -2136456401, -2132228626, -2122791506, -2091070041, -2107647561, -2108769915, -2100384379] + + +def create_sample_data(ctx): + acct1 = Account(name='user1', apikey='user1_api_key') + ctx.db.add(acct1) + + app1 = Application(name='app1', apikey='app1_api_key', version='1.0', account=acct1) + ctx.db.add(app1) + + tr1 = Track(gid='eb31d1c3-950e-468b-9e36-e46fa75b1291') + ctx.db.add(tr1) + + m1 = Meta(track='Custom Track', artist='Custom Artist') + ctx.db.add(m1) + + ctx.db.add(TrackMeta(track=tr1, meta=m1, submission_count=1)) + + fp1 = Fingerprint(id=1, track=tr1, fingerprint=TEST_2_FP_RAW, length=TEST_2_LENGTH, submission_count=1) + ctx.db.add(fp1) diff --git a/tests/test_api.py b/tests/test_api.py index d3e2bd0d5840626b3ae1d82a7dc10d826ee39f17..68526db5d9600407c16169897d9026cdb82b7263 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,40 +1,8 @@ -# Copyright (C) 2011 Lukas Lalinsky -# Distributed under the MIT license, see the LICENSE file for details. +import pytest +from webtest import TestApp, AppError -from nose.tools import assert_equals -from acoustid.api import serialize_response - -def test_serialize_json(): - data = {'status': 'ok', 'artists': [{'name': 'Jean Michel Jarre', 'year': 1948, 'cities': ['Paris', 'Lyon']}]} - resp = serialize_response(data, 'json') - assert_equals('application/json; charset=UTF-8', resp.content_type) - expected = b'''{"artists": [{"cities": ["Paris", "Lyon"], "name": "Jean Michel Jarre", "year": 1948}], "status": "ok"}''' - assert_equals(expected, resp.data) - - -def test_serialize_jsonp(): - data = {'status': 'ok', 'artists': [{'name': 'Jean Michel Jarre', 'year': 1948, 'cities': ['Paris', 'Lyon']}]} - resp = serialize_response(data, 'jsonp:getData') - assert_equals('application/javascript; charset=UTF-8', resp.content_type) - expected = b'''getData({"artists": [{"cities": ["Paris", "Lyon"], "name": "Jean Michel Jarre", "year": 1948}], "status": "ok"})''' - assert_equals(expected, resp.data) - - -def test_serialize_xml(): - data = {'status': 'ok', 'artists': [{'name': 'Jean Michel Jarre', 'year': 1948, 'cities': ['Paris', 'Lyon']}]} - resp = serialize_response(data, 'xml') - assert_equals('text/xml; charset=UTF-8', resp.content_type) - expected = ( - '''\nokParisLyon''' - '''Jean Michel Jarre1948''' - ) - assert_equals(expected, resp.data) - - -def test_serialize_xml_attribute(): - data = {'@status': 'ok'} - resp = serialize_response(data, 'xml') - assert_equals('text/xml; charset=UTF-8', resp.content_type) - expected = '''\n''' - assert_equals(expected, resp.data) +def test_server(server): + client = TestApp(server) + with pytest.raises(AppError, match=r".* 404 NOT FOUND .*"): + client.get('/') diff --git a/tests/test_api_health.py b/tests/test_api_health.py new file mode 100644 index 0000000000000000000000000000000000000000..8037e7f9e1a458e62ca2d327de267486f989d753 --- /dev/null +++ b/tests/test_api_health.py @@ -0,0 +1,63 @@ +import os +import pytest +from webtest import TestApp, AppError + + +def test_health(server): + client = TestApp(server) + resp = client.get('/_health') + assert resp.status_int == 200 + + +def test_health_on_slave(server): + server.config.cluster.role = 'slave' + try: + client = TestApp(server) + with pytest.raises(AppError, match=r".* 503 SERVICE UNAVAILABLE .*"): + client.get('/_health') + finally: + server.config.cluster.role = 'master' + + +def test_health_ro(server): + client = TestApp(server) + resp = client.get('/_health_ro') + assert resp.status_int == 200 + + +def test_health_ro_on_slave(server): + server.config.cluster.role = 'slave' + try: + client = TestApp(server) + resp = client.get('/_health_ro') + assert resp.status_int == 200 + finally: + server.config.cluster.role = 'master' + + +def test_health_docker(server): + client = TestApp(server) + resp = client.get('/_health_docker') + assert resp.status_int == 200 + + +def test_health_docker_on_slave(server): + server.config.cluster.role = 'slave' + try: + client = TestApp(server) + resp = client.get('/_health_docker') + assert resp.status_int == 200 + finally: + server.config.cluster.role = 'master' + + +@pytest.mark.parametrize('endpoint', ['health', 'health_ro', 'health_docker']) +def test_health_while_shutdown(server, endpoint): + with open(server.config.website.shutdown_file_path, 'wt') as shutdown_file: + shutdown_file.write('shutdown') + try: + client = TestApp(server) + with pytest.raises(AppError, match=r".* 503 SERVICE UNAVAILABLE .*"): + client.get('/_' + endpoint) + finally: + os.remove(server.config.website.shutdown_file_path) diff --git a/tests/test_api_v2_lookup.py b/tests/test_api_v2_lookup.py new file mode 100644 index 0000000000000000000000000000000000000000..7170d1e77d85e7b46821a605ba6e91cdcd7dc1d2 --- /dev/null +++ b/tests/test_api_v2_lookup.py @@ -0,0 +1,82 @@ +import pytest +from webtest import TestApp, AppError +from tests.data import TEST_2_FP, TEST_2_LENGTH + + +def test_lookup_invalid_client(server): + client = TestApp(server) + with pytest.raises(AppError, match=r".* 400 BAD REQUEST .*") as ex: + client.post('/v2/lookup', { + 'client': 'xxx', + }) + ex.match(r'.*"invalid API key\b.*') + + +def test_lookup_missing_fingerprint(server): + client = TestApp(server) + with pytest.raises(AppError, match=r".* 400 BAD REQUEST .*") as ex: + client.post('/v2/lookup', { + 'client': 'app1_api_key', + }) + ex.match(r'.*"missing required parameter.*fingerprint') + + +def test_lookup_missing_duration(server): + client = TestApp(server) + with pytest.raises(AppError, match=r".* 400 BAD REQUEST .*") as ex: + client.post('/v2/lookup', { + 'client': 'app1_api_key', + 'fingerprint': TEST_2_FP, + }) + ex.match(r'.*"missing required parameter.*duration') + + +def test_lookup_by_fingerprint(server): + client = TestApp(server) + resp = client.post('/v2/lookup', { + 'client': 'app1_api_key', + 'fingerprint': TEST_2_FP, + 'duration': str(TEST_2_LENGTH), + 'meta': 'recordings usermeta', + }) + assert resp.status_int == 200 + assert resp.json == { + 'status': 'ok', + 'results': [ + { + 'id': 'eb31d1c3-950e-468b-9e36-e46fa75b1291', + 'score': 1.0, + 'recordings': [ + { + 'artists': ['Custom Artist'], + 'title': 'Custom Track', + } + ], + }, + ], + } + + +def test_lookup_by_track_id(server): + client = TestApp(server) + resp = client.post('/v2/lookup', { + 'client': 'app1_api_key', + 'trackid': 'eb31d1c3-950e-468b-9e36-e46fa75b1291', + 'meta': 'recordings usermeta', + }) + assert resp.status_int == 200 + assert resp.json == { + 'status': 'ok', + 'results': [ + { + 'id': 'eb31d1c3-950e-468b-9e36-e46fa75b1291', + 'score': 1.0, + 'recordings': [ + { + 'artists': ['Custom Artist'], + 'title': 'Custom Track', + } + ], + }, + ], + } diff --git a/tests/test_api_v2_submit.py b/tests/test_api_v2_submit.py new file mode 100644 index 0000000000000000000000000000000000000000..f6c8d51732d894ee7cd0bc4cfe94cc434aa689b8 --- /dev/null +++ b/tests/test_api_v2_submit.py @@ -0,0 +1,73 @@ +import pytest +from webtest import TestApp, AppError +from acoustid.models import Submission +from tests.data import TEST_2_FP, TEST_2_FP_RAW, TEST_2_LENGTH + + +def test_submit_invalid_client(server): + client = TestApp(server) + with pytest.raises(AppError, match=r".* 400 BAD REQUEST .*") as ex: + client.post('/v2/submit', { + 'client': 'xxx', + }) + ex.match(r'.*"invalid API key\b.*') + + +def test_submit_invalid_user(server): + client = TestApp(server) + with pytest.raises(AppError, match=r".* 400 BAD REQUEST .*") as ex: + client.post('/v2/submit', { + 'client': 'app1_api_key', + 'user': 'xxx', + }) + ex.match(r'.*"invalid user API key\b.*') + + +@pytest.mark.usefixtures("cleanup") +def test_submit(server): + client = TestApp(server) + resp = client.post('/v2/submit', { + 'client': 'app1_api_key', + 'user': 'user1_api_key', + 'fingerprint': TEST_2_FP, + 'duration': str(TEST_2_LENGTH), + 'mbid': '7b924948-200e-4517-921f-5eaaa9300db2', + 'puid': 'b2325721-1584-4afb-ac84-f265d80650c4', + 'foreignid': 'youtube:dN44xpHjNxE', + }) + assert resp.status_int == 200 + + with server.context() as ctx: + submissions = ctx.db.query(Submission).all() + assert len(submissions) == 1 + s = submissions[0] + assert s.fingerprint == TEST_2_FP_RAW + assert s.duration == TEST_2_LENGTH + assert s.mbid == '7b924948-200e-4517-921f-5eaaa9300db2' + assert s.puid == 'b2325721-1584-4afb-ac84-f265d80650c4' + assert s.foreignid == 'youtube:dN44xpHjNxE' + + assert resp.json == { + 'status': 'ok', + 'submissions': [ + { + 'id': s.id, + 'status': 'pending', + }, + ], + } + + resp = client.post('/v2/submission_status', { + 'client': 'app1_api_key', + 'id': str(s.id), + }) + assert resp.status_int == 200 + assert resp.json == { + 'status': 'ok', + 'submissions': [ + { + 'id': s.id, + 'status': 'pending', + }, + ], + } diff --git a/tests/test_web.py b/tests/test_web.py index bd0a2913636a0e801e3e19a8f9d4deb42d6cfdb8..cc71007e40b8aa1feca4a844ebbdcc59b1cceb0a 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -1,21 +1,126 @@ -from nose.tools import assert_equal -from acoustid.web import db -from tests import make_web_application +from webtest import TestApp -app = None +def test_index(web_app): + client = TestApp(web_app) + resp = client.get('/') + resp.mustcontain( + 'Welcome to AcoustID!', + 'Applications', + 'Statistics', + 'Documentation', + 'Donate', + 'Sign in', + ) -def setup(): - global app - app = make_web_application() - app.config['TESTING'] = True +def test_applications(web_app): + client = TestApp(web_app) + resp = client.get('/applications') + resp.mustcontain('MusicBrainz Picard') -def test_track_page(): - client = app.test_client() - rv = client.get('/track/eb31d1c3-950e-468b-9e36-e46fa75b1291') - assert_equal(rv.status_code, 200) - assert_equal(rv.data.count('Custom Track'), 1) - assert_equal(rv.data.count('Custom Artist'), 1) - assert not db.session.registry.has() +def test_stats(web_app): + client = TestApp(web_app) + resp = client.get('/stats') + resp.mustcontain( + 'Basic statistics', + 'Daily additions', + 'Searches', + 'AcoustIDs per the number of linked recordings', + 'Recordings per the number of linked AcoustIDs', + ) + + +def test_docs(web_app): + client = TestApp(web_app) + resp = client.get('/docs') + resp.mustcontain( + 'Documentation', + 'Frequently Asked Questions', + 'Web Service', + 'Server', + 'Chromaprint', + 'Fingerprinter', + ) + + +def test_faq(web_app): + client = TestApp(web_app) + resp = client.get('/faq') + resp.mustcontain('Frequently Asked Questions') + + +def test_webservice(web_app): + client = TestApp(web_app) + resp = client.get('/webservice') + resp.mustcontain( + 'Web Service', + 'Usage Guidelines', + 'Overview', + 'Compression', + 'API Keys', + 'Methods', + 'Lookup by fingerprint', + 'Lookup by track ID', + 'Submit', + 'Get submission status', + 'List AcoustIDs by MBID', + ) + + +def test_server(web_app): + client = TestApp(web_app) + resp = client.get('/server') + resp.mustcontain('Server') + + +def test_chromaprint(web_app): + client = TestApp(web_app) + resp = client.get('/chromaprint') + resp.mustcontain('Chromaprint', 'Download', 'Usage') + + +def test_donate(web_app): + client = TestApp(web_app) + resp = client.get('/donate') + resp.mustcontain('Donate Money') + + +def test_login(web_app): + client = TestApp(web_app) + resp = client.get('/login') + resp.mustcontain('Sign in') + + +def test_contact(web_app): + client = TestApp(web_app) + resp = client.get('/contact') + resp.mustcontain( + 'Contact us', + 'acoustid@googlegroups.com', + 'info@acoustid.org', + '#acoustid', + ) + + +def test_track(web_app): + client = TestApp(web_app) + + resp = client.get('/track/eb31d1c3-950e-468b-9e36-e46fa75b1291') + resp.mustcontain( + 'eb31d1c3-950e-468b-9e36-e46fa75b1291', + 'Track', + 'Custom Track', + 'Custom Artist', + ) + + +def test_fingerprint(web_app): + client = TestApp(web_app) + + resp = client.get('/fingerprint/1') + resp.mustcontain( + 'Fingerprint #1', + 'eb31d1c3-950e-468b-9e36-e46fa75b1291', + ) diff --git a/tox.ini b/tox.ini index f272f0c1eca9cab8e3d705e064c89e6aa6b56390..75ab1a67b69af35df4dc655e9c44bf0abfb96966 100644 --- a/tox.ini +++ b/tox.ini @@ -7,8 +7,10 @@ skipsdist = true deps = -rrequirements.txt -rrequirements_dev.txt -commands = nosetests -v -setenv = PYTHONHASHSEED = 123 +commands = py.test -v tests/ +setenv = + PYTHONHASHSEED = 123 + PYTHONPATH = {toxinidir} passenv = ACOUSTID_TEST_* [testenv:mypy-py2] @@ -29,6 +31,9 @@ skip_install = true deps = flake8 commands = flake8 acoustid/ tests/ +[pytest] +python_classes = !TestApp + [flake8] ignore = E128,E121 max-line-length = 160 @@ -36,3 +41,4 @@ per-file-ignores = tests/__init__.py:E501 tests/test_api_v1.py:E501 tests/test_pg.py:E231,E501 + tests/data.py:E501