diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6aa7874..e20d949 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,8 @@ Note: Breaking changes between versions are indicated by "💥".
## Unreleased
+- [Feature] Add a `tutor images build --target=...` argument for [multi-stage Docker builds](https://docs.docker.com/develop/develop-images/multistage-build/).
+- [Feature] Create a test version of the openedx-dev Docker image for running edx-platform unit tests.
- [Security] Apply security patch [26112](https://github.com/edx/edx-platform/pull/26112)
- [Bugfix] Fix `local exec` command which crashed with a `AttributeError`.
diff --git a/docs/dev.rst b/docs/dev.rst
index ef5f2e2..9c19aed 100644
--- a/docs/dev.rst
+++ b/docs/dev.rst
@@ -236,3 +236,35 @@ You should then specify the settings to use on the host::
From then on, all ``dev`` commands will use the ``mysettings`` module. For instance::
tutor dev runserver lms
+
+Running edx-platform unit tests
+-------------------------------
+
+It's possible to run the full set of unit tests that ship with `edx-platform `__. To do so, you should first build the "test" target of the "openedx-dev" Docker image::
+
+ tutor images build --target=test openedx-dev
+
+.. warning::
+ Don't forget to re-build the development image afterwards if you'd like to run ``dev`` commands again! To do so, run ``tutor images build openedx-dev`` after you are done testing.
+
+Then, run unit tests with ``pytest`` commands::
+
+ # Run a test container
+ tutor dev run lms bash
+
+ # Run tests on common apps
+ unset DJANGO_SETTINGS_MODULE
+ export EDXAPP_TEST_MONGO_HOST=mongodb
+ pytest common
+ pytest openedx
+
+ # Run tests on LMS
+ export DJANGO_SETTINGS_MODULE=lms.envs.tutor.test
+ pytest lms
+
+ # Run tests on CMS
+ export DJANGO_SETTINGS_MODULE=cms.envs.tutor.test
+ pytest cms
+
+.. note::
+ Getting all edx-platform unit tests to pass on Tutor is currently a work-in-progress. Some unit tests are still failing. If you manage to fix some of these, please report your findings in the `Tutor forums `__.
diff --git a/tutor/commands/images.py b/tutor/commands/images.py
index 63bbaf7..ac2d6d3 100644
--- a/tutor/commands/images.py
+++ b/tutor/commands/images.py
@@ -45,8 +45,12 @@ def images_command():
multiple=True,
help="Set a custom host-to-IP mapping (host:ip).",
)
+@click.option(
+ "--target",
+ help="Set the target build stage to build.",
+)
@click.pass_obj
-def build(context, image_names, no_cache, build_args, add_hosts):
+def build(context, image_names, no_cache, build_args, add_hosts, target):
config = tutor_config.load(context.root)
command_args = []
if no_cache:
@@ -55,6 +59,8 @@ def build(context, image_names, no_cache, build_args, add_hosts):
command_args += ["--build-arg", build_arg]
for add_host in add_hosts:
command_args += ["--add-host", add_host]
+ if target:
+ command_args += ["--target", target]
for image in image_names:
build_image(context.root, config, image, *command_args)
diff --git a/tutor/templates/apps/openedx/settings/cms/test.py b/tutor/templates/apps/openedx/settings/cms/test.py
new file mode 100644
index 0000000..687d0fe
--- /dev/null
+++ b/tutor/templates/apps/openedx/settings/cms/test.py
@@ -0,0 +1 @@
+{% include "apps/openedx/settings/partials/common_test.py" %}
diff --git a/tutor/templates/apps/openedx/settings/lms/test.py b/tutor/templates/apps/openedx/settings/lms/test.py
new file mode 100644
index 0000000..687d0fe
--- /dev/null
+++ b/tutor/templates/apps/openedx/settings/lms/test.py
@@ -0,0 +1 @@
+{% include "apps/openedx/settings/partials/common_test.py" %}
diff --git a/tutor/templates/apps/openedx/settings/partials/common_test.py b/tutor/templates/apps/openedx/settings/partials/common_test.py
new file mode 100644
index 0000000..2716441
--- /dev/null
+++ b/tutor/templates/apps/openedx/settings/partials/common_test.py
@@ -0,0 +1,9 @@
+# TODO clean this up
+import os
+os.environ["EDXAPP_TEST_MONGO_HOST"] = "mongodb"
+
+from ..test import *
+
+# Fix MongoDb connection credentials
+DOC_STORE_CONFIG["user"] = None
+DOC_STORE_CONFIG["password"] = None
diff --git a/tutor/templates/build/openedx-dev/Dockerfile b/tutor/templates/build/openedx-dev/Dockerfile
index 84bc198..342b7b2 100644
--- a/tutor/templates/build/openedx-dev/Dockerfile
+++ b/tutor/templates/build/openedx-dev/Dockerfile
@@ -1,4 +1,4 @@
-FROM {{ DOCKER_IMAGE_OPENEDX }}
+FROM {{ DOCKER_IMAGE_OPENEDX }} as base
MAINTAINER Overhang.io
# Install useful system requirements
@@ -28,5 +28,15 @@ RUN chmod a+x /openedx/bin/*
ARG USERID=1000
RUN create-user.sh $USERID
+######## Test image
+FROM base as test
+
+# Run all tests by default
+ENTRYPOINT ["docker-entrypoint-test.sh"]
+CMD pytest
+
+######## Development image
+FROM base as dev
+
# Default django settings
ENV SETTINGS tutor.development
diff --git a/tutor/templates/build/openedx-dev/bin/docker-entrypoint-test.sh b/tutor/templates/build/openedx-dev/bin/docker-entrypoint-test.sh
new file mode 100644
index 0000000..cabbf76
--- /dev/null
+++ b/tutor/templates/build/openedx-dev/bin/docker-entrypoint-test.sh
@@ -0,0 +1,8 @@
+#!/bin/sh -e
+
+# Unset service variantm, which otherwise causes some unit tests to fail
+unset SERVICE_VARIANT
+# This variable should be set adequately depending on the set of unit tests to run
+unset DJANGO_SETTINGS_MODULE
+
+exec "$@"
diff --git a/tutor/templates/dev/docker-compose.yml b/tutor/templates/dev/docker-compose.yml
index 228ed08..a8d5b6a 100644
--- a/tutor/templates/dev/docker-compose.yml
+++ b/tutor/templates/dev/docker-compose.yml
@@ -3,8 +3,6 @@ version: "3.7"
x-openedx-service:
&openedx-service
image: {{ DOCKER_IMAGE_OPENEDX_DEV }}
- environment:
- SETTINGS: ${TUTOR_EDX_PLATFORM_SETTINGS:-tutor.development}
volumes:
# Settings & config
- ../apps/openedx/settings/lms/:/openedx/edx-platform/lms/envs/tutor/:ro
@@ -44,4 +42,4 @@ services:
command: openedx-assets watch-themes --env dev
restart: unless-stopped
- {{ patch("local-docker-compose-dev-services")|indent(2) }}
\ No newline at end of file
+ {{ patch("local-docker-compose-dev-services")|indent(2) }}