Skip to content

Commit 4d7e3cf

Browse files
author
Nils Bars
committed
Add E2E test for Docker resource prefix verification
Verify that Docker resources created during tests use the correct test-specific prefix, enabling proper cleanup and isolation.
1 parent 67e93ae commit 4d7e3cf

1 file changed

Lines changed: 178 additions & 0 deletions

File tree

tests/e2e/test_resource_prefix.py

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
"""
2+
E2E Test: Docker Resource Prefix Verification
3+
4+
Tests that Docker resources (images, containers, networks) created during tests
5+
have the correct test-specific prefix, enabling proper cleanup and isolation.
6+
7+
This test validates that the fix for the prefix override bug is working:
8+
- The DOCKER_RESSOURCE_PREFIX environment variable is passed to the web container
9+
- The Flask app respects this environment variable instead of using the installation ID
10+
"""
11+
12+
import subprocess
13+
import uuid
14+
from pathlib import Path
15+
from typing import TYPE_CHECKING
16+
17+
import pytest
18+
19+
from helpers.exercise_factory import create_sample_exercise
20+
from helpers.web_client import REFWebClient
21+
22+
if TYPE_CHECKING:
23+
from helpers.ref_instance import REFInstance
24+
25+
26+
class TestResourcePrefix:
27+
"""Test that Docker resources use the correct test prefix."""
28+
29+
@pytest.mark.e2e
30+
def test_exercise_image_has_test_prefix(
31+
self,
32+
ref_instance: "REFInstance",
33+
admin_client: REFWebClient,
34+
exercises_path: Path,
35+
) -> None:
36+
"""
37+
Verify that built exercise images have the test prefix.
38+
39+
The prefix should match the ref_instance's config prefix,
40+
NOT the installation ID stored in the database.
41+
"""
42+
# Get expected prefix from the test instance
43+
expected_prefix = f"{ref_instance.config.prefix}-"
44+
45+
# Create a unique exercise for this test
46+
exercise_name = f"prefix_test_{uuid.uuid4().hex[:6]}"
47+
exercise_dir = exercises_path / exercise_name
48+
49+
try:
50+
# Create the exercise
51+
create_sample_exercise(
52+
exercise_dir,
53+
short_name=exercise_name,
54+
version=1,
55+
category="Prefix Test",
56+
has_deadline=False,
57+
has_submission_test=False,
58+
)
59+
60+
# Import the exercise
61+
success = admin_client.import_exercise(str(exercise_dir))
62+
assert success, f"Failed to import exercise from {exercise_dir}"
63+
64+
# Get exercise ID
65+
exercise = admin_client.get_exercise_by_name(exercise_name)
66+
assert exercise is not None, f"Exercise {exercise_name} not found"
67+
exercise_id = exercise.get("id")
68+
assert exercise_id is not None, "Exercise ID not found"
69+
assert isinstance(exercise_id, int), "Exercise ID must be an integer"
70+
71+
# Build the exercise
72+
success = admin_client.build_exercise(exercise_id)
73+
assert success, "Failed to start exercise build"
74+
75+
build_success = admin_client.wait_for_build(exercise_id, timeout=300.0)
76+
assert build_success, "Exercise build did not complete successfully"
77+
78+
# Query Docker for images
79+
result = subprocess.run(
80+
["docker", "images", "--format", "{{.Repository}}:{{.Tag}}"],
81+
capture_output=True,
82+
text=True,
83+
check=True,
84+
)
85+
images = result.stdout.strip().split("\n")
86+
87+
# Find the exercise image with the expected prefix
88+
exercise_images = [
89+
img for img in images if expected_prefix in img and exercise_name in img
90+
]
91+
92+
assert len(exercise_images) > 0, (
93+
f"Exercise image for '{exercise_name}' not found with prefix "
94+
f"'{expected_prefix}'. All images containing exercise name: "
95+
f"{[img for img in images if exercise_name in img]}"
96+
)
97+
98+
# Verify the image name format
99+
for img in exercise_images:
100+
assert img.startswith(expected_prefix), (
101+
f"Image '{img}' does not start with expected prefix "
102+
f"'{expected_prefix}'"
103+
)
104+
105+
finally:
106+
# Cleanup: Remove exercise directory
107+
if exercise_dir.exists():
108+
import shutil
109+
110+
shutil.rmtree(exercise_dir)
111+
112+
@pytest.mark.e2e
113+
def test_cleanup_removes_prefixed_resources(
114+
self,
115+
ref_instance: "REFInstance",
116+
) -> None:
117+
"""
118+
Verify cleanup correctly identifies and removes resources with test prefix.
119+
120+
This test creates a dummy container with the test prefix and verifies
121+
that cleanup_docker_resources_by_prefix can remove it.
122+
"""
123+
from helpers.ref_instance import cleanup_docker_resources_by_prefix
124+
125+
expected_prefix = f"{ref_instance.config.prefix}-"
126+
127+
# Create a test container with our prefix
128+
test_container_name = f"{expected_prefix}cleanup-test-{uuid.uuid4().hex[:6]}"
129+
130+
try:
131+
# Create a simple container
132+
subprocess.run(
133+
[
134+
"docker",
135+
"run",
136+
"-d",
137+
"--name",
138+
test_container_name,
139+
"alpine:latest",
140+
"sleep",
141+
"3600",
142+
],
143+
capture_output=True,
144+
check=True,
145+
)
146+
147+
# Verify it exists
148+
result = subprocess.run(
149+
["docker", "ps", "-a", "--format", "{{.Names}}"],
150+
capture_output=True,
151+
text=True,
152+
check=True,
153+
)
154+
assert test_container_name in result.stdout, (
155+
"Test container was not created"
156+
)
157+
158+
# Run cleanup
159+
cleanup_docker_resources_by_prefix(expected_prefix)
160+
161+
# Verify container is gone
162+
result = subprocess.run(
163+
["docker", "ps", "-a", "--format", "{{.Names}}"],
164+
capture_output=True,
165+
text=True,
166+
check=True,
167+
)
168+
assert test_container_name not in result.stdout, (
169+
f"Container '{test_container_name}' still exists after cleanup"
170+
)
171+
172+
except subprocess.CalledProcessError:
173+
# If container creation failed, try to clean up anyway
174+
subprocess.run(
175+
["docker", "rm", "-f", test_container_name],
176+
capture_output=True,
177+
)
178+
raise

0 commit comments

Comments
 (0)