diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/base_test.py b/tests/base_test.py
new file mode 100644
index 0000000..7207443
--- /dev/null
+++ b/tests/base_test.py
@@ -0,0 +1,81 @@
+"""
+ARCHES - a program developed to inventory and manage immovable cultural heritage.
+Copyright (C) 2013 J. Paul Getty Trust and World Monuments Fund
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+"""
+
+from contextlib import contextmanager
+
+from arches.app.models.system_settings import settings
+from arches.app.utils.context_processors import app_settings
+from django.test.runner import DiscoverRunner
+
+from arches.app.search.mappings import (
+ prepare_terms_index,
+ delete_terms_index,
+ prepare_concepts_index,
+ delete_concepts_index,
+ prepare_search_index,
+ delete_search_index,
+)
+
+# these tests can be run from the command line via
+# python manage.py test tests --pattern="*.py" --settings="tests.test_settings"
+
+
+class ArchesTestRunner(DiscoverRunner):
+ def __init__(self, *args, **kwargs) -> None:
+ kwargs["debug_mode"] = True
+ # Unless the user has something other than the Django default, give them
+ # what they probably want.
+ if kwargs["pattern"] == "test*.py":
+ kwargs["pattern"] = "*.py"
+ super().__init__(*args, **kwargs)
+
+ def setup_databases(self, **kwargs):
+ ret = super().setup_databases(**kwargs)
+
+ # Some tests don't use the database.
+ if kwargs.get("aliases", None):
+ app_settings() # adds languages to system
+ prepare_terms_index(create=True)
+ prepare_concepts_index(create=True)
+ prepare_search_index(create=True)
+
+ return ret
+
+ def teardown_databases(self, old_config, **kwargs):
+ delete_terms_index()
+ delete_concepts_index()
+ delete_search_index()
+
+ super().teardown_databases(old_config, **kwargs)
+
+
+@contextmanager
+def sync_overridden_test_settings_to_arches():
+ """Django's @override_settings test util acts on django.conf.settings,
+ which is not enough for us, because we use SystemSettings at runtime.
+
+ This context manager swaps in the overridden django.conf.settings for SystemSettings.
+ """
+ from django.conf import settings as patched_settings
+
+ original_settings_wrapped = settings._wrapped
+ try:
+ settings._wrapped = patched_settings._wrapped
+ yield
+ finally:
+ settings._wrapped = original_settings_wrapped
diff --git a/tests/test_settings.py b/tests/test_settings.py
new file mode 100644
index 0000000..c2d19ee
--- /dev/null
+++ b/tests/test_settings.py
@@ -0,0 +1,73 @@
+"""
+ARCHES - a program developed to inventory and manage immovable cultural heritage.
+Copyright (C) 2013 J. Paul Getty Trust and World Monuments Fund
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+"""
+
+import os
+
+from disco.settings import *
+
+PACKAGE_NAME = "disco"
+
+PROJECT_TEST_ROOT = os.path.dirname(__file__)
+MEDIA_ROOT = os.path.join(PROJECT_TEST_ROOT, "fixtures", "data")
+
+BUSINESS_DATA_FILES = (
+ # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+)
+
+DATABASES = {
+ "default": {
+ "ATOMIC_REQUESTS": False,
+ "AUTOCOMMIT": True,
+ "CONN_MAX_AGE": 0,
+ "ENGINE": "django.contrib.gis.db.backends.postgis",
+ "HOST": "localhost",
+ "NAME": "disco",
+ "OPTIONS": {},
+ "PASSWORD": "postgis",
+ "PORT": "5432",
+ "POSTGIS_TEMPLATE": "template_postgis",
+ "TEST": {"CHARSET": None, "COLLATION": None, "MIRROR": None, "NAME": None},
+ "TIME_ZONE": None,
+ "USER": "postgres",
+ }
+}
+
+CACHES = {
+ "default": {
+ "BACKEND": "django.core.cache.backends.dummy.DummyCache",
+ },
+ "user_permission": {
+ "BACKEND": "django.core.cache.backends.dummy.DummyCache",
+ "LOCATION": "user_permission_cache",
+ },
+}
+
+LOGGING["loggers"]["arches"]["level"] = "ERROR"
+
+ELASTICSEARCH_PREFIX = "test"
+
+TEST_RUNNER = "tests.base_test.ArchesTestRunner"
+SILENCED_SYSTEM_CHECKS.append(
+ "arches.W001", # Cache backend does not support rate-limiting
+)
+
+ELASTICSEARCH_HOSTS = [
+ {"scheme": "http", "host": "localhost", "port": ELASTICSEARCH_HTTP_PORT}
+]