""" Tests for Server Service. """ import base64 import pytest import time from pathlib import Path from app.models.server import Server from app.models.ssh_key import SshKey from app.services.server_service import ServerService from app.config import get_settings # Valid test SSH key VALID_SSH_KEY = """-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACB/pDNwjNcznNaRlLNF5G9hCQNjbqNZ7QeKyLIy/nvHAAAAJi/vqmQv6pk AAAAAtzc2gtZWQyNTUxOQAAACB/pDNwjNcznNaRlLNF5G9hCQNjbqNZ7QeKyLIy/nvHAA AAAEBD0cWNQnpLDUYEGNMSgVIApVJfCFuRfGG3uxJZRKLvqH+kM3CM1zOc1pGUssXkb2E JA2uuo1ntB4rIsjL+e8cAAAADm1lc3NlbmdlckBrZW50cm9zBAgMEBQ= -----END OPENSSH PRIVATE KEY----- """ def create_test_ssh_key(db_session, name="test-ssh-key"): """Helper to create a test SSH key.""" from app.services.ssh_key_service import SshKeyService ssh_service = SshKeyService(db_session) return ssh_service.create_ssh_key(name=name, private_key=VALID_SSH_KEY) def test_create_server_success(db_session, test_env_vars): """Test successful server creation with token encryption.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="gitea-server", url="https://gitea.example.com", api_token="test-api-token-123", ssh_key_id=ssh_key.id, sync_enabled=True, schedule_cron="0 0 * * *" ) assert server.id is not None assert server.name == "gitea-server" assert server.url == "https://gitea.example.com" assert server.api_token != "test-api-token-123" # Should be encrypted assert server.ssh_key_id == ssh_key.id assert server.sync_enabled is True assert server.schedule_cron == "0 0 * * *" assert server.local_path is not None assert server.status == "untested" assert server.created_at is not None assert server.updated_at is not None def test_create_server_with_duplicate_name(db_session, test_env_vars): """Test that duplicate server names are not allowed.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) service.create_server( name="duplicate-server", url="https://gitea1.example.com", api_token="token1", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) with pytest.raises(ValueError, match="already exists"): service.create_server( name="duplicate-server", url="https://gitea2.example.com", api_token="token2", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) def test_create_server_with_invalid_ssh_key_id(db_session, test_env_vars): """Test that invalid SSH key ID is rejected.""" service = ServerService(db_session) with pytest.raises(ValueError, match="SSH key not found"): service.create_server( name="test-server", url="https://gitea.example.com", api_token="test-token", ssh_key_id=99999, sync_enabled=False, schedule_cron=None ) def test_create_server_generates_local_path(db_session, test_env_vars): """Test that local_path is generated correctly based on server name.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="my-gitea", url="https://gitea.example.com", api_token="token123", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) settings = get_settings() expected_path = settings.repos_dir / "my-gitea" assert server.local_path == str(expected_path) def test_create_server_without_schedule(db_session, test_env_vars): """Test creating a server without sync schedule.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="no-schedule-server", url="https://gitea.example.com", api_token="token", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) assert server.schedule_cron is None assert server.sync_enabled is False def test_list_servers_empty(db_session, test_env_vars): """Test listing servers when none exist.""" service = ServerService(db_session) servers = service.list_servers() assert servers == [] def test_list_servers_multiple(db_session, test_env_vars): """Test listing multiple servers.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) service.create_server( name="server-1", url="https://gitea1.example.com", api_token="token1", ssh_key_id=ssh_key.id, sync_enabled=True, schedule_cron="0 0 * * *" ) service.create_server( name="server-2", url="https://gitea2.example.com", api_token="token2", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) servers = service.list_servers() assert len(servers) == 2 assert any(s.name == "server-1" for s in servers) assert any(s.name == "server-2" for s in servers) def test_get_server_by_id(db_session, test_env_vars): """Test getting a server by ID.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) created_server = service.create_server( name="get-test-server", url="https://gitea.example.com", api_token="token", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) retrieved_server = service.get_server(created_server.id) assert retrieved_server is not None assert retrieved_server.id == created_server.id assert retrieved_server.name == "get-test-server" def test_get_server_not_found(db_session, test_env_vars): """Test getting a non-existent server.""" service = ServerService(db_session) server = service.get_server(99999) assert server is None def test_update_server_name(db_session, test_env_vars): """Test updating server name.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="old-name", url="https://gitea.example.com", api_token="token", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) updated_server = service.update_server(server.id, name="new-name") assert updated_server.name == "new-name" assert updated_server.id == server.id def test_update_server_url(db_session, test_env_vars): """Test updating server URL.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="test-server", url="https://old-url.example.com", api_token="token", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) updated_server = service.update_server( server.id, url="https://new-url.example.com" ) assert updated_server.url == "https://new-url.example.com" def test_update_server_api_token(db_session, test_env_vars): """Test updating server API token with encryption.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="test-server", url="https://gitea.example.com", api_token="old-token", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) updated_server = service.update_server( server.id, api_token="new-token" ) # The token should be encrypted (different from original) assert updated_server.api_token != "new-token" def test_update_server_sync_settings(db_session, test_env_vars): """Test updating server sync settings.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="test-server", url="https://gitea.example.com", api_token="token", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) updated_server = service.update_server( server.id, sync_enabled=True, schedule_cron="*/5 * * * *" ) assert updated_server.sync_enabled is True assert updated_server.schedule_cron == "*/5 * * * *" def test_update_server_not_found(db_session, test_env_vars): """Test updating a non-existent server.""" service = ServerService(db_session) with pytest.raises(ValueError, match="Server not found"): service.update_server(99999, name="new-name") def test_update_server_duplicate_name(db_session, test_env_vars): """Test that updating to duplicate name is rejected.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server1 = service.create_server( name="server-1", url="https://gitea1.example.com", api_token="token1", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) server2 = service.create_server( name="server-2", url="https://gitea2.example.com", api_token="token2", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) with pytest.raises(ValueError, match="already exists"): service.update_server(server2.id, name="server-1") def test_delete_server_success(db_session, test_env_vars): """Test successful server deletion.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="delete-test-server", url="https://gitea.example.com", api_token="token", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) result = service.delete_server(server.id) assert result is True # Verify the server is deleted retrieved_server = service.get_server(server.id) assert retrieved_server is None def test_delete_server_not_found(db_session, test_env_vars): """Test deleting a non-existent server.""" service = ServerService(db_session) result = service.delete_server(99999) assert result is False def test_get_decrypted_token(db_session, test_env_vars): """Test getting decrypted API token.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) original_token = "my-secret-api-token" server = service.create_server( name="decrypt-test-server", url="https://gitea.example.com", api_token=original_token, ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) decrypted_token = service.get_decrypted_token(server) assert decrypted_token == original_token assert decrypted_token != server.api_token # Should differ from encrypted value def test_get_decrypted_token_with_server_object(db_session, test_env_vars): """Test getting decrypted token using server object from database.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) original_token = "another-secret-token" server = service.create_server( name="token-test-server", url="https://gitea.example.com", api_token=original_token, ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) # Retrieve server from DB (simulates real usage) db_server = db_session.query(Server).filter_by(name="token-test-server").first() decrypted_token = service.get_decrypted_token(db_server) assert decrypted_token == original_token def test_create_server_creates_repos_directory(db_session, test_env_vars, tmp_path): """Test that repos directory is created when adding a server.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="dir-test-server", url="https://gitea.example.com", api_token="token", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) settings = get_settings() repos_dir = settings.repos_dir assert repos_dir.exists() def test_server_default_status(db_session, test_env_vars): """Test that new servers have default 'untested' status.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="status-test-server", url="https://gitea.example.com", api_token="token", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) assert server.status == "untested" def test_update_server_updates_timestamp(db_session, test_env_vars): """Test that updating server updates the updated_at timestamp.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server = service.create_server( name="timestamp-server", url="https://gitea.example.com", api_token="token", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) original_updated_at = server.updated_at # Small delay to ensure timestamp difference time.sleep(0.1) updated_server = service.update_server(server.id, url="https://new-url.example.com") # Verify the URL was updated (which means update_server was called) assert updated_server.url == "https://new-url.example.com" # The updated_at should be >= original (may be equal on fast systems) assert updated_server.updated_at >= original_updated_at def test_encryption_is_different(db_session, test_env_vars): """Test that API tokens are encrypted and different from plaintext.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) original_token = "plaintext-token-12345" service.create_server( name="encryption-test-server", url="https://gitea.example.com", api_token=original_token, ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) # Get the raw database record db_server = db_session.query(Server).filter_by(name="encryption-test-server").first() # The stored token should be encrypted assert db_server.api_token != original_token # Should be base64 encoded (longer) assert len(db_server.api_token) > len(original_token) def test_list_servers_ordered_by_creation(db_session, test_env_vars): """Test that servers are listed in creation order.""" ssh_key = create_test_ssh_key(db_session) service = ServerService(db_session) server1 = service.create_server( name="first-server", url="https://gitea1.example.com", api_token="token1", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) server2 = service.create_server( name="second-server", url="https://gitea2.example.com", api_token="token2", ssh_key_id=ssh_key.id, sync_enabled=False, schedule_cron=None ) servers = service.list_servers() assert servers[0].id == server1.id assert servers[1].id == server2.id