169 lines
5.8 KiB
Python
169 lines
5.8 KiB
Python
"""
|
|
Unit tests for URL generation
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import Mock, MagicMock
|
|
from src.generation.url_generator import generate_slug, generate_urls_for_batch
|
|
from src.database.models import GeneratedContent, SiteDeployment
|
|
|
|
|
|
class TestGenerateSlug:
|
|
"""Tests for generate_slug function"""
|
|
|
|
def test_basic_slug_generation(self):
|
|
assert generate_slug("How to Fix Your Engine") == "how-to-fix-your-engine"
|
|
|
|
def test_slug_with_numbers(self):
|
|
assert generate_slug("10 Best SEO Tips for 2024") == "10-best-seo-tips-for-2024"
|
|
|
|
def test_slug_with_special_characters(self):
|
|
assert generate_slug("C++ Programming Guide") == "c-programming-guide"
|
|
assert generate_slug("SEO Tips & Tricks!") == "seo-tips-tricks"
|
|
|
|
def test_slug_with_multiple_spaces(self):
|
|
assert generate_slug("How to Fix") == "how-to-fix"
|
|
|
|
def test_slug_with_leading_trailing_hyphens(self):
|
|
assert generate_slug("---Title---") == "title"
|
|
|
|
def test_slug_max_length(self):
|
|
long_title = "a" * 200
|
|
slug = generate_slug(long_title, max_length=100)
|
|
assert len(slug) == 100
|
|
|
|
def test_empty_string_fallback(self):
|
|
assert generate_slug("") == "article"
|
|
assert generate_slug("!!!") == "article"
|
|
assert generate_slug(" ") == "article"
|
|
|
|
def test_unicode_characters(self):
|
|
slug = generate_slug("Café Programming Guide")
|
|
assert "caf" in slug.lower()
|
|
|
|
|
|
class TestGenerateUrlsForBatch:
|
|
"""Tests for generate_urls_for_batch function"""
|
|
|
|
def test_url_generation_with_custom_hostname(self):
|
|
content = Mock(spec=GeneratedContent)
|
|
content.id = 1
|
|
content.title = "How to Fix Engines"
|
|
content.tier = "tier1"
|
|
content.site_deployment_id = 10
|
|
|
|
site = Mock(spec=SiteDeployment)
|
|
site.id = 10
|
|
site.custom_hostname = "www.example.com"
|
|
site.pull_zone_bcdn_hostname = "example.b-cdn.net"
|
|
|
|
site_repo = Mock()
|
|
site_repo.get_by_id.return_value = site
|
|
|
|
urls = generate_urls_for_batch([content], site_repo)
|
|
|
|
assert len(urls) == 1
|
|
assert urls[0]["content_id"] == 1
|
|
assert urls[0]["title"] == "How to Fix Engines"
|
|
assert urls[0]["url"] == "https://www.example.com/how-to-fix-engines.html"
|
|
assert urls[0]["tier"] == "tier1"
|
|
assert urls[0]["slug"] == "how-to-fix-engines"
|
|
assert urls[0]["hostname"] == "www.example.com"
|
|
|
|
def test_url_generation_with_bcdn_hostname_only(self):
|
|
content = Mock(spec=GeneratedContent)
|
|
content.id = 2
|
|
content.title = "SEO Guide"
|
|
content.tier = "tier2"
|
|
content.site_deployment_id = 20
|
|
|
|
site = Mock(spec=SiteDeployment)
|
|
site.id = 20
|
|
site.custom_hostname = None
|
|
site.pull_zone_bcdn_hostname = "mysite123.b-cdn.net"
|
|
|
|
site_repo = Mock()
|
|
site_repo.get_by_id.return_value = site
|
|
|
|
urls = generate_urls_for_batch([content], site_repo)
|
|
|
|
assert len(urls) == 1
|
|
assert urls[0]["url"] == "https://mysite123.b-cdn.net/seo-guide.html"
|
|
assert urls[0]["hostname"] == "mysite123.b-cdn.net"
|
|
|
|
def test_error_if_missing_site_deployment_id(self):
|
|
content = Mock(spec=GeneratedContent)
|
|
content.id = 3
|
|
content.title = "Test"
|
|
content.site_deployment_id = None
|
|
|
|
site_repo = Mock()
|
|
|
|
with pytest.raises(ValueError, match="missing site_deployment_id"):
|
|
generate_urls_for_batch([content], site_repo)
|
|
|
|
def test_error_if_site_not_found(self):
|
|
content = Mock(spec=GeneratedContent)
|
|
content.id = 4
|
|
content.title = "Test"
|
|
content.site_deployment_id = 999
|
|
|
|
site_repo = Mock()
|
|
site_repo.get_by_id.return_value = None
|
|
|
|
with pytest.raises(ValueError, match="not found"):
|
|
generate_urls_for_batch([content], site_repo)
|
|
|
|
def test_fallback_slug_for_empty_title(self):
|
|
content = Mock(spec=GeneratedContent)
|
|
content.id = 5
|
|
content.title = "!!!"
|
|
content.tier = "tier1"
|
|
content.site_deployment_id = 10
|
|
|
|
site = Mock(spec=SiteDeployment)
|
|
site.id = 10
|
|
site.custom_hostname = "www.example.com"
|
|
site.pull_zone_bcdn_hostname = "example.b-cdn.net"
|
|
|
|
site_repo = Mock()
|
|
site_repo.get_by_id.return_value = site
|
|
|
|
urls = generate_urls_for_batch([content], site_repo)
|
|
|
|
assert urls[0]["slug"] == "article-5"
|
|
assert urls[0]["url"] == "https://www.example.com/article-5.html"
|
|
|
|
def test_multiple_articles(self):
|
|
content1 = Mock(spec=GeneratedContent)
|
|
content1.id = 1
|
|
content1.title = "Article One"
|
|
content1.tier = "tier1"
|
|
content1.site_deployment_id = 10
|
|
|
|
content2 = Mock(spec=GeneratedContent)
|
|
content2.id = 2
|
|
content2.title = "Article Two"
|
|
content2.tier = "tier2"
|
|
content2.site_deployment_id = 20
|
|
|
|
site1 = Mock(spec=SiteDeployment)
|
|
site1.id = 10
|
|
site1.custom_hostname = "www.site1.com"
|
|
site1.pull_zone_bcdn_hostname = "site1.b-cdn.net"
|
|
|
|
site2 = Mock(spec=SiteDeployment)
|
|
site2.id = 20
|
|
site2.custom_hostname = None
|
|
site2.pull_zone_bcdn_hostname = "site2.b-cdn.net"
|
|
|
|
site_repo = Mock()
|
|
site_repo.get_by_id.side_effect = lambda sid: site1 if sid == 10 else site2
|
|
|
|
urls = generate_urls_for_batch([content1, content2], site_repo)
|
|
|
|
assert len(urls) == 2
|
|
assert urls[0]["url"] == "https://www.site1.com/article-one.html"
|
|
assert urls[1]["url"] == "https://site2.b-cdn.net/article-two.html"
|
|
|