# Path: electrumx/server/storage.py
# class Storage(object):
#     '''Abstract base class of the DB backend abstraction.'''
# 
#     def __init__(self, name, for_sync):
#         self.is_new = not os.path.exists(name)
#         self.for_sync = for_sync or self.is_new
#         self.open(name, create=self.is_new)
# 
#     @classmethod
#     def import_module(cls):
#         '''Import the DB engine module.'''
#         raise NotImplementedError
# 
#     def open(self, name, create):
#         '''Open an existing database or create a new one.'''
#         raise NotImplementedError
# 
#     def close(self):
#         '''Close an existing database.'''
#         raise NotImplementedError
# 
#     def get(self, key):
#         raise NotImplementedError
# 
#     def put(self, key, value):
#         raise NotImplementedError
# 
#     def write_batch(self):
#         '''Return a context manager that provides `put` and `delete`.
# 
#         Changes should only be committed when the context manager
#         closes without an exception.
#         '''
#         raise NotImplementedError
# 
#     def iterator(self, prefix=b'', reverse=False):
#         '''Return an iterator that yields (key, value) pairs from the
#         database sorted by key.
# 
#         If `prefix` is set, only keys starting with `prefix` will be
#         included.  If `reverse` is True the items are returned in
#         reverse order.
#         '''
#         raise NotImplementedError
# 
# def db_class(name):
#     '''Returns a DB engine class.'''
#     for db_class_ in util.subclasses(Storage):
#         if db_class_.__name__.lower() == name.lower():
#             db_class_.import_module()
#             return db_class_
#     raise RuntimeError('unrecognised DB engine "{}"'.format(name))
# 
# Path: electrumx/lib/util.py
# def subclasses(base_class, strict=True):
#     '''Return a list of subclasses of base_class in its module.'''
#     def select(obj):
#         return (inspect.isclass(obj) and issubclass(obj, base_class)
#                 and (not strict or obj != base_class))
# 
#     pairs = inspect.getmembers(sys.modules[base_class.__module__], select)
#     return [pair[1] for pair in pairs]


import pytest
import os
from electrumx.server.storage import Storage, db_class
from electrumx.lib.util import subclasses
def test_iterator(db):
    """
    The iterator should contain all key/value pairs starting with prefix
    ordered by key.
    """
    for i in range(5):
        db.put(b"abc" + str.encode(str(i)), str.encode(str(i)))
    db.put(b"abc", b"")
    db.put(b"a", b"xyz")
    db.put(b"abd", b"x")
    assert list(db.iterator(prefix=b"abc")) == [(b"abc", b"")] + [
            (b"abc" + str.encode(str(i)), str.encode(str(i))) for
            i in range(5)
        ]


def test_iterator_reverse(db):
    for i in range(5):
        db.put(b"abc" + str.encode(str(i)), str.encode(str(i)))
    db.put(b"a", b"xyz")
    db.put(b"abd", b"x")
    assert list(db.iterator(prefix=b"abc", reverse=True)) == [
            (b"abc" + str.encode(str(i)), str.encode(str(i))) for
            i in reversed(range(5))
        ]


def test_close(db):
    db.put(b"a", b"b")
    db.close()

# Path: electrumx/server/storage.py
# class Storage(object):
#     '''Abstract base class of the DB backend abstraction.'''
# 
#     def __init__(self, name, for_sync):
#         self.is_new = not os.path.exists(name)
#         self.for_sync = for_sync or self.is_new
#         self.open(name, create=self.is_new)
# 
#     @classmethod
#     def import_module(cls):
#         '''Import the DB engine module.'''
#         raise NotImplementedError
# 
#     def open(self, name, create):
#         '''Open an existing database or create a new one.'''
#         raise NotImplementedError
# 
#     def close(self):
#         '''Close an existing database.'''
#         raise NotImplementedError
# 
#     def get(self, key):
#         raise NotImplementedError
# 
#     def put(self, key, value):
#         raise NotImplementedError
# 
#     def write_batch(self):
#         '''Return a context manager that provides `put` and `delete`.
# 
#         Changes should only be committed when the context manager
#         closes without an exception.
#         '''
#         raise NotImplementedError
# 
#     def iterator(self, prefix=b'', reverse=False):
#         '''Return an iterator that yields (key, value) pairs from the
#         database sorted by key.
# 
#         If `prefix` is set, only keys starting with `prefix` will be
#         included.  If `reverse` is True the items are returned in
#         reverse order.
#         '''
#         raise NotImplementedError
# 
# def db_class(name):
#     '''Returns a DB engine class.'''
#     for db_class_ in util.subclasses(Storage):
#         if db_class_.__name__.lower() == name.lower():
#             db_class_.import_module()
#             return db_class_
#     raise RuntimeError('unrecognised DB engine "{}"'.format(name))
# 
# Path: electrumx/lib/util.py
# def subclasses(base_class, strict=True):
#     '''Return a list of subclasses of base_class in its module.'''
#     def select(obj):
#         return (inspect.isclass(obj) and issubclass(obj, base_class)
#                 and (not strict or obj != base_class))
# 
#     pairs = inspect.getmembers(sys.modules[base_class.__module__], select)
#     return [pair[1] for pair in pairs]


# Path: tests/server/test_storage.py
import pytest
import os
from electrumx.server.storage import Storage, db_class
from electrumx.lib.util import subclasses

def test_iterator(db):
    """
    The iterator should contain all key/value pairs starting with prefix
    ordered by key.
    """
    for i in range(5):
        db.put(b"abc" + str.encode(str(i)), str.encode(str(i)))
    db.put(b"abc", b"")
    db.put(b"a", b"xyz")
    db.put(b"abd", b"x")
    assert list(db.iterator(prefix=b"abc")) == [(b"abc", b"")] + [
            (b"abc" + str.encode(str(i)), str.encode(str(i))) for
            i in range(5)
        ]


def test_iterator_reverse(db):
    for i in range(5):
        db.put(b"abc" + str.encode(str(i)), str.encode(str(i)))
    db.put(b"a", b"xyz")
    db.put(b"abd", b"x")
    assert list(db.iterator(prefix=b"abc", reverse=True)) == [
            (b"abc" + str.encode(str(i)), str.encode(str(i))) for
            i in reversed(range(5))
        ]


def test_close(db):
    db.put(b"a", b"b")
    db.close()

    db = db_class(db.__class__.__name__)("db", False)