Skip to content
This repository was archived by the owner on Sep 12, 2025. It is now read-only.

Commit e2dbca4

Browse files
authored
Run end to end tests in their own container (#51)
Adds a bespoke end to end test container which depends on the successful completion of azurite setup. This means the azurite blob containers are present and writable. The e2e test container shares logs with the function apps which allows it to poll for expected log output indicating the functions have been triggered and the execution was successful.
1 parent 98fc6ba commit e2dbca4

File tree

7 files changed

+91
-82
lines changed

7 files changed

+91
-82
lines changed

compose.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,32 @@ services:
4242
profiles:
4343
- test
4444

45+
end-to-end-tests:
46+
container_name: end-to-end-tests
47+
network_mode: host
48+
build:
49+
context: ./tests/end_to_end/
50+
additional_contexts:
51+
root_dir: .
52+
dockerfile: Dockerfile
53+
environment:
54+
- AZURITE_CONNECTION_STRING=${AZURITE_CONNECTION_STRING}
55+
- BLOB_CONTAINER_NAME=${BLOB_CONTAINER_NAME}
56+
profiles:
57+
- test
58+
depends_on:
59+
azurite-setup:
60+
condition: service_completed_successfully
61+
azurite:
62+
condition: service_started
63+
notify:
64+
condition: service_started
65+
process-pilot-data:
66+
condition: service_started
67+
volumes:
68+
- ./log/functions/notify:/tests/end_to_end/log/functions/notify
69+
- ./log/functions/process-pilot-data:/tests/end_to_end/log/functions/process-pilot-data
70+
4571
# Functions
4672
message-status:
4773
container_name: message-status
@@ -71,6 +97,8 @@ services:
7197
- OAUTH2_API_KID=${OAUTH2_API_KID}
7298
- OAUTH2_TOKEN_URL=${OAUTH2_TOKEN_URL}
7399
- PRIVATE_KEY=${PRIVATE_KEY}
100+
volumes:
101+
- ./log/functions/notify:/tmp/Functions/Host
74102

75103
process-pilot-data:
76104
container_name: process-pilot-data
@@ -87,6 +115,8 @@ services:
87115
- BLOB_CONTAINER_NAME=${BLOB_CONTAINER_NAME}
88116
- CONTACT_TELEPHONE_NUMBER=${CONTACT_TELEPHONE_NUMBER}
89117
- NOTIFY_FUNCTION_URL=${NOTIFY_FUNCTION_URL}
118+
volumes:
119+
- ./log/functions/process-pilot-data:/tmp/Functions/Host
90120

91121
networks:
92122
frontend:

src/functions/notify/host.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
"extensionBundle": {
44
"id": "Microsoft.Azure.Functions.ExtensionBundle",
55
"version": "[4.*, 5.0.0)"
6+
},
7+
"logging": {
8+
"fileLoggingMode": "always",
9+
"logLevel": {
10+
"default": "Information",
11+
"Host.Results": "Error",
12+
"Function": "Trace",
13+
"Host.Aggregator": "Trace"
14+
}
615
}
716
}
817

src/functions/process_pilot_data/host.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
"extensionBundle": {
44
"id": "Microsoft.Azure.Functions.ExtensionBundle",
55
"version": "[4.*, 5.0.0)"
6+
},
7+
"logging": {
8+
"fileLoggingMode": "always",
9+
"logLevel": {
10+
"default": "Information",
11+
"Host.Results": "Error",
12+
"Function": "Trace",
13+
"Host.Aggregator": "Trace"
14+
}
615
}
716
}
817

test-end-to-end.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
docker compose --env-file .env.e2e --profile test run --build --quiet-pull end-to-end-tests pytest /tests/end_to_end
3+
test_exit_code=$?
4+
docker compose --env-file .env.e2e --profile test down --volumes --remove-orphans
5+
exit $test_exit_code

