#31055: Omits test_ prefix from database name when running subset of tests
-----------------------------------+--------------------------------------
Reporter: Matthijs Kooijman | Owner: nobody
Type: Bug | Status: new
Component: Testing framework | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------------+--------------------------------------
Changes (by Matthijs Kooijman):
* cc: Matthijs Kooijman (added)
* version: 3.0 => master
Old description:
> While debugging some test framework issues wrt mysql, I noticed a problem
> where the test runner would try to access the test database without
> prefixing `test_`, leading to an access denied error (because my
> permissions are set up tightly).
>
> What I suspect happens is that this subset of tests only uses the
> `default` database, so only that one is set up by
> `DisoveryRunner.setup_databases`. This is confirmed by using a debugger,
> which shows
> [[https://github.com/django/django/blob/845042b3d9faaefef8855c2bab48bd9532cd00ca/django/test/runner.py#L683|databases]]
> only contains 'default'. Then, it runs the `check` management command,
> which looks at `settings.DATABASES`, which still contains the settings
> for `default` and `other`. This in turn causes a connection to the
> `other` database to be made, but since the name of that database is not
> modified by
> [[https://github.com/django/django/blob/845042b3d9faaefef8855c2bab48bd9532cd00ca/django/db/backends/base/creation.py#L61|create_test_db]],
> that still refers to the original name, and the connection fails.
>
> To reproduce, I have a clean master
> (c33eb6dcd0c211f8f02b2976fe3b3463f0a54498), with the following
> `tests/test_mysql.py`:
>
> {{{
> DATABASES = {
> 'default': {
> 'ENGINE': 'django.db.backends.mysql',
> 'HOST': 'localhost',
> 'USER': 'test_django',
> 'PASSWORD': 'XXX',
> # Django prepends test_ to this name...
> 'NAME': 'django_main',
> },
> 'other': {
> 'ENGINE': 'django.db.backends.mysql',
> 'HOST': 'localhost',
> 'USER': 'test_django',
> 'PASSWORD': 'XXX',
> # Django prepends test_ to this name...
> 'NAME': 'django_other',
> }
> }
>
> SECRET_KEY = "django_tests_secret_key"
>
> # Use a fast hasher to speed up tests.
> PASSWORD_HASHERS = [
> 'django.contrib.auth.hashers.MD5PasswordHasher',
> ]
> }}}
>
> Then inside `tests`, I run:
>
> ./runtests.py --settings test_mysql --parallel 1 timezones
>
> I think the `--parallel 1` is not strictly needed, but might make things
> easier to debug. With the above, I get:
>
> {{{
> Creating test database for alias 'default'...
> Destroying test database for alias 'default'...
> Testing against Django installed in
> '/home/matthijs/docs/src/upstream/django/django'
> Traceback (most recent call last):
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
> line 220, in ensure_connection
> self.connect()
> File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
> line 24, in inner
> return func(*args, **kwargs)
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
> line 197, in connect
> self.connection = self.get_new_connection(conn_params)
> File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
> line 24, in inner
> return func(*args, **kwargs)
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/mysql/base.py",
> line 233, in get_new_connection
> return Database.connect(**conn_params)
> File "/home/matthijs/docs/src/upstream/django/venv/lib/python3.7/site-
> packages/MySQLdb/__init__.py", line 84, in Connect
> return Connection(*args, **kwargs)
> File "/home/matthijs/docs/src/upstream/django/venv/lib/python3.7/site-
> packages/MySQLdb/connections.py", line 179, in __init__
> super(Connection, self).__init__(*args, **kwargs2)
> MySQLdb._exceptions.OperationalError: (1044, "Access denied for user
> 'test_django'@'localhost' to database 'django_other'")
>
> The above exception was the direct cause of the following exception:
>
> Traceback (most recent call last):
> File "./runtests.py", line 566, in <module>
> options.start_at, options.start_after, options.pdb,
> File "./runtests.py", line 308, in django_tests
> extra_tests=extra_tests,
> File "/home/matthijs/docs/src/upstream/django/django/test/runner.py",
> line 687, in run_tests
> self.run_checks()
> File "/home/matthijs/docs/src/upstream/django/django/test/runner.py",
> line 625, in run_checks
> call_command('check', verbosity=self.verbosity)
> File
> "/home/matthijs/docs/src/upstream/django/django/core/management/__init__.py",
> line 168, in call_command
> return command.execute(*args, **defaults)
> File
> "/home/matthijs/docs/src/upstream/django/django/core/management/base.py",
> line 369, in execute
> output = self.handle(*args, **options)
> File
> "/home/matthijs/docs/src/upstream/django/django/core/management/commands/check.py",
> line 64, in handle
> fail_level=getattr(checks, options['fail_level']),
> File
> "/home/matthijs/docs/src/upstream/django/django/core/management/base.py",
> line 395, in check
> include_deployment_checks=include_deployment_checks,
> File
> "/home/matthijs/docs/src/upstream/django/django/core/management/base.py",
> line 382, in _run_checks
> return checks.run_checks(**kwargs)
> File
> "/home/matthijs/docs/src/upstream/django/django/core/checks/registry.py",
> line 72, in run_checks
> new_errors = check(app_configs=app_configs)
> File
> "/home/matthijs/docs/src/upstream/django/django/core/checks/model_checks.py",
> line 34, in check_all_models
> errors.extend(model.check(**kwargs))
> File
> "/home/matthijs/docs/src/upstream/django/django/db/models/base.py", line
> 1276, in check
> *cls._check_constraints(),
> File
> "/home/matthijs/docs/src/upstream/django/django/db/models/base.py", line
> 1842, in _check_constraints
> connection.features.supports_table_check_constraints or
> File
> "/home/matthijs/docs/src/upstream/django/django/utils/functional.py",
> line 48, in __get__
> res = instance.__dict__[self.name] = self.func(instance)
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/mysql/features.py",
> line 97, in supports_column_check_constraints
> if self.connection.mysql_is_mariadb:
> File
> "/home/matthijs/docs/src/upstream/django/django/utils/functional.py",
> line 48, in __get__
> res = instance.__dict__[self.name] = self.func(instance)
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/mysql/base.py",
> line 364, in mysql_is_mariadb
> return 'mariadb' in self.mysql_server_info.lower()
> File
> "/home/matthijs/docs/src/upstream/django/django/utils/functional.py",
> line 48, in __get__
> res = instance.__dict__[self.name] = self.func(instance)
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/mysql/base.py",
> line 351, in mysql_server_info
> with self.temporary_connection() as cursor:
> File "/usr/lib/python3.7/contextlib.py", line 112, in __enter__
> return next(self.gen)
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
> line 604, in temporary_connection
> with self.cursor() as cursor:
> File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
> line 24, in inner
> return func(*args, **kwargs)
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
> line 260, in cursor
> return self._cursor()
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
> line 236, in _cursor
> self.ensure_connection()
> File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
> line 24, in inner
> return func(*args, **kwargs)
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
> line 220, in ensure_connection
> self.connect()
> File "/home/matthijs/docs/src/upstream/django/django/db/utils.py", line
> 90, in __exit__
> raise dj_exc_value.with_traceback(traceback) from exc_value
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
> line 220, in ensure_connection
> self.connect()
> File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
> line 24, in inner
> return func(*args, **kwargs)
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
> line 197, in connect
> self.connection = self.get_new_connection(conn_params)
> File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
> line 24, in inner
> return func(*args, **kwargs)
> File
> "/home/matthijs/docs/src/upstream/django/django/db/backends/mysql/base.py",
> line 233, in get_new_connection
> return Database.connect(**conn_params)
> File "/home/matthijs/docs/src/upstream/django/venv/lib/python3.7/site-
> packages/MySQLdb/__init__.py", line 84, in Connect
> return Connection(*args, **kwargs)
> File "/home/matthijs/docs/src/upstream/django/venv/lib/python3.7/site-
> packages/MySQLdb/connections.py", line 179, in __init__
> super(Connection, self).__init__(*args, **kwargs2)
> django.db.utils.OperationalError: (1044, "Access denied for user
> 'test_django'@'localhost' to database 'django_other'")
> }}}
>
> I am not quite familiar with this code, and this is already a distraction
> from a distraction from a distraction from the actual project I was
> working on, so I'm going to leave this here for others to fix :-)
New description:
While debugging some test framework issues wrt mysql, I noticed a problem
where the test runner would try to access the test database without
prefixing `test_`, leading to an access denied error (because my
permissions are set up tightly).
What I suspect happens is that this subset of tests only uses the
`default` database, so only that one is set up by
`DisoveryRunner.setup_databases`. This is confirmed by using a debugger,
which shows
[[https://github.com/django/django/blob/845042b3d9faaefef8855c2bab48bd9532cd00ca/django/test/runner.py#L683|databases]]
only contains 'default'. Then, it runs the `check` management command,
which looks at `settings.DATABASES`, which still contains the settings for
`default` and `other`. This in turn causes a connection to the `other`
database to be made, but since the name of that database is not modified
by
[[https://github.com/django/django/blob/845042b3d9faaefef8855c2bab48bd9532cd00ca/django/db/backends/base/creation.py#L61|create_test_db]],
that still refers to the original name, and the connection fails.
To reproduce, I have a clean master
(c33eb6dcd0c211f8f02b2976fe3b3463f0a54498), with the following
`tests/test_mysql.py`:
{{{
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': 'localhost',
'USER': 'test_django',
'PASSWORD': 'XXX',
# Django prepends test_ to this name...
'NAME': 'django_main',
},
'other': {
'ENGINE': 'django.db.backends.mysql',
'HOST': 'localhost',
'USER': 'test_django',
'PASSWORD': 'XXX',
# Django prepends test_ to this name...
'NAME': 'django_other',
}
}
SECRET_KEY = "django_tests_secret_key"
# Use a fast hasher to speed up tests.
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.MD5PasswordHasher',
]
}}}
Then inside `tests`, I run:
{{{
./runtests.py --settings test_mysql --parallel 1 timezones
}}}
I think the `--parallel 1` is not strictly needed, but might make things
easier to debug. With the above, I get:
{{{
Creating test database for alias 'default'...
Destroying test database for alias 'default'...
Testing against Django installed in
'/home/matthijs/docs/src/upstream/django/django'
Traceback (most recent call last):
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
line 220, in ensure_connection
self.connect()
File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
line 24, in inner
return func(*args, **kwargs)
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
line 197, in connect
self.connection = self.get_new_connection(conn_params)
File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
line 24, in inner
return func(*args, **kwargs)
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/mysql/base.py",
line 233, in get_new_connection
return Database.connect(**conn_params)
File "/home/matthijs/docs/src/upstream/django/venv/lib/python3.7/site-
packages/MySQLdb/__init__.py", line 84, in Connect
return Connection(*args, **kwargs)
File "/home/matthijs/docs/src/upstream/django/venv/lib/python3.7/site-
packages/MySQLdb/connections.py", line 179, in __init__
super(Connection, self).__init__(*args, **kwargs2)
MySQLdb._exceptions.OperationalError: (1044, "Access denied for user
'test_django'@'localhost' to database 'django_other'")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "./runtests.py", line 566, in <module>
options.start_at, options.start_after, options.pdb,
File "./runtests.py", line 308, in django_tests
extra_tests=extra_tests,
File "/home/matthijs/docs/src/upstream/django/django/test/runner.py",
line 687, in run_tests
self.run_checks()
File "/home/matthijs/docs/src/upstream/django/django/test/runner.py",
line 625, in run_checks
call_command('check', verbosity=self.verbosity)
File
"/home/matthijs/docs/src/upstream/django/django/core/management/__init__.py",
line 168, in call_command
return command.execute(*args, **defaults)
File
"/home/matthijs/docs/src/upstream/django/django/core/management/base.py",
line 369, in execute
output = self.handle(*args, **options)
File
"/home/matthijs/docs/src/upstream/django/django/core/management/commands/check.py",
line 64, in handle
fail_level=getattr(checks, options['fail_level']),
File
"/home/matthijs/docs/src/upstream/django/django/core/management/base.py",
line 395, in check
include_deployment_checks=include_deployment_checks,
File
"/home/matthijs/docs/src/upstream/django/django/core/management/base.py",
line 382, in _run_checks
return checks.run_checks(**kwargs)
File
"/home/matthijs/docs/src/upstream/django/django/core/checks/registry.py",
line 72, in run_checks
new_errors = check(app_configs=app_configs)
File
"/home/matthijs/docs/src/upstream/django/django/core/checks/model_checks.py",
line 34, in check_all_models
errors.extend(model.check(**kwargs))
File "/home/matthijs/docs/src/upstream/django/django/db/models/base.py",
line 1276, in check
*cls._check_constraints(),
File "/home/matthijs/docs/src/upstream/django/django/db/models/base.py",
line 1842, in _check_constraints
connection.features.supports_table_check_constraints or
File
"/home/matthijs/docs/src/upstream/django/django/utils/functional.py", line
48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/mysql/features.py",
line 97, in supports_column_check_constraints
if self.connection.mysql_is_mariadb:
File
"/home/matthijs/docs/src/upstream/django/django/utils/functional.py", line
48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/mysql/base.py",
line 364, in mysql_is_mariadb
return 'mariadb' in self.mysql_server_info.lower()
File
"/home/matthijs/docs/src/upstream/django/django/utils/functional.py", line
48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/mysql/base.py",
line 351, in mysql_server_info
with self.temporary_connection() as cursor:
File "/usr/lib/python3.7/contextlib.py", line 112, in __enter__
return next(self.gen)
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
line 604, in temporary_connection
with self.cursor() as cursor:
File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
line 24, in inner
return func(*args, **kwargs)
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
line 260, in cursor
return self._cursor()
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
line 236, in _cursor
self.ensure_connection()
File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
line 24, in inner
return func(*args, **kwargs)
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
line 220, in ensure_connection
self.connect()
File "/home/matthijs/docs/src/upstream/django/django/db/utils.py", line
90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
line 220, in ensure_connection
self.connect()
File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
line 24, in inner
return func(*args, **kwargs)
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/base/base.py",
line 197, in connect
self.connection = self.get_new_connection(conn_params)
File "/home/matthijs/docs/src/upstream/django/django/utils/asyncio.py",
line 24, in inner
return func(*args, **kwargs)
File
"/home/matthijs/docs/src/upstream/django/django/db/backends/mysql/base.py",
line 233, in get_new_connection
return Database.connect(**conn_params)
File "/home/matthijs/docs/src/upstream/django/venv/lib/python3.7/site-
packages/MySQLdb/__init__.py", line 84, in Connect
return Connection(*args, **kwargs)
File "/home/matthijs/docs/src/upstream/django/venv/lib/python3.7/site-
packages/MySQLdb/connections.py", line 179, in __init__
super(Connection, self).__init__(*args, **kwargs2)
django.db.utils.OperationalError: (1044, "Access denied for user
'test_django'@'localhost' to database 'django_other'")
}}}
I am not quite familiar with this code, and this is already a distraction
from a distraction from a distraction from the actual project I was
working on, so I'm going to leave this here for others to fix :-)
--
--
Ticket URL: <https://code.djangoproject.com/ticket/31055#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/074.ce12b3677843102db2ef8cb7bedad65f%40djangoproject.com.