82 lines
3.4 KiB
Python
82 lines
3.4 KiB
Python
import base64
|
|
import hashlib
|
|
from pathlib import Path
|
|
from urllib.parse import parse_qs, urlparse
|
|
|
|
import pytest
|
|
from django.core.management import call_command
|
|
from django.core.exceptions import ImproperlyConfigured
|
|
|
|
from apps.attachments.delivery import signed_media_redirect
|
|
from apps.integrations.bale.providers import get_bale_provider
|
|
|
|
|
|
def test_signed_media_redirect_matches_nginx_secure_link(settings):
|
|
settings.MEDIA_SIGNING_SECRET = "deployment-test-secret-with-enough-entropy"
|
|
response = signed_media_redirect("tickets/2026/09/example.pdf", lifetime=300)
|
|
parsed = urlparse(response.url)
|
|
query = parse_qs(parsed.query)
|
|
expires = query["expires"][0]
|
|
expected = hashlib.md5(
|
|
f"{expires}{parsed.path} {settings.MEDIA_SIGNING_SECRET}".encode(),
|
|
usedforsecurity=False,
|
|
).digest()
|
|
expected = base64.urlsafe_b64encode(expected).rstrip(b"=").decode()
|
|
assert response.status_code == 302
|
|
assert query["md5"] == [expected]
|
|
|
|
|
|
def test_bale_missing_token_is_not_silently_successful(settings):
|
|
settings.DEBUG = False
|
|
settings.BALE_BOT_TOKEN = ""
|
|
with pytest.raises(ImproperlyConfigured, match="BALE_BOT_TOKEN"):
|
|
get_bale_provider()
|
|
|
|
|
|
def test_nginx_serves_assets_without_proxying_django():
|
|
root = Path(__file__).resolve().parents[1]
|
|
nginx = (root / "docker" / "nginx.conf").read_text(encoding="utf-8")
|
|
nginx_dockerfile = (root / "Dockerfile.nginx").read_text(encoding="utf-8")
|
|
compose = (root / "docker-compose.yml").read_text(encoding="utf-8")
|
|
assert "location /static/" in nginx
|
|
assert "location ^~ /media/branding/" in nginx
|
|
assert "location /protected-media/" in nginx
|
|
assert "location = /healthz/" in nginx
|
|
assert "return 200" in nginx
|
|
assert "secure_link_md5" in nginx
|
|
assert "proxy_pass" not in nginx
|
|
assert "dockerfile: Dockerfile.nginx" in compose
|
|
assert "./docker/nginx.conf:" not in compose
|
|
assert (
|
|
"COPY docker/nginx.conf /etc/nginx/templates/default.conf.template"
|
|
in nginx_dockerfile
|
|
)
|
|
assert "media_data:/app/media:ro" in compose
|
|
assert "notification-worker:" in compose
|
|
assert " db:" not in compose
|
|
# Coolify owns Host/PathPrefix/TLS generation. The Compose file only pins
|
|
# the generated nginx routers to a high-priority port-80 service so web's
|
|
# catch-all route cannot capture static/media traffic.
|
|
assert ".rule=Host(" not in compose
|
|
assert "customer-ticket-assets-${COOLIFY_RESOURCE_UUID:?}" in compose
|
|
assert "traefik.docker.network=${COOLIFY_RESOURCE_UUID:?}" in compose
|
|
assert compose.count(".priority=10000") == 6
|
|
assert compose.count(".service=customer-ticket-assets-${COOLIFY_RESOURCE_UUID:?}") == 6
|
|
assert ".loadbalancer.server.port=80" in compose
|
|
assert "Set MEDIA_SIGNING_SECRET in Coolify" not in compose
|
|
|
|
|
|
def test_startup_imports_production_seed_only_when_database_has_no_seed_marker():
|
|
root = Path(__file__).resolve().parents[1]
|
|
entrypoint = (root / "docker" / "entrypoint.sh").read_text(encoding="utf-8")
|
|
marker_check = "ProductionSeedState.objects.exists()"
|
|
import_command = 'python manage.py import_production_seed "$seed_dir"'
|
|
assert marker_check in entrypoint
|
|
assert entrypoint.index(marker_check) < entrypoint.index(import_command)
|
|
assert "Production seed was already applied; skipping seed import." in entrypoint
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_notification_worker_command_starts_and_exits_in_once_mode():
|
|
call_command("process_notifications", once=True)
|