Skip to content

Commit

Permalink
delete test backend classes
Browse files Browse the repository at this point in the history
  • Loading branch information
ltalirz committed Feb 25, 2021
1 parent c975336 commit ccca6f6
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 169 deletions.
38 changes: 0 additions & 38 deletions aiida/backends/djsite/db/testbase.py

This file was deleted.

45 changes: 0 additions & 45 deletions aiida/backends/sqlalchemy/testbase.py

This file was deleted.

51 changes: 6 additions & 45 deletions aiida/backends/testbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import unittest
import traceback

from aiida.common.exceptions import ConfigurationError, TestsNotAllowedError, InternalError
from aiida.common.exceptions import TestsNotAllowedError
from aiida.manage import configuration
from aiida.manage.manager import get_manager, reset_manager
from aiida import orm
Expand All @@ -34,47 +34,16 @@ class AiidaTestCase(unittest.TestCase):
Internally it loads the AiidaTestImplementation subclass according to the current backend."""
_computer = None # type: aiida.orm.Computer
_class_was_setup = False
__backend_instance = None
backend = None # type: aiida.orm.implementation.Backend

@classmethod
def get_backend_class(cls):
"""Get backend class."""
from aiida.backends.testimplbase import AiidaTestImplementation
from aiida.backends import BACKEND_SQLA, BACKEND_DJANGO
from aiida.manage.configuration import PROFILE

# Freeze the __impl_class after the first run
if not hasattr(cls, '__impl_class'):
if PROFILE.database_backend == BACKEND_SQLA:
from aiida.backends.sqlalchemy.testbase import SqlAlchemyTests
cls.__impl_class = SqlAlchemyTests
elif PROFILE.database_backend == BACKEND_DJANGO:
from aiida.backends.djsite.db.testbase import DjangoTests
cls.__impl_class = DjangoTests
else:
raise ConfigurationError('Unknown backend type')

# Check that it is of the right class
if not issubclass(cls.__impl_class, AiidaTestImplementation):
raise InternalError(
'The AiiDA test implementation is not of type '
'{}, that is not a subclass of AiidaTestImplementation'.format(cls.__impl_class.__name__)
)

return cls.__impl_class

@classmethod
def setUpClass(cls):
"""Set up test class."""
# Note: this will raise an exception, that will be seen as a test
# failure. To be safe, you should do the same check also in the tearDownClass
# to avoid that it is run
# Note: this will raise an exception, that will be seen as a test failure.
check_if_tests_can_run()

# Force the loading of the backend which will load the required database environment
cls.backend = get_manager().get_backend()
cls.__backend_instance = cls.get_backend_class()()
cls._class_was_setup = True

cls.refurbish_db()
Expand All @@ -83,18 +52,11 @@ def setUpClass(cls):
def tearDownClass(cls):
"""Tear down test class.
Note: Also cleans file repository.
Note: Cleans database and also the file repository.
"""
# Double check for double security to avoid to run the tearDown
# if this is not a test profile

check_if_tests_can_run()
cls.clean_db()
cls.clean_repository()

def tearDown(self):
reset_manager()

### Database/repository-related methods

@classmethod
Expand All @@ -105,15 +67,14 @@ def clean_db(cls):
"""
from aiida.common.exceptions import InvalidOperation

# Note: this will raise an exception, that will be seen as a test
# failure. To be safe, you should do the same check also in the tearDownClass
# to avoid that it is run
# Note: this will raise an exception, that will be seen as a test failure.
# Just another safety check to prevent deleting production databases
check_if_tests_can_run()

if not cls._class_was_setup:
raise InvalidOperation('You cannot call clean_db before running the setUpClass')

cls.__backend_instance.clean_db()
cls.backend._clean_db() # pylint: disable=protected-access
cls._computer = None

orm.User.objects.reset() # clear Aiida's cache of the default user
Expand Down
32 changes: 4 additions & 28 deletions aiida/manage/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,43 +137,22 @@ def __init__(self, profile_name):
raise TestManagerError('Unable to load test profile \'{}\'.'.format(profile_name))
check_if_tests_can_run()

self._select_db_test_case(backend=self._profile.database_backend)

def _select_db_test_case(self, backend):
"""
Selects tests case for the correct database backend.
"""
if backend == BACKEND_DJANGO:
from aiida.backends.djsite.db.testbase import DjangoTests
self._test_case = DjangoTests()
elif backend == BACKEND_SQLA:
from aiida.backends.sqlalchemy.testbase import SqlAlchemyTests
self._test_case = SqlAlchemyTests()

def reset_db(self):
self._test_case.clean_db() # will drop all users
self._profile.database_backend._clean_db() # pylint: disable=protected-access
manager.reset_manager()
self.init_db()

def init_db(self):
def init_db(self): # pylint: disable=no-self-use
"""Initialise the database state for running of tests.
Adds default user if necessary.
"""
from aiida.orm import User
from aiida.cmdline.commands.cmd_user import set_default_user

