diff --git a/ci/create-new-project b/ci/create-new-project
index 62b07fcfd1c..f8d0304c5e9 100755
--- a/ci/create-new-project
+++ b/ci/create-new-project
@@ -1,5 +1,7 @@
#!/bin/bash -eux
+echo ::group::Create the project
+
TAG=$(scripts/get-version --major)
DOCKER_TAG=${TAG} make build-tools
DOCKER_TAG=${TAG} make build-runner
@@ -37,8 +39,6 @@ if [ $BASE_APP != true ]; then
mv ${WORKSPACE}/${PACKAGE}/geoportal/vars.yaml ${WORKSPACE}/${PACKAGE}/geoportal/vars_origin.yaml
cp ci/vars.yaml ${WORKSPACE}/${PACKAGE}/geoportal/
mkdir -p ${WORKSPACE}/${PACKAGE}/geoportal/ci
- cp ci/waitwsgi ${WORKSPACE}/${PACKAGE}/geoportal/ci
- cp ci/test-new-project ${WORKSPACE}/${PACKAGE}/geoportal/ci
mkdir -p ${WORKSPACE}/${PACKAGE}/ci
echo "PGSSLMODE=prefer" >> ${WORKSPACE}/${PACKAGE}/env.project
fi
@@ -75,12 +75,15 @@ git add --all
git commit --quiet --message='Initial commit'
mv ci/config.yaml{,_}
+echo ::endgroup::
c2cciutils-checks --fix --check=isort
c2cciutils-checks --fix --check=black
c2cciutils-checks --fix --check=prettier
mv ci/config.yaml{_,}
+echo ::group::Diff
git --no-pager diff
+echo ::endgroup::
git add --all
git commit --quiet --message='Format'
git clean -fX
diff --git a/ci/test-app b/ci/test-app
index 6fb66707ed4..61c79355899 100755
--- a/ci/test-app
+++ b/ci/test-app
@@ -3,8 +3,9 @@
rm -rf "${HOME}/workspace/testgeomapfishapp"
ci/create-new-project "${HOME}/workspace"
+
+# Copy the files needed to run the tests
cp ci/docker-compose.override.yaml "${HOME}/workspace/testgeomapfishapp/"
-cp ci/waitwsgi "${HOME}/workspace/testgeomapfishapp/ci/"
cp ci/empty.qgz "${HOME}/workspace/testgeomapfishapp/qgisserver/project.qgz"
cp -r ci/test-app-db "${HOME}/workspace/testgeomapfishapp/ci/"
cp -r ci/test-external-db "${HOME}/workspace/testgeomapfishapp/ci/"
@@ -14,12 +15,21 @@ cd "${HOME}/workspace/testgeomapfishapp/"
folder=geoportal/interfaces
cp -r CONST_create_template/${folder} ${folder}
+file=tests/test_testapp.py
+cp CONST_create_template/${file} ${file}
+# Check if the `c2cciutils-checks` pass
c2cciutils-checks
cp "${PROJECT_DIR}/ci/project-config.yaml" ci/config.yaml
c2cciutils-checks
+
+# Build
+echo ::group::Build
CI=true ./build
+echo ::endgroup::
+# Lint
+echo ::group::Lint
docker-compose run --entrypoint= --no-deps --rm --volume=$(pwd)/geoportal:/app geoportal \
prospector --output-format=pylint --die-on-tool-error
docker-compose run --entrypoint= --no-deps --rm --volume=$(pwd)/geoportal:/app geoportal \
@@ -28,69 +38,124 @@ docker-compose run --entrypoint= --no-deps --rm --volume=$(pwd)/geoportal:/app g
eslint $(find testgeomapfishapp -type f -name '*.ts' -print 2> /dev/null)
docker-compose down --volumes
+echo ::endgroup::
+
+# Start
+echo ::group::Start
docker-compose up -d
docker-compose exec -T geoportal bash -c 'PGHOST=externaldb PGDATABASE=test wait-db;'
docker-compose exec -T geoportal wait-db
-docker-compose ps
+echo ::endgroup::
+c2cciutils-docker-logs
+
+# Run alembic
+echo ::group::Run alembic
docker-compose exec -T geoportal alembic --config=alembic.ini --name=main upgrade head
docker-compose exec -T geoportal alembic --config=alembic.ini --name=static upgrade head
+docker-compose start alembic
+echo ::endgroup::
+c2cciutils-docker-logs
+
+# Create the Theme
+echo ::group::Create the Theme
docker-compose exec -T geoportal create-demo-theme
+echo ::endgroup::
c2cciutils-docker-logs
+
+# Test `theme2fts`
+echo ::group::Test theme2fts
docker-compose exec -T geoportal theme2fts
+echo ::endgroup::
+
+# Test `update-po`
+echo ::group::Test update-po
make update-po
+echo ::endgroup::
-docker-compose exec -T geoportal ci/waitwsgi https://front/themes
-for path in c2c/health_check c2c/health_check?max_level=9 c2c/health_check?checks=check_collector "layers/test/values/type enum" admin/layertree admin/layertree/children; do
- docker-compose exec -T geoportal ci/test-new-project https://front/${path}
+# Test the checker and QGIS
+echo ::group::Test the checker and QGIS
+for url in 'https://front/c2c/health_check?max_level=9' 'http://qgisserver:8080/mapserv_proxy?SERVICE=WMS&REQUEST=GetCapabilities'; do
+ docker-compose exec -T geoportal curl --fail --insecure "${url}" \
+ || (docker-compose exec -T geoportal curl --insecure "${url}" && false)
done
-docker-compose exec -T geoportal ci/test-new-project 'http://qgisserver:8080/mapserv_proxy?SERVICE=WMS&REQUEST=GetCapabilities'
-docker-compose exec -T geoportal curl --insecure https://front/admin/ | grep Login
-docker-compose exec -T geoportal curl --insecure 'https://front/dynamic.json?interface=desktop&query=&path=%2F'
-docker-compose exec -T geoportal curl --insecure https://front/static-geomapfish/0/locales/fr.json
-docker-compose exec -T geoportal curl --insecure https://front/desktop_alt
-docker-compose exec -T geoportal curl --insecure https://front/desktop_alt \
- | grep ''
-docker-compose exec -T geoportal curl --insecure https://front/desktop_alt \
- | grep ' /tmp/locale.pot
- if [ "$(wc -l < /tmp/locale.pot)" -eq "$(wc -l < geoportal/testgeomapfishapp_geoportal/locale/testgeomapfishapp_geoportal-client.pot)" ]; then
- echo 'View locale.pot match in size with update-po'
- else
- echo 'Error: View locale.pot does not match in size with update-po'
- sed -i 's%/app/%./geoportal/%g' /tmp/locale.pot
- diff geoportal/testgeomapfishapp_geoportal/locale/testgeomapfishapp_geoportal-client.pot /tmp/locale.pot
- exit 1
- fi
-}
-test_pot
-# A second time to check that the initialisation is working
-test_pot
+echo ::endgroup::
+# Test the `proutes` and `pviews` commands
+echo ::group::Test the proutes command with development.ini
docker-compose exec -T geoportal proutes c2c://development.ini
+echo ::endgroup::
+echo ::group::Test the pviews command with development.ini
docker-compose exec -T geoportal pviews c2c://development.ini /
+echo ::endgroup::
+echo ::group::Test the proutes command with production.ini
docker-compose exec -T geoportal proutes c2c://production.ini
+echo ::endgroup::
+echo ::group::Test the pviews command with production.ini
docker-compose exec -T geoportal pviews c2c://production.ini /
+echo ::endgroup::
-docker-compose stop qgisserver
-docker-compose exec -T geoportal alembic --config=alembic.ini --name=static downgrade base
-docker-compose exec -T geoportal alembic --config=alembic.ini --name=main downgrade base
-
+# Test the `db-backup` and `db-restore` commands
+echo ::group::Test the db-backup command
cp .env env.int
echo PGHOST_SLAVE="$(ip -4 addr show docker0 | grep -Po 'inet \K[\d.]+')" >> env.int
echo PGPORT_SLAVE=54321 >> env.int
scripts/db-backup --env=env.int dump.backup
-scripts/db-backup --env=env.int --arg=--schema=main dump.backup
+scripts/db-backup --env=env.int --arg=--schema=main main-dump.backup
+echo ::endgroup::
+
+echo ::group::Test the db-restore command
docker-compose exec -T tools psql --command="ALTER SCHEMA main RENAME TO main_old"
docker-compose exec -T tools psql --command="CREATE SCHEMA main"
scripts/db-restore --arg=--schema=main dump.backup
+echo ::endgroup::
+# Empty the database
+echo ::group::Empty the database
+docker-compose exec -T geoportal alembic --config=alembic.ini --name=static downgrade base
+docker-compose exec -T geoportal alembic --config=alembic.ini --name=main downgrade base
+echo ::endgroup::
+
+# Test the `mappyfile validate` command
+echo ::group::Test the mappyfile validate command
docker-compose exec -T tools rm /etc/mapserver/demo.map
docker-compose exec -T tools mappyfile validate --version=7.6 /etc/mapserver/*.map
+echo ::endgroup::
c2cciutils-docker-logs
+
+# Run the acceptances tests
+echo ::group::Stop
docker-compose down
-sudo rm -rf "${HOME}/workspace/testgeomapfishapp"
+mv docker-compose.override.yaml docker-compose.test.yaml
+echo ::endgroup::
+
+echo ::group::Init acceptance tests
+make acceptance-init
+docker-compose \
+ --file=docker-compose.yaml \
+ --file=docker-compose-db.yaml \
+ --file=docker-compose.test.yaml up -d
+echo ::endgroup::
+c2cciutils-docker-logs
+
+echo ::group::Run acceptance tests
+make acceptance
+echo ::endgroup::
+c2cciutils-docker-logs
+echo ::group::Run acceptance tests in dev mode
+docker-compose \
+ --file=docker-compose.yaml \
+ --file=docker-compose-db.yaml \
+ --file=docker-compose.test.yaml \
+ --file=docker-compose.override.sample.yaml up -d
+make acceptance
+echo ::endgroup::
+c2cciutils-docker-logs
+
+# Remove the test project
+echo ::group::Remove the test project
+docker-compose down --remove-orphans || true
+cd -
+sudo rm -rf "${HOME}/workspace/testgeomapfishapp"
docker rmi camptocamp/testgeomapfishapp-config:latest
+echo ::endgroup::
diff --git a/ci/test-new-project b/ci/test-new-project
deleted file mode 100755
index 0efb5f47371..00000000000
--- a/ci/test-new-project
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (c) 2011-2017, Camptocamp SA
-# All rights reserved.
-
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-
-# 1. Redistributions of source code must retain the above copyright notice, this
-# list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# The views and conclusions contained in the software and documentation are those
-# of the authors and should not be interpreted as representing official policies,
-# either expressed or implied, of the FreeBSD Project.
-
-import sys
-import time
-
-import requests
-
-initial_error_length = 0
-initial_access_length = 0
-
-url = sys.argv[1]
-
-for _ in range(10):
- print(f"Query {url}")
- # See also:
- # ci/vars.yaml geoportal/GUNICORN_PARAMS
- result = requests.get(url, timeout=240, verify=False) # nosec
- if result.ok:
- break
- time.sleep(1)
-
-if len(sys.argv) == 3 and sys.argv[2] == "enum":
- if result.ok and result.json() == {"items": [{"value": "car"}, {"value": "train"}]}:
- print("enum OK")
- sys.exit()
- else:
- print(f"Incorrect result ({result.status_code}):")
- print(result.text)
- sys.exit(2)
-
-if result.ok:
- print("OK")
-else:
- print("NOT OK")
- print(result.text)
- sys.exit(2)
diff --git a/ci/waitwsgi b/ci/waitwsgi
deleted file mode 100755
index 1ca4d4c13f0..00000000000
--- a/ci/waitwsgi
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (c) 2011-2017, Camptocamp SA
-# All rights reserved.
-
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-
-# 1. Redistributions of source code must retain the above copyright notice, this
-# list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# The views and conclusions contained in the software and documentation are those
-# of the authors and should not be interpreted as representing official policies,
-# either expressed or implied, of the FreeBSD Project.
-
-import sys
-import time
-
-import requests
-
-sleep_time = 1
-max_sleep = 30
-
-# Wait for the WSGI to be UP
-while sleep_time < max_sleep:
- print("Waiting for the WSGI server to be reachable")
- sys.stdout.flush()
- try:
- requests.get(sys.argv[1], timeout=1, verify=False) # nosec
- sys.exit(0)
- except Exception as e:
- print(str(e))
- print("Waiting for the WSGI server to be reachable")
- sys.stdout.flush()
- time.sleep(sleep_time)
- sleep_time *= 2
-sys.exit(1)
diff --git a/doc/integrator/create_application.rst b/doc/integrator/create_application.rst
index a2a09393fcd..28d6de3693f 100644
--- a/doc/integrator/create_application.rst
+++ b/doc/integrator/create_application.rst
@@ -294,7 +294,57 @@ Then follow the sections in the install application guide:
Dynamic configuration
---------------------
-Several files are generated on runtime, their content depending of the variables you
+Several files are generated on runtime, their content depending on the variables you
have set as environment variables.
The files can have the extension ``.tmpl`` and it use bash syntax (``${VARIABLE}``).
+
+GitHub workflows
+----------------
+
+With the application we have some predefined workflows.
+
+`.github/workflows/main.yaml`
+.............................
+
+The workflow that will run on all your commits, he will:
+- Run some code style checks on your code.
+- Build you application.
+- Run the acceptance tests (if configured).
+- Publish the application on DockerHub.
+- Trigger another workflow (on ArgoCD repository) to deploy you new application.
+
+`.github/workflows/rebuild.yaml`
+................................
+
+This workflow run on each night to rebuild the application with the new version of the base images.
+
+Be careful, GitHub will read only the file present on the main branch.
+
+`.github/workflows/update_l10n.yaml`
+....................................
+
+This workflow will query on of your server on the URL configured in the `Makefile` as
+`PROJECT_PUBLIC_URL` the get the available list of string that should be translated (`.pot`)
+and open a pull request with the new translations key tin your localized files (`.po`).
+
+Be careful, GitHub will read only the file present on the main branch.
+
+Acceptance tests
+................
+
+To have some acceptance tests you need to have a minimal dump of your application in the repository,
+it can be obtained with:
+
+.. prompt:: bash
+
+ scripts/db-backup --arg=--schema= ../dump.backup
+
+In the `Makefile` you should configure the dump file as `DUMP_FILE`, the `db-restore` call in `acceptance-init`
+should probably also be updated.
+
+In the file `.github/workflows/main.yaml` you should uncomment all the lines related to the
+acceptance tests.
+
+The acceptance tests will test that we didn't have any service in error, test the response of some URL,
+see in the file `tests/test_app.py`.
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml
index a2d8bdcf18e..43aab613815 100644
--- a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml
@@ -6,10 +6,6 @@ on:
env:
PROJECT: {{cookiecutter.package}}
- # Requires CI_GPG_PRIVATE_KEY and GOPASS_CI_GITHUB_TOKEN secrets.
- # OPENSHIFT_PROJECT: gs-gmf-{{cookiecutter.package}}
- # The release branches
- HELM_RELEASE_NAMES: int-{{cookiecutter.geomapfish_main_version_dash}},prod-{{cookiecutter.geomapfish_main_version_dash}}
jobs:
main:
@@ -30,9 +26,23 @@ jobs:
- run: echo "${HOME}/.local/bin" >> ${GITHUB_PATH}
- run: python3 -m pip install --user --requirement=ci/requirements.txt
+ # Can be used to have some secrets (with mask)
+ # - run: make secrets
+ # - run: cat env.secrets |grep '^[# A-Z0-9_]\+='|sed -e 's/^[# A-Z0-9_]\+=\(.*\)/::add-mask::\1/g'
+
- name: Checks
run: c2cciutils-checks
+ # - name: Initialize the acceptance tests
+ # run: make acceptance-init
+ # - run: c2cciutils-docker-logs
+ # if: always()
+
+ # - name: Run the acceptance tests
+ # run: make acceptance
+ # - run: c2cciutils-docker-logs
+ # if: always()
+
- name: Build
run: ./build
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile
index 0f507275ca6..eb89f66a711 100644
--- a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile
@@ -1,4 +1,5 @@
PROJECT_PUBLIC_URL=https://example.camptocamp.com/
+DUMP_FILE=dump.backup
PACKAGE={{cookiecutter.package}}
LANGUAGES=en fr de it
@@ -34,3 +35,34 @@ eslint: ## Runs the eslint checks
eslint $(find {{cookiecutter.package}} -type f -name '*.js' -print 2> /dev/null)
docker-compose run --entrypoint= --no-deps --rm --volume=$(pwd)/geoportal:/app geoportal \
eslint $(find {{cookiecutter.package}} -type f -name '*.ts' -print 2> /dev/null)
+
+.PHONY: qgis
+qgis: ## Run QGIS desktop
+ docker-compose -f docker-compose.yaml -f docker-compose-qgis.yaml run --rm qgis
+
+secrets.tar.bz2.gpg: env.secrets ## Create the secrets.tar.bz2.gpg files we depends on
+ tar -jcf secrets.tar.bz2 $^
+ rm -f $@
+ gpg --symmetric --cipher-algo AES256 --batch \
+ --passphrase=$(shell gopass show gs/ci/large-secret-passphrase) secrets.tar.bz2
+ rm secrets.tar.bz2
+
+.PHONY: secrets
+secrets: ## Decript the sectrets.tar.bz2.gpg file
+ gpg --quiet --batch --yes --decrypt --passphrase=$(shell gopass show gs/ci/large-secret-passphrase) \
+ --output secrets.tar.bz2 secrets.tar.bz2.gpg
+ tar --touch -jxf secrets.tar.bz2
+ rm secrets.tar.bz2
+
+.PHONY: acceptance-init
+acceptance-init: ## Initialize the acceptance tests
+ docker-compose --file=docker-compose.yaml --file=docker-compose-db.yaml up -d
+ docker-compose exec -T geoportal wait-db
+ docker-compose exec -T tools psql --command="DROP EXTENSION IF EXISTS postgis CASCADE"
+ scripts/db-restore --docker-compose-file=docker-compose.yaml --docker-compose-file=docker-compose-db.yaml \
+ --arg=--clean --arg=--if-exists --arg=--verbose $(DUMP_FILE)
+
+.PHONY: acceptance
+acceptance: ## Run the acceptance tests
+ docker-compose exec -T tools pytest -vv tests/
+ ci/docker-compose-check
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/docker-compose-check b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/docker-compose-check
new file mode 100755
index 00000000000..5feb9c31b3c
--- /dev/null
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/docker-compose-check
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+
+import argparse
+import subprocess
+import sys
+
+
+def _main() -> None:
+ argparser = argparse.ArgumentParser("Tests that's all the services are running")
+ argparser.parse_args()
+
+ services = [
+ s.strip()
+ for s in subprocess.run(["docker-compose", "ps"], check=True, stdout=subprocess.PIPE)
+ .stdout.decode("utf-8")
+ .splitlines()
+ ]
+ errors_statuses = [s for s in services if " Exit " in s and not s.endswith(" Exit 0")]
+ if errors_statuses:
+ print("\n".join(errors_statuses))
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ _main()
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-db.yaml b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-db.yaml
new file mode 100644
index 00000000000..6e5e992218d
--- /dev/null
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-db.yaml
@@ -0,0 +1,26 @@
+# This file is used by the acceptance tests to have a local database.
+
+version: '2.3'
+
+volumes:
+ postgresql_data:
+
+services:
+ config: &db-config
+ environment:
+ - PGHOST=db
+ - PGHOST_SLAVE=db
+ - PGSSLMODE=prefer
+ geoportal: *db-config
+ # geoportal-advance: *db-config
+ tools: *db-config
+ alembic: *db-config
+ # alembic-advance: *db-config
+ # webpack_dev_server: *db-config
+
+ db:
+ extends:
+ file: docker-compose-lib.yaml
+ service: db
+ volumes:
+ - postgresql_data:/var/lib/postgresql/data
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml
index 19aac8d7704..febefadfeb8 100644
--- a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml
@@ -1,4 +1,5 @@
----
+# This files is managed by c2cgeoportal, he contains the default services configuration
+
version: '2.3'
services:
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml
new file mode 100644
index 00000000000..af3860dd718
--- /dev/null
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml
@@ -0,0 +1,23 @@
+# This file is used to run QGIS client from the Docker image
+
+version: '2'
+
+services:
+ qgis:
+ extends:
+ file: docker-compose-lib.yaml
+ service: qgisserver
+ image: camptocamp/qgis-server:${QGIS_VERSION}-desktop
+ user: root
+ volumes_from:
+ - config
+ volumes:
+ - ${HOME}:${HOME}
+ - /tmp/.X11-unix:/tmp/.X11-unix
+ environment:
+ - PGSERVICEFILE=/etc/qgisserver/pg_service.conf
+ - DISPLAY=unix${DISPLAY}
+ - QGIS_DEBUG=0
+ - QGIS_LOG_FILE=/dev/null
+ - PGOPTIONS=-c statement_timeout=30000
+ - AZURE_STORAGE_CONNECTION_STRING
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml
index c346b42afc0..cb1a9227ad3 100644
--- a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml
@@ -1,4 +1,3 @@
----
# This file can be renamed as `docker-compose.override.yaml` and uncomment the desired lines for
# development. The file `docker-compose.override.yaml` is ignored by Git by default.
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.yaml b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.yaml
index 46a3f639302..a9e110330da 100644
--- a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.yaml
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.yaml
@@ -1,5 +1,4 @@
----
-# The project Docker compose file for development.
+# The project Docker compose file.
version: '2.3'
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/__init__.py b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/test_app.py b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/test_app.py
new file mode 100644
index 00000000000..fa59c6164d2
--- /dev/null
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/test_app.py
@@ -0,0 +1,43 @@
+import time
+from typing import Dict
+
+import pytest
+import requests
+
+
+@pytest.mark.parametrize(
+ "url,params",
+ [
+ ("https://front", {}),
+ ("https://front/themes", {}),
+ ("https://front/static-geomapfish/0/locales/fr.json", {}),
+ ("https://front/dynamic.json", {"interface": "desktop"}),
+ ("https://front/dynamic.json", {"interface": "desktop", "query": "", "path": "/"}),
+ ("https://front/c2c/health_check", {}),
+ ("https://front/c2c/health_check", {"max_level": "1"}),
+ ("https://front/c2c/health_check", {"checker": "check_collector"}),
+ ("https://front/admin/layertree", {}),
+ ("https://front/admin/layertree/children", {}),
+ ("http://mapserver:8080/mapserv_proxy", {"SERVICE": "WMS", "REQUEST": "GetCapabilities"}),
+ (
+ "https://front/mapserv_proxy",
+ {"ogcserver": "source for image/png", "SERVICE": "WMS", "REQUEST": "GetCapabilities"},
+ ),
+ ],
+)
+def test_url(url: str, params: Dict[str, str]) -> None:
+ """Tests that some URL didn't return an error."""
+ for _ in range(60):
+ response = requests.get(url, params=params, verify=False, timeout=240) # nosec
+ if response.status_code == 503:
+ time.sleep(1)
+ continue
+ break
+ assert response.status_code == 200, response.text
+
+
+def test_admin() -> None:
+ """Tests that the admin page will provide the login page."""
+ response = requests.get("https://front/admin/", verify=False, timeout=240) # nosec
+ assert response.status_code == 200, response.text
+ assert "Login" in response.text
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml b/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml
index efa803582a1..1e4868ac0bd 100644
--- a/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml
@@ -44,6 +44,7 @@ extra:
- geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/js/apps/sass/vars_oeedit\.scss
- geoportal/interfaces/desktop_alt\.html\.mako
- geoportal/{{cookiecutter.package}}_geoportal/static/images/background-layer-button\.png
+ - tests/test_testapp.py
# Automated file system operations:
# Remove some files or directories:
diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py b/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py
new file mode 100644
index 00000000000..48d5c0f87cc
--- /dev/null
+++ b/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py
@@ -0,0 +1,49 @@
+# This file should be used only on the test project in the c2cgeoportal CI
+
+import re
+
+import polib
+import pytest
+import requests
+
+
+@pytest.mark.parametrize("test_number", [0, 1])
+def test_po(test_number: int) -> None:
+ """Tests that the generated pot files are identical between the command lin and the view."""
+ del test_number
+
+ response = requests.get("https://front/locale.pot", verify=False, timeout=240) # nosec
+ assert response.status_code == 200, response.text
+ response_keys = {e.msgid for e in polib.pofile(response.text)}
+
+ with open(
+ "geoportal/{{cookiecutter.package}}_geoportal/locale/{{cookiecutter.package}}_geoportal-client.pot",
+ "r",
+ encoding="utf-8",
+ ) as current_file:
+ current_content = current_file.read()
+ current_content_keys = {e.msgid for e in polib.pofile(current_content)}
+
+ if response_keys != current_content_keys:
+ assert response.text == current_content
+
+
+@pytest.mark.parametrize("url", ["https://front/desktop_alt"])
+def test_desktop_alt(url: str) -> None:
+ """Tests the desktop alt page."""
+ response = requests.get(url, verify=False, timeout=240) # nosec
+ assert response.status_code == 200, response.text
+
+ assert re.search(
+ r'',
+ response.text,
+ ), response.text
+ assert re.search(r' None:
+ """Test the enumerations view"""
+ response = requests.get("https://front/layers/test/values/type", verify=False, timeout=240) # nosec
+ assert response.status_code == 200, response.text
+
+ assert response.json() == {"items": [{"value": "car"}, {"value": "train"}]}, response.text