Repository URL to install this package:
|
Version:
3.12.2 ▾
|
# Copyright 2016-present MongoDB, Inc.
#
# 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 collation module."""
import functools
import warnings
from pymongo.collation import (
Collation,
CollationCaseFirst, CollationStrength, CollationAlternate,
CollationMaxVariable)
from pymongo.errors import ConfigurationError
from pymongo.operations import (DeleteMany, DeleteOne, IndexModel, ReplaceOne,
UpdateMany, UpdateOne)
from pymongo.write_concern import WriteConcern
from test import client_context, IntegrationTest, unittest
from test.utils import EventListener, ignore_deprecations, rs_or_single_client
class TestCollationObject(unittest.TestCase):
def test_constructor(self):
self.assertRaises(TypeError, Collation, locale=42)
# Fill in a locale to test the other options.
_Collation = functools.partial(Collation, 'en_US')
# No error.
_Collation(caseFirst=CollationCaseFirst.UPPER)
self.assertRaises(TypeError, _Collation, caseLevel='true')
self.assertRaises(ValueError, _Collation, strength='six')
self.assertRaises(TypeError, _Collation,
numericOrdering='true')
self.assertRaises(TypeError, _Collation, alternate=5)
self.assertRaises(TypeError, _Collation, maxVariable=2)
self.assertRaises(TypeError, _Collation, normalization='false')
self.assertRaises(TypeError, _Collation, backwards='true')
# No errors.
Collation('en_US', future_option='bar', another_option=42)
collation = Collation(
'en_US',
caseLevel=True,
caseFirst=CollationCaseFirst.UPPER,
strength=CollationStrength.QUATERNARY,
numericOrdering=True,
alternate=CollationAlternate.SHIFTED,
maxVariable=CollationMaxVariable.SPACE,
normalization=True,
backwards=True)
self.assertEqual({
'locale': 'en_US',
'caseLevel': True,
'caseFirst': 'upper',
'strength': 4,
'numericOrdering': True,
'alternate': 'shifted',
'maxVariable': 'space',
'normalization': True,
'backwards': True
}, collation.document)
self.assertEqual({
'locale': 'en_US',
'backwards': True
}, Collation('en_US', backwards=True).document)
def raisesConfigurationErrorForOldMongoDB(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
if client_context.version.at_least(3, 3, 9):
return func(self, *args, **kwargs)
else:
with self.assertRaises(ConfigurationError):
return func(self, *args, **kwargs)
return wrapper
class TestCollation(IntegrationTest):
@classmethod
@client_context.require_connection
def setUpClass(cls):
super(TestCollation, cls).setUpClass()
cls.listener = EventListener()
cls.client = rs_or_single_client(event_listeners=[cls.listener])
cls.db = cls.client.pymongo_test
cls.collation = Collation('en_US')
cls.warn_context = warnings.catch_warnings()
cls.warn_context.__enter__()
warnings.simplefilter("ignore", DeprecationWarning)
@classmethod
def tearDownClass(cls):
cls.warn_context.__exit__()
cls.warn_context = None
cls.client.close()
super(TestCollation, cls).tearDownClass()
def tearDown(self):
self.listener.results.clear()
super(TestCollation, self).tearDown()
def last_command_started(self):
return self.listener.results['started'][-1].command
def assertCollationInLastCommand(self):
self.assertEqual(
self.collation.document,
self.last_command_started()['collation'])
@raisesConfigurationErrorForOldMongoDB
def test_create_collection(self):
self.db.test.drop()
self.db.create_collection('test', collation=self.collation)
self.assertCollationInLastCommand()
# Test passing collation as a dict as well.
self.db.test.drop()
self.listener.results.clear()
self.db.create_collection('test', collation=self.collation.document)
self.assertCollationInLastCommand()
def test_index_model(self):
model = IndexModel([('a', 1), ('b', -1)], collation=self.collation)
self.assertEqual(self.collation.document, model.document['collation'])
@raisesConfigurationErrorForOldMongoDB
def test_create_index(self):
self.db.test.create_index('foo', collation=self.collation)
ci_cmd = self.listener.results['started'][0].command
self.assertEqual(
self.collation.document,
ci_cmd['indexes'][0]['collation'])
@raisesConfigurationErrorForOldMongoDB
def test_ensure_index(self):
self.db.test.ensure_index('foo', collation=self.collation)
ci_cmd = self.listener.results['started'][0].command
self.assertEqual(
self.collation.document,
ci_cmd['indexes'][0]['collation'])
@raisesConfigurationErrorForOldMongoDB
def test_aggregate(self):
self.db.test.aggregate([{'$group': {'_id': 42}}],
collation=self.collation)
self.assertCollationInLastCommand()
@raisesConfigurationErrorForOldMongoDB
@ignore_deprecations
def test_count(self):
self.db.test.count(collation=self.collation)
self.assertCollationInLastCommand()
self.listener.results.clear()
self.db.test.find(collation=self.collation).count()
self.assertCollationInLastCommand()
@raisesConfigurationErrorForOldMongoDB
def test_count_documents(self):
self.db.test.count_documents({}, collation=self.collation)
self.assertCollationInLastCommand()
@raisesConfigurationErrorForOldMongoDB
def test_distinct(self):
self.db.test.distinct('foo', collation=self.collation)
self.assertCollationInLastCommand()
self.listener.results.clear()
self.db.test.find(collation=self.collation).distinct('foo')
self.assertCollationInLastCommand()
@raisesConfigurationErrorForOldMongoDB
def test_find_command(self):
self.db.test.insert_one({'is this thing on?': True})
self.listener.results.clear()
next(self.db.test.find(collation=self.collation))
self.assertCollationInLastCommand()
@raisesConfigurationErrorForOldMongoDB
def test_explain_command(self):
self.listener.results.clear()
self.db.test.find(collation=self.collation).explain()
# The collation should be part of the explained command.
self.assertEqual(
self.collation.document,
self.last_command_started()['explain']['collation'])
@raisesConfigurationErrorForOldMongoDB
@client_context.require_version_max(4, 1, 0, -1)
def test_group(self):
self.db.test.group('foo', {'foo': {'$gt': 42}}, {},
'function(a, b) { return a; }',
collation=self.collation)
self.assertCollationInLastCommand()
@raisesConfigurationErrorForOldMongoDB
def test_map_reduce(self):
self.db.test.map_reduce('function() {}', 'function() {}', 'output',
collation=self.collation)
self.assertCollationInLastCommand()
@raisesConfigurationErrorForOldMongoDB
def test_delete(self):
self.db.test.delete_one({'foo': 42}, collation=self.collation)
command = self.listener.results['started'][0].command
self.assertEqual(
self.collation.document,
command['deletes'][0]['collation'])
self.listener.results.clear()
self.db.test.delete_many({'foo': 42}, collation=self.collation)
command = self.listener.results['started'][0].command
self.assertEqual(
self.collation.document,
command['deletes'][0]['collation'])
self.listener.results.clear()
self.db.test.remove({'foo': 42}, collation=self.collation)
command = self.listener.results['started'][0].command
self.assertEqual(
self.collation.document,
command['deletes'][0]['collation'])
@raisesConfigurationErrorForOldMongoDB
def test_update(self):
self.db.test.update({'foo': 42}, {'$set': {'foo': 'bar'}},
collation=self.collation)
command = self.listener.results['started'][0].command
self.assertEqual(
self.collation.document,
command['updates'][0]['collation'])
self.listener.results.clear()
self.db.test.save({'_id': 12345}, collation=self.collation)
command = self.listener.results['started'][0].command
self.assertEqual(
self.collation.document,
command['updates'][0]['collation'])
self.listener.results.clear()
self.db.test.replace_one({'foo': 42}, {'foo': 43},
collation=self.collation)
command = self.listener.results['started'][0].command
self.assertEqual(
self.collation.document,
command['updates'][0]['collation'])
self.listener.results.clear()
self.db.test.update_one({'foo': 42}, {'$set': {'foo': 43}},
collation=self.collation)
command = self.listener.results['started'][0].command
self.assertEqual(
self.collation.document,
command['updates'][0]['collation'])
self.listener.results.clear()
self.db.test.update_many({'foo': 42}, {'$set': {'foo': 43}},
collation=self.collation)
command = self.listener.results['started'][0].command
self.assertEqual(
self.collation.document,
command['updates'][0]['collation'])
@raisesConfigurationErrorForOldMongoDB
def test_find_and(self):
self.db.test.find_and_modify({'foo': 42}, {'$set': {'foo': 43}},
collation=self.collation)
self.assertCollationInLastCommand()
self.listener.results.clear()
self.db.test.find_one_and_delete({'foo': 42}, collation=self.collation)
self.assertCollationInLastCommand()
self.listener.results.clear()
self.db.test.find_one_and_update({'foo': 42}, {'$set': {'foo': 43}},
collation=self.collation)
self.assertCollationInLastCommand()
self.listener.results.clear()
self.db.test.find_one_and_replace({'foo': 42}, {'foo': 43},
collation=self.collation)
self.assertCollationInLastCommand()
@raisesConfigurationErrorForOldMongoDB
def test_bulk_write(self):
self.db.test.collection.bulk_write([
DeleteOne({'noCollation': 42}),
DeleteMany({'noCollation': 42}),
DeleteOne({'foo': 42}, collation=self.collation),
DeleteMany({'foo': 42}, collation=self.collation),
ReplaceOne({'noCollation': 24}, {'bar': 42}),
UpdateOne({'noCollation': 84}, {'$set': {'bar': 10}}, upsert=True),
UpdateMany({'noCollation': 45}, {'$set': {'bar': 42}}),
ReplaceOne({'foo': 24}, {'foo': 42}, collation=self.collation),
UpdateOne({'foo': 84}, {'$set': {'foo': 10}}, upsert=True,
collation=self.collation),
UpdateMany({'foo': 45}, {'$set': {'foo': 42}},
collation=self.collation)
])
delete_cmd = self.listener.results['started'][0].command
update_cmd = self.listener.results['started'][1].command
def check_ops(ops):
for op in ops:
if 'noCollation' in op['q']:
self.assertNotIn('collation', op)
else:
self.assertEqual(self.collation.document,
op['collation'])
check_ops(delete_cmd['deletes'])
check_ops(update_cmd['updates'])
@raisesConfigurationErrorForOldMongoDB
def test_bulk(self):
bulk = self.db.test.initialize_ordered_bulk_op()
bulk.find({'noCollation': 42}).remove_one()
bulk.find({'noCollation': 42}).remove()
bulk.find({'foo': 42}, collation=self.collation).remove_one()
bulk.find({'foo': 42}, collation=self.collation).remove()
bulk.find({'noCollation': 24}).replace_one({'bar': 42})
bulk.find({'noCollation': 84}).upsert().update_one(
{'$set': {'foo': 10}})
bulk.find({'noCollation': 45}).update({'$set': {'bar': 42}})
bulk.find({'foo': 24}, collation=self.collation).replace_one(
{'foo': 42})
bulk.find({'foo': 84}, collation=self.collation).upsert().update_one(
{'$set': {'foo': 10}})
bulk.find({'foo': 45}, collation=self.collation).update({
'$set': {'foo': 42}})
bulk.execute()
delete_cmd = self.listener.results['started'][0].command
update_cmd = self.listener.results['started'][1].command
def check_ops(ops):
for op in ops:
if 'noCollation' in op['q']:
self.assertNotIn('collation', op)
else:
self.assertEqual(self.collation.document,
op['collation'])
check_ops(delete_cmd['deletes'])
check_ops(update_cmd['updates'])
@client_context.require_version_max(3, 3, 8)
def test_mixed_bulk_collation(self):
bulk = self.db.test.initialize_unordered_bulk_op()
bulk.find({'foo': 42}).upsert().update_one(
{'$set': {'bar': 10}})
bulk.find({'foo': 43}, collation=self.collation).remove_one()
with self.assertRaises(ConfigurationError):
bulk.execute()
self.assertIsNone(self.db.test.find_one({'foo': 42}))
@raisesConfigurationErrorForOldMongoDB
def test_indexes_same_keys_different_collations(self):
self.db.test.drop()
usa_collation = Collation('en_US')
ja_collation = Collation('ja')
self.db.test.create_indexes([
IndexModel('fieldname', collation=usa_collation),
IndexModel('fieldname', name='japanese_version',
collation=ja_collation),
IndexModel('fieldname', name='simple')
])
indexes = self.db.test.index_information()
self.assertEqual(usa_collation.document['locale'],
indexes['fieldname_1']['collation']['locale'])
self.assertEqual(ja_collation.document['locale'],
indexes['japanese_version']['collation']['locale'])
self.assertNotIn('collation', indexes['simple'])
self.db.test.drop_index('fieldname_1')
indexes = self.db.test.index_information()
self.assertIn('japanese_version', indexes)
self.assertIn('simple', indexes)
self.assertNotIn('fieldname', indexes)
def test_unacknowledged_write(self):
unacknowledged = WriteConcern(w=0)
collection = self.db.get_collection(
'test', write_concern=unacknowledged)
with self.assertRaises(ConfigurationError):
collection.update_one(
{'hello': 'world'}, {'$set': {'hello': 'moon'}},
collation=self.collation)
bulk = collection.initialize_ordered_bulk_op()
bulk.find({'hello': 'world'}, collation=self.collation).update_one(
{'$set': {'hello': 'moon'}})
with self.assertRaises(ConfigurationError):
bulk.execute()
update_one = UpdateOne({'hello': 'world'}, {'$set': {'hello': 'moon'}},
collation=self.collation)
with self.assertRaises(ConfigurationError):
collection.bulk_write([update_one])
@raisesConfigurationErrorForOldMongoDB
def test_cursor_collation(self):
self.db.test.insert_one({'hello': 'world'})
next(self.db.test.find().collation(self.collation))
self.assertCollationInLastCommand()