Add working tests, can shutdown conn and pool
This commit is contained in:
@@ -48,6 +48,11 @@ class Connection:
|
|||||||
|
|
||||||
return connection
|
return connection
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def disconnect(self):
|
||||||
|
if self.transport:
|
||||||
|
return self.transport.close()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def transport(self):
|
def transport(self):
|
||||||
""" The transport instance that the protocol is currently using. """
|
""" The transport instance that the protocol is currently using. """
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ class Pool:
|
|||||||
Pool of connections. Each
|
Pool of connections. Each
|
||||||
Takes care of setting up the connection and connection pooling.
|
Takes care of setting up the connection and connection pooling.
|
||||||
|
|
||||||
When poolsize > 1 and some connections are in use because of transactions
|
When pool_size > 1 and some connections are in use because of transactions
|
||||||
or blocking requests, the other are preferred.
|
or blocking requests, the other are preferred.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pool = yield from Pool.create(host='localhost', port=6379, poolsize=10)
|
pool = yield from Pool.create(host='localhost', port=6379, pool_size=10)
|
||||||
result = yield from connection.set('key', 'value')
|
result = yield from connection.set('key', 'value')
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -38,19 +38,19 @@ class Pool:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def create(cls, host='localhost', port=6379, loop=None, password=None, db=0, poolsize=1, auto_reconnect=True):
|
def create(cls, host='localhost', port=6379, loop=None, password=None, db=0, pool_size=1, auto_reconnect=True):
|
||||||
"""
|
"""
|
||||||
Create a new connection instance.
|
Create a new connection instance.
|
||||||
"""
|
"""
|
||||||
self = cls()
|
self = cls()
|
||||||
self._host = host
|
self._host = host
|
||||||
self._port = port
|
self._port = port
|
||||||
self._poolsize = poolsize
|
self._pool_size = pool_size
|
||||||
|
|
||||||
# Create connections
|
# Create connections
|
||||||
self._connections = []
|
self._connections = []
|
||||||
|
|
||||||
for i in range(poolsize):
|
for i in range(pool_size):
|
||||||
connection_class = cls.get_connection_class()
|
connection_class = cls.get_connection_class()
|
||||||
connection = yield from connection_class.create(host=host, port=port, loop=loop,
|
connection = yield from connection_class.create(host=host, port=port, loop=loop,
|
||||||
password=password, db=db, auto_reconnect=auto_reconnect)
|
password=password, db=db, auto_reconnect=auto_reconnect)
|
||||||
@@ -59,10 +59,10 @@ class Pool:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'Pool(host=%r, port=%r, poolsize=%r)' % (self._host, self._port, self._poolsize)
|
return 'Pool(host=%r, port=%r, pool_size=%r)' % (self._host, self._port, self._poolsize)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def poolsize(self):
|
def pool_size(self):
|
||||||
""" Number of parallel connections in the pool."""
|
""" Number of parallel connections in the pool."""
|
||||||
return self._poolsize
|
return self._poolsize
|
||||||
|
|
||||||
@@ -80,6 +80,10 @@ class Pool:
|
|||||||
"""
|
"""
|
||||||
return sum([ 1 for c in self._connections if c.protocol.is_connected ])
|
return sum([ 1 for c in self._connections if c.protocol.is_connected ])
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
for conn in self._connections:
|
||||||
|
conn.disconnect()
|
||||||
|
|
||||||
def _get_free_connection(self):
|
def _get_free_connection(self):
|
||||||
"""
|
"""
|
||||||
Return the next protocol instance that's not in use.
|
Return the next protocol instance that's not in use.
|
||||||
@@ -94,7 +98,7 @@ class Pool:
|
|||||||
|
|
||||||
def _shuffle_connections(self):
|
def _shuffle_connections(self):
|
||||||
"""
|
"""
|
||||||
'shuffle' protocols. Make sure that we devide the load equally among the protocols.
|
'shuffle' protocols. Make sure that we divide the load equally among the protocols.
|
||||||
"""
|
"""
|
||||||
self._connections = self._connections[1:] + self._connections[:1]
|
self._connections = self._connections[1:] + self._connections[:1]
|
||||||
|
|
||||||
@@ -103,10 +107,14 @@ class Pool:
|
|||||||
Proxy to a protocol. (This will choose a protocol instance that's not
|
Proxy to a protocol. (This will choose a protocol instance that's not
|
||||||
busy in a blocking request or transaction.)
|
busy in a blocking request or transaction.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if 'close' == name:
|
||||||
|
return self.close
|
||||||
|
|
||||||
connection = self._get_free_connection()
|
connection = self._get_free_connection()
|
||||||
|
|
||||||
if connection:
|
if connection:
|
||||||
return getattr(connection, name)
|
return getattr(connection, name)
|
||||||
else:
|
else:
|
||||||
raise NoAvailableConnectionsInPoolError('No available connections in the pool: size=%s, in_use=%s, connected=%s' % (
|
raise NoAvailableConnectionsInPoolError('No available connections in the pool: size=%s, in_use=%s, connected=%s' % (
|
||||||
self.poolsize, self.connections_in_use, self.connections_connected))
|
self.pool_size, self.connections_in_use, self.connections_connected))
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ _ZERO = b"\x00\x00\x00\x00"
|
|||||||
|
|
||||||
|
|
||||||
class _MongoQuery(object):
|
class _MongoQuery(object):
|
||||||
def __init__(self, id, collection, limit):
|
def __init__(self, id, collection, limit):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.limit = limit
|
self.limit = limit
|
||||||
self.collection = collection
|
self.collection = collection
|
||||||
|
|||||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
48
tests/base.py
Normal file
48
tests/base.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import inspect
|
||||||
|
import unittest
|
||||||
|
from asyncio import coroutine
|
||||||
|
import asyncio
|
||||||
|
import asyncio_mongo
|
||||||
|
|
||||||
|
mongo_host = "localhost"
|
||||||
|
mongo_port = 27017
|
||||||
|
|
||||||
|
|
||||||
|
def yields(value):
|
||||||
|
return isinstance(value, asyncio.futures.Future) or inspect.isgenerator(value)
|
||||||
|
|
||||||
|
|
||||||
|
@coroutine
|
||||||
|
def call_maybe_yield(func, *args, **kwargs):
|
||||||
|
rv = func(*args, **kwargs)
|
||||||
|
if yields(rv):
|
||||||
|
rv = yield from rv
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
def run_now(func, *args, **kwargs):
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
return loop.run_until_complete(
|
||||||
|
asyncio.Task(call_maybe_yield(func, *args, **kwargs))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def async(func):
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
run_now(func, *args, **kwargs)
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
|
class MongoTest(unittest.TestCase):
|
||||||
|
|
||||||
|
@async
|
||||||
|
def setUp(self):
|
||||||
|
self.conn = yield from asyncio_mongo.Connection.create(mongo_host, mongo_port)
|
||||||
|
self.db = self.conn.mydb
|
||||||
|
self.coll = self.db.mycol
|
||||||
|
yield from self.coll.drop()
|
||||||
|
|
||||||
|
@async
|
||||||
|
def tearDown(self):
|
||||||
|
yield from self.coll.drop()
|
||||||
|
self.conn.disconnect()
|
||||||
46
tests/test_aggregate.py
Normal file
46
tests/test_aggregate.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
# Copyright 2010 Tryggvi Bjorgvinsson
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
from tests.base import MongoTest, async
|
||||||
|
|
||||||
|
|
||||||
|
class TestAggregate(MongoTest):
|
||||||
|
|
||||||
|
timeout = 5
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_aggregate(self):
|
||||||
|
yield from self.coll.insert([{'oh':'hai', 'lulz':123},
|
||||||
|
{'oh':'kthxbye', 'lulz':456},
|
||||||
|
{'oh':'hai', 'lulz':789},], safe=True)
|
||||||
|
|
||||||
|
res = yield from self.coll.aggregate([
|
||||||
|
{'$project': {'oh':1, 'lolz':'$lulz'}},
|
||||||
|
{'$group': {'_id':'$oh', 'many_lolz': {'$sum':'$lolz'}}},
|
||||||
|
{'$sort': {'_id':1}}
|
||||||
|
])
|
||||||
|
|
||||||
|
self.assertEqual(len(res), 2)
|
||||||
|
self.assertEqual(res[0]['_id'], 'hai')
|
||||||
|
self.assertEqual(res[0]['many_lolz'], 912)
|
||||||
|
self.assertEqual(res[1]['_id'], 'kthxbye')
|
||||||
|
self.assertEqual(res[1]['many_lolz'], 456)
|
||||||
|
|
||||||
|
res = yield from self.coll.aggregate([
|
||||||
|
{'$match': {'oh':'hai'}}
|
||||||
|
], full_response=True)
|
||||||
|
|
||||||
|
self.assertIn('ok', res)
|
||||||
|
self.assertIn('result', res)
|
||||||
|
self.assertEqual(len(res['result']), 2)
|
||||||
202
tests/test_collection.py
Normal file
202
tests/test_collection.py
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2012 Renzo S.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Test the collection module.
|
||||||
|
Based on pymongo driver's test_collection.py
|
||||||
|
"""
|
||||||
|
from asyncio_mongo._pymongo import errors
|
||||||
|
from asyncio_mongo.collection import Collection
|
||||||
|
from asyncio_mongo._bson.son import SON
|
||||||
|
from asyncio_mongo import filter
|
||||||
|
|
||||||
|
from tests.base import MongoTest, async
|
||||||
|
|
||||||
|
|
||||||
|
class TestCollection(MongoTest):
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_collection(self):
|
||||||
|
self.assertRaises(TypeError, Collection, self.db, 5)
|
||||||
|
|
||||||
|
def make_col(base, name):
|
||||||
|
return base[name]
|
||||||
|
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db, "")
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db, "te$t")
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db, ".test")
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db, "test.")
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db, "tes..t")
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db.test, "")
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db.test, "te$t")
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db.test, ".test")
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db.test, "test.")
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db.test, "tes..t")
|
||||||
|
self.assertRaises(errors.InvalidName, make_col, self.db.test, "tes\x00t")
|
||||||
|
|
||||||
|
self.assert_(isinstance(self.db.test, Collection))
|
||||||
|
self.assertEqual(self.db.test, Collection(self.db, "test"))
|
||||||
|
self.assertEqual(self.db.test.mike, self.db["test.mike"])
|
||||||
|
self.assertEqual(self.db.test["mike"], self.db["test.mike"])
|
||||||
|
|
||||||
|
yield from self.db.drop_collection('test')
|
||||||
|
collection_names = yield from self.db.collection_names()
|
||||||
|
self.assertFalse('test' in collection_names)
|
||||||
|
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_create_index(self):
|
||||||
|
db = self.db
|
||||||
|
coll = self.coll
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, coll.create_index, 5)
|
||||||
|
self.assertRaises(TypeError, coll.create_index, {"hello": 1})
|
||||||
|
|
||||||
|
yield from coll.drop_indexes()
|
||||||
|
count = yield from db.system.indexes.count({"ns": u"mydb.mycol"})
|
||||||
|
self.assertEqual(count, 1)
|
||||||
|
|
||||||
|
result1 = yield from coll.create_index(filter.sort(filter.ASCENDING("hello")))
|
||||||
|
result2 = yield from coll.create_index(filter.sort(filter.ASCENDING("hello") + \
|
||||||
|
filter.DESCENDING("world")))
|
||||||
|
|
||||||
|
count = yield from db.system.indexes.count({"ns": u"mydb.mycol"})
|
||||||
|
self.assertEqual(count, 3)
|
||||||
|
|
||||||
|
yield from coll.drop_indexes()
|
||||||
|
ix = yield from coll.create_index(filter.sort(filter.ASCENDING("hello") + \
|
||||||
|
filter.DESCENDING("world")), name="hello_world")
|
||||||
|
self.assertEquals(ix, "hello_world")
|
||||||
|
|
||||||
|
yield from coll.drop_indexes()
|
||||||
|
count = yield from db.system.indexes.count({"ns": u"mydb.mycol"})
|
||||||
|
self.assertEqual(count, 1)
|
||||||
|
|
||||||
|
yield from coll.create_index(filter.sort(filter.ASCENDING("hello")))
|
||||||
|
indices = yield from db.system.indexes.find({"ns": u"mydb.mycol"})
|
||||||
|
self.assert_(u"hello_1" in [a["name"] for a in indices])
|
||||||
|
|
||||||
|
yield from coll.drop_indexes()
|
||||||
|
count = yield from db.system.indexes.count({"ns": u"mydb.mycol"})
|
||||||
|
self.assertEqual(count, 1)
|
||||||
|
|
||||||
|
ix = yield from coll.create_index(filter.sort(filter.ASCENDING("hello") + \
|
||||||
|
filter.DESCENDING("world")))
|
||||||
|
self.assertEquals(ix, "hello_1_world_-1")
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_create_index_nodup(self):
|
||||||
|
coll = self.coll
|
||||||
|
|
||||||
|
yield from coll.drop()
|
||||||
|
yield from coll.insert({'b': 1})
|
||||||
|
yield from coll.insert({'b': 1})
|
||||||
|
|
||||||
|
self.assertRaises(errors.DuplicateKeyError, coll.create_index, filter.sort(filter.ASCENDING("b")), unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_ensure_index(self):
|
||||||
|
db = self.db
|
||||||
|
coll = self.coll
|
||||||
|
|
||||||
|
yield from coll.ensure_index(filter.sort(filter.ASCENDING("hello")))
|
||||||
|
indices = yield from db.system.indexes.find({"ns": u"mydb.mycol"})
|
||||||
|
self.assert_(u"hello_1" in [a["name"] for a in indices])
|
||||||
|
|
||||||
|
yield from coll.drop_indexes()
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_index_info(self):
|
||||||
|
db = self.db
|
||||||
|
|
||||||
|
yield from db.test.drop_indexes()
|
||||||
|
yield from db.test.remove({})
|
||||||
|
|
||||||
|
yield from db.test.save({}) # create collection
|
||||||
|
ix_info = yield from db.test.index_information()
|
||||||
|
self.assertEqual(len(ix_info), 1)
|
||||||
|
|
||||||
|
self.assert_("_id_" in ix_info)
|
||||||
|
|
||||||
|
yield from db.test.create_index(filter.sort(filter.ASCENDING("hello")))
|
||||||
|
ix_info = yield from db.test.index_information()
|
||||||
|
self.assertEqual(len(ix_info), 2)
|
||||||
|
|
||||||
|
self.assertEqual(ix_info["hello_1"], [("hello", 1)])
|
||||||
|
|
||||||
|
yield from db.test.create_index(filter.sort(filter.DESCENDING("hello") + filter.ASCENDING("world")), unique=True)
|
||||||
|
ix_info = yield from db.test.index_information()
|
||||||
|
|
||||||
|
self.assertEqual(ix_info["hello_1"], [("hello", 1)])
|
||||||
|
self.assertEqual(len(ix_info), 3)
|
||||||
|
self.assertEqual([("world", 1), ("hello", -1)], ix_info["hello_-1_world_1"])
|
||||||
|
# Unique key will not show until index_information is updated with changes introduced in version 1.7
|
||||||
|
#self.assertEqual(True, ix_info["hello_-1_world_1"]["unique"])
|
||||||
|
|
||||||
|
yield from db.test.drop_indexes()
|
||||||
|
yield from db.test.remove({})
|
||||||
|
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_index_geo2d(self):
|
||||||
|
db = self.db
|
||||||
|
coll = self.coll
|
||||||
|
yield from coll.drop_indexes()
|
||||||
|
geo_ix = yield from coll.create_index(filter.sort(filter.GEO2D("loc")))
|
||||||
|
|
||||||
|
self.assertEqual('loc_2d', geo_ix)
|
||||||
|
|
||||||
|
index_info = yield from coll.index_information()
|
||||||
|
self.assertEqual([('loc', '2d')], index_info['loc_2d'])
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_index_haystack(self):
|
||||||
|
db = self.db
|
||||||
|
coll = self.coll
|
||||||
|
yield from coll.drop_indexes()
|
||||||
|
|
||||||
|
_id = yield from coll.insert({
|
||||||
|
"pos": {"long": 34.2, "lat": 33.3},
|
||||||
|
"type": "restaurant"
|
||||||
|
})
|
||||||
|
yield from coll.insert({
|
||||||
|
"pos": {"long": 34.2, "lat": 37.3}, "type": "restaurant"
|
||||||
|
})
|
||||||
|
yield from coll.insert({
|
||||||
|
"pos": {"long": 59.1, "lat": 87.2}, "type": "office"
|
||||||
|
})
|
||||||
|
|
||||||
|
yield from coll.create_index(filter.sort(filter.GEOHAYSTACK("pos") + filter.ASCENDING("type")), **{'bucket_size': 1})
|
||||||
|
|
||||||
|
# TODO: A db.command method has not been implemented yet.
|
||||||
|
# Sending command directly
|
||||||
|
command = SON([
|
||||||
|
("geoSearch", "mycol"),
|
||||||
|
("near", [33, 33]),
|
||||||
|
("maxDistance", 6),
|
||||||
|
("search", {"type": "restaurant"}),
|
||||||
|
("limit", 30),
|
||||||
|
])
|
||||||
|
|
||||||
|
results = yield from db["$cmd"].find_one(command)
|
||||||
|
self.assertEqual(2, len(results['results']))
|
||||||
|
self.assertEqual({
|
||||||
|
"_id": _id,
|
||||||
|
"pos": {"long": 34.2, "lat": 33.3},
|
||||||
|
"type": "restaurant"
|
||||||
|
}, results["results"][0])
|
||||||
|
|
||||||
|
|
||||||
44
tests/test_connection.py
Normal file
44
tests/test_connection.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
# Copyright 2009 Alexandre Fiori
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
import asyncio_mongo
|
||||||
|
from tests.base import MongoTest, async
|
||||||
|
|
||||||
|
|
||||||
|
mongo_host = "localhost"
|
||||||
|
mongo_port = 27017
|
||||||
|
|
||||||
|
|
||||||
|
class TestMongoConnectionMethods(MongoTest):
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_connection(self):
|
||||||
|
# MongoConnection returns deferred, which gets MongoAPI
|
||||||
|
conn = asyncio_mongo.Connection.create(mongo_host, mongo_port)
|
||||||
|
self.assertTrue(inspect.isgenerator(conn))
|
||||||
|
rapi = yield from conn
|
||||||
|
self.assertEqual(isinstance(rapi, asyncio_mongo.Connection), True)
|
||||||
|
rapi.disconnect()
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_pool(self):
|
||||||
|
# MongoConnectionPool returns deferred, which gets MongoAPI
|
||||||
|
pool = asyncio_mongo.Pool.create(mongo_host, mongo_port, pool_size=2)
|
||||||
|
self.assertTrue(inspect.isgenerator(pool))
|
||||||
|
rapi = yield from pool
|
||||||
|
print('rapi %s' % rapi.__class__)
|
||||||
|
self.assertEqual(isinstance(rapi, asyncio_mongo.Pool), True)
|
||||||
|
rapi.close()
|
||||||
37
tests/test_find_and_modify.py
Normal file
37
tests/test_find_and_modify.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
# Copyright 2010 Mark L.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
from tests.base import MongoTest, async
|
||||||
|
|
||||||
|
|
||||||
|
class TestFindAndModify(MongoTest):
|
||||||
|
@async
|
||||||
|
def test_update(self):
|
||||||
|
yield from self.coll.insert([{'oh': 'hai', 'lulz': 123},
|
||||||
|
{'oh': 'kthxbye', 'lulz': 456}], safe=True)
|
||||||
|
|
||||||
|
res = yield from self.coll.find_one({'oh': 'hai'})
|
||||||
|
self.assertEqual(res['lulz'], 123)
|
||||||
|
|
||||||
|
res = yield from self.coll.find_and_modify({'o2h': 'hai'}, {'$inc': {'lulz': 1}})
|
||||||
|
self.assertEqual(res, None)
|
||||||
|
|
||||||
|
res = yield from self.coll.find_and_modify({'oh': 'hai'}, {'$inc': {'lulz': 1}})
|
||||||
|
print(res)
|
||||||
|
self.assertEqual(res['lulz'], 123)
|
||||||
|
res = yield from self.coll.find_and_modify({'oh': 'hai'}, {'$inc': {'lulz': 1}}, new=True)
|
||||||
|
self.assertEqual(res['lulz'], 125)
|
||||||
|
|
||||||
|
res = yield from self.coll.find_one({'oh': 'kthxbye'})
|
||||||
|
self.assertEqual(res['lulz'], 456)
|
||||||
171
tests/test_objects.py
Normal file
171
tests/test_objects.py
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
# Copyright 2009 Alexandre Fiori
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
import time
|
||||||
|
|
||||||
|
from asyncio_mongo import database
|
||||||
|
from asyncio_mongo import collection
|
||||||
|
from asyncio_mongo import filter as qf
|
||||||
|
from asyncio_mongo._bson import objectid, timestamp
|
||||||
|
from tests.base import MongoTest, async
|
||||||
|
|
||||||
|
|
||||||
|
class TestMongoObjects(MongoTest):
|
||||||
|
@async
|
||||||
|
def test_MongoObjects(self):
|
||||||
|
""" Tests creating mongo objects """
|
||||||
|
self.assertEqual(isinstance(self.db, database.Database), True)
|
||||||
|
self.assertEqual(isinstance(self.coll, collection.Collection), True)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_MongoOperations(self):
|
||||||
|
""" Tests mongo operations """
|
||||||
|
test = self.coll
|
||||||
|
|
||||||
|
# insert
|
||||||
|
doc = {"foo": "bar", "items": [1, 2, 3]}
|
||||||
|
yield from test.insert(doc, safe=True)
|
||||||
|
result = yield from test.find_one(doc)
|
||||||
|
self.assertEqual("_id" in result, True)
|
||||||
|
self.assertEqual(result["foo"], "bar")
|
||||||
|
self.assertEqual(result["items"], [1, 2, 3])
|
||||||
|
|
||||||
|
# insert preserves object id
|
||||||
|
doc.update({'_id': objectid.ObjectId()})
|
||||||
|
yield from test.insert(doc, safe=True)
|
||||||
|
result = yield from test.find_one(doc)
|
||||||
|
self.assertEqual(result.get('_id'), doc.get('_id'))
|
||||||
|
self.assertEqual(result["foo"], "bar")
|
||||||
|
self.assertEqual(result["items"], [1, 2, 3])
|
||||||
|
|
||||||
|
# update
|
||||||
|
yield from test.update({"_id": result["_id"]}, {"$set": {"one": "two"}}, safe=True)
|
||||||
|
result = yield from test.find_one({"_id": result["_id"]})
|
||||||
|
self.assertEqual(result["one"], "two")
|
||||||
|
|
||||||
|
# delete
|
||||||
|
yield from test.remove(result["_id"], safe=True)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_Timestamps(self):
|
||||||
|
"""Tests mongo operations with Timestamps"""
|
||||||
|
test = self.coll
|
||||||
|
|
||||||
|
# insert with specific timestamp
|
||||||
|
doc1 = {'_id': objectid.ObjectId(),
|
||||||
|
'ts': timestamp.Timestamp(1, 2)}
|
||||||
|
yield from test.insert(doc1, safe=True)
|
||||||
|
|
||||||
|
result = yield from test.find_one(doc1)
|
||||||
|
self.assertEqual(result.get('ts').time, 1)
|
||||||
|
self.assertEqual(result.get('ts').inc, 2)
|
||||||
|
|
||||||
|
# insert with specific timestamp
|
||||||
|
doc2 = {'_id': objectid.ObjectId(),
|
||||||
|
'ts': timestamp.Timestamp(2, 1)}
|
||||||
|
yield from test.insert(doc2, safe=True)
|
||||||
|
|
||||||
|
# the objects come back sorted by ts correctly.
|
||||||
|
# (test that we stored inc/time in the right fields)
|
||||||
|
result = yield from test.find(filter=qf.sort(qf.ASCENDING('ts')))
|
||||||
|
self.assertEqual(result[0]['_id'], doc1['_id'])
|
||||||
|
self.assertEqual(result[1]['_id'], doc2['_id'])
|
||||||
|
|
||||||
|
# insert with null timestamp
|
||||||
|
doc3 = {'_id': objectid.ObjectId(),
|
||||||
|
'ts': timestamp.Timestamp(0, 0)}
|
||||||
|
yield from test.insert(doc3, safe=True)
|
||||||
|
|
||||||
|
# time field loaded correctly
|
||||||
|
result = yield from test.find_one(doc3['_id'])
|
||||||
|
now = time.time()
|
||||||
|
self.assertTrue(now - 2 <= result['ts'].time <= now)
|
||||||
|
|
||||||
|
# delete
|
||||||
|
yield from test.remove(doc1["_id"], safe=True)
|
||||||
|
yield from test.remove(doc2["_id"], safe=True)
|
||||||
|
yield from test.remove(doc3["_id"], safe=True)
|
||||||
|
|
||||||
|
|
||||||
|
# class TestGridFsObjects(unittest.TestCase):
|
||||||
|
# """ Test the GridFS operations from asyncio_mongo._gridfs """
|
||||||
|
# @async
|
||||||
|
# def _disconnect(self, conn):
|
||||||
|
# """ Disconnect the connection """
|
||||||
|
# yield from conn.disconnect()
|
||||||
|
#
|
||||||
|
# @async
|
||||||
|
# def test_GridFsObjects(self):
|
||||||
|
# """ Tests gridfs objects """
|
||||||
|
# conn = yield from asyncio_mongo.MongoConnection(mongo_host, mongo_port)
|
||||||
|
# db = conn.test
|
||||||
|
# collection = db.fs
|
||||||
|
#
|
||||||
|
# gfs = gridfs.GridFS(db) # Default collection
|
||||||
|
#
|
||||||
|
# gridin = GridIn(collection, filename='test', contentType="text/plain",
|
||||||
|
# chunk_size=2**2**2**2)
|
||||||
|
# new_file = gfs.new_file(filename='test2', contentType="text/plain",
|
||||||
|
# chunk_size=2**2**2**2)
|
||||||
|
#
|
||||||
|
# # disconnect
|
||||||
|
# yield from conn.disconnect()
|
||||||
|
#
|
||||||
|
# @async
|
||||||
|
# def test_GridFsOperations(self):
|
||||||
|
# """ Tests gridfs operations """
|
||||||
|
# conn = yield from asyncio_mongo.MongoConnection(mongo_host, mongo_port)
|
||||||
|
# db = conn.test
|
||||||
|
# collection = db.fs
|
||||||
|
#
|
||||||
|
# # Don't forget to disconnect
|
||||||
|
# self.addCleanup(self._disconnect, conn)
|
||||||
|
# try:
|
||||||
|
# in_file = StringIO("Test input string")
|
||||||
|
# out_file = StringIO()
|
||||||
|
# except Exception, e:
|
||||||
|
# self.fail("Failed to create memory files for testing: %s" % e)
|
||||||
|
#
|
||||||
|
# try:
|
||||||
|
# # Tests writing to a new gridfs file
|
||||||
|
# gfs = gridfs.GridFS(db) # Default collection
|
||||||
|
# g_in = gfs.new_file(filename='optest', contentType="text/plain",
|
||||||
|
# chunk_size=2**2**2**2) # non-default chunk size used
|
||||||
|
# # yielding to ensure writes complete before we close and close before we try to read
|
||||||
|
# yield from g_in.write(in_file.read())
|
||||||
|
# yield from g_in.close()
|
||||||
|
#
|
||||||
|
# # Tests reading from an existing gridfs file
|
||||||
|
# g_out = yield from gfs.get_last_version('optest')
|
||||||
|
# data = yield from g_out.read()
|
||||||
|
# out_file.write(data)
|
||||||
|
# _id = g_out._id
|
||||||
|
# except Exception,e:
|
||||||
|
# self.fail("Failed to communicate with the GridFS. " +
|
||||||
|
# "Is MongoDB running? %s" % e)
|
||||||
|
# else:
|
||||||
|
# self.assertEqual(in_file.getvalue(), out_file.getvalue(),
|
||||||
|
# "Could not read the value from writing an input")
|
||||||
|
# finally:
|
||||||
|
# in_file.close()
|
||||||
|
# out_file.close()
|
||||||
|
# g_out.close()
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# listed_files = yield from gfs.list()
|
||||||
|
# self.assertEqual(['optest'], listed_files,
|
||||||
|
# "'optest' is the only expected file and we received %s" % listed_files)
|
||||||
|
#
|
||||||
|
# yield from gfs.delete(_id)
|
||||||
|
|
||||||
153
tests/test_queries.py
Normal file
153
tests/test_queries.py
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
# Copyright 2010 Mark L.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
from tests.base import async, MongoTest
|
||||||
|
|
||||||
|
|
||||||
|
class TestMongoQueries(MongoTest):
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_SingleCursorIteration(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(10)], safe=True)
|
||||||
|
res = yield from self.coll.find()
|
||||||
|
self.assertEqual(len(res), 10)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_MultipleCursorIterations(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(450)], safe=True)
|
||||||
|
res = yield from self.coll.find()
|
||||||
|
self.assertEqual(len(res), 450)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_LargeData(self):
|
||||||
|
yield from self.coll.insert([{'v':' '*(2**19)} for i in range(4)], safe=True)
|
||||||
|
res = yield from self.coll.find()
|
||||||
|
self.assertEqual(len(res), 4)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMongoQueriesEdgeCases(MongoTest):
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_BelowBatchThreshold(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(100)], safe=True)
|
||||||
|
res = yield from self.coll.find()
|
||||||
|
self.assertEqual(len(res), 100)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_EqualToBatchThreshold(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(101)], safe=True)
|
||||||
|
res = yield from self.coll.find()
|
||||||
|
self.assertEqual(len(res), 101)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_AboveBatchThreshold(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(102)], safe=True)
|
||||||
|
res = yield from self.coll.find()
|
||||||
|
self.assertEqual(len(res), 102)
|
||||||
|
|
||||||
|
|
||||||
|
class TestLimit(MongoTest):
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_LimitBelowBatchThreshold(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(50)], safe=True)
|
||||||
|
res = yield from self.coll.find(limit=20)
|
||||||
|
self.assertEqual(len(res), 20)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_LimitAboveBatchThreshold(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(200)], safe=True)
|
||||||
|
res = yield from self.coll.find(limit=150)
|
||||||
|
self.assertEqual(len(res), 150)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_LimitAtBatchThresholdEdge(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(200)], safe=True)
|
||||||
|
res = yield from self.coll.find(limit=100)
|
||||||
|
self.assertEqual(len(res), 100)
|
||||||
|
|
||||||
|
yield from self.coll.drop(safe=True)
|
||||||
|
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(200)], safe=True)
|
||||||
|
res = yield from self.coll.find(limit=101)
|
||||||
|
self.assertEqual(len(res), 101)
|
||||||
|
|
||||||
|
yield from self.coll.drop(safe=True)
|
||||||
|
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(200)], safe=True)
|
||||||
|
res = yield from self.coll.find(limit=102)
|
||||||
|
self.assertEqual(len(res), 102)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_LimitAboveMessageSizeThreshold(self):
|
||||||
|
yield from self.coll.insert([{'v':' '*(2**20)} for i in range(8)], safe=True)
|
||||||
|
res = yield from self.coll.find(limit=5)
|
||||||
|
self.assertEqual(len(res), 5)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_HardLimit(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(200)], safe=True)
|
||||||
|
res = yield from self.coll.find(limit=-150)
|
||||||
|
self.assertEqual(len(res), 150)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_HardLimitAboveMessageSizeThreshold(self):
|
||||||
|
yield from self.coll.insert([{'v':' '*(2**20)} for i in range(8)], safe=True)
|
||||||
|
res = yield from self.coll.find(limit=-6)
|
||||||
|
self.assertEqual(len(res), 4)
|
||||||
|
|
||||||
|
|
||||||
|
class TestSkip(MongoTest):
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_Skip(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(5)], safe=True)
|
||||||
|
res = yield from self.coll.find(skip=3)
|
||||||
|
self.assertEqual(len(res), 2)
|
||||||
|
|
||||||
|
yield from self.coll.drop(safe=True)
|
||||||
|
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(5)], safe=True)
|
||||||
|
res = yield from self.coll.find(skip=5)
|
||||||
|
self.assertEqual(len(res), 0)
|
||||||
|
|
||||||
|
yield from self.coll.drop(safe=True)
|
||||||
|
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(5)], safe=True)
|
||||||
|
res = yield from self.coll.find(skip=6)
|
||||||
|
self.assertEqual(len(res), 0)
|
||||||
|
|
||||||
|
@async
|
||||||
|
def test_SkipWithLimit(self):
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(5)], safe=True)
|
||||||
|
res = yield from self.coll.find(skip=3, limit=1)
|
||||||
|
self.assertEqual(len(res), 1)
|
||||||
|
|
||||||
|
yield from self.coll.drop(safe=True)
|
||||||
|
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(5)], safe=True)
|
||||||
|
res = yield from self.coll.find(skip=4, limit=2)
|
||||||
|
self.assertEqual(len(res), 1)
|
||||||
|
|
||||||
|
yield from self.coll.drop(safe=True)
|
||||||
|
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(5)], safe=True)
|
||||||
|
res = yield from self.coll.find(skip=4, limit=1)
|
||||||
|
self.assertEqual(len(res), 1)
|
||||||
|
|
||||||
|
yield from self.coll.drop(safe=True)
|
||||||
|
|
||||||
|
yield from self.coll.insert([{'v':i} for i in range(5)], safe=True)
|
||||||
|
res = yield from self.coll.find(skip=5, limit=1)
|
||||||
|
self.assertEqual(len(res), 0)
|
||||||
Reference in New Issue
Block a user