"""Tests for Site Manager (core/site_manager.py)."""
import pytest
from core.site_manager import SiteConfig, SiteManager
# --- SiteConfig Tests ---
class TestSiteConfig:
"""Test SiteConfig Pydantic model."""
def test_basic_creation(self):
"""Should create a config with required fields."""
config = SiteConfig(
site_id="site1",
plugin_type="wordpress",
url="https://example.com",
)
assert config.site_id == "site1"
assert config.plugin_type == "wordpress"
assert config.url == "https://example.com"
def test_alias_none_when_not_provided(self):
"""Alias should be None when not explicitly provided."""
config = SiteConfig(site_id="site1", plugin_type="wordpress")
# Pydantic V2: field_validator doesn't run on default values
# get_display_name() handles the fallback to site_id
assert config.alias is None
assert config.get_display_name() == "site1"
def test_custom_alias(self):
"""Custom alias should override default."""
config = SiteConfig(site_id="site1", plugin_type="wordpress", alias="myblog")
assert config.alias == "myblog"
def test_get_full_id(self):
"""Full ID should be plugin_type_site_id."""
config = SiteConfig(site_id="site1", plugin_type="wordpress")
assert config.get_full_id() == "wordpress_site1"
def test_get_display_name_with_alias(self):
"""Display name should prefer alias."""
config = SiteConfig(site_id="site1", plugin_type="wordpress", alias="myblog")
assert config.get_display_name() == "myblog"
def test_get_display_name_without_alias(self):
"""Display name should fall back to site_id."""
config = SiteConfig(site_id="site1", plugin_type="wordpress", alias=None)
assert config.get_display_name() == "site1"
def test_extra_fields_allowed(self):
"""Plugin-specific fields should be accepted."""
config = SiteConfig(
site_id="site1",
plugin_type="wordpress",
consumer_key="ck_123",
consumer_secret="cs_456",
)
assert config.model_extra["consumer_key"] == "ck_123"
def test_to_dict(self):
"""to_dict should include all fields."""
config = SiteConfig(
site_id="site1",
plugin_type="wordpress",
url="https://example.com",
)
d = config.to_dict()
assert d["site_id"] == "site1"
assert d["plugin_type"] == "wordpress"
assert d["url"] == "https://example.com"
# --- SiteManager Tests ---
class TestSiteManagerRegistration:
"""Test site registration and lookup."""
@pytest.fixture
def manager(self):
return SiteManager()
def test_register_site(self, manager):
"""Should register a site and retrieve it by ID."""
config = SiteConfig(site_id="site1", plugin_type="wordpress", url="https://example.com")
manager.register_site(config)
result = manager.get_site_config("wordpress", "site1")
assert result.url == "https://example.com"
def test_register_site_with_alias(self, manager):
"""Should be retrievable by alias."""
config = SiteConfig(
site_id="site1", plugin_type="wordpress", alias="myblog", url="https://example.com"
)
manager.register_site(config)
result = manager.get_site_config("wordpress", "myblog")
assert result.site_id == "site1"
def test_site_not_found_raises(self, manager):
"""Should raise ValueError for unknown site."""
config = SiteConfig(site_id="site1", plugin_type="wordpress")
manager.register_site(config)
with pytest.raises(ValueError, match="not configured"):
manager.get_site_config("wordpress", "nonexistent")
def test_unknown_plugin_type_raises(self, manager):
"""Should raise ValueError for unknown plugin type."""
with pytest.raises(ValueError, match="No sites configured"):
manager.get_site_config("unknown_plugin", "site1")
def test_list_sites(self, manager):
"""Should list all site IDs and aliases."""
config1 = SiteConfig(site_id="site1", plugin_type="wordpress", alias="blog1")
config2 = SiteConfig(site_id="site2", plugin_type="wordpress", alias="blog2")
manager.register_site(config1)
manager.register_site(config2)
sites = manager.list_sites("wordpress")
assert "site1" in sites
assert "site2" in sites
assert "blog1" in sites
assert "blog2" in sites
def test_list_sites_empty_plugin(self, manager):
"""Should return empty list for unknown plugin type."""
assert manager.list_sites("nonexistent") == []
class TestSiteManagerDiscovery:
"""Test site discovery from environment variables."""
def test_discover_wordpress_sites(self, monkeypatch):
"""Should discover sites from WORDPRESS_SITE1_* env vars."""
monkeypatch.setenv("WORDPRESS_SITE1_URL", "https://wp1.example.com")
monkeypatch.setenv("WORDPRESS_SITE1_USERNAME", "admin")
monkeypatch.setenv("WORDPRESS_SITE1_APP_PASSWORD", "xxxx")
monkeypatch.setenv("WORDPRESS_SITE1_ALIAS", "myblog")
manager = SiteManager()
count = manager.discover_sites(["wordpress"])
assert count == 1
config = manager.get_site_config("wordpress", "site1")
assert config.url == "https://wp1.example.com"
assert config.username == "admin"
assert config.alias == "myblog"
def test_discover_multiple_sites(self, monkeypatch):
"""Should discover multiple sites for same plugin type."""
monkeypatch.setenv("WORDPRESS_SITE1_URL", "https://wp1.example.com")
monkeypatch.setenv("WORDPRESS_SITE1_USERNAME", "admin")
monkeypatch.setenv("WORDPRESS_SITE2_URL", "https://wp2.example.com")
monkeypatch.setenv("WORDPRESS_SITE2_USERNAME", "editor")
manager = SiteManager()
count = manager.discover_sites(["wordpress"])
assert count == 2
def test_discover_across_plugin_types(self, monkeypatch):
"""Should discover sites across different plugin types."""
monkeypatch.setenv("WORDPRESS_SITE1_URL", "https://wp.example.com")
monkeypatch.setenv("WORDPRESS_SITE1_USERNAME", "admin")
monkeypatch.setenv("GITEA_SITE1_URL", "https://git.example.com")
monkeypatch.setenv("GITEA_SITE1_TOKEN", "tok_123")
manager = SiteManager()
count = manager.discover_sites(["wordpress", "gitea"])
assert count == 2
def test_reserved_words_skipped(self, monkeypatch):
"""Reserved words like LIMIT, RATE should not become site IDs."""
monkeypatch.setenv("WORDPRESS_LIMIT_PER_MINUTE", "100")
monkeypatch.setenv("WORDPRESS_RATE_PER_HOUR", "500")
manager = SiteManager()
count = manager.discover_sites(["wordpress"])
assert count == 0
def test_incomplete_config_skipped(self, monkeypatch):
"""Sites with no config data (only prefix match) should be skipped."""
# WORDPRESS_SITE1_ exists as prefix but no actual config keys
# This is hard to test directly since env vars always have a value
# Instead, test that a site with only site_id and plugin_type (no config) is skipped
manager = SiteManager()
count = manager.discover_sites(["wordpress"])
assert count == 0
class TestSiteManagerCounts:
"""Test counting and listing methods."""
@pytest.fixture
def populated_manager(self):
manager = SiteManager()
for i in range(3):
config = SiteConfig(
site_id=f"site{i+1}",
plugin_type="wordpress",
url=f"https://wp{i+1}.example.com",
)
manager.register_site(config)
config = SiteConfig(
site_id="repo1",
plugin_type="gitea",
url="https://git.example.com",
)
manager.register_site(config)
return manager
def test_get_count(self, populated_manager):
assert populated_manager.get_count() == 4
def test_get_count_by_type(self, populated_manager):
counts = populated_manager.get_count_by_type()
assert counts["wordpress"] == 3
assert counts["gitea"] == 1
def test_get_sites_by_type(self, populated_manager):
wp_sites = populated_manager.get_sites_by_type("wordpress")
assert len(wp_sites) == 3
def test_get_sites_by_type_empty(self, populated_manager):
assert populated_manager.get_sites_by_type("nonexistent") == []
def test_list_all_sites(self, populated_manager):
all_sites = populated_manager.list_all_sites()
assert len(all_sites) == 4
plugin_types = {s["plugin_type"] for s in all_sites}
assert plugin_types == {"wordpress", "gitea"}
def test_repr(self, populated_manager):
r = repr(populated_manager)
assert "SiteManager" in r
assert "total=4" in r