Source

doqu / tests / base_query.py

Full commit
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Base for backend-specific query tests.

Not intended to be run itself.
"""
from datetime import date
import os
import unittest2 as unittest

from doqu import Document, get_db
from doqu.utils import is_doc_valid
from doqu import validators


__test__ = False  # tell nose to skip this module


class BaseQueryTestCase(unittest.TestCase):
    FIXTURE = {
        'john-id': {'name': u'John', 'last_name': u'Connor', 'age': 30,
                    'birth_date': date(1985,2,28)},
        'mary-id': {'name': u'Mary', 'last_name': u'Connor', 'age': 25}
    }
    TMP_FILENAME_EXTENSION = 'tmp'  # matters for Tokyo Cabinet

    #-- These should be overloaded:

    def get_connection(self):
        raise NotImplementedError('Backend test case must implement '
                                  'the method get_connection().')

    #-- All below till EOF should not be touched or overloaded

    @property
    def _tmp_filename(self):
        return os.path.abspath('doqu_tests_{modname}.{clsname}.{ext}'.format(
            modname = __name__,
            clsname = self.__class__.__name__,
            ext = self.TMP_FILENAME_EXTENSION
        ))

    def setUp(self):
        self.db = self.get_connection()
        self.db.clear()
        for pk, data in self.FIXTURE.items():
            self.db.save(pk, data)

    def tearDown(self):
        if self.db:
            self.db.clear()
            self.db.disconnect()
        if os.path.exists(self._tmp_filename):
            os.unlink(self._tmp_filename)

    def assert_finds(self, *names, **lookups):
        # asserts that given lookups yield all
        # people with given names and nobody else
        query = self.db.find(**lookups)
        self.assertEquals(sorted(names),
                          sorted(data['name'] for key, data in query))
        return query

    def assert_finds_nobody(self, **lookups):
        # syntax sugar
        return self.assert_finds(**lookups)

    #-- Actual tests

    def test_no_conditions(self):
        "Simple schema-bound query"
        self.assert_finds('John', 'Mary')

    # Note: docstrings are not included below because they would hide the
    # module name in test results (which is crucial to determine the exact
    # source of the error because the tests themselves are located here for all
    # backends)

    def test_op_between(self):
        # Operator `between` is inclusive.
        self.assert_finds('John', age__between=(26, 55))
        self.assert_finds('John', 'Mary', age__between=(25, 55))

    def test_op_contains(self):
        # Operator `contains` accepts literals and lists.
        self.assert_finds('John', name__contains='J')
        self.assert_finds('John', name__contains=['J', 'o'])
        self.assert_finds_nobody(name__contains=['J', 'M'])  # nobody

    def test_op_contains_any(self):
        self.assert_finds('John', name__contains_any=['J'])
        self.assert_finds('John', 'Mary', name__contains_any=['J','M'])

    def test_op_endswith(self):
        self.assert_finds('John', name__endswith='hn')
        self.assert_finds('Mary', name__endswith='y')

    def test_op_equals(self):
        "Items can be found by simple value comparison"
        self.assert_finds('John', name='John')
        self.assert_finds('Mary', age=25)
        self.assert_finds('John', 'Mary', last_name='Connor')

    def test_op_exists(self):
        self.assert_finds('John', 'Mary', name__exists=True)
        self.assert_finds_nobody(name__exists=False)
        self.assert_finds_nobody(whatchamacallit__exists=True)
        self.assert_finds('John', 'Mary', whatchamacallit__exists=False)

    def test_op_gt(self):
        # int
        self.assert_finds('John', age__gt=25)
        # date
        self.assert_finds('John', birth_date__gt=date(1985,2,1))
        self.assert_finds_nobody(birth_date__gt=date(1985,3,1))

    def test_op_gte(self):
        # int
        self.assert_finds('John', 'Mary', age__gte=25)
        # date
        self.assert_finds('John', birth_date__gte=date(1985,2,28))
        self.assert_finds_nobody(birth_date__gte=date(1985,3,1))

    def test_op_in(self):
        self.assert_finds('John', age__in=[29,30])
        self.assert_finds('Mary', age__in=[15,25])
        self.assert_finds('John', 'Mary', age__in=[25,29,30])

    def test_op_lt(self):
        # int
        self.assert_finds('Mary', age__lt=30)
        # date
        self.assert_finds('John', birth_date__lt=date(1985,3,1))
        self.assert_finds_nobody(birth_date__lt=date(1985,2,1))

    def test_op_lte(self):
        # int
        self.assert_finds('John', 'Mary', age__lte=30)
        # date
        self.assert_finds('John', birth_date__lte=date(1985,2,28))
        self.assert_finds_nobody(birth_date__lte=date(1985,2,27))

    def test_op_matches(self):
        self.assert_finds('Mary', name__matches='M.ry')
        self.assert_finds('Mary', name__matches='^M')
        self.assert_finds_nobody(name__matches='^m')

    def test_op_startswith(self):
        self.assert_finds('John', name__startswith='J')
        self.assert_finds('Mary', name__startswith='M')

    def test_op_year(self):
        self.assert_finds('John', birth_date__year=1985)
        self.assert_finds_nobody(birth_date__year=1777)

    def test_op_month(self):
        self.assert_finds('John', birth_date__month=2)
        self.assert_finds_nobody(birth_date__month=3)

    def test_op_day(self):
        self.assert_finds('John', birth_date__day=28)
        self.assert_finds_nobody(birth_date__day=29)

    def test_sorting(self):
        # string
        keys = [key for key, data in self.db.find().order_by('name')]
        self.assertEquals(keys, ['john-id', 'mary-id'])

        # numeric
        keys = [key for key, data in self.db.find().order_by('age')]
        self.assertEquals(keys, ['mary-id', 'john-id'])

    def test_sorting_reversed(self):
        # string
        keys = [key for key, data in
                self.db.find().order_by('name', reverse=True)]
        self.assertEquals(keys, ['mary-id', 'john-id'])

        # numeric
        keys = [key for key, data in
                self.db.find().order_by('age', reverse=True)]
        self.assertEquals(keys, ['john-id', 'mary-id'])

    def test_values(self):
        # TODO: check uniqueness!
        # TODO: optionally unwrap lists (see failing Dark tests)

        # string
        self.assertEquals(['John', 'Mary'],
                          sorted(self.db.find().values('name')))
        # string (distinct)
        self.assertEquals(['Connor'], list(self.db.find().values('last_name')))

        # numeric
        #   some backends (TC, TT) cannot restore the original datatype without
        #   metadata so we'll check if both dicts and Document instances
        #   behave correctly
        self.assertEquals(['25', '30'],
                          sorted(str(x) for x in
                                 self.db.find().values('age')))
        class Person(Document): structure = {'age': int}
        self.assertEquals([25, 30],
                          sorted(self.db.find(Person).values('age')))


if __name__ == '__main__':
    unittest.main()