name: PR Pricing Validation on: pull_request: types: [opened, synchronize, reopened] paths: - "hub/services/models/pricing.py" - "hub/services/tests/test_pricing*.py" - "hub/services/views/**" - "hub/services/forms.py" - "hub/services/admin/**" jobs: pricing-validation: name: Validate Pricing Changes runs-on: ubuntu-latest steps: - name: Checkout PR branch uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v4 with: python-version: "3.13" - name: Install uv uses: astral-sh/setup-uv@v3 with: enable-cache: true cache-dependency-glob: "uv.lock" - name: Install dependencies run: | uv sync --extra dev - name: Check for pricing model migrations run: | echo "::group::Checking for required database migrations" # Check if pricing models were changed if git diff --name-only origin/main...HEAD | grep -q "hub/services/models/pricing.py"; then echo "📝 Pricing models were modified, checking for migrations..." # Check if there are new migration files if git diff --name-only origin/main...HEAD | grep -q "hub/services/migrations/"; then echo "✅ Found migration files in the PR" git diff --name-only origin/main...HEAD | grep "hub/services/migrations/" | head -5 else echo "âš ī¸ Pricing models were changed but no migrations found" echo "Please run: uv run --extra dev manage.py makemigrations" echo "This will be treated as a warning, not a failure" fi else echo "â„šī¸ No pricing model changes detected" fi echo "::endgroup::" - name: Run pricing tests with coverage env: DJANGO_SETTINGS_MODULE: hub.settings run: | echo "::group::Running pricing tests with coverage tracking" # Run tests with coverage uv run coverage run --source='hub/services/models/pricing,hub/services/views' \ manage.py test \ hub.services.tests.test_pricing \ hub.services.tests.test_pricing_edge_cases \ hub.services.tests.test_pricing_integration \ --verbosity=2 # Generate coverage report uv run coverage report --show-missing --fail-under=85 # Generate HTML coverage report uv run coverage html echo "::endgroup::" - name: Upload coverage report uses: actions/upload-artifact@v4 with: name: pr-pricing-coverage path: htmlcov/ retention-days: 7 - name: Detect pricing calculation changes run: | echo "::group::Analyzing pricing calculation changes" # Check if critical pricing methods were modified CRITICAL_METHODS=( "calculate_discount" "calculate_final_price" "get_price" "get_unit_rate" "get_base_fee" ) echo "🔍 Checking for changes to critical pricing methods..." changed_methods=() for method in "${CRITICAL_METHODS[@]}"; do if git diff origin/main...HEAD -- hub/services/models/pricing.py | grep -q "def $method"; then changed_methods+=("$method") echo "âš ī¸ Critical method '$method' was modified" fi done if [ ${#changed_methods[@]} -gt 0 ]; then echo "" echo "🚨 CRITICAL PRICING METHODS CHANGED:" printf ' - %s\n' "${changed_methods[@]}" echo "" echo "📋 Extra validation required:" echo " 1. All pricing tests must pass" echo " 2. Manual testing of price calculations recommended" echo " 3. Consider adding regression tests for specific scenarios" echo "" echo "This will not fail the build but requires careful review." else echo "✅ No critical pricing methods were modified" fi echo "::endgroup::" - name: Validate test additions run: | echo "::group::Validating test additions for pricing changes" # Check if new pricing features have corresponding tests python3 << 'EOF' import subprocess import re def get_git_diff(): result = subprocess.run( ['git', 'diff', 'origin/main...HEAD', '--', 'hub/services/models/pricing.py'], capture_output=True, text=True ) return result.stdout def get_test_diff(): result = subprocess.run( ['git', 'diff', 'origin/main...HEAD', '--', 'hub/services/tests/test_pricing*.py'], capture_output=True, text=True ) return result.stdout pricing_diff = get_git_diff() test_diff = get_test_diff() # Look for new methods in pricing models new_methods = re.findall(r'^\+\s*def\s+(\w+)', pricing_diff, re.MULTILINE) new_classes = re.findall(r'^\+class\s+(\w+)', pricing_diff, re.MULTILINE) # Look for new test methods new_test_methods = re.findall(r'^\+\s*def\s+(test_\w+)', test_diff, re.MULTILINE) print("📊 Analysis of pricing changes:") if new_classes: print(f" New classes: {', '.join(new_classes)}") if new_methods: print(f" New methods: {', '.join(new_methods)}") if new_test_methods: print(f" New test methods: {', '.join(new_test_methods)}") if (new_classes or new_methods) and not new_test_methods: print("âš ī¸ New pricing functionality detected but no new tests found") print(" Consider adding tests for new features") elif new_test_methods: print("✅ New tests found alongside pricing changes") else: print("â„šī¸ No new pricing functionality detected") EOF echo "::endgroup::" - name: Run backward compatibility check env: DJANGO_SETTINGS_MODULE: hub.settings run: | echo "::group::Checking backward compatibility of pricing changes" # Create a simple backward compatibility test cat << 'EOF' > check_compatibility.py import os import django from decimal import Decimal os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hub.settings') django.setup() from hub.services.models.base import Currency, Term from hub.services.models.providers import CloudProvider from hub.services.models.services import Service from hub.services.models.pricing import VSHNAppCatPrice, VSHNAppCatBaseFee, VSHNAppCatUnitRate print("🔄 Testing backward compatibility of pricing API...") try: # Test basic model creation (should work with existing API) provider = CloudProvider.objects.create( name="BC Test", slug="bc-test", description="Test", website="https://test.com" ) service = Service.objects.create( name="BC Service", slug="bc-service", description="Test", features="Test" ) price_config = VSHNAppCatPrice.objects.create( service=service, variable_unit=VSHNAppCatPrice.VariableUnit.RAM, term=Term.MTH ) VSHNAppCatBaseFee.objects.create( vshn_appcat_price_config=price_config, currency=Currency.CHF, amount=Decimal('50.00') ) VSHNAppCatUnitRate.objects.create( vshn_appcat_price_config=price_config, currency=Currency.CHF, service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED, amount=Decimal('5.0000') ) # Test basic price calculation result = price_config.calculate_final_price(Currency.CHF, 'GA', 4) if result and 'total_price' in result: print(f"✅ Basic price calculation works: {result['total_price']} CHF") else: print("❌ Price calculation API may have changed") exit(1) # Test price retrieval methods base_fee = price_config.get_base_fee(Currency.CHF) unit_rate = price_config.get_unit_rate(Currency.CHF, 'GA') if base_fee and unit_rate: print("✅ Price retrieval methods work correctly") else: print("❌ Price retrieval API may have changed") exit(1) print("🎉 Backward compatibility check passed!") except Exception as e: print(f"❌ Backward compatibility issue detected: {e}") exit(1) EOF uv run python check_compatibility.py echo "::endgroup::" - name: Generate pricing test summary if: always() run: | echo "::group::Pricing Test Summary" echo "## 🧮 Pricing Test Results" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY # Count test files and methods total_test_files=$(find hub/services/tests -name "test_pricing*.py" | wc -l) total_test_methods=$(grep -r "def test_" hub/services/tests/test_pricing*.py | wc -l) echo "- **Test Files**: $total_test_files pricing-specific test files" >> $GITHUB_STEP_SUMMARY echo "- **Test Methods**: $total_test_methods individual test methods" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY # Check if any pricing files were changed if git diff --name-only origin/main...HEAD | grep -q "pricing"; then echo "### 📝 Pricing-Related Changes Detected" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "The following pricing-related files were modified:" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY git diff --name-only origin/main...HEAD | grep "pricing" | sed 's/^/- /' >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "✅ All pricing tests have been executed to validate these changes." >> $GITHUB_STEP_SUMMARY else echo "### â„šī¸ No Pricing Changes" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "No pricing-related files were modified in this PR." >> $GITHUB_STEP_SUMMARY fi echo "" >> $GITHUB_STEP_SUMMARY echo "---" >> $GITHUB_STEP_SUMMARY echo "*Pricing validation completed at $(date)*" >> $GITHUB_STEP_SUMMARY echo "::endgroup::"