Skip to content

Fix tutorial and add automated validation #1

Fix tutorial and add automated validation

Fix tutorial and add automated validation #1

name: Validate Tutorial
on:
push:
branches: [ master ]
paths:
- 'examples/tenant_tutorial/**'
- 'docs/examples.rst'
pull_request:
branches: [ master ]
paths:
- 'examples/tenant_tutorial/**'
- 'docs/examples.rst'
jobs:
validate-tutorial:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: root
POSTGRES_DB: tenant_tutorial
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Change to tutorial directory
run: cd examples/tenant_tutorial
- name: Create virtual environment and install dependencies
run: |
cd examples/tenant_tutorial
python -m venv tutorial_env
source tutorial_env/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
- name: Verify database connection
run: |
cd examples/tenant_tutorial
source tutorial_env/bin/activate
python -c "
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tenant_tutorial.settings')
import django
django.setup()
from django.db import connection
with connection.cursor() as cursor:
cursor.execute('SELECT 1')
result = cursor.fetchone()
print(f'Database connection successful: {result}')
"
- name: Run initial migrations
run: |
cd examples/tenant_tutorial
source tutorial_env/bin/activate
python manage.py migrate_schemas --shared
- name: Create public tenant
run: |
cd examples/tenant_tutorial
source tutorial_env/bin/activate
python manage.py shell << 'EOF'
from customers.models import Client
Client(
domain_url='localhost',
schema_name='public',
name='Tutorial Public Tenant',
description='Public tenant for tutorial validation'
).save()
EOF
- name: Create sample tenants
run: |
cd examples/tenant_tutorial
source tutorial_env/bin/activate
python manage.py shell << 'EOF'
from customers.models import Client
# Create first tenant
Client(
domain_url='tenant1.example.com',
schema_name='tenant1',
name='Tenant 1 - Test Company',
description='First test tenant for validation'
).save()
# Create second tenant
Client(
domain_url='tenant2.example.com',
schema_name='tenant2',
name='Tenant 2 - Another Company',
description='Second test tenant for validation'
).save()
EOF
- name: Verify tenants were created
run: |
cd examples/tenant_tutorial
source tutorial_env/bin/activate
python manage.py list_tenants
# Verify we have exactly 3 tenants (public + 2 test tenants)
tenant_count=$(python manage.py list_tenants | grep -E '^(public|tenant1|tenant2)' | wc -l)
if [ "$tenant_count" != "3" ]; then
echo "Expected 3 tenants, found $tenant_count"
exit 1
fi
echo "All tenants created successfully"
- name: Start Django development server
run: |
cd examples/tenant_tutorial
source tutorial_env/bin/activate
nohup python manage.py runserver 0.0.0.0:8000 > server.log 2>&1 &
echo $! > django_server.pid
# Wait for server to start
sleep 15
# Check if server is running
if ! kill -0 $(cat django_server.pid) 2>/dev/null; then
echo "Server failed to start"
cat server.log
exit 1
fi
- name: Test public tenant endpoint
run: |
cd examples/tenant_tutorial
response=$(curl -s -w "%{http_code}" -H "Host: localhost" http://localhost:8000/ -o /tmp/public_response.html)
echo "Public tenant response code: $response"
if [ "$response" != "200" ]; then
echo "Public tenant test failed with response code: $response"
echo "Response content:"
cat /tmp/public_response.html
exit 1
fi
# Check that the response contains expected tutorial content
if ! grep -q "Welcome to the Tenant Tutorial" /tmp/public_response.html; then
echo "Public tenant response doesn't contain expected tutorial content"
cat /tmp/public_response.html
exit 1
fi
echo "Public tenant test passed"
- name: Test first tenant endpoint
run: |
cd examples/tenant_tutorial
response=$(curl -s -w "%{http_code}" -H "Host: tenant1.example.com" http://localhost:8000/ -o /tmp/tenant1_response.html)
echo "Tenant 1 response code: $response"
if [ "$response" != "200" ]; then
echo "Tenant 1 test failed with response code: $response"
echo "Response content:"
cat /tmp/tenant1_response.html
exit 1
fi
# Check that the response contains tenant-specific content
if ! grep -q "Tenant 1 - Test Company" /tmp/tenant1_response.html; then
echo "Tenant 1 response doesn't contain expected tenant name"
cat /tmp/tenant1_response.html
exit 1
fi
echo "Tenant 1 test passed"
- name: Test second tenant endpoint
run: |
cd examples/tenant_tutorial
response=$(curl -s -w "%{http_code}" -H "Host: tenant2.example.com" http://localhost:8000/ -o /tmp/tenant2_response.html)
echo "Tenant 2 response code: $response"
if [ "$response" != "200" ]; then
echo "Tenant 2 test failed with response code: $response"
echo "Response content:"
cat /tmp/tenant2_response.html
exit 1
fi
# Check that the response contains tenant-specific content
if ! grep -q "Tenant 2 - Another Company" /tmp/tenant2_response.html; then
echo "Tenant 2 response doesn't contain expected tenant name"
cat /tmp/tenant2_response.html
exit 1
fi
echo "Tenant 2 test passed"
- name: Test tenant data isolation
run: |
cd examples/tenant_tutorial
source tutorial_env/bin/activate
# Add users to tenant1
python manage.py tenant_command shell --schema=tenant1 << 'EOF'
from django.contrib.auth.models import User
User.objects.create_user('tenant1user', '[email protected]', 'password')
print(f"Tenant1 users count: {User.objects.count()}")
EOF
# Check that tenant2 doesn't have these users
python manage.py tenant_command shell --schema=tenant2 << 'EOF'
from django.contrib.auth.models import User
count = User.objects.count()
print(f"Tenant2 users count: {count}")
if count != 0:
print("ERROR: Data isolation failed - tenant2 can see tenant1 users")
exit(1)
print("Data isolation test passed")
EOF
- name: Stop Django server
if: always()
run: |
cd examples/tenant_tutorial
if [ -f django_server.pid ]; then
kill $(cat django_server.pid) || true
rm django_server.pid
fi
- name: Upload server logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: server-logs
path: examples/tenant_tutorial/server.log
retention-days: 1
- name: Upload response files on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: response-files
path: /tmp/*_response.html
retention-days: 1