PYTHON-2545 Test Atlas Serverless (#664)
This commit is contained in:
parent
9833ce0a03
commit
f07da34f97
@ -415,6 +415,12 @@ functions:
|
||||
export SINGLE_MONGOS_LB_URI="${SINGLE_MONGOS_LB_URI}"
|
||||
export MULTI_MONGOS_LB_URI="${MULTI_MONGOS_LB_URI}"
|
||||
fi
|
||||
if [ -n "${test_serverless}" ]; then
|
||||
export TEST_SERVERLESS=1
|
||||
export MONGODB_URI="${MONGODB_URI}"
|
||||
export SERVERLESS_ATLAS_USER="${SERVERLESS_ATLAS_USER}"
|
||||
export SERVERLESS_ATLAS_PASSWORD="${SERVERLESS_ATLAS_PASSWORD}"
|
||||
fi
|
||||
|
||||
PYTHON_BINARY=${PYTHON_BINARY} \
|
||||
GREEN_FRAMEWORK=${GREEN_FRAMEWORK} \
|
||||
@ -836,9 +842,41 @@ post:
|
||||
- func: "cleanup"
|
||||
- func: "teardown_docker"
|
||||
|
||||
task_groups:
|
||||
- name: serverless_task_group
|
||||
setup_group_can_fail_task: true
|
||||
setup_group_timeout_secs: 1800 # 30 minutes
|
||||
setup_group:
|
||||
- func: "fetch source"
|
||||
- func: "prepare resources"
|
||||
- command: shell.exec
|
||||
params:
|
||||
shell: "bash"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
set +o xtrace
|
||||
SERVERLESS_DRIVERS_GROUP=${SERVERLESS_DRIVERS_GROUP} \
|
||||
SERVERLESS_API_PUBLIC_KEY=${SERVERLESS_API_PUBLIC_KEY} \
|
||||
SERVERLESS_API_PRIVATE_KEY=${SERVERLESS_API_PRIVATE_KEY} \
|
||||
bash ${DRIVERS_TOOLS}/.evergreen/serverless/create-instance.sh
|
||||
- command: expansions.update
|
||||
params:
|
||||
file: serverless-expansion.yml
|
||||
teardown_group:
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
set +o xtrace
|
||||
SERVERLESS_DRIVERS_GROUP=${SERVERLESS_DRIVERS_GROUP} \
|
||||
SERVERLESS_API_PUBLIC_KEY=${SERVERLESS_API_PUBLIC_KEY} \
|
||||
SERVERLESS_API_PRIVATE_KEY=${SERVERLESS_API_PRIVATE_KEY} \
|
||||
SERVERLESS_INSTANCE_NAME=${SERVERLESS_INSTANCE_NAME} \
|
||||
bash ${DRIVERS_TOOLS}/.evergreen/serverless/delete-instance.sh
|
||||
tasks:
|
||||
- ".serverless"
|
||||
|
||||
tasks:
|
||||
|
||||
|
||||
# Wildcard task. Do you need to find out what tools are available and where?
|
||||
# Throw it here, and execute this task on all buildvariants
|
||||
- name: getdata
|
||||
@ -1184,6 +1222,11 @@ tasks:
|
||||
TOPOLOGY: "sharded_cluster"
|
||||
- func: "run tests"
|
||||
|
||||
- name: "test-serverless"
|
||||
tags: ["serverless"]
|
||||
commands:
|
||||
- func: "run tests"
|
||||
|
||||
- name: "test-enterprise-auth"
|
||||
tags: ["enterprise-auth"]
|
||||
commands:
|
||||
@ -2040,6 +2083,15 @@ axes:
|
||||
test_loadbalancer: true
|
||||
batchtime: 10080 # 7 days
|
||||
|
||||
- id: serverless
|
||||
display_name: "Serverless"
|
||||
values:
|
||||
- id: "enabled"
|
||||
display_name: "Serverless"
|
||||
variables:
|
||||
test_serverless: true
|
||||
batchtime: 10080 # 7 days
|
||||
|
||||
buildvariants:
|
||||
- matrix_name: "tests-all"
|
||||
matrix_spec:
|
||||
@ -2473,6 +2525,16 @@ buildvariants:
|
||||
tasks:
|
||||
- name: "atlas-connect"
|
||||
|
||||
- matrix_name: "serverless"
|
||||
matrix_spec:
|
||||
platform: awslinux
|
||||
python-version: *amazon1-pythons
|
||||
auth-ssl: auth-ssl
|
||||
serverless: "*"
|
||||
display_name: "Serverless ${python-version} ${platform}"
|
||||
tasks:
|
||||
- "serverless_task_group"
|
||||
|
||||
- matrix_name: "data-lake-spec-tests"
|
||||
matrix_spec:
|
||||
platform: ubuntu-16.04
|
||||
|
||||
@ -39,12 +39,15 @@ if [ -n "$MONGODB_API_VERSION" ]; then
|
||||
fi
|
||||
|
||||
if [ "$AUTH" != "noauth" ]; then
|
||||
if [ -z "$DATA_LAKE" ]; then
|
||||
export DB_USER="bob"
|
||||
export DB_PASSWORD="pwd123"
|
||||
else
|
||||
if [ ! -z "$DATA_LAKE" ]; then
|
||||
export DB_USER="mhuser"
|
||||
export DB_PASSWORD="pencil"
|
||||
elif [ ! -z "$TEST_SERVERLESS" ]; then
|
||||
export DB_USER=$SERVERLESS_ATLAS_USER
|
||||
export DB_PASSWORD=$SERVERLESS_ATLAS_PASSWORD
|
||||
else
|
||||
export DB_USER="bob"
|
||||
export DB_PASSWORD="pwd123"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
101
test/__init__.py
101
test/__init__.py
@ -94,6 +94,7 @@ if CA_PEM:
|
||||
COMPRESSORS = os.environ.get("COMPRESSORS")
|
||||
MONGODB_API_VERSION = os.environ.get("MONGODB_API_VERSION")
|
||||
TEST_LOADBALANCER = bool(os.environ.get("TEST_LOADBALANCER"))
|
||||
TEST_SERVERLESS = bool(os.environ.get("TEST_SERVERLESS"))
|
||||
SINGLE_MONGOS_LB_URI = os.environ.get("SINGLE_MONGOS_LB_URI")
|
||||
MULTI_MONGOS_LB_URI = os.environ.get("MULTI_MONGOS_LB_URI")
|
||||
if TEST_LOADBALANCER:
|
||||
@ -104,6 +105,13 @@ if TEST_LOADBALANCER:
|
||||
host, port = res['nodelist'][0]
|
||||
db_user = res['username'] or db_user
|
||||
db_pwd = res['password'] or db_pwd
|
||||
elif TEST_SERVERLESS:
|
||||
res = parse_uri(os.environ["MONGODB_URI"])
|
||||
host, port = res['nodelist'].pop(0)
|
||||
additional_serverless_mongoses = res['nodelist']
|
||||
db_user = res['username'] or db_user
|
||||
db_pwd = res['password'] or db_pwd
|
||||
TLS_OPTIONS = {'tls': True}
|
||||
|
||||
|
||||
def is_server_resolvable():
|
||||
@ -231,6 +239,7 @@ class ClientContext(object):
|
||||
self.conn_lock = threading.Lock()
|
||||
self.is_data_lake = False
|
||||
self.load_balancer = TEST_LOADBALANCER
|
||||
self.serverless = TEST_SERVERLESS
|
||||
if self.load_balancer:
|
||||
self.default_client_options["loadBalanced"] = True
|
||||
if COMPRESSORS:
|
||||
@ -309,22 +318,26 @@ class ClientContext(object):
|
||||
if self.client:
|
||||
self.connected = True
|
||||
|
||||
try:
|
||||
self.cmd_line = self.client.admin.command('getCmdLineOpts')
|
||||
except pymongo.errors.OperationFailure as e:
|
||||
msg = e.details.get('errmsg', '')
|
||||
if e.code == 13 or 'unauthorized' in msg or 'login' in msg:
|
||||
# Unauthorized.
|
||||
self.auth_enabled = True
|
||||
else:
|
||||
raise
|
||||
if self.serverless:
|
||||
self.auth_enabled = True
|
||||
else:
|
||||
self.auth_enabled = self._server_started_with_auth()
|
||||
try:
|
||||
self.cmd_line = self.client.admin.command('getCmdLineOpts')
|
||||
except pymongo.errors.OperationFailure as e:
|
||||
msg = e.details.get('errmsg', '')
|
||||
if e.code == 13 or 'unauthorized' in msg or 'login' in msg:
|
||||
# Unauthorized.
|
||||
self.auth_enabled = True
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
self.auth_enabled = self._server_started_with_auth()
|
||||
|
||||
if self.auth_enabled:
|
||||
# See if db_user already exists.
|
||||
if not self._check_user_provided():
|
||||
_create_user(self.client.admin, db_user, db_pwd)
|
||||
if not self.serverless:
|
||||
# See if db_user already exists.
|
||||
if not self._check_user_provided():
|
||||
_create_user(self.client.admin, db_user, db_pwd)
|
||||
|
||||
self.client = self._connect(
|
||||
host, port, username=db_user, password=db_pwd,
|
||||
@ -334,10 +347,13 @@ class ClientContext(object):
|
||||
# May not have this if OperationFailure was raised earlier.
|
||||
self.cmd_line = self.client.admin.command('getCmdLineOpts')
|
||||
|
||||
self.server_status = self.client.admin.command('serverStatus')
|
||||
if self.storage_engine == "mmapv1":
|
||||
# MMAPv1 does not support retryWrites=True.
|
||||
self.default_client_options['retryWrites'] = False
|
||||
if self.serverless:
|
||||
self.server_status = {}
|
||||
else:
|
||||
self.server_status = self.client.admin.command('serverStatus')
|
||||
if self.storage_engine == "mmapv1":
|
||||
# MMAPv1 does not support retryWrites=True.
|
||||
self.default_client_options['retryWrites'] = False
|
||||
|
||||
ismaster = self.ismaster
|
||||
self.sessions_enabled = 'logicalSessionTimeoutMinutes' in ismaster
|
||||
@ -374,33 +390,41 @@ class ClientContext(object):
|
||||
self.nodes = set([(host, port)])
|
||||
self.w = len(ismaster.get("hosts", [])) or 1
|
||||
self.version = Version.from_client(self.client)
|
||||
self.server_parameters = self.client.admin.command(
|
||||
'getParameter', '*')
|
||||
|
||||
if 'enableTestCommands=1' in self.cmd_line['argv']:
|
||||
if TEST_SERVERLESS:
|
||||
self.test_commands_enabled = True
|
||||
elif 'parsed' in self.cmd_line:
|
||||
params = self.cmd_line['parsed'].get('setParameter', [])
|
||||
if 'enableTestCommands=1' in params:
|
||||
self.has_ipv6 = False
|
||||
else:
|
||||
self.server_parameters = self.client.admin.command(
|
||||
'getParameter', '*')
|
||||
if 'enableTestCommands=1' in self.cmd_line['argv']:
|
||||
self.test_commands_enabled = True
|
||||
else:
|
||||
params = self.cmd_line['parsed'].get('setParameter', {})
|
||||
if params.get('enableTestCommands') == '1':
|
||||
elif 'parsed' in self.cmd_line:
|
||||
params = self.cmd_line['parsed'].get('setParameter', [])
|
||||
if 'enableTestCommands=1' in params:
|
||||
self.test_commands_enabled = True
|
||||
else:
|
||||
params = self.cmd_line['parsed'].get('setParameter', {})
|
||||
if params.get('enableTestCommands') == '1':
|
||||
self.test_commands_enabled = True
|
||||
self.has_ipv6 = self._server_started_with_ipv6()
|
||||
|
||||
self.is_mongos = (self.ismaster.get('msg') == 'isdbgrid')
|
||||
self.has_ipv6 = self._server_started_with_ipv6()
|
||||
if self.is_mongos:
|
||||
# Check for another mongos on the next port.
|
||||
address = self.client.address
|
||||
next_address = address[0], address[1] + 1
|
||||
self.mongoses.append(address)
|
||||
mongos_client = self._connect(*next_address,
|
||||
**self.default_client_options)
|
||||
if mongos_client:
|
||||
ismaster = mongos_client.admin.command('ismaster')
|
||||
if ismaster.get('msg') == 'isdbgrid':
|
||||
self.mongoses.append(next_address)
|
||||
if self.serverless:
|
||||
self.mongoses.append(self.client.address)
|
||||
self.mongoses.extend(additional_serverless_mongoses)
|
||||
else:
|
||||
# Check for another mongos on the next port.
|
||||
address = self.client.address
|
||||
next_address = address[0], address[1] + 1
|
||||
self.mongoses.append(address)
|
||||
mongos_client = self._connect(
|
||||
*next_address, **self.default_client_options)
|
||||
if mongos_client:
|
||||
ismaster = mongos_client.admin.command('ismaster')
|
||||
if ismaster.get('msg') == 'isdbgrid':
|
||||
self.mongoses.append(next_address)
|
||||
|
||||
def init(self):
|
||||
with self.conn_lock:
|
||||
@ -891,6 +915,9 @@ class IntegrationTest(PyMongoTestCase):
|
||||
if (client_context.load_balancer and
|
||||
not getattr(cls, 'RUN_ON_LOAD_BALANCER', False)):
|
||||
raise SkipTest('this test does not support load balancers')
|
||||
if (client_context.serverless and
|
||||
not getattr(cls, 'RUN_ON_SERVERLESS', False)):
|
||||
raise SkipTest('this test does not support serverless')
|
||||
cls.client = client_context.client
|
||||
cls.db = cls.client.pymongo_test
|
||||
if client_context.auth_enabled:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"description": "aggregate-let",
|
||||
"schemaVersion": "1.0",
|
||||
"schemaVersion": "1.4",
|
||||
"createEntities": [
|
||||
{
|
||||
"client": {
|
||||
@ -310,7 +310,8 @@
|
||||
"description": "Aggregate to collection with let option",
|
||||
"runOnRequirements": [
|
||||
{
|
||||
"minServerVersion": "5.0"
|
||||
"minServerVersion": "5.0",
|
||||
"serverless": "forbid"
|
||||
}
|
||||
],
|
||||
"operations": [
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
{
|
||||
"description": "aggregate-out-readConcern",
|
||||
"schemaVersion": "1.1",
|
||||
"schemaVersion": "1.4",
|
||||
"runOnRequirements": [
|
||||
{
|
||||
"minServerVersion": "4.1.0",
|
||||
"topologies": [
|
||||
"replicaset",
|
||||
"sharded"
|
||||
]
|
||||
],
|
||||
"serverless": "forbid"
|
||||
}
|
||||
],
|
||||
"createEntities": [
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "Aggregate with collation",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "2.6",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "Aggregate with $out",
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "Count documents with collation",
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "Distinct with a collation",
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "Find with a collation",
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "BulkWrite with delete operations and collation",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "DeleteMany when many documents match with collation",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "DeleteOne when many documents matches with collation",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "FindOneAndDelete when one document matches with collation",
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "FindOneAndReplace when one document matches with collation returning the document after modification",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "FindOneAndUpdate when many documents match with collation returning the document before modification",
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "ReplaceOne when one document matches with collation",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "UpdateMany when many documents match with collation",
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
}
|
||||
],
|
||||
"minServerVersion": "3.4",
|
||||
"serverless": "forbid",
|
||||
"tests": [
|
||||
{
|
||||
"description": "UpdateOne when one document matches with collation",
|
||||
|
||||
@ -12,7 +12,8 @@
|
||||
"topology": [
|
||||
"sharded",
|
||||
"load-balanced"
|
||||
]
|
||||
],
|
||||
"serverless": "forbid"
|
||||
}
|
||||
],
|
||||
"database_name": "retryable-reads-tests",
|
||||
|
||||
@ -27,7 +27,7 @@ from pymongo.auth import HAVE_KERBEROS, _build_credentials_tuple
|
||||
from pymongo.errors import OperationFailure
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from pymongo.saslprep import HAVE_STRINGPREP
|
||||
from test import client_context, SkipTest, unittest, Version
|
||||
from test import client_context, IntegrationTest, SkipTest, unittest, Version
|
||||
from test.utils import (delay,
|
||||
ignore_deprecations,
|
||||
single_client,
|
||||
@ -303,11 +303,12 @@ class TestSASLPlain(unittest.TestCase):
|
||||
self.assertRaises(OperationFailure, bad_pwd.admin.command, 'ismaster')
|
||||
|
||||
|
||||
class TestSCRAMSHA1(unittest.TestCase):
|
||||
class TestSCRAMSHA1(IntegrationTest):
|
||||
|
||||
@client_context.require_auth
|
||||
@client_context.require_version_min(2, 7, 2)
|
||||
def setUp(self):
|
||||
super(TestSCRAMSHA1, self).setUp()
|
||||
# Before 2.7.7, SCRAM-SHA-1 had to be enabled from the command line.
|
||||
if client_context.version < Version(2, 7, 7):
|
||||
cmd_line = client_context.cmd_line
|
||||
@ -321,6 +322,7 @@ class TestSCRAMSHA1(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
client_context.drop_user('pymongo_test', 'user')
|
||||
super(TestSCRAMSHA1, self).tearDown()
|
||||
|
||||
def test_scram_sha1(self):
|
||||
host, port = client_context.host, client_context.port
|
||||
@ -343,11 +345,12 @@ class TestSCRAMSHA1(unittest.TestCase):
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst#scram-sha-256-and-mechanism-negotiation
|
||||
class TestSCRAM(unittest.TestCase):
|
||||
class TestSCRAM(IntegrationTest):
|
||||
|
||||
@client_context.require_auth
|
||||
@client_context.require_version_min(3, 7, 2)
|
||||
def setUp(self):
|
||||
super(TestSCRAM, self).setUp()
|
||||
self._SENSITIVE_COMMANDS = monitoring._SENSITIVE_COMMANDS
|
||||
monitoring._SENSITIVE_COMMANDS = set([])
|
||||
self.listener = WhiteListEventListener("saslStart")
|
||||
@ -356,6 +359,7 @@ class TestSCRAM(unittest.TestCase):
|
||||
monitoring._SENSITIVE_COMMANDS = self._SENSITIVE_COMMANDS
|
||||
client_context.client.testscram.command("dropAllUsersFromDatabase")
|
||||
client_context.client.drop_database("testscram")
|
||||
super(TestSCRAM, self).tearDown()
|
||||
|
||||
def test_scram_skip_empty_exchange(self):
|
||||
listener = WhiteListEventListener("saslStart", "saslContinue")
|
||||
@ -571,10 +575,11 @@ class TestSCRAM(unittest.TestCase):
|
||||
self.assertTrue(thread.success)
|
||||
|
||||
|
||||
class TestAuthURIOptions(unittest.TestCase):
|
||||
class TestAuthURIOptions(IntegrationTest):
|
||||
|
||||
@client_context.require_auth
|
||||
def setUp(self):
|
||||
super(TestAuthURIOptions, self).setUp()
|
||||
client_context.create_user('admin', 'admin', 'pass')
|
||||
client_context.create_user(
|
||||
'pymongo_test', 'user', 'pass', ['userAdmin', 'readWrite'])
|
||||
@ -582,6 +587,7 @@ class TestAuthURIOptions(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
client_context.drop_user('pymongo_test', 'user')
|
||||
client_context.drop_user('admin', 'admin')
|
||||
super(TestAuthURIOptions, self).tearDown()
|
||||
|
||||
def test_uri_options(self):
|
||||
# Test default to admin
|
||||
|
||||
@ -1046,14 +1046,17 @@ class TestAllLegacyScenarios(IntegrationTest):
|
||||
@classmethod
|
||||
@client_context.require_connection
|
||||
def setUpClass(cls):
|
||||
super(TestAllLegacyScenarios, cls).setUpClass()
|
||||
cls.listener = WhiteListEventListener("aggregate", "getMore")
|
||||
cls.client = rs_or_single_client(event_listeners=[cls.listener])
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.client.close()
|
||||
super(TestAllLegacyScenarios, cls).tearDownClass()
|
||||
|
||||
def setUp(self):
|
||||
super(TestAllLegacyScenarios, self).setUp()
|
||||
self.listener.results.clear()
|
||||
|
||||
def setUpCluster(self, scenario_dict):
|
||||
|
||||
@ -30,6 +30,15 @@ class TestClientContext(unittest.TestCase):
|
||||
'PYMONGO_MUST_CONNECT is set. Failed attempts:\n%s' %
|
||||
(client_context.connection_attempt_info(),))
|
||||
|
||||
def test_serverless(self):
|
||||
if 'TEST_SERVERLESS' not in os.environ:
|
||||
raise SkipTest('TEST_SERVERLESS is not set')
|
||||
|
||||
self.assertTrue(client_context.connected and client_context.serverless,
|
||||
'client context must be connected to serverless when '
|
||||
'TEST_SERVERLESS is set. Failed attempts:\n%s' %
|
||||
(client_context.connection_attempt_info(),))
|
||||
|
||||
def test_enableTestCommands_is_disabled(self):
|
||||
if 'PYMONGO_DISABLE_TEST_COMMANDS' not in os.environ:
|
||||
raise SkipTest('PYMONGO_DISABLE_TEST_COMMANDS is not set')
|
||||
|
||||
@ -25,7 +25,7 @@ from pymongo.errors import ConfigurationError
|
||||
from pymongo.operations import (DeleteMany, DeleteOne, IndexModel, ReplaceOne,
|
||||
UpdateMany, UpdateOne)
|
||||
from pymongo.write_concern import WriteConcern
|
||||
from test import unittest, client_context
|
||||
from test import client_context, IntegrationTest, unittest
|
||||
from test.utils import EventListener, ignore_deprecations, rs_or_single_client
|
||||
|
||||
|
||||
@ -88,11 +88,11 @@ def raisesConfigurationErrorForOldMongoDB(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
class TestCollation(unittest.TestCase):
|
||||
|
||||
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
|
||||
@ -106,9 +106,11 @@ class TestCollation(unittest.TestCase):
|
||||
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
|
||||
|
||||
@ -28,7 +28,8 @@ TEST_PATH = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)), 'crud', 'unified')
|
||||
|
||||
# Generate unified tests.
|
||||
globals().update(generate_test_classes(TEST_PATH, module=__name__))
|
||||
globals().update(generate_test_classes(
|
||||
TEST_PATH, module=__name__, RUN_ON_SERVERLESS=True))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@ -32,7 +32,7 @@ from pymongo.operations import (InsertOne,
|
||||
UpdateOne,
|
||||
UpdateMany)
|
||||
|
||||
from test import unittest, IntegrationTest
|
||||
from test import client_context, unittest, IntegrationTest
|
||||
from test.utils import (camel_to_snake, camel_to_upper_camel,
|
||||
camel_to_snake_args, drop_collections, TestCreator)
|
||||
|
||||
@ -42,7 +42,7 @@ _TEST_PATH = os.path.join(
|
||||
|
||||
|
||||
class TestAllScenarios(IntegrationTest):
|
||||
pass
|
||||
RUN_ON_SERVERLESS = True
|
||||
|
||||
|
||||
def check_result(self, expected_result, result):
|
||||
|
||||
@ -32,7 +32,7 @@ from pymongo.read_preferences import ReadPreference
|
||||
from pymongo.write_concern import WriteConcern
|
||||
from test import (client_context,
|
||||
client_knobs,
|
||||
PyMongoTestCase,
|
||||
IntegrationTest,
|
||||
sanitize_cmd,
|
||||
unittest)
|
||||
from test.utils import (EventListener,
|
||||
@ -42,11 +42,12 @@ from test.utils import (EventListener,
|
||||
wait_until)
|
||||
|
||||
|
||||
class TestCommandMonitoring(PyMongoTestCase):
|
||||
class TestCommandMonitoring(IntegrationTest):
|
||||
|
||||
@classmethod
|
||||
@client_context.require_connection
|
||||
def setUpClass(cls):
|
||||
super(TestCommandMonitoring, cls).setUpClass()
|
||||
cls.listener = EventListener()
|
||||
cls.client = rs_or_single_client(
|
||||
event_listeners=[cls.listener],
|
||||
@ -55,9 +56,11 @@ class TestCommandMonitoring(PyMongoTestCase):
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.client.close()
|
||||
super(TestCommandMonitoring, cls).tearDownClass()
|
||||
|
||||
def tearDown(self):
|
||||
self.listener.results.clear()
|
||||
super(TestCommandMonitoring, self).tearDown()
|
||||
|
||||
def test_started_simple(self):
|
||||
self.client.pymongo_test.command('ismaster')
|
||||
@ -1129,11 +1132,12 @@ class TestCommandMonitoring(PyMongoTestCase):
|
||||
self.assertEqual({}, succeeded.reply)
|
||||
|
||||
|
||||
class TestGlobalListener(PyMongoTestCase):
|
||||
class TestGlobalListener(IntegrationTest):
|
||||
|
||||
@classmethod
|
||||
@client_context.require_connection
|
||||
def setUpClass(cls):
|
||||
super(TestGlobalListener, cls).setUpClass()
|
||||
cls.listener = EventListener()
|
||||
# We plan to call register(), which internally modifies _LISTENERS.
|
||||
cls.saved_listeners = copy.deepcopy(monitoring._LISTENERS)
|
||||
@ -1146,8 +1150,10 @@ class TestGlobalListener(PyMongoTestCase):
|
||||
def tearDownClass(cls):
|
||||
monitoring._LISTENERS = cls.saved_listeners
|
||||
cls.client.close()
|
||||
super(TestGlobalListener, cls).tearDownClass()
|
||||
|
||||
def setUp(self):
|
||||
super(TestGlobalListener, self).setUp()
|
||||
self.listener.results.clear()
|
||||
|
||||
def test_simple(self):
|
||||
@ -1167,7 +1173,7 @@ class TestGlobalListener(PyMongoTestCase):
|
||||
self.assertTrue(isinstance(started.request_id, int))
|
||||
|
||||
|
||||
class TestEventClasses(PyMongoTestCase):
|
||||
class TestEventClasses(unittest.TestCase):
|
||||
|
||||
def test_command_event_repr(self):
|
||||
request_id, connection_id, operation_id = 1, ('localhost', 27017), 2
|
||||
|
||||
@ -34,7 +34,7 @@ sys.path[0:0] = [""]
|
||||
|
||||
from pymongo.pool import Pool, PoolOptions
|
||||
from pymongo.socket_checker import SocketChecker
|
||||
from test import client_context, unittest
|
||||
from test import client_context, IntegrationTest, unittest
|
||||
from test.utils import (get_pool,
|
||||
joinall,
|
||||
delay,
|
||||
@ -154,10 +154,11 @@ def run_cases(client, cases):
|
||||
assert t.passed, "%s.run() threw an exception" % repr(t)
|
||||
|
||||
|
||||
class _TestPoolingBase(unittest.TestCase):
|
||||
class _TestPoolingBase(IntegrationTest):
|
||||
"""Base class for all connection-pool tests."""
|
||||
|
||||
def setUp(self):
|
||||
super(_TestPoolingBase, self).setUp()
|
||||
self.c = rs_or_single_client()
|
||||
db = self.c[DB]
|
||||
db.unique.drop()
|
||||
@ -167,6 +168,7 @@ class _TestPoolingBase(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
self.c.close()
|
||||
super(_TestPoolingBase, self).tearDown()
|
||||
|
||||
def create_pool(
|
||||
self,
|
||||
|
||||
@ -15,18 +15,19 @@
|
||||
"""Test the read_concern module."""
|
||||
|
||||
from bson.son import SON
|
||||
from pymongo.errors import ConfigurationError, OperationFailure
|
||||
from pymongo.errors import ConfigurationError
|
||||
from pymongo.read_concern import ReadConcern
|
||||
|
||||
from test import client_context, PyMongoTestCase
|
||||
from test import client_context, IntegrationTest
|
||||
from test.utils import single_client, rs_or_single_client, OvertCommandListener
|
||||
|
||||
|
||||
class TestReadConcern(PyMongoTestCase):
|
||||
class TestReadConcern(IntegrationTest):
|
||||
|
||||
@classmethod
|
||||
@client_context.require_connection
|
||||
def setUpClass(cls):
|
||||
super(TestReadConcern, cls).setUpClass()
|
||||
cls.listener = OvertCommandListener()
|
||||
cls.client = single_client(event_listeners=[cls.listener])
|
||||
cls.db = cls.client.pymongo_test
|
||||
@ -36,9 +37,11 @@ class TestReadConcern(PyMongoTestCase):
|
||||
def tearDownClass(cls):
|
||||
cls.client.close()
|
||||
client_context.client.pymongo_test.drop_collection('coll')
|
||||
super(TestReadConcern, cls).tearDownClass()
|
||||
|
||||
def tearDown(self):
|
||||
self.listener.results.clear()
|
||||
super(TestReadConcern, self).tearDown()
|
||||
|
||||
def test_read_concern(self):
|
||||
rc = ReadConcern()
|
||||
|
||||
@ -49,7 +49,7 @@ from test.utils import (connected,
|
||||
from test.version import Version
|
||||
|
||||
|
||||
class TestSelections(unittest.TestCase):
|
||||
class TestSelections(IntegrationTest):
|
||||
|
||||
@client_context.require_connection
|
||||
def test_bool(self):
|
||||
@ -471,7 +471,8 @@ class TestMovingAverage(unittest.TestCase):
|
||||
avg.add_sample(30)
|
||||
self.assertAlmostEqual(15.6, avg.get())
|
||||
|
||||
class TestMongosAndReadPreference(unittest.TestCase):
|
||||
|
||||
class TestMongosAndReadPreference(IntegrationTest):
|
||||
|
||||
def test_read_preference_document(self):
|
||||
|
||||
|
||||
@ -52,6 +52,7 @@ class TestClientOptions(PyMongoTestCase):
|
||||
|
||||
class TestSpec(SpecRunner):
|
||||
RUN_ON_LOAD_BALANCER = True
|
||||
RUN_ON_SERVERLESS = True
|
||||
|
||||
@classmethod
|
||||
@client_context.require_failCommand_fail_point
|
||||
@ -68,11 +69,25 @@ class TestSpec(SpecRunner):
|
||||
if name.lower() in test['description'].lower():
|
||||
self.skipTest('PyMongo does not support %s' % (name,))
|
||||
|
||||
# Skip changeStream related tests on MMAPv1.
|
||||
# Serverless does not support $out and collation.
|
||||
if client_context.serverless:
|
||||
for operation in test['operations']:
|
||||
if operation['name'] == 'aggregate':
|
||||
for stage in operation['arguments']['pipeline']:
|
||||
if "$out" in stage:
|
||||
self.skipTest(
|
||||
"MongoDB Serverless does not support $out")
|
||||
if "collation" in operation['arguments']:
|
||||
self.skipTest(
|
||||
"MongoDB Serverless does not support collations")
|
||||
|
||||
# Skip changeStream related tests on MMAPv1 and serverless.
|
||||
test_name = self.id().rsplit('.')[-1]
|
||||
if ('changestream' in test_name.lower() and
|
||||
client_context.storage_engine == 'mmapv1'):
|
||||
self.skipTest("MMAPv1 does not support change streams.")
|
||||
if 'changestream' in test_name.lower():
|
||||
if client_context.storage_engine == 'mmapv1':
|
||||
self.skipTest("MMAPv1 does not support change streams.")
|
||||
if client_context.serverless:
|
||||
self.skipTest("Serverless does not support change streams.")
|
||||
|
||||
def get_scenario_coll_name(self, scenario_def):
|
||||
"""Override a test's collection name to support GridFS tests."""
|
||||
|
||||
@ -55,6 +55,7 @@ _TEST_PATH = os.path.join(
|
||||
|
||||
class TestAllScenarios(SpecRunner):
|
||||
RUN_ON_LOAD_BALANCER = True
|
||||
RUN_ON_SERVERLESS = True
|
||||
|
||||
def get_object_name(self, op):
|
||||
return op.get('object', 'collection')
|
||||
@ -123,6 +124,7 @@ def non_retryable_single_statement_ops(coll):
|
||||
|
||||
class IgnoreDeprecationsTest(IntegrationTest):
|
||||
RUN_ON_LOAD_BALANCER = True
|
||||
RUN_ON_SERVERLESS = True
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@ -420,6 +422,7 @@ class TestRetryableWrites(IgnoreDeprecationsTest):
|
||||
|
||||
class TestWriteConcernError(IntegrationTest):
|
||||
RUN_ON_LOAD_BALANCER = True
|
||||
RUN_ON_SERVERLESS = True
|
||||
|
||||
@classmethod
|
||||
@client_context.require_replica_set
|
||||
|
||||
@ -958,17 +958,27 @@ class TestCausalConsistency(unittest.TestCase):
|
||||
lambda coll, session: coll.drop_index("foo_1", session=session))
|
||||
self._test_no_read_concern(
|
||||
lambda coll, session: coll.drop_indexes(session=session))
|
||||
|
||||
# Not a write, but explain also doesn't support readConcern.
|
||||
self._test_no_read_concern(
|
||||
lambda coll, session: coll.find({}, session=session).explain())
|
||||
|
||||
@client_context.require_no_standalone
|
||||
@unittest.skipIf(client_context.serverless,
|
||||
"Serverless does not support currentOp")
|
||||
def test_writes_do_not_include_read_concern_current_op(self):
|
||||
# Not a write, but currentOp also doesn't support readConcern.
|
||||
self._test_no_read_concern(
|
||||
lambda coll, session: coll.database.current_op(session=session))
|
||||
|
||||
@client_context.require_no_standalone
|
||||
@unittest.skipIf(client_context.serverless,
|
||||
"Serverless does not support mapReduce")
|
||||
def test_writes_do_not_include_read_concern_map_reduce(self):
|
||||
self._test_no_read_concern(
|
||||
lambda coll, session: coll.map_reduce(
|
||||
'function() {}', 'function() {}', 'mrout', session=session))
|
||||
|
||||
# They are not writes, but currentOp and explain also don't support
|
||||
# readConcern.
|
||||
self._test_no_read_concern(
|
||||
lambda coll, session: coll.database.current_op(session=session))
|
||||
self._test_no_read_concern(
|
||||
lambda coll, session: coll.find({}, session=session).explain())
|
||||
|
||||
@client_context.require_no_standalone
|
||||
@client_context.require_version_max(4, 1, 0)
|
||||
def test_aggregate_out_does_not_include_read_concern(self):
|
||||
@ -1164,6 +1174,7 @@ class TestClusterTime(IntegrationTest):
|
||||
|
||||
|
||||
class TestSpec(SpecRunner):
|
||||
RUN_ON_SERVERLESS = True
|
||||
# Location of JSON test specifications.
|
||||
TEST_PATH = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)), 'sessions', 'legacy')
|
||||
|
||||
@ -76,6 +76,7 @@ class TransactionsBase(SpecRunner):
|
||||
|
||||
|
||||
class TestTransactions(TransactionsBase):
|
||||
RUN_ON_SERVERLESS = True
|
||||
@client_context.require_transactions
|
||||
def test_transaction_options_validation(self):
|
||||
default_options = TransactionOptions()
|
||||
|
||||
@ -34,6 +34,7 @@ globals().update(generate_test_classes(TEST_PATH, module=__name__))
|
||||
|
||||
class TestServerApi(IntegrationTest):
|
||||
RUN_ON_LOAD_BALANCER = True
|
||||
RUN_ON_SERVERLESS = True
|
||||
|
||||
def test_server_api_defaults(self):
|
||||
api = ServerApi(ServerApiVersion.V1)
|
||||
|
||||
@ -117,6 +117,14 @@ def is_run_on_requirement_satisfied(requirement):
|
||||
max_version_satisfied = Version.from_string(
|
||||
req_max_server_version) >= server_version
|
||||
|
||||
serverless = requirement.get('serverless')
|
||||
if serverless == "require":
|
||||
serverless_satisfied = client_context.serverless
|
||||
elif serverless == "forbid":
|
||||
serverless_satisfied = not client_context.serverless
|
||||
else: # unset or "allow"
|
||||
serverless_satisfied = True
|
||||
|
||||
params_satisfied = True
|
||||
params = requirement.get('serverParameters')
|
||||
if params:
|
||||
@ -135,7 +143,8 @@ def is_run_on_requirement_satisfied(requirement):
|
||||
auth_satisfied = not client_context.auth_enabled
|
||||
|
||||
return (topology_satisfied and min_version_satisfied and
|
||||
max_version_satisfied and params_satisfied and auth_satisfied)
|
||||
max_version_satisfied and serverless_satisfied and
|
||||
params_satisfied and auth_satisfied)
|
||||
|
||||
|
||||
def parse_collection_or_database_options(options):
|
||||
@ -1154,7 +1163,8 @@ _SCHEMA_VERSION_MAJOR_TO_MIXIN_CLASS = {
|
||||
|
||||
def generate_test_classes(test_path, module=__name__, class_name_prefix='',
|
||||
expected_failures=[],
|
||||
bypass_test_generation_errors=False):
|
||||
bypass_test_generation_errors=False,
|
||||
**kwargs):
|
||||
"""Method for generating test classes. Returns a dictionary where keys are
|
||||
the names of test classes and values are the test class objects."""
|
||||
test_klasses = {}
|
||||
@ -1195,10 +1205,12 @@ def generate_test_classes(test_path, module=__name__, class_name_prefix='',
|
||||
raise ValueError(
|
||||
"test file '%s' has unsupported schemaVersion '%s'" % (
|
||||
fpath, schema_version))
|
||||
module_dict = {'__module__': module}
|
||||
module_dict.update(kwargs)
|
||||
test_klasses[class_name] = type(
|
||||
class_name,
|
||||
(mixin_class, test_base_class_factory(scenario_def),),
|
||||
{'__module__': module})
|
||||
module_dict)
|
||||
except Exception:
|
||||
if bypass_test_generation_errors:
|
||||
continue
|
||||
|
||||
@ -24,6 +24,7 @@ import shutil
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
from collections import abc, defaultdict
|
||||
@ -391,6 +392,18 @@ class TestCreator(object):
|
||||
if max_ver is not None:
|
||||
method = client_context.require_version_max(*max_ver)(method)
|
||||
|
||||
if 'serverless' in scenario_def:
|
||||
serverless = scenario_def['serverless']
|
||||
if serverless == "require":
|
||||
serverless_satisfied = client_context.serverless
|
||||
elif serverless == "forbid":
|
||||
serverless_satisfied = not client_context.serverless
|
||||
else: # unset or "allow"
|
||||
serverless_satisfied = True
|
||||
method = unittest.skipUnless(
|
||||
serverless_satisfied,
|
||||
"Serverless requirement not satisfied")(method)
|
||||
|
||||
return method
|
||||
|
||||
@staticmethod
|
||||
@ -423,6 +436,16 @@ class TestCreator(object):
|
||||
return not client_context.auth_enabled
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def serverless_ok(run_on_req):
|
||||
serverless = run_on_req['serverless']
|
||||
if serverless == "require":
|
||||
return client_context.serverless
|
||||
elif serverless == "forbid":
|
||||
return not client_context.serverless
|
||||
else: # unset or "allow"
|
||||
return True
|
||||
|
||||
def should_run_on(self, scenario_def):
|
||||
run_on = scenario_def.get('runOn', [])
|
||||
if not run_on:
|
||||
@ -433,7 +456,8 @@ class TestCreator(object):
|
||||
if (self.valid_topology(req) and
|
||||
self.min_server_version(req) and
|
||||
self.max_server_version(req) and
|
||||
self.valid_auth_enabled(req)):
|
||||
self.valid_auth_enabled(req) and
|
||||
self.serverless_ok(req)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user