test.sh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,17 @@ install_pipenv_dependencies() {
126126

127127
run_all_test_suites() {
128128
tests_dir="tests/"
129-
pytest $tests_dir || {
129+
end_to_end_tests_dir="$tests_dir/end_to_end"
130+
end_to_end_tests_script="./test-end-to-end.sh"
131+
132+
pytest --ignore=$end_to_end_tests_dir $tests_dir || {
130133
echo "Tests failed in $tests_dir"
131134
exit 1
132135
}
136+
source $end_to_end_tests_script || {
137+
echo "End to end tests failed in $end_to_end_tests_dir"
138+
exit 1
139+
}
133140
}
134141

135142
# Actually run the things

tests/end_to_end/Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# To enable ssh & remote debugging on app service change the base image to the one below
2+
# FROM mcr.microsoft.com/azure-functions/python:4-python3.11-appservice
3+
FROM mcr.microsoft.com/azure-functions/python:4-python3.11
4+
5+
ENV DATABASE_HOST=${DATABASE_HOST} \
6+
DATABASE_USER=${DATABASE_USER} \
7+
DATABASE_PASSWORD=${DATABASE_PASSWORD} \
8+
DATABASE_NAME=${DATABASE_NAME}
9+
10+
11+
COPY --from=root_dir Pipfile /
12+
COPY --from=root_dir Pipfile.lock /
13+
RUN pip install pipenv
14+
RUN pipenv install --system
15+
RUN pipenv install --dev --system
16+
17+
COPY . /tests/end_to_end

tests/test_end_to_end.py renamed to tests/end_to_end/test_file_upload_to_notifying_recipients.py

Lines changed: 13 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,19 @@
11
import azure.storage.blob
22
import dotenv
3+
import glob
34
import logging
45
import os
56
import pytest
6-
import subprocess
77
import time
88

9-
"""
10-
End to end test for the azure functions using docker compose.
11-
To run the test with logging output use the following command:
12-
13-
pytest --log-cli-level=INFO tests/test_end_to_end.py
14-
"""
15-
169
ENV_FILE = os.getenv("ENV_FILE", ".env.e2e")
1710

1811

1912
@pytest.fixture()
2013
def setup():
21-
if not os.getenv("GITHUB_ACTIONS"):
22-
pytest.skip("Skipping end to end test, set GITHUB_ACTIONS env var to run this test")
23-
2414
dotenv.load_dotenv(dotenv_path=ENV_FILE)
2515

2616

27-
@pytest.fixture
28-
def docker():
29-
try:
30-
assert docker_compose_build(), "Error building containers"
31-
assert docker_compose_up()
32-
yield
33-
finally:
34-
docker_compose_down()
35-
36-
37-
def docker_arglist(command, *args):
38-
return ['docker', 'compose', '--env-file', ENV_FILE, '--profile', 'test', command, *args]
39-
40-
41-
def docker_compose_build():
42-
logging.info("Building containers")
43-
try:
44-
subprocess.run(
45-
docker_arglist('build', 'azurite', 'azurite-setup', 'notify', 'process-pilot-data'),
46-
check=True,
47-
stderr=subprocess.PIPE,
48-
stdout=subprocess.PIPE,
49-
text=True,
50-
)
51-
return True
52-
except Exception as e:
53-
logging.error(f"Error building containers: {e.stderr}")
54-
return False
55-
56-
57-
def docker_compose_up():
58-
logging.info("Starting containers")
59-
try:
60-
subprocess.Popen(
61-
docker_arglist('up', '-d'),
62-
stderr=subprocess.PIPE,
63-
stdout=subprocess.PIPE,
64-
)
65-
return True
66-
except Exception as e:
67-
logging.error(f"Error building containers: {e.stderr}")
68-
return False
69-
70-
71-
def docker_compose_down():
72-
logging.info("Stopping containers")
73-
subprocess.run(
74-
docker_arglist('down'),
75-
stdout=subprocess.PIPE,
76-
stderr=subprocess.PIPE,
77-
)
78-
79-
8017
def upload_file_to_blob_storage():
8118
logging.info("Uploading file to blob storage")
8219
try:
@@ -99,17 +36,17 @@ def upload_file_to_blob_storage():
9936
return True
10037

10138

39+
def log_dir_for_container(container_name):
40+
return f"/tests/end_to_end/log/functions/{container_name}"
41+
42+
10243
def logs_for_container(container_name):
103-
result = subprocess.run(
104-
docker_arglist('logs', container_name, '--since', '30s'),
105-
check=True,
106-
stderr=subprocess.PIPE,
107-
stdout=subprocess.PIPE,
108-
text=True,
109-
)
110-
logging.debug(f"Logs for {container_name}: {result.stdout}")
44+
logs = glob.glob(f"{log_dir_for_container(container_name)}/*.log")
45+
46+
assert logs, f"No logs found for container {container_name}"
11147

112-
return result.stdout
48+
with open(logs[0]) as f:
49+
return f.read()
11350

11451

11552
def logs_contain_message(container_name, message):
@@ -126,21 +63,16 @@ def poll_logs_for_message(container_name, message, cycles=20):
12663
return False
12764

12865

129-
def test_end_to_end(setup, docker):
130-
assert poll_logs_for_message("azurite-setup", "Blob containers created"), (
131-
"Containers not ready")
132-
133-
logging.info("Containers are ready")
134-
66+
def test_end_to_end(setup):
13567
assert upload_file_to_blob_storage(), "File upload unsuccessful"
13668

13769
assert poll_logs_for_message("process-pilot-data", "Trigger Details:"), (
13870
"ProcessPilotData function not triggered by file upload")
13971

140-
assert logs_contain_message(
72+
assert poll_logs_for_message(
14173
"process-pilot-data", "Executing 'Functions.ProcessPilotData'"), (
14274
"ProcessPilotData function not executed")
143-
assert logs_contain_message(
75+
assert poll_logs_for_message(
14476
"notify", "Executing 'Functions.Notify'"), (
14577
"Notify function not executed")
14678
assert poll_logs_for_message(

0 commit comments

Comments
 (0)