Files
MathApp/config/tests_domains.py
Zareian 4b246e0c70 make the domain configuration, not code: two env vars move everything
A domain change is coming (the Zarinpal terminal rejects the current callback
domain). ALLOWED_HOSTS, CSRF_TRUSTED_ORIGINS, CORS defaults, the Bazaar
redirect URI and deploy.sh's public health URL each spelled the domain out.
They now derive from PUBLIC_BASE_URL and FRONTEND_URL, which keep today's
domain as their default; env values for the three lists are still added on
top, so the old domain can stay allowed while DNS moves.

ZARINPAL_CALLBACK_URL used os.environ.get(key, default), so an empty key in
.env produced an empty callback and every payment would fail closed. It now
uses the same `.strip() or` fallback as its neighbours; the new test caught it.

config/tests_domains.py loads settings in a subprocess with a new domain and
fails if any derived value still mentions the old one. docs/operations.md
gains the step-by-step domain move (DNS, .env, Zarinpal/Bazaar panels,
frontend config.js, verification). CLAUDE.md records that the frontend is
now ours and where the rebuild stands. 624 tests green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 23:46:25 +03:30

63 lines
3.0 KiB
Python

"""
دامنه فقط از PUBLIC_BASE_URL و FRONTEND_URL می‌آید.
تنظیمات در زمانِ import ساخته می‌شوند، پس هر حالت در یک پروسه‌ی جدا با env
خودش بار می‌شود. اگر کسی دوباره دامنه‌ای را مستقیم در settings بنویسد، حالتِ
«دامنه‌ی تازه» اینجا قرمز می‌شود.
"""
import json
import os
import subprocess
import sys
from pathlib import Path
from django.test import SimpleTestCase
BASE_DIR = Path(__file__).resolve().parent.parent
PROBE = """
import json, os
os.environ['DJANGO_SETTINGS_MODULE'] = 'config.settings'
import django; django.setup()
from django.conf import settings as s
print(json.dumps({
'hosts': s.ALLOWED_HOSTS, 'csrf': s.CSRF_TRUSTED_ORIGINS, 'cors': s.CORS_ALLOWED_ORIGINS,
'callback': s.ZARINPAL_CALLBACK_URL, 'redirect': s.ZARINPAL_REDIRECT_URL, 'bazaar': s.BAZAAR_REDIRECT_URI,
}))
"""
def load_settings(**env):
clean = {k: v for k, v in os.environ.items() if not k.startswith(('DJANGO_ALLOWED', 'CSRF_', 'CORS_', 'ZARINPAL_', 'BAZAAR_REDIRECT', 'PUBLIC_BASE', 'FRONTEND_URL'))}
clean.update(env)
# .env محلی نباید مقدارهای آزمون را بپوشاند؛ python-dotenv متغیرِ موجود را عوض نمی‌کند،
# پس کلیدهای خالی را هم صریح می‌گذاریم.
for key in ('DJANGO_ALLOWED_HOSTS', 'CSRF_TRUSTED_ORIGINS', 'CORS_ALLOWED_ORIGINS', 'ZARINPAL_CALLBACK_URL',
'ZARINPAL_REDIRECT_URL', 'BAZAAR_REDIRECT_URI'):
clean.setdefault(key, '')
out = subprocess.run([sys.executable, '-c', PROBE], cwd=BASE_DIR, env=clean, capture_output=True, text=True, check=True)
return json.loads(out.stdout.strip().splitlines()[-1])
class DomainSettingsTests(SimpleTestCase):
def test_new_domain_moves_everything(self):
s = load_settings(PUBLIC_BASE_URL='https://api.new-domain.ir', FRONTEND_URL='https://app.new-domain.ir/')
self.assertIn('api.new-domain.ir', s['hosts'])
self.assertIn('app.new-domain.ir', s['hosts'])
self.assertIn('https://app.new-domain.ir', s['cors'])
self.assertIn('https://api.new-domain.ir', s['csrf'])
self.assertEqual(s['callback'], 'https://api.new-domain.ir/api/payment/zarinpal/callback')
self.assertEqual(s['redirect'], 'https://app.new-domain.ir/')
self.assertEqual(s['bazaar'], 'https://api.new-domain.ir/api/payment/bazaar/verify')
self.assertNotIn('tadrisino2', json.dumps(s))
def test_extra_domain_is_added_not_replacing(self):
s = load_settings(
PUBLIC_BASE_URL='https://api.new-domain.ir', FRONTEND_URL='https://app.new-domain.ir',
DJANGO_ALLOWED_HOSTS='old.example.ir', CORS_ALLOWED_ORIGINS='https://old.example.ir',
)
self.assertIn('old.example.ir', s['hosts'])
self.assertIn('api.new-domain.ir', s['hosts'])
self.assertIn('https://old.example.ir', s['cors'])
self.assertIn('https://app.new-domain.ir', s['cors'])