# Path: cliche/orm.py
# def downgrade_database(engine, revision):
#     """Reverts to a previous ``revision``.
# 
#     :param engine: the database engine to revert
#     :type engine: :class:`sqlalchemy.engine.base.Engine`
#     :param revision: the previous revision to revert to
#     :type revision: :class:`str`
# 
#     """
#     config = get_alembic_config(engine)
#     downgrade(config, revision)
# 
# def get_database_revision(engine):
#     """Gets the current revision of the database.
# 
#     :param engine: the database engine to get the current revision
#     :type engine: :class:`sqlalchemy.engine.base.Engine`
#     :returns: the script of the current revision
#     :rtype: :class:`alembic.script.Script`
# 
#     """
#     config = get_alembic_config(engine)
#     script = ScriptDirectory.from_config(config)
#     result = [None]
# 
#     def get_revision(rev, context):
#         result[0] = rev and script.get_revision(rev)
#         return []
#     with EnvironmentContext(config, script, fn=get_revision, as_sql=False,
#                             destination_rev=None, tag=None):
#         script.run_env()
#     return result[0]
# 
# def import_all_modules(dry_run=False):  # FIXME
#     """Import all submodules of :mod:`cliche` to ensure every ORM
#     entity classes are ready to use.  It's useful for being ready to
#     auto-generate a migration script.
# 
#     :return: the set of module names
#     :rtype: :class:`collections.abc.Set`
# 
#     """
#     import cliche.celery
#     import cliche.cli
#     import cliche.config
#     import cliche.name
#     import cliche.orm
#     import cliche.people
#     import cliche.services
#     import cliche.services.tvtropes
#     import cliche.services.tvtropes.crawler
#     import cliche.services.tvtropes.entities
#     import cliche.services.wikipedia
#     import cliche.services.wikipedia.crawler
#     import cliche.services.wikipedia.work
#     import cliche.sqltypes
#     import cliche.web
#     import cliche.web.app
#     import cliche.web.db
#     import cliche.web.ontology
#     import cliche.work  # noqa
#     return frozenset([
#         'cliche.celery',
#         'cliche.cli',
#         'cliche.config',
#         'cliche.name',
#         'cliche.orm',
#         'cliche.people',
#         'cliche.services',
#         'cliche.services.tvtropes',
#         'cliche.services.tvtropes.crawler',
#         'cliche.services.tvtropes.entities',
#         'cliche.services.wikipedia',
#         'cliche.services.wikipedia.crawler',
#         'cliche.services.wikipedia.work',
#         'cliche.sqltypes',
#         'cliche.web',
#         'cliche.web.app',
#         'cliche.web.db',
#         'cliche.web.ontology',
#         'cliche.work'
#     ])
# 
#     # current_dir = os.path.join(os.path.dirname(__file__), '..')
#     # modules = frozenset(mod
#     #                     for _, mod, __ in pkgutil.walk_packages(current_dir)
#     #                     if mod.startswith('cliche.'))
#     # for mod in modules:
#     #     __import__(mod)
#     # return modules
# 
# def initialize_database(engine):
#     """Creates all database schemas and stamps it as the head of versions.
# 
#     :param engine: the database engine to initialize
#     :type engine: :class:`sqlalchemy.engine.base.Engine`
# 
#     """
#     import_all_modules()
#     Base.metadata.create_all(engine, checkfirst=False)
#     alembic_cfg = get_alembic_config(engine)
#     stamp(alembic_cfg, 'head')
# 
# def upgrade_database(engine, revision='head'):
#     """Upgrades the database schema to the chosen ``revision`` (default is
#     head).
# 
#     :param engine: the database engine to upgrade
#     :type engine: :class:`sqlalchemy.engine.base.Engine`
#     :param revision: the revision to upgrade to.  default is ``'head'``
#     :type revision: :class:`str`
# 
#     """
#     config = get_alembic_config(engine)
#     script = ScriptDirectory.from_config(config)
# 
#     def upgrade(rev, context):
#         def update_current_rev(old, new):
#             if old == new:
#                 return
#             if new is None:
#                 context.impl._exec(context._version.delete())
#             elif old is None:
#                 context.impl._exec(
#                     context._version.insert().values(
#                         version_num=literal_column("'%s'" % new)
#                     )
#                 )
#             else:
#                 context.impl._exec(
#                     context._version.update().values(
#                         version_num=literal_column("'%s'" % new)
#                     )
#                 )
# 
#         if not rev and revision == 'head':
#             import_all_modules()
#             Base.metadata.create_all(engine)
#             dest = script.get_revision(revision)
#             update_current_rev(None, dest and dest.revision)
#             return []
#         return script._upgrade_revs(revision, rev)
#     with EnvironmentContext(config, script, fn=upgrade, as_sql=False,
#                             destination_rev=revision, tag=None):
#         script.run_env()


import functools
import os
import sys
import tempfile
from pytest import fixture
from sqlalchemy import create_engine
from cliche.orm import (downgrade_database, get_database_revision,
                        import_all_modules, initialize_database,
                        upgrade_database)




@fixture
def tmp_engine(request):
    """An empty SQLite database."""
    _, filename = tempfile.mkstemp()
    engine = create_engine('sqlite:///' + filename)
    request.addfinalizer(functools.partial(os.remove, filename))
    return engine


def test_initialize(tmp_engine):
    initialize_database(tmp_engine)
    script = get_database_revision(tmp_engine)
    assert script.is_head


def test_upgrade_downgrade(tmp_engine):
    rev = '27e81ea4d86'
    upgrade_database(tmp_engine, rev)
    script = get_database_revision(tmp_engine)
    assert not script.is_head
    assert script.revision == rev

# Path: cliche/orm.py
# def downgrade_database(engine, revision):
#     """Reverts to a previous ``revision``.
# 
#     :param engine: the database engine to revert
#     :type engine: :class:`sqlalchemy.engine.base.Engine`
#     :param revision: the previous revision to revert to
#     :type revision: :class:`str`
# 
#     """
#     config = get_alembic_config(engine)
#     downgrade(config, revision)
# 
# def get_database_revision(engine):
#     """Gets the current revision of the database.
# 
#     :param engine: the database engine to get the current revision
#     :type engine: :class:`sqlalchemy.engine.base.Engine`
#     :returns: the script of the current revision
#     :rtype: :class:`alembic.script.Script`
# 
#     """
#     config = get_alembic_config(engine)
#     script = ScriptDirectory.from_config(config)
#     result = [None]
# 
#     def get_revision(rev, context):
#         result[0] = rev and script.get_revision(rev)
#         return []
#     with EnvironmentContext(config, script, fn=get_revision, as_sql=False,
#                             destination_rev=None, tag=None):
#         script.run_env()
#     return result[0]
# 
# def import_all_modules(dry_run=False):  # FIXME
#     """Import all submodules of :mod:`cliche` to ensure every ORM
#     entity classes are ready to use.  It's useful for being ready to
#     auto-generate a migration script.
# 
#     :return: the set of module names
#     :rtype: :class:`collections.abc.Set`
# 
#     """
#     import cliche.celery
#     import cliche.cli
#     import cliche.config
#     import cliche.name
#     import cliche.orm
#     import cliche.people
#     import cliche.services
#     import cliche.services.tvtropes
#     import cliche.services.tvtropes.crawler
#     import cliche.services.tvtropes.entities
#     import cliche.services.wikipedia
#     import cliche.services.wikipedia.crawler
#     import cliche.services.wikipedia.work
#     import cliche.sqltypes
#     import cliche.web
#     import cliche.web.app
#     import cliche.web.db
#     import cliche.web.ontology
#     import cliche.work  # noqa
#     return frozenset([
#         'cliche.celery',
#         'cliche.cli',
#         'cliche.config',
#         'cliche.name',
#         'cliche.orm',
#         'cliche.people',
#         'cliche.services',
#         'cliche.services.tvtropes',
#         'cliche.services.tvtropes.crawler',
#         'cliche.services.tvtropes.entities',
#         'cliche.services.wikipedia',
#         'cliche.services.wikipedia.crawler',
#         'cliche.services.wikipedia.work',
#         'cliche.sqltypes',
#         'cliche.web',
#         'cliche.web.app',
#         'cliche.web.db',
#         'cliche.web.ontology',
#         'cliche.work'
#     ])
# 
#     # current_dir = os.path.join(os.path.dirname(__file__), '..')
#     # modules = frozenset(mod
#     #                     for _, mod, __ in pkgutil.walk_packages(current_dir)
#     #                     if mod.startswith('cliche.'))
#     # for mod in modules:
#     #     __import__(mod)
#     # return modules
# 
# def initialize_database(engine):
#     """Creates all database schemas and stamps it as the head of versions.
# 
#     :param engine: the database engine to initialize
#     :type engine: :class:`sqlalchemy.engine.base.Engine`
# 
#     """
#     import_all_modules()
#     Base.metadata.create_all(engine, checkfirst=False)
#     alembic_cfg = get_alembic_config(engine)
#     stamp(alembic_cfg, 'head')
# 
# def upgrade_database(engine, revision='head'):
#     """Upgrades the database schema to the chosen ``revision`` (default is
#     head).
# 
#     :param engine: the database engine to upgrade
#     :type engine: :class:`sqlalchemy.engine.base.Engine`
#     :param revision: the revision to upgrade to.  default is ``'head'``
#     :type revision: :class:`str`
# 
#     """
#     config = get_alembic_config(engine)
#     script = ScriptDirectory.from_config(config)
# 
#     def upgrade(rev, context):
#         def update_current_rev(old, new):
#             if old == new:
#                 return
#             if new is None:
#                 context.impl._exec(context._version.delete())
#             elif old is None:
#                 context.impl._exec(
#                     context._version.insert().values(
#                         version_num=literal_column("'%s'" % new)
#                     )
#                 )
#             else:
#                 context.impl._exec(
#                     context._version.update().values(
#                         version_num=literal_column("'%s'" % new)
#                     )
#                 )
# 
#         if not rev and revision == 'head':
#             import_all_modules()
#             Base.metadata.create_all(engine)
#             dest = script.get_revision(revision)
#             update_current_rev(None, dest and dest.revision)
#             return []
#         return script._upgrade_revs(revision, rev)
#     with EnvironmentContext(config, script, fn=upgrade, as_sql=False,
#                             destination_rev=revision, tag=None):
#         script.run_env()


# Path: tests/orm_test.py
import functools
import os
import sys
import tempfile
from pytest import fixture
from sqlalchemy import create_engine
from cliche.orm import (downgrade_database, get_database_revision,
                        import_all_modules, initialize_database,
                        upgrade_database)





@fixture
def tmp_engine(request):
    """An empty SQLite database."""
    _, filename = tempfile.mkstemp()
    engine = create_engine('sqlite:///' + filename)
    request.addfinalizer(functools.partial(os.remove, filename))
    return engine


def test_initialize(tmp_engine):
    initialize_database(tmp_engine)
    script = get_database_revision(tmp_engine)
    assert script.is_head


def test_upgrade_downgrade(tmp_engine):
    rev = '27e81ea4d86'
    upgrade_database(tmp_engine, rev)
    script = get_database_revision(tmp_engine)
    assert not script.is_head
    assert script.revision == rev

    downgrade_database(tmp_engine, 'base')