From ecc16e6cbf10e1c09614b2b8c139a90c64d044e2 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 12 Aug 2025 13:01:58 +0930 Subject: [PATCH] test(docker): Add compose setup for integration testing ref: #947 #152 #774 --- .gitignore | 2 + makefile | 64 ++++++++++++++++++++++++++ test/docker-compose.yaml | 89 +++++++++++++++++++++++++++++++++++++ test/docker/centurion.sql | 2 + test/docker/settings.py | 79 ++++++++++++++++++++++++++++++++ test/page_speed.js | 0 test/parameterizedData.json | 0 test/setup-integration.sh | 70 +++++++++++++++++++++++++++++ 8 files changed, 306 insertions(+) create mode 100644 test/docker-compose.yaml create mode 100755 test/docker/centurion.sql create mode 100755 test/docker/settings.py mode change 100644 => 100755 test/page_speed.js mode change 100644 => 100755 test/parameterizedData.json create mode 100755 test/setup-integration.sh diff --git a/.gitignore b/.gitignore index 40a278c8..5b75fc07 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ log/ # Integration testing app/artifacts/ app/pyproject.toml +app/histogram_** +app/counter_** diff --git a/makefile b/makefile index 7f780aaa..cf3cebc8 100644 --- a/makefile +++ b/makefile @@ -69,6 +69,70 @@ lint: markdown-mkdocs-lint test: pytest --cov-report xml:artifacts/coverage_unit_functional.xml --cov-report html:artifacts/coverage/unit_functional/ --junit-xml=artifacts/unit_functional.JUnit.xml app/**/tests/unit app/**/tests/functional + + +test-integration: + export exit_code=0; + cp pyproject.toml app/; + sed -i 's|^source = \[ "./app" \]|source = [ "." ]|' app/pyproject.toml; + cd test; + if docker-compose up -d; then + + docker ps -a; + + chmod +x setup-integration.sh; + + if ./setup-integration.sh; then + + cd ..; + + ls -laR test/; + + docker exec -i centurion-erp supervisorctl stop gunicorn; + docker exec -i centurion-erp sh -c 'rm -rf /app/artifacts/* /app/artifacts/.[!.]*'; + docker exec -i centurion-erp supervisorctl start gunicorn; + sleep 30; + docker ps -a; + curl --trace-ascii - http://localhost:8003/api; + echo '--------------------------------------------------------------------'; + curl --trace-ascii - http://127.0.0.1:8003/api; + + + if [ "0${GITHUB_SHA}"!="0" ]; then + + sudo chmod 777 -R ./test + + fi; + + docker logs centurion-erp; + pytest --override-ini addopts= --no-migrations --tb=long --verbosity=2 --full-trace --showlocals --junit-xml=integration.JUnit.xml app/*/tests/integration; + docker exec -i centurion-erp supervisorctl restart gunicorn; + docker exec -i centurion-erp sh -c 'coverage combine; coverage report --skip-covered; coverage html -d artifacts/html/;'; + docker logs centurion-erp-init > ./test/volumes/log/docker-log-centurion-erp-init.log; + docker logs centurion-erp> ./test/volumes/log/docker-log-centurion-erp.log; + docker logs postgres > ./test/volumes/log/docker-log-postgres.log; + docker logs rabbitmq > ./test/volumes/log/docker-log-rabbitmq.log; + cd test; + + else + + echo 'Error: could not setup containers for testing'; + export exit_code=10; + + fi; + else + + echo 'Error: Failed to launch containers'; + export exit_code=20; + + fi; + cd test; + docker-compose down -v; + cd ..; + exit ${exit_code}; + + + test-functional: pytest --cov-report xml:artifacts/coverage_functional.xml --cov-report html:artifacts/coverage/functional/ --junit-xml=artifacts/functional.JUnit.xml app/**/tests/functional diff --git a/test/docker-compose.yaml b/test/docker-compose.yaml new file mode 100644 index 00000000..b078c969 --- /dev/null +++ b/test/docker-compose.yaml @@ -0,0 +1,89 @@ +--- + +x-app: ¢urion + image: ${CENTURION_IMAGE:-ghcr.io/nofusscomputing/centurion-erp}:${CENTURION_IMAGE_TAG:-dev} + volumes: + - ./docker/settings.py:/etc/itsm/settings.py:ro + + +services: + + + postgres: + image: ${CENTURION_POSTGRES_IMAGE:-postgres}:${CENTURION_POSTGRES_IMAGE_TAG:-13.21}-alpine # 14.18-alpine, 15.13-alpine, 16.9-alpine, 17.5-alpine + container_name: postgres + restart: always + environment: + POSTGRES_USER: admin + POSTGRES_PASSWORD: admin + expose: + - 5432 + volumes: + - ./docker/centurion.sql:/docker-entrypoint-initdb.d/centurion.sql:ro + + + rabbitmq: + image: ${CENTURION_RABBITMQ_IMAGE:-rabbitmq}:${CENTURION_RABBITMQ_IMAGE_TAG:-4.0.9}-management-alpine # 4.1.3-management-alpine + container_name: rabbitmq + environment: + - RABBITMQ_DEFAULT_USER=admin + - RABBITMQ_DEFAULT_PASS=admin + - RABBITMQ_DEFAULT_VHOST=itsm + expose: + - 5672 + ports: + # - "5672:5672" + - "15672:15672" + + + centurion-init: + <<: *centurion + container_name: centurion-erp-init + restart: "no" + entrypoint: "" + command: sh -c 'sleep 15; python manage.py migrate' + depends_on: + - postgres + - rabbitmq + + + centurion: + <<: *centurion + container_name: centurion-erp + restart: always + hostname: centurion-erp + volumes: + - ./volumes/log:/var/log:rw + - ./docker/settings.py:/etc/itsm/settings.py:ro + - ./volumes/data:/data:rw + - ./volumes/artifacts:/app/artifacts:rw + ports: + - "8003:8000" + depends_on: + - postgres + - rabbitmq + + + worker: + <<: *centurion + container_name: centurion-worker + restart: always + environment: + - IS_WORKER=true + hostname: centurion-worker + depends_on: + - postgres + - rabbitmq + - centurion + + centurion-ui: + image: ${CENTURION_UI_IMAGE:-ghcr.io/nofusscomputing/centurion-erp-ui}:${CENTURION_UI_IMAGE_TAG:-dev} + container_name: centurion-ui + restart: always + environment: + - API_URL=http://127.0.0.1:8003/api/v2 + hostname: centurion-ui + ports: + - "3000:80" + depends_on: + - centurion diff --git a/test/docker/centurion.sql b/test/docker/centurion.sql new file mode 100755 index 00000000..0caab97c --- /dev/null +++ b/test/docker/centurion.sql @@ -0,0 +1,2 @@ +CREATE DATABASE itsm; +GRANT ALL PRIVILEGES ON DATABASE itsm TO admin; diff --git a/test/docker/settings.py b/test/docker/settings.py new file mode 100755 index 00000000..cbaaa5f8 --- /dev/null +++ b/test/docker/settings.py @@ -0,0 +1,79 @@ +# ITSM Docker Settings + +# If metrics enabled, see https://nofusscomputing.com/projects/centurion_erp/administration/monitoring/#django-exporter-setup) +# to configure the database metrics. + +API_TEST = True + +AUTH_PASSWORD_VALIDATORS = [] + +CELERY_BROKER_URL = 'amqp://admin:admin@rabbitmq:5672/itsm' # 'amqp://' is the connection protocol + +CORS_ALLOW_CREDENTIALS = True + +CORS_ALLOW_METHODS = ( + "DELETE", + "GET", + "OPTIONS", + "PATCH", + "POST", + "PUT", +) + +CORS_ALLOWED_ORIGINS = [ + "http://127.0.0.1:3000", + "http://localhost:3000", + "http://127.0.0.1:8003", + "http://localhost:8003", + "http://127.0.0.1", +] + +CORS_EXPOSE_HEADERS = ['Content-Type', 'X-CSRFToken'] + +CSRF_COOKIE_SECURE = False + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'itsm', + 'USER': 'admin', + 'PASSWORD': 'admin', + 'HOST': 'postgres', + 'PORT': '5432', + } +} + +DEBUG = True + +FEATURE_FLAGGING_ENABLED = True # Turn Feature Flagging on/off + +FEATURE_FLAG_OVERRIDES = [] # Feature Flag Overrides. Takes preceedence over downloaded feature flags. + +LOG_FILES = { # Location where log files will be created + "centurion": "/var/log/centurion.log", + "weblog": "/var/log/weblog.log", + "rest_api": "/var/log/rest_api.log", + "catch_all":"/var/log/catch-all.log" +} + +METRICS_ENABLED = True + +SECRET_KEY = 'django-insecure-b*41-$afq0yl)1e#qpz^-nbt-opvjwb#avv++b9rfdxa@b55sk' + +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") + +SECURE_SSL_REDIRECT = False + +SESSION_COOKIE_SECURE = False + +SITE_URL = 'http://127.0.0.1:8003' + +TRUSTED_ORIGINS = [ + "http://127.0.0.1:3000", + "http://localhost:3000", + "http://127.0.0.1:8003", + "http://localhost:8003", + "http://127.0.0.1", +] + +USE_X_FORWARDED_HOST = True diff --git a/test/page_speed.js b/test/page_speed.js old mode 100644 new mode 100755 diff --git a/test/parameterizedData.json b/test/parameterizedData.json old mode 100644 new mode 100755 diff --git a/test/setup-integration.sh b/test/setup-integration.sh new file mode 100755 index 00000000..d1ccbf39 --- /dev/null +++ b/test/setup-integration.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +set -e + +docker exec -i centurion-erp pip install -r /requirements_test.txt + +docker exec -i centurion-erp supervisorctl restart gunicorn + + +CONTAINER_NAME="centurion-erp-init" +TIMEOUT=400 +INTERVAL=5 +ELAPSED=0 +STATUS="" + +while [ "$STATUS" != "exited" ] && [ "$STATUS" != "dead" ]; do + + STATUS=$(docker inspect --format '{{.State.Status}}' "$CONTAINER_NAME" 2>/dev/null || echo "not_found") + + + if [ "$STATUS" = "not_found" ]; then + docker ps -a + echo "Container $CONTAINER_NAME was not found." + exit 2 + fi + + if [ $ELAPSED -ge $TIMEOUT ]; then + echo "Timeout reached. Container $CONTAINER_NAME still running (status: $STATUS)." + exit 3 + fi + + echo "Waiting for container $CONTAINER_NAME to complete... Current status: $STATUS" + sleep $INTERVAL + ELAPSED=$((ELAPSED + INTERVAL)) +done + +echo "Container $CONTAINER_NAME has completed." + + +CONTAINER_NAME="centurion-erp" +TIMEOUT=90 +INTERVAL=5 +ELAPSED=0 +STATUS="" + +while [ "$STATUS" != "healthy" ]; do + STATUS=$(docker inspect --format '{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || echo "none") + + if [ $ELAPSED -ge $TIMEOUT ]; then + echo "Timeout reached. Container $CONTAINER_NAME is not healthy." + exit 4 + fi + + echo "Waiting for container $CONTAINER_NAME to be healthy... Current status: $STATUS" + sleep $INTERVAL + ELAPSED=$((ELAPSED + INTERVAL)) +done + +docker exec -i centurion-erp python manage.py createsuperuser --username admin --email admin@localhost --noinput + +docker exec -i centurion-erp apk add expect + +docker exec -i centurion-erp expect -c " + spawn python manage.py changepassword admin + expect \"Password:\" + send \"admin\r\" + expect \"Password (again):\" + send \"admin\r\" + expect eof + "