diff --git a/.travis.yml b/.travis.yml index ce213f91..c411e2ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,8 @@ env: - DB=mysql://root:@localhost/test_w2p - DB=postgres://postgres:@localhost/test_w2p - DB=google:datastore + - DB=mongodb://mongodb:mongodb@localhost/test_w2p + - DB=imap://imap:imap@localhost:993 before_script: - if [[ $TRAVIS_PYTHON_VERSION != '2.7' ]]; then pip install unittest2; fi - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install coverage; fi; @@ -29,6 +31,9 @@ before_script: - if [[ $DB == google* ]]; then unzip -q google_appengine_$GAERELEASE.zip; fi - if [[ $DB == google* ]]; then mv -f ./google_appengine/google ./google; fi + - if [[ $DB == mongodb* ]]; then pip install pymongo; fi + - if [[ $DB == mongodb* ]]; then mongo test_w2p --eval 'db.addUser("mongodb", "mongodb");'; fi + #Temporal solution to travis issue #155 - sudo chmod 777 /dev/shm - sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm @@ -43,6 +48,7 @@ matrix: - python: '2.6' env: DB=google:datastore + script: export COVERAGE_PROCESS_START=gluon/tests/coverage.ini; ./web2py.py --run_system_tests --with_coverage after_success: - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then coverage combine; fi @@ -51,3 +57,5 @@ after_success: notifications: email: true +services: mongodb + diff --git a/gluon/contrib/mockimaplib.py b/gluon/contrib/mockimaplib.py new file mode 100644 index 00000000..87f8b479 --- /dev/null +++ b/gluon/contrib/mockimaplib.py @@ -0,0 +1,255 @@ +# -*- encoding: utf-8 -*- + +from imaplib import ParseFlags + +# mockimaplib: A very simple mock server module for imap client APIs +# Copyright (C) 2014 Alan Etkin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or(at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# + +""" +mockimaplib allows you to test applications connecting to a dummy imap +service. For more details on the api subset implemented, +refer to the imaplib docs. + +The client should configure a dictionary to map imap string queries to sets +of entries stored in a message dummy storage dictionary. The module includes +a small set of default message records (SPAM and MESSAGES), two mailboxes +(Draft and INBOX) and a list of query/resultset entries (RESULTS). + +Usage: + +>>> import mockimaplib +>>> connection = mockimaplib.IMAP4_SSL() +>>> connection.login(, ) +None +>>> connection.select("INBOX") +("OK", ... ) +# fetch commands specifying single uid or message id +# will try to get messages recorded in SPAM +>>> connection.uid(...) + +# returns a string list of matching message ids +>>> connection.search() +("OK", ... "1 2 ... n") +""" + +MESSAGES = ( +'MIME-Version: 1.0\r\nReceived: by 10.140.91.199 with HTTP; Mon, 27 Jan 2014 13:52:30 -0800 (PST)\r\nDate: Mon, 27 Jan 2014 19:52:30 -0200\r\nDelivered-To: nurse@example.com\r\nMessage-ID: <10101010101010010000010101010001010101001010010000001@mail.example.com>\r\nSubject: spam1\r\nFrom: Mr. Gumby \r\nTo: The nurse \r\nContent-Type: text/plain; charset=ISO-8859-1\r\n\r\nNurse!\r\n\r\n\r\n', +'MIME-Version: 1.0\r\nReceived: by 10.140.91.199 with HTTP; Mon, 27 Jan 2014 13:52:47 -0800 (PST)\r\nDate: Mon, 27 Jan 2014 19:52:47 -0200\r\nDelivered-To: nurse@example.com\r\nMessage-ID: <101010101010100100000101010100010101010010100100000010@mail.example.com>\r\nSubject: spam2\r\nFrom: Mr. Gumby \r\nTo: The nurse \r\nContent-Type: text/plain; charset=ISO-8859-1\r\n\r\nNurse, nurse!', +'MIME-Version: 1.0\r\nReceived: by 10.140.91.199 with HTTP; Mon, 27 Jan 2014 13:54:54 -0800 (PST)\r\nDate: Mon, 27 Jan 2014 19:54:54 -0200\r\nDelivered-To: nurse@example.com\r\nMessage-ID: <1010101010101001000001010101000101010100101001000000101@mail.example.com>\r\nSubject: spamalot1\r\nFrom: Mr. Gumby \r\nTo: The nurse \r\nContent-Type: text/plain; charset=ISO-8859-1\r\n\r\nNurse!\r\n\r\n\r\n', +'MIME-Version: 1.0\r\n\r\nReceived: by 10.140.91.199 with HTTP; Mon, 27 Jan 2014 13:54:54 -0800 (PST)\r\nDate: Mon, 27 Jan 2014 19:54:54 -0200\r\nDelivered-To: nurse@example.com\r\nMessage-ID: <101010101010100100000101010100010101010010100100000010101@mail.example.com>\r\nSubject: spamalot2\r\nFrom: Mr. Gumby \r\nTo: The nurse \r\nContent-Type: text/plain; charset=ISO-8859-1\r\n\r\nNurse! ... Nurse! ... Nurse!\r\n\r\n\r\n') + +SPAM = { + "INBOX": [ + {"uid": "483209", + "headers": MESSAGES[0], + "complete": MESSAGES[0], + "flags": ""}, + {"uid": "483211", + "headers": MESSAGES[1], + "complete": MESSAGES[1], + "flags": ""}, + {"uid": "483225", + "headers": MESSAGES[2], + "complete": MESSAGES[2], + "flags": ""}], + "Draft":[ + {"uid": "483432", + "headers": MESSAGES[3], + "complete": MESSAGES[3], + "flags": ""},] +} + +RESULTS = { + # : [ | , ...] + "INBOX": { + "(ALL)": (1, 2, 3), + "(1:3)": (1, 2, 3)}, + "Draft": { + "(1:1)": (1,)}, +} + +class Connection(object): + """Dummy connection object for the imap client. + By default, uses the module SPAM and RESULT + sets (use Connection.setup for custom values)""" + def login(self, user, password): + pass + + def __init__(self): + self._readonly = False + self._mailbox = None + self.setup() + + def list(self): + return ('OK', ['(\\HasNoChildren) "/" "%s"' % key for key in self.spam]) + + def select(self, tablename, readonly=False): + self._readonly = readonly + """args: mailbox, boolean + result[1][0] -> int last message id / mailbox lenght + result[0] = 'OK' + """ + self._mailbox = tablename + return ('OK', (len(SPAM[self._mailbox]), None)) + + def uid(self, command, uid, arg): + """ args: + command: "search" | "fetch" + uid: None | uid + parts: "(ALL)" | "(RFC822 FLAGS)" | "(RFC822.HEADER FLAGS)" + + "search", None, "(ALL)" -> ("OK", ("uid_1 uid_2 ... uid_", None)) + "search", None, "" -> ("OK", ("uid_1 uid_2 ... uid_n", None)) + "fetch", uid, parts -> ("OK", ((" ...", ""), "") + [0] [1][0][0] [1][0][1] [1][1] + """ + if command == "search": + return self._search(arg) + elif command == "fetch": + return self._fetch(uid, arg) + + def _search(self, query): + return ("OK", (" ".join([str(item["uid"]) for item in self._get_messages(query)]), None)) + + def _fetch(self, value, arg): + try: + message = self.spam[self._mailbox][value - 1] + message_id = value + except TypeError: + for x, item in enumerate(self.spam[self._mailbox]): + if item["uid"] == value: + message = item + message_id = x + 1 + break + + parts = "headers" + if arg in ("(ALL)", "(RFC822 FLAGS)"): + parts = "complete" + + return ("OK", (("%s " % message_id, message[parts]), message["flags"])) + + def _get_messages(self, query): + if query.strip().isdigit(): + return [self.spam[self._mailbox][int(query.strip()) - 1],] + elif query[1:-1].strip().isdigit(): + return [self.spam[self._mailbox][int(query[1:-1].strip()) -1],] + elif query[1:-1].replace("UID", "").strip().isdigit(): + for item in self.spam[self._mailbox]: + if item["uid"] == query[1:-1].replace("UID", "").strip(): + return [item,] + messages = [] + try: + for m in self.results[self._mailbox][query]: + try: + self.spam[self._mailbox][m - 1]["id"] = m + messages.append(self.spam[self._mailbox][m - 1]) + except TypeError: + for x, item in enumerate(self.spam[self._mailbox]): + if item["uid"] == m: + item["id"] = x + 1 + messages.append(item) + break + except IndexError: + # message removed + pass + return messages + except KeyError: + raise ValueError("The client issued an unexpected query: %s" % query) + + def setup(self, spam={}, results={}): + """adds custom message and query databases or sets + the values to the module defaults. + """ + + self.spam = spam + self.results = results + if not spam: + for key in SPAM: + self.spam[key] = [] + for d in SPAM[key]: + self.spam[key].append(d.copy()) + if not results: + for key in RESULTS: + self.results[key] = RESULTS[key].copy() + + + def search(self, first, query): + """ args: + first: None + query: string with mailbox query (flags, date, uid, id, ...) + example: '2:15723 BEFORE 27-Jan-2014 FROM "gumby"' + result[1][0] -> "id_1 id_2 ... id_n" + """ + messages = self._get_messages(query) + ids = " ".join([str(item["id"]) for item in messages]) + return ("OK", (ids, None)) + + def append(self, mailbox, flags, struct_time, message): + """ + result, data = self.connection.append(mailbox, flags, struct_time, message) + if result == "OK": + uid = int(re.findall("\d+", str(data))[-1]) + """ + last = self.spam[mailbox][-1] + try: + uid = int(last["uid"]) +1 + except ValueError: + alluids = [] + for _mailbox in self.spam.keys(): + for item in self.spam[_mailbox]: + try: + alluids.append(int(item["uid"])) + except: + pass + if len(alluids) > 0: + uid = max(alluids) + 1 + else: + uid = 1 + flags = "FLAGS " + flags + item = {"uid": str(uid), "headers": message, "complete": message, "flags": flags} + self.spam[mailbox].append(item) + return ("OK", "spam spam %s spam" % uid) + + + def store(self, *args): + """ + implements some flag commands + args: ("", "<+|->FLAGS", "(\\Flag1 \\Flag2 ... \\Flagn)") + """ + message = self.spam[self._mailbox][int(args[0] - 1)] + old_flags = ParseFlags(message["flags"]) + flags = ParseFlags("FLAGS" + args[2]) + if args[1].strip().startswith("+"): + message["flags"] = "FLAGS (%s)" % " ".join(set(flags + old_flags)) + elif args[1].strip().startswith("-"): + message["flags"] = "FLAGS (%s)" % " ".join([flag for flag in old_flags if not flag in flags]) + + def expunge(self): + """implements removal of deleted flag messages""" + for x, item in enumerate(self.spam[self._mailbox]): + if "\\Deleted" in item["flags"]: + self.spam[self._mailbox].pop(x) + + +class IMAP4(object): + """>>> connection = IMAP4() # creates the dummy imap4 client object""" + def __new__(self, *args, **kwargs): + # args: (server, port) + return Connection() + +IMAP4_SSL = IMAP4 + diff --git a/gluon/tests/__init__.py b/gluon/tests/__init__.py index 9b4c15c9..fbc12288 100644 --- a/gluon/tests/__init__.py +++ b/gluon/tests/__init__.py @@ -3,7 +3,9 @@ import os, sys from test_http import * from test_cache import * -if "google" in (os.getenv("DB") or []): +NOSQL = any([name in (os.getenv("DB") or "") + for name in ("datastore", "mongodb", "imap")]) +if NOSQL: from test_dal_nosql import * else: from test_dal import * diff --git a/gluon/tests/test_dal_nosql.py b/gluon/tests/test_dal_nosql.py index 4532bc22..a4eb0dad 100644 --- a/gluon/tests/test_dal_nosql.py +++ b/gluon/tests/test_dal_nosql.py @@ -48,27 +48,36 @@ def fix_sys_path(): fix_sys_path() -from dal import DAL, Field, Table, SQLALL - #for travis-ci DEFAULT_URI = os.environ.get('DB', 'sqlite:memory') print 'Testing against %s engine (%s)' % (DEFAULT_URI.partition(':')[0], DEFAULT_URI) IS_GAE = "datastore" in DEFAULT_URI +IS_MONGODB = "mongodb" in DEFAULT_URI +IS_IMAP = "imap" in DEFAULT_URI + +if IS_IMAP: + from dal import IMAPAdapter + from contrib import mockimaplib + IMAPAdapter.driver = mockimaplib + +from dal import DAL, Field, Table, SQLALL def drop(table, cascade=None): - if not IS_GAE: - if cascade: - table.drop(cascade) - else: - table.drop() - else: + # mongodb implements drop() + # although it seems it does not work properly + if (IS_GAE or IS_MONGODB or IS_IMAP): # GAE drop/cleanup is not implemented db = table._db db(table).delete() del db[table._tablename] del db.tables[db.tables.index(table._tablename)] db._remove_references_to(table) + else: + if cascade: + table.drop(cascade) + else: + table.drop() # setup GAE dummy database @@ -104,7 +113,7 @@ def tearDownModule(): for a in glob.glob('*.table'): os.unlink(a) -@unittest.skipIf(IS_GAE, 'TODO: Datastore throws "AssertionError: SyntaxError not raised"') +@unittest.skipIf(IS_GAE or IS_IMAP, 'TODO: Datastore throws "AssertionError: SyntaxError not raised"') class TestFields(unittest.TestCase): def testFieldName(self): @@ -157,7 +166,7 @@ class TestFields(unittest.TestCase): else: isinstance(f.formatter(datetime.datetime.now()), str) - @unittest.skipIf(IS_GAE, 'TODO: Datastore does accept dict objects as json field input') + @unittest.skipIf(IS_GAE or IS_MONGODB, 'TODO: Datastore does accept dict objects as json field input. MongoDB assertion error Binary("x", 0) != "x"') def testRun(self): db = DAL(DEFAULT_URI, check_reserved=['all']) for ft in ['string', 'text', 'password', 'upload', 'blob']: @@ -228,7 +237,7 @@ class TestFields(unittest.TestCase): drop(db.tt) -@unittest.skipIf(IS_GAE, 'TODO: Datastore throws "AssertionError: SyntaxError not raised"') +@unittest.skipIf(IS_GAE or IS_IMAP, 'TODO: Datastore throws "AssertionError: SyntaxError not raised"') class TestTables(unittest.TestCase): def testTableNames(self): @@ -247,7 +256,7 @@ class TestTables(unittest.TestCase): self.assert_(Table(None, 'a_bc'), "Table isn't allowing underscores in tablename. It should.") - +@unittest.skipIf(IS_IMAP, "Skip IMAP") class TestAll(unittest.TestCase): def setUp(self): @@ -257,7 +266,7 @@ class TestAll(unittest.TestCase): ans = 'PseudoTable.id, PseudoTable.name, PseudoTable.birthdate' self.assertEqual(str(SQLALL(self.pt)), ans) - +@unittest.skipIf(IS_IMAP, "Skip IMAP") class TestTable(unittest.TestCase): def testTableCreation(self): @@ -280,7 +289,7 @@ class TestTable(unittest.TestCase): self.assert_('persons.firstname, persons.lastname' in str(persons.ALL)) - @unittest.skipIf(IS_GAE, "No table alias on GAE") + @unittest.skipIf(IS_GAE or IS_MONGODB, "No table alias for this backend") def testTableAlias(self): db = DAL(DEFAULT_URI, check_reserved=['all']) persons = Table(db, 'persons', Field('firstname', @@ -303,24 +312,35 @@ class TestTable(unittest.TestCase): class TestInsert(unittest.TestCase): - def testRun(self): - db = DAL(DEFAULT_URI, check_reserved=['all']) - db.define_table('tt', Field('aa')) - self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True) - self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True) - self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True) - self.assertEqual(db(db.tt.aa == '1').count(), 3) - self.assertEqual(db(db.tt.aa == '2').isempty(), True) - self.assertEqual(db(db.tt.aa == '1').update(aa='2'), 3) - self.assertEqual(db(db.tt.aa == '2').count(), 3) - self.assertEqual(db(db.tt.aa == '2').isempty(), False) - self.assertEqual(db(db.tt.aa == '2').delete(), 3) - self.assertEqual(db(db.tt.aa == '2').isempty(), True) - drop(db.tt) + if IS_IMAP: + imap = DAL(DEFAULT_URI) + imap.define_tables() + self.assertEqual(imap.Draft.insert(to="nurse@example.com", + subject="Nurse!", + sender="gumby@example.com", + content="Nurse!\r\nNurse!"), 2) + self.assertEqual(imap.Draft[2].subject, "Nurse!") + self.assertEqual(imap.Draft[2].sender, "gumby@example.com") + self.assertEqual(isinstance(imap.Draft[2].uid, long), True) + self.assertEqual(imap.Draft[2].content[0]["text"], "Nurse!\r\nNurse!") + else: + db = DAL(DEFAULT_URI, check_reserved=['all']) + db.define_table('tt', Field('aa')) + self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True) + self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True) + self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True) + self.assertEqual(db(db.tt.aa == '1').count(), 3) + self.assertEqual(db(db.tt.aa == '2').isempty(), True) + self.assertEqual(db(db.tt.aa == '1').update(aa='2'), 3) + self.assertEqual(db(db.tt.aa == '2').count(), 3) + self.assertEqual(db(db.tt.aa == '2').isempty(), False) + self.assertEqual(db(db.tt.aa == '2').delete(), 3) + self.assertEqual(db(db.tt.aa == '2').isempty(), True) + drop(db.tt) -@unittest.skipIf(IS_GAE, 'TODO: Datastore throws "SyntaxError: Not supported (query using or)"') +@unittest.skipIf(IS_GAE or IS_MONGODB or IS_IMAP, 'TODO: Datastore throws "SyntaxError: Not supported (query using or)". MongoDB assertionerror 5L != 3') class TestSelect(unittest.TestCase): def testRun(self): @@ -351,7 +371,7 @@ class TestSelect(unittest.TestCase): self.assertEqual(db(~(db.tt.aa > '1') & (db.tt.aa > '2')).count(), 0) drop(db.tt) - +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestAddMethod(unittest.TestCase): def testRun(self): @@ -366,7 +386,7 @@ class TestAddMethod(unittest.TestCase): self.assertEqual(len(db.tt.all()), 3) drop(db.tt) - +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestBelongs(unittest.TestCase): def testRun(self): @@ -378,7 +398,7 @@ class TestBelongs(unittest.TestCase): self.assertEqual(isinstance(db.tt.insert(aa='3'), long), True) self.assertEqual(db(db.tt.aa.belongs(('1', '3'))).count(), 2) - if not (IS_GAE): + if not (IS_GAE or IS_MONGODB): self.assertEqual(db(db.tt.aa.belongs(db(db.tt.id > 2)._select(db.tt.aa))).count(), 1) self.assertEqual(db(db.tt.aa.belongs(db(db.tt.aa.belongs(('1', @@ -388,12 +408,13 @@ class TestBelongs(unittest.TestCase): db.tt.aa))).count(), 2) else: - print "Datastore belongs does not accept queries (skipping)" + print "Datastore/Mongodb belongs does not accept queries (skipping)" drop(db.tt) -@unittest.skipIf(IS_GAE, "Contains not supported on GAE Datastore") +@unittest.skipIf(IS_GAE or IS_IMAP, "Contains not supported on GAE Datastore. TODO: IMAP tests") class TestContains(unittest.TestCase): + @unittest.skipIf(IS_MONGODB, "TODO: MongoDB Contains error") def testRun(self): db = DAL(DEFAULT_URI, check_reserved=['all']) db.define_table('tt', Field('aa', 'list:string'), Field('bb','string')) @@ -410,7 +431,7 @@ class TestContains(unittest.TestCase): drop(db.tt) -@unittest.skipIf(IS_GAE, "Like not supported on GAE Datastore") +@unittest.skipIf(IS_GAE or IS_MONGODB or IS_IMAP, "Like not supported on GAE Datastore. TODO: IMAP test") class TestLike(unittest.TestCase): def testRun(self): @@ -436,7 +457,7 @@ class TestLike(unittest.TestCase): self.assertEqual(db(db.tt.aa.like('2%')).count(), 0) drop(db.tt) - +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestDatetime(unittest.TestCase): def testRun(self): @@ -453,7 +474,7 @@ class TestDatetime(unittest.TestCase): self.assertEqual(db(db.tt.aa >= datetime.datetime(1971, 1, 1)).count(), 2) drop(db.tt) -@unittest.skipIf(IS_GAE, "Expressions not supported in GAE Datastore") +@unittest.skipIf(IS_GAE or IS_MONGODB or IS_IMAP, "Expressions are not supported") class TestExpressions(unittest.TestCase): def testRun(self): @@ -541,7 +562,7 @@ class TestJoin(unittest.TestCase): drop(db.person) class TestMinMaxSumAvg(unittest.TestCase): - @unittest.skipIf(IS_GAE, 'TODO: Datastore throws "AttributeError: Row object has no attribute _extra"') + @unittest.skipIf(IS_GAE or IS_MONGODB or IS_IMAP, 'TODO: Datastore throws "AttributeError: Row object has no attribute _extra"') def testRun(self): db = DAL(DEFAULT_URI, check_reserved=['all']) db.define_table('tt', Field('aa', 'integer')) @@ -562,7 +583,7 @@ class TestMinMaxSumAvg(unittest.TestCase): self.assertEqual(db().select(s).first()[s], 2) drop(db.tt) - +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestCache(unittest.TestCase): def testRun(self): from cache import CacheInRam @@ -581,7 +602,7 @@ class TestCache(unittest.TestCase): self.assertEqual(len(r0),len(r4)) drop(db.tt) - +@unittest.skipIf(IS_IMAP, "Skip IMAP") class TestMigrations(unittest.TestCase): def testRun(self): @@ -612,7 +633,7 @@ class TestMigrations(unittest.TestCase): os.unlink('.storage.table') class TestReference(unittest.TestCase): - + @unittest.skipIf(IS_MONGODB or IS_IMAP, "TODO: MongoDB assertion error (long object has no attribute id)") def testRun(self): db = DAL(DEFAULT_URI, check_reserved=['all']) if DEFAULT_URI.startswith('mssql'): @@ -636,6 +657,7 @@ class TestReference(unittest.TestCase): drop(db.tt) db.commit() +@unittest.skipIf(IS_IMAP, "Skip IMAP") class TestClientLevelOps(unittest.TestCase): def testRun(self): @@ -658,7 +680,7 @@ class TestClientLevelOps(unittest.TestCase): drop(db.tt) db.commit() - +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestVirtualFields(unittest.TestCase): def testRun(self): @@ -673,6 +695,7 @@ class TestVirtualFields(unittest.TestCase): drop(db.tt) db.commit() +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestComputedFields(unittest.TestCase): def testRun(self): @@ -699,9 +722,10 @@ class TestComputedFields(unittest.TestCase): drop(db.tt) db.commit() - +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestCommonFilters(unittest.TestCase): + @unittest.skipIf(IS_MONGODB, "TODO: MongoDB Assertion error") def testRun(self): db = DAL(DEFAULT_URI, check_reserved=['all']) db.define_table('t1', Field('aa')) @@ -727,6 +751,7 @@ class TestCommonFilters(unittest.TestCase): # drop(db.t2) drop(db.t1) +@unittest.skipIf(IS_IMAP, "Skip IMAP test") class TestImportExportFields(unittest.TestCase): def testRun(self): @@ -752,6 +777,7 @@ class TestImportExportFields(unittest.TestCase): drop(db.person) db.commit() +@unittest.skipIf(IS_IMAP, "Skip IMAP test") class TestImportExportUuidFields(unittest.TestCase): def testRun(self): @@ -777,7 +803,7 @@ class TestImportExportUuidFields(unittest.TestCase): drop(db.person) db.commit() - +@unittest.skipIf(IS_IMAP, "Skip IMAP test") class TestDALDictImportExport(unittest.TestCase): def testRun(self): @@ -873,7 +899,7 @@ class TestDALDictImportExport(unittest.TestCase): drop(db6.tvshow) db6.commit() - +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestValidateAndInsert(unittest.TestCase): def testRun(self): @@ -898,6 +924,7 @@ class TestValidateAndInsert(unittest.TestCase): #cleanup table drop(db.val_and_insert) +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestSelectAsDict(unittest.TestCase): def testSelect(self): @@ -916,9 +943,10 @@ class TestSelectAsDict(unittest.TestCase): drop(db.a_table) +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestRNameTable(unittest.TestCase): #tests for highly experimental rname attribute - + @unittest.skipIf(IS_MONGODB, "TODO: MongoDB assertion error (long object has no attribute id)") def testSelect(self): db = DAL(DEFAULT_URI, check_reserved=['all']) rname = db._adapter.QUOTE_TEMPLATE % 'a very complicated tablename' @@ -1010,9 +1038,10 @@ class TestRNameTable(unittest.TestCase): self.assertEqual(len(db.person._referenced_by),0) drop(db.person) +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestRNameFields(unittest.TestCase): # tests for highly experimental rname attribute - @unittest.skipIf(IS_GAE, 'TODO: Datastore throws unsupported error for AGGREGATE') + @unittest.skipIf(IS_GAE or IS_MONGODB, 'TODO: Datastore throws unsupported error for AGGREGATE. MongoDB assertion error (long object has no attribute id)') def testSelect(self): db = DAL(DEFAULT_URI, check_reserved=['all']) rname = db._adapter.QUOTE_TEMPLATE % 'a very complicated fieldname' @@ -1128,7 +1157,7 @@ class TestRNameFields(unittest.TestCase): drop(db.person) drop(db.easy_name) - @unittest.skipIf(IS_GAE, 'TODO: Datastore does not accept dict objects as json field input') + @unittest.skipIf(IS_GAE or IS_MONGODB, 'TODO: Datastore does not accept dict objects as json field input. MongoDB assertionerror Binary("x", 0) != "x"') def testRun(self): db = DAL(DEFAULT_URI, check_reserved=['all']) rname = db._adapter.QUOTE_TEMPLATE % 'a very complicated fieldname' @@ -1284,6 +1313,7 @@ class TestRNameFields(unittest.TestCase): self.assertEqual(len(db.person._referenced_by),0) drop(db.person) +@unittest.skipIf(IS_IMAP, "TODO: IMAP test") class TestQuoting(unittest.TestCase): # tests for case sensitivity