Fix tutorial and add automated validation #2
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |