From 6b044f18dbf18f75dd09ed354b1d60ff42b27c49 Mon Sep 17 00:00:00 2001 From: nolash Date: Fri, 2 Apr 2021 10:31:50 +0200 Subject: [PATCH] Initial commit --- .gitignore | 2 + chainqueue/db/__init__.py | 57 +++++++++ chainqueue/db/migrations/default/README | 1 + chainqueue/db/migrations/default/alembic.ini | 85 ++++++++++++ chainqueue/db/migrations/default/env.py | 77 +++++++++++ chainqueue/db/migrations/default/gmon.out | Bin 0 -> 1293597 bytes .../db/migrations/default/script.py.mako | 24 ++++ .../2215c497248b_transaction_cache.py | 38 ++++++ .../3e43847c0717_otx_state_history.py | 30 +++++ .../versions/c537a0fd8466_outgoing_queue.py | 35 +++++ chainqueue/db/models/base.py | 121 ++++++++++++++++++ requirements.txt | 4 + setup.cfg | 33 +++++ setup.py | 36 ++++++ tests/base.py | 83 ++++++++++++ tests/test_basic.py | 19 +++ 16 files changed, 645 insertions(+) create mode 100644 .gitignore create mode 100644 chainqueue/db/__init__.py create mode 100644 chainqueue/db/migrations/default/README create mode 100644 chainqueue/db/migrations/default/alembic.ini create mode 100644 chainqueue/db/migrations/default/env.py create mode 100644 chainqueue/db/migrations/default/gmon.out create mode 100644 chainqueue/db/migrations/default/script.py.mako create mode 100644 chainqueue/db/migrations/default/versions/2215c497248b_transaction_cache.py create mode 100644 chainqueue/db/migrations/default/versions/3e43847c0717_otx_state_history.py create mode 100644 chainqueue/db/migrations/default/versions/c537a0fd8466_outgoing_queue.py create mode 100644 chainqueue/db/models/base.py create mode 100644 requirements.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 tests/base.py create mode 100644 tests/test_basic.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d35cb3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +*.pyc diff --git a/chainqueue/db/__init__.py b/chainqueue/db/__init__.py new file mode 100644 index 0000000..5d74bff --- /dev/null +++ b/chainqueue/db/__init__.py @@ -0,0 +1,57 @@ +# standard imports +import os +import logging + +# local imports +from chainqueue.db.models.base import SessionBase + +logg = logging.getLogger().getChild(__name__) + + +# an Engine, which the Session will use for connection +# resources + + +def dsn_from_config(config): + """Generate a dsn string from the provided config dict. + + The config dict must include all well-known database connection parameters, and must implement the method "get(key)" to retrieve them. Any missing parameters will be be rendered as the literal string "None" + + :param config: Configuration object + :type config: Varies + :returns: dsn string + :rtype: str + """ + scheme = config.get('DATABASE_ENGINE') + if config.get('DATABASE_DRIVER') != None: + scheme += '+{}'.format(config.get('DATABASE_DRIVER')) + + dsn = '' + dsn_out = '' + if config.get('DATABASE_ENGINE') == 'sqlite': + dsn = '{}:///{}'.format( + scheme, + config.get('DATABASE_NAME'), + ) + dsn_out = dsn + + else: + dsn = '{}://{}:{}@{}:{}/{}'.format( + scheme, + config.get('DATABASE_USER'), + config.get('DATABASE_PASSWORD'), + config.get('DATABASE_HOST'), + config.get('DATABASE_PORT'), + config.get('DATABASE_NAME'), + ) + dsn_out = '{}://{}:{}@{}:{}/{}'.format( + scheme, + config.get('DATABASE_USER'), + '***', + config.get('DATABASE_HOST'), + config.get('DATABASE_PORT'), + config.get('DATABASE_NAME'), + ) + logg.debug('parsed dsn from config: {}'.format(dsn_out)) + return dsn + diff --git a/chainqueue/db/migrations/default/README b/chainqueue/db/migrations/default/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/chainqueue/db/migrations/default/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/chainqueue/db/migrations/default/alembic.ini b/chainqueue/db/migrations/default/alembic.ini new file mode 100644 index 0000000..ab88225 --- /dev/null +++ b/chainqueue/db/migrations/default/alembic.ini @@ -0,0 +1,85 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = . + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# timezone to use when rendering the date +# within the migration file as well as the filename. +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; this defaults +# to ./versions. When using multiple version +# directories, initial revisions must be specified with --version-path +# version_locations = %(here)s/bar %(here)s/bat ./versions + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks=black +# black.type=console_scripts +# black.entrypoint=black +# black.options=-l 79 + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/chainqueue/db/migrations/default/env.py b/chainqueue/db/migrations/default/env.py new file mode 100644 index 0000000..70518a2 --- /dev/null +++ b/chainqueue/db/migrations/default/env.py @@ -0,0 +1,77 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = None + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/chainqueue/db/migrations/default/gmon.out b/chainqueue/db/migrations/default/gmon.out new file mode 100644 index 0000000000000000000000000000000000000000..fc0391e6b78254a71a76cf76fd314416010ddc76 GIT binary patch literal 1293597 zcmeF)ci3ZPx$p6{hBinGQie`ufT0KyqzVXu8IW$MA~p05p`)}Q(p%^qLkB?(Sg=rn zh=6W1qS8wck&YR9_dL&CGHYe8Kh8c!_jcoVt_xq^U+$-@&#GA|?;iUduz#A2{3|~^ zoh0Wivbg-LZ$38JHT=JWciZKF{dYastB3!`1AZca00IagfB*srAbhc2(tlJ(^n(Bb2q1s}0tg_000IagfB*srAbcfXc>mIW5sY#K5I_I{1Q0*~0R;Yy1bDviZ?v*m zR0I$}009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_Kd zPo+Tmsaz@+3IPNVKmY**{uKrOw_SJqv*!EH>i1u9o&S5xi8cD~v7T8<1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009L4I|X1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009IL_^%4^{b~PIG3pUO009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R;ZF1k!)0>HKTW=3k12$^07%us{48u5=a|0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R;Xp1=9b^I?510009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009IL_(uz*BOOlted_-kDdATH5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009I(^#XkA{!_n- zSwI93KmdV%0Rhgp{{kz4DIkCV0tg_000IagfB*srAn;EX;Q9YQ8JO-7KmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0;8QG+eu|fkMMD4q1Q0*~0R#|0009ILKmY**5I_I{ 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 P<)xP@;)_e*Lx=w#NIY!w literal 0 HcmV?d00001 diff --git a/chainqueue/db/migrations/default/script.py.mako b/chainqueue/db/migrations/default/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/chainqueue/db/migrations/default/script.py.mako @@ -0,0 +1,24 @@ +"""${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)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/chainqueue/db/migrations/default/versions/2215c497248b_transaction_cache.py b/chainqueue/db/migrations/default/versions/2215c497248b_transaction_cache.py new file mode 100644 index 0000000..130239f --- /dev/null +++ b/chainqueue/db/migrations/default/versions/2215c497248b_transaction_cache.py @@ -0,0 +1,38 @@ +"""Transaction cache + +Revision ID: 2215c497248b +Revises: c537a0fd8466 +Create Date: 2021-04-02 10:09:11.923949 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '2215c497248b' +down_revision = 'c537a0fd8466' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + 'tx_cache', + sa.Column('id', sa.Integer, primary_key=True), + sa.Column('otx_id', sa.Integer, sa.ForeignKey('otx.id'), nullable=True), + sa.Column('date_created', sa.DateTime, nullable=False), + sa.Column('date_updated', sa.DateTime, nullable=False), + sa.Column('source_token_address', sa.String(42), nullable=False), + sa.Column('destination_token_address', sa.String(42), nullable=False), + sa.Column('sender', sa.String(42), nullable=False), + sa.Column('recipient', sa.String(42), nullable=False), + sa.Column('from_value', sa.NUMERIC(), nullable=False), + sa.Column('to_value', sa.NUMERIC(), nullable=True), + sa.Column('block_number', sa.BIGINT(), nullable=True), + sa.Column('tx_index', sa.Integer, nullable=True), + ) + + +def downgrade(): + op.drop_table('tx_cache') diff --git a/chainqueue/db/migrations/default/versions/3e43847c0717_otx_state_history.py b/chainqueue/db/migrations/default/versions/3e43847c0717_otx_state_history.py new file mode 100644 index 0000000..4b3dea3 --- /dev/null +++ b/chainqueue/db/migrations/default/versions/3e43847c0717_otx_state_history.py @@ -0,0 +1,30 @@ +"""Otx state history + +Revision ID: 3e43847c0717 +Revises: 2215c497248b +Create Date: 2021-04-02 10:10:58.656139 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '3e43847c0717' +down_revision = '2215c497248b' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + 'otx_state_log', + sa.Column('id', sa.Integer, primary_key=True), + sa.Column('otx_id', sa.Integer, sa.ForeignKey('otx.id'), nullable=False), + sa.Column('date', sa.DateTime, nullable=False), + sa.Column('status', sa.Integer, nullable=False), + ) + + +def downgrade(): + op.drop_table('otx_state_log') diff --git a/chainqueue/db/migrations/default/versions/c537a0fd8466_outgoing_queue.py b/chainqueue/db/migrations/default/versions/c537a0fd8466_outgoing_queue.py new file mode 100644 index 0000000..c2cea1c --- /dev/null +++ b/chainqueue/db/migrations/default/versions/c537a0fd8466_outgoing_queue.py @@ -0,0 +1,35 @@ +"""Outgoing queue + +Revision ID: c537a0fd8466 +Revises: +Create Date: 2021-04-02 10:04:27.092819 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'c537a0fd8466' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + 'otx', + sa.Column('id', sa.Integer, primary_key=True), + sa.Column('date_created', sa.DateTime, nullable=False), + sa.Column('nonce', sa.Integer, nullable=False), + sa.Column('tx_hash', sa.Text, nullable=False), + sa.Column('signed_tx', sa.Text, nullable=False), + sa.Column('status', sa.Integer, nullable=False, default=0), + sa.Column('block', sa.Integer), + ) + op.create_index('idx_otx_tx', 'otx', ['tx_hash'], unique=True) + + +def downgrade(): + op.drop_index('idx_otx_tx') + op.drop_table('otx') diff --git a/chainqueue/db/models/base.py b/chainqueue/db/models/base.py new file mode 100644 index 0000000..fc3541c --- /dev/null +++ b/chainqueue/db/models/base.py @@ -0,0 +1,121 @@ +# stanard imports +import logging + +# third-party imports +from sqlalchemy import Column, Integer +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import ( + StaticPool, + QueuePool, + AssertionPool, + ) + +logg = logging.getLogger() + +Model = declarative_base(name='Model') + + +class SessionBase(Model): + """The base object for all SQLAlchemy enabled models. All other models must extend this. + """ + __abstract__ = True + + id = Column(Integer, primary_key=True) + + engine = None + """Database connection engine of the running aplication""" + sessionmaker = None + """Factory object responsible for creating sessions from the connection pool""" + transactional = True + """Whether the database backend supports query transactions. Should be explicitly set by initialization code""" + poolable = True + """Whether the database backend supports connection pools. Should be explicitly set by initialization code""" + procedural = True + """Whether the database backend supports stored procedures""" + localsessions = {} + """Contains dictionary of sessions initiated by db model components""" + + + @staticmethod + def create_session(): + """Creates a new database session. + """ + return SessionBase.sessionmaker() + + + @staticmethod + def _set_engine(engine): + """Sets the database engine static property + """ + SessionBase.engine = engine + SessionBase.sessionmaker = sessionmaker(bind=SessionBase.engine) + + + @staticmethod + def connect(dsn, pool_size=16, debug=False): + """Create new database connection engine and connect to database backend. + + :param dsn: DSN string defining connection. + :type dsn: str + """ + e = None + if SessionBase.poolable: + poolclass = QueuePool + if pool_size > 1: + e = create_engine( + dsn, + max_overflow=pool_size*3, + pool_pre_ping=True, + pool_size=pool_size, + pool_recycle=60, + poolclass=poolclass, + echo=debug, + ) + else: + if debug: + poolclass = AssertionPool + else: + poolclass = StaticPool + + e = create_engine( + dsn, + poolclass=poolclass, + echo=debug, + ) + else: + e = create_engine( + dsn, + echo=debug, + ) + + SessionBase._set_engine(e) + + + @staticmethod + def disconnect(): + """Disconnect from database and free resources. + """ + SessionBase.engine.dispose() + SessionBase.engine = None + + + @staticmethod + def bind_session(session=None): + localsession = session + if localsession == None: + localsession = SessionBase.create_session() + localsession_key = str(id(localsession)) + logg.debug('creating new session {}'.format(localsession_key)) + SessionBase.localsessions[localsession_key] = localsession + return localsession + + + @staticmethod + def release_session(session=None): + session_key = str(id(session)) + if SessionBase.localsessions.get(session_key) != None: + logg.debug('commit and destroy session {}'.format(session_key)) + session.commit() + session.close() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ab46a9d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +pysha3==1.0.2 +hexathon~=0.0.1a7 +alembic==1.4.2 +SQLAlchemy==1.3.20 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..d35617a --- /dev/null +++ b/setup.cfg @@ -0,0 +1,33 @@ +[metadata] +name = chainqueue +version = 0.0.1a1 +description = Generic blockchain transaction queue control +author = Louis Holbrook +author_email = dev@holbrook.no +url = https://gitlab.com/nolash/chainlib +keywords = + cic + cryptocurrency + ethereum + solidarity + mutual_credit +classifiers = + Programming Language :: Python :: 3 + Operating System :: OS Independent + Development Status :: 3 - Alpha + Environment :: Console + Intended Audience :: Developers + License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) + Topic :: Internet +# Topic :: Blockchain :: EVM +license = GPL3 +licence_files = + LICENSE.txt + +[options] +python_requires = >= 3.6 +packages = + chainqueue + +#[options.entry_points] +#console_scripts = diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..153068a --- /dev/null +++ b/setup.py @@ -0,0 +1,36 @@ +from setuptools import setup +import configparser +import os + + +requirements = [] +f = open('requirements.txt', 'r') +while True: + l = f.readline() + if l == '': + break + requirements.append(l.rstrip()) +f.close() + +test_requirements = [] +f = open('test_requirements.txt', 'r') +while True: + l = f.readline() + if l == '': + break + test_requirements.append(l.rstrip()) +f.close() + +postgres_requirements = [ + 'psycopg2==2.8.6', + ] + requirements +sqlite_requirements = [ + ] + requirements +setup( + install_requires=requirements, + tests_require=test_requirements, + extras_require={ + 'postgres': postgres_requirements, + 'sqlite': sqlite_requirements, + } + ) diff --git a/tests/base.py b/tests/base.py new file mode 100644 index 0000000..2257faf --- /dev/null +++ b/tests/base.py @@ -0,0 +1,83 @@ +# standard imports +import logging +import unittest +import tempfile +import os +#import pysqlite + +# external imports +from chainlib.chain import ChainSpec +import alembic +import alembic.config + +# local imports +from chainqueue.db import dsn_from_config +from chainqueue.db.models.base import SessionBase + +script_dir = os.path.realpath(os.path.dirname(__file__)) + +logg = logging.getLogger().getChild(__name__) + + +class TestBase(unittest.TestCase): + + def setUp(self): + rootdir = os.path.dirname(os.path.dirname(__file__)) + dbdir = os.path.join(rootdir, 'chainqueue', 'db') + #migrationsdir = os.path.join(dbdir, 'migrations', load_config.get('DATABASE_ENGINE')) + #if not os.path.isdir(migrationsdir): + migrationsdir = os.path.join(dbdir, 'migrations', 'default') + logg.info('using migrations directory {}'.format(migrationsdir)) + +# db_dir = tempfile.mkdtemp() +# self.db_path = os.path.join(db_dir, 'test.sqlite') +# config = { +# 'DATABASE_ENGINE': 'sqlite', +# 'DATABASE_DRIVER': 'pysqlite', +# 'DATABASE_NAME': self.db_path, +# } + + config = { + 'DATABASE_ENGINE': 'sqlite', + 'DATABASE_DRIVER': 'pysqlite', + 'DATABASE_NAME': 'chainqueue.sqlite', + } + logg.debug('config {}'.format(config)) + + dsn = dsn_from_config(config) + SessionBase.poolable = False + SessionBase.transactional = False + SessionBase.procedural = False + SessionBase.connect(dsn, debug=False) + + ac = alembic.config.Config(os.path.join(migrationsdir, 'alembic.ini')) + ac.set_main_option('sqlalchemy.url', dsn) + ac.set_main_option('script_location', migrationsdir) + + alembic.command.downgrade(ac, 'base') + alembic.command.upgrade(ac, 'head') + + + + self.session = SessionBase.create_session() +# +# f = open(os.path.join(script_dir, '..', 'sql', 'sqlite', '1.sql'), 'r') +# sql = f.read() +# f.close() +# +# conn = SessionBase.engine.connect() +# conn.execute(sql) +# +# f = open(os.path.join(script_dir, '..', 'sql', 'sqlite', '2.sql'), 'r') +# sql = f.read() +# f.close() +# +# conn = SessionBase.engine.connect() +# conn.execute(sql) +# + self.chain_spec = ChainSpec('evm', 'foo', 42, 'bar') + + + def tearDown(self): + self.session.commit() + self.session.close() diff --git a/tests/test_basic.py b/tests/test_basic.py new file mode 100644 index 0000000..a1c458a --- /dev/null +++ b/tests/test_basic.py @@ -0,0 +1,19 @@ +# standard imports +import logging +import unittest + +# test imports +from tests.base import TestBase + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +class TestBasic(TestBase): + + def test_hello(self): + logg.debug('foo') + pass + + +if __name__ == '__main__': + unittest.main()