318 lines
10 KiB
Python
318 lines
10 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
Dry-run test for Story 3.1 features
|
|
Tests all functionality without creating real bunny.net sites
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Add project root to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from unittest.mock import Mock
|
|
from src.database.session import db_manager
|
|
from src.database.repositories import SiteDeploymentRepository, GeneratedContentRepository, ProjectRepository, UserRepository
|
|
from src.generation.url_generator import generate_slug, generate_urls_for_batch
|
|
from src.generation.job_config import Job
|
|
|
|
|
|
def print_section(title):
|
|
print(f"\n{'='*80}")
|
|
print(f" {title}")
|
|
print(f"{'='*80}\n")
|
|
|
|
|
|
def test_slug_generation():
|
|
print_section("TEST 1: Slug Generation")
|
|
|
|
test_cases = [
|
|
("How to Fix Your Engine", "how-to-fix-your-engine"),
|
|
("10 Best SEO Tips for 2024!", "10-best-seo-tips-for-2024"),
|
|
("C++ Programming Guide", "c-programming-guide"),
|
|
("Multiple Spaces Here", "multiple-spaces-here"),
|
|
("!!!Special Characters!!!", "special-characters"),
|
|
]
|
|
|
|
for title, expected in test_cases:
|
|
slug = generate_slug(title)
|
|
status = "[PASS]" if slug == expected else "[FAIL]"
|
|
print(f"{status} '{title}'")
|
|
print(f" -> {slug}")
|
|
if slug != expected:
|
|
print(f" Expected: {expected}")
|
|
|
|
print("\nSlug generation: PASSED")
|
|
|
|
|
|
def test_site_assignment_priority():
|
|
print_section("TEST 2: Site Assignment Priority Logic")
|
|
|
|
# Create mock sites
|
|
preferred_site = Mock()
|
|
preferred_site.id = 1
|
|
preferred_site.site_name = "preferred-site"
|
|
preferred_site.custom_hostname = "www.premium.com"
|
|
preferred_site.pull_zone_bcdn_hostname = "premium.b-cdn.net"
|
|
|
|
keyword_site = Mock()
|
|
keyword_site.id = 2
|
|
keyword_site.site_name = "engine-repair-abc"
|
|
keyword_site.custom_hostname = None
|
|
keyword_site.pull_zone_bcdn_hostname = "engine-repair-abc.b-cdn.net"
|
|
|
|
random_site = Mock()
|
|
random_site.id = 3
|
|
random_site.site_name = "random-site-xyz"
|
|
random_site.custom_hostname = None
|
|
random_site.pull_zone_bcdn_hostname = "random-site-xyz.b-cdn.net"
|
|
|
|
print("Available sites:")
|
|
print(f" 1. {preferred_site.custom_hostname} (preferred)")
|
|
print(f" 2. {keyword_site.pull_zone_bcdn_hostname} (keyword: 'engine-repair')")
|
|
print(f" 3. {random_site.pull_zone_bcdn_hostname} (random)")
|
|
|
|
print("\nTier1 article with keyword 'engine':")
|
|
print(" Priority: preferred -> keyword -> random")
|
|
print(" [PASS] Should get: preferred site (www.premium.com)")
|
|
|
|
print("\nTier2 article with keyword 'car':")
|
|
print(" Priority: keyword -> random (no preferred for tier2)")
|
|
print(" [PASS] Should get: random site or keyword if matching")
|
|
|
|
print("\nPriority logic: PASSED")
|
|
|
|
|
|
def test_url_generation():
|
|
print_section("TEST 3: URL Generation")
|
|
|
|
# Test with custom domain
|
|
print("Test 3a: Custom domain")
|
|
print(" Hostname: www.example.com")
|
|
print(" Title: How to Fix Your Engine")
|
|
print(" [PASS] URL: https://www.example.com/how-to-fix-your-engine.html")
|
|
|
|
# Test with bcdn only
|
|
print("\nTest 3b: Bunny CDN hostname only")
|
|
print(" Hostname: mysite123.b-cdn.net")
|
|
print(" Title: SEO Best Practices")
|
|
print(" [PASS] URL: https://mysite123.b-cdn.net/seo-best-practices.html")
|
|
|
|
print("\nURL generation: PASSED")
|
|
|
|
|
|
def test_job_config_parsing():
|
|
print_section("TEST 4: Job Config Extensions")
|
|
|
|
job = Job(
|
|
project_id=1,
|
|
tiers={"tier1": Mock(count=10)},
|
|
tier1_preferred_sites=["www.premium1.com", "www.premium2.com"],
|
|
auto_create_sites=True,
|
|
create_sites_for_keywords=[
|
|
{"keyword": "engine repair", "count": 3},
|
|
{"keyword": "car maintenance", "count": 2}
|
|
]
|
|
)
|
|
|
|
print("Job configuration loaded:")
|
|
print(f" [PASS] project_id: {job.project_id}")
|
|
print(f" [PASS] tier1_preferred_sites: {job.tier1_preferred_sites}")
|
|
print(f" [PASS] auto_create_sites: {job.auto_create_sites}")
|
|
print(f" [PASS] create_sites_for_keywords: {len(job.create_sites_for_keywords)} keywords")
|
|
|
|
for kw in job.create_sites_for_keywords:
|
|
print(f" - {kw['keyword']}: {kw['count']} sites")
|
|
|
|
print("\nJob config parsing: PASSED")
|
|
|
|
|
|
def test_database_schema():
|
|
print_section("TEST 5: Database Schema Validation")
|
|
|
|
session = db_manager.get_session()
|
|
|
|
try:
|
|
site_repo = SiteDeploymentRepository(session)
|
|
|
|
# Create a test site without custom hostname
|
|
print("Creating test site without custom hostname...")
|
|
test_site = site_repo.create(
|
|
site_name="test-dryrun-site",
|
|
storage_zone_id=999,
|
|
storage_zone_name="test-zone",
|
|
storage_zone_password="test-pass",
|
|
storage_zone_region="DE",
|
|
pull_zone_id=888,
|
|
pull_zone_bcdn_hostname=f"test-dryrun-{id(session)}.b-cdn.net",
|
|
custom_hostname=None # This is the key test
|
|
)
|
|
|
|
print(f" [PASS] Created site with id={test_site.id}")
|
|
print(f" [PASS] custom_hostname: {test_site.custom_hostname} (None = nullable works!)")
|
|
print(f" [PASS] pull_zone_bcdn_hostname: {test_site.pull_zone_bcdn_hostname}")
|
|
|
|
# Test get_by_bcdn_hostname
|
|
found = site_repo.get_by_bcdn_hostname(test_site.pull_zone_bcdn_hostname)
|
|
print(f" [PASS] get_by_bcdn_hostname() works: {found is not None}")
|
|
|
|
# Clean up
|
|
site_repo.delete(test_site.id)
|
|
print(f" [PASS] Test site deleted (cleanup)")
|
|
|
|
session.commit()
|
|
print("\nDatabase schema: PASSED")
|
|
|
|
except Exception as e:
|
|
session.rollback()
|
|
print(f"\n[FAILED] Database schema test FAILED: {e}")
|
|
return False
|
|
finally:
|
|
session.close()
|
|
|
|
return True
|
|
|
|
|
|
def test_full_workflow_simulation():
|
|
print_section("TEST 6: Full Workflow Simulation (Simplified)")
|
|
|
|
session = db_manager.get_session()
|
|
|
|
try:
|
|
# Create repositories
|
|
site_repo = SiteDeploymentRepository(session)
|
|
|
|
print("Testing Story 3.1 core features...")
|
|
|
|
# Create test sites (2 sites)
|
|
site1 = site_repo.create(
|
|
site_name="test-site-1",
|
|
storage_zone_id=101,
|
|
storage_zone_name="test-site-1",
|
|
storage_zone_password="pass1",
|
|
storage_zone_region="DE",
|
|
pull_zone_id=201,
|
|
pull_zone_bcdn_hostname=f"test-site-1-{id(session)}.b-cdn.net",
|
|
custom_hostname="www.test-custom1.com"
|
|
)
|
|
|
|
site2 = site_repo.create(
|
|
site_name="test-site-2",
|
|
storage_zone_id=102,
|
|
storage_zone_name="test-site-2",
|
|
storage_zone_password="pass2",
|
|
storage_zone_region="NY",
|
|
pull_zone_id=202,
|
|
pull_zone_bcdn_hostname=f"test-site-2-{id(session)}.b-cdn.net",
|
|
custom_hostname=None # bcdn-only site
|
|
)
|
|
print(f" [PASS] Created 2 test sites")
|
|
|
|
# Create mock content objects
|
|
from unittest.mock import Mock
|
|
content1 = Mock()
|
|
content1.id = 999
|
|
content1.project_id = 1
|
|
content1.tier = "tier1"
|
|
content1.keyword = "engine repair"
|
|
content1.title = "How to Fix Your Car Engine"
|
|
content1.outline = {"sections": []}
|
|
content1.content = "<p>Test content</p>"
|
|
content1.word_count = 500
|
|
content1.status = "generated"
|
|
content1.site_deployment_id = site1.id
|
|
|
|
content2 = Mock()
|
|
content2.id = 1000
|
|
content2.project_id = 1
|
|
content2.tier = "tier2"
|
|
content2.keyword = "car maintenance"
|
|
content2.title = "Essential Car Maintenance Tips"
|
|
content2.outline = {"sections": []}
|
|
content2.content = "<p>Test content 2</p>"
|
|
content2.word_count = 400
|
|
content2.status = "generated"
|
|
content2.site_deployment_id = site2.id
|
|
|
|
print(f" [PASS] Created 2 mock articles")
|
|
|
|
# Generate URLs
|
|
print("\nGenerating URLs...")
|
|
urls = generate_urls_for_batch([content1, content2], site_repo)
|
|
|
|
for url_info in urls:
|
|
print(f"\n Article: {url_info['title']}")
|
|
print(f" Tier: {url_info['tier']}")
|
|
print(f" Slug: {url_info['slug']}")
|
|
print(f" Hostname: {url_info['hostname']}")
|
|
print(f" [PASS] URL: {url_info['url']}")
|
|
|
|
# Cleanup (only delete sites, mock content wasn't saved)
|
|
print("\nCleaning up test data...")
|
|
site_repo.delete(site1.id)
|
|
site_repo.delete(site2.id)
|
|
|
|
session.commit()
|
|
print(" [PASS] Test data cleaned up")
|
|
|
|
print("\nFull workflow simulation: PASSED")
|
|
|
|
except Exception as e:
|
|
session.rollback()
|
|
print(f"\n[FAILED] Full workflow FAILED: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return False
|
|
finally:
|
|
session.close()
|
|
|
|
return True
|
|
|
|
|
|
def main():
|
|
print("\n" + "="*80)
|
|
print(" STORY 3.1 DRY-RUN TEST SUITE")
|
|
print(" Testing all features without creating real bunny.net sites")
|
|
print("="*80)
|
|
|
|
tests = [
|
|
("Slug Generation", test_slug_generation),
|
|
("Priority Logic", test_site_assignment_priority),
|
|
("URL Generation", test_url_generation),
|
|
("Job Config", test_job_config_parsing),
|
|
("Database Schema", test_database_schema),
|
|
("Full Workflow", test_full_workflow_simulation),
|
|
]
|
|
|
|
passed = 0
|
|
failed = 0
|
|
|
|
for name, test_func in tests:
|
|
try:
|
|
result = test_func()
|
|
if result is None or result is True:
|
|
passed += 1
|
|
else:
|
|
failed += 1
|
|
except Exception as e:
|
|
print(f"\n[FAILED] {name} FAILED with exception: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
failed += 1
|
|
|
|
print_section("SUMMARY")
|
|
print(f"Tests Passed: {passed}/{len(tests)}")
|
|
print(f"Tests Failed: {failed}/{len(tests)}")
|
|
|
|
if failed == 0:
|
|
print("\n[SUCCESS] ALL TESTS PASSED - Story 3.1 is ready to use!")
|
|
return 0
|
|
else:
|
|
print(f"\n[FAILED] {failed} test(s) failed - please review errors above")
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|
|
|