if not User.objects.get_default():
user_dict = get_user_dict(_DEFAULT_PROFILE_INFO)
try:
user = User(**user_dict)
user.store()
except exceptions.IntegrityError:
# The user already exists, no problem
user = User.objects.get(**user_dict)

set_default_user(self._profile, user)
User.objects.reset() # necessary to pick up new default user
_created, _user = User.objects.get_or_create_default(**get_user_dict(_DEFAULT_PROFILE_INFO))
_user.store()

def has_profile_open(self):
return self._profile is not None
Expand Down Expand Up @@ -228,8 +207,6 @@ class TemporaryProfileManager(ProfileManager):
"""

_test_case = None

def __init__(self, backend=BACKEND_DJANGO, pgtest=None): # pylint: disable=super-init-not-called
"""Construct a TemporaryProfileManager
Expand Down Expand Up @@ -337,7 +314,6 @@ def create_profile(self):
backend = manager.get_manager()._load_backend(schema_check=False)
backend.migrate()

self._select_db_test_case(backend=self._profile.database_backend)
self.init_db()

def repo_ok(self):
Expand Down
13 changes: 13 additions & 0 deletions aiida/orm/implementation/django/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,16 @@ def get_connection():
# For now we just return the global but if we ever support multiple Django backends
# being loaded this should be specific to this backend
return connection

def _clean_db(self):
from aiida.backends.djsite.db import models as dbmodels
# I first need to delete the links, because in principle I could not delete input nodes, only outputs.
# For simplicity, since I am deleting everything, I delete the links first
dbmodels.DbLink.objects.all().delete()

# Then I delete the nodes, otherwise I cannot delete computers and users
dbmodels.DbLog.objects.all().delete()
dbmodels.DbNode.objects.all().delete() # pylint: disable=no-member
dbmodels.DbUser.objects.all().delete() # pylint: disable=no-member
dbmodels.DbComputer.objects.all().delete()
dbmodels.DbGroup.objects.all().delete()
7 changes: 7 additions & 0 deletions aiida/orm/implementation/sql/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,10 @@ def execute_prepared_statement(self, sql, parameters):
results.append(row)

return results

@abc.abstractmethod
def _clean_db(self):
"""Cleans entire database (used in tests).
.. warning:: this will lead to catastrophic data loss!
"""
23 changes: 23 additions & 0 deletions aiida/orm/implementation/sqlalchemy/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,26 @@ def get_connection():
"""
from aiida.backends import sqlalchemy as sa
return sa.ENGINE.raw_connection()

def _clean_db(self):
from sqlalchemy.sql import table
# pylint: disable=invalid-name
DbGroupNodes = table('db_dbgroup_dbnodes')
DbGroup = table('db_dbgroup')
DbLink = table('db_dblink')
DbNode = table('db_dbnode')
DbLog = table('db_dblog')
DbAuthInfo = table('db_dbauthinfo')
DbUser = table('db_dbuser')
DbComputer = table('db_dbcomputer')

with self.transaction() as session:
session.execute(DbGroupNodes.delete())
session.execute(DbGroup.delete())
session.execute(DbLog.delete())
session.execute(DbLink.delete())
session.execute(DbNode.delete())
session.execute(DbAuthInfo.delete())
session.execute(DbComputer.delete())
session.execute(DbUser.delete())
session.commit()
19 changes: 7 additions & 12 deletions aiida/orm/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ def get_or_create_default(self, email=None, **kwargs):
"""
Get the default user or create an unstored one
:param email: The email of the default user (required only, if the profile has no default user)
Returns the default user and populates the `aiida.orm.User.objects._default_user` cache.
Does *not* update the default user email stored in the AiiDA profile.
:param email: The email of the default user. Will take defalt user email from profile if not supplied
:param kwargs: Remaining properties of the default user to get or create (optional)
:return: created, user object
:rtype: bool, :class:`aiida.orm.User`
Expand All @@ -88,21 +91,13 @@ def get_or_create_default(self, email=None, **kwargs):
)
return False, self._default_user

current_profile = get_profile()
email = email or current_profile.default_user
if not email:
raise ValueError(
f'Profile {current_profile} does not define a default user.' +
'Please specify email to create default user.'
)
if email != current_profile.default_user:
raise ValueError(
f'Specified email {email} does not match default user email {current_profile.default_user} ' +
f'of profile {current_profile}.'
)
# Note: this can be `None` as well (storing a user with email `None` is valid).
email = get_profile().default_user

created, default_user = self.get_or_create(email=email, **kwargs)
self._default_user = default_user

return created, default_user

def reset(self):
Expand Down
2 changes: 1 addition & 1 deletion tests/backends/aiida_sqlalchemy/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def set_connection(self, expire_on_commit=True):
# way of managing connections and sessions in SQLA...
# For instance, we should use probably a scopedsession wrapper
session = sessionmaker(expire_on_commit=expire_on_commit)
aiida.backends.sqlalchemy.sessionfactory = session(bind=self._AiidaTestCase__backend_instance.connection)
aiida.backends.sqlalchemy.sessionfactory = session(bind=None)

# Cleaning the database
self.clean_db()
Expand Down

0 comments on commit ccca6f6

Please sign in to comment.