Skip to main content
Glama
test_news.py•9.76 kB
# SPDX-License-Identifier: GPL-3.0-only OR MIT """ Tests for arch_ops_server.news module. """ from datetime import datetime from unittest.mock import AsyncMock, MagicMock, patch, mock_open from xml.etree import ElementTree as ET import httpx import pytest from arch_ops_server.news import ( ARCH_NEWS_URL, CRITICAL_KEYWORDS, get_latest_news, check_critical_news, get_news_since_last_update, ) class TestNewsRetrieval: """Test Arch Linux news feed retrieval.""" @pytest.fixture def sample_rss_feed(self): """Sample RSS feed XML for testing.""" return """<?xml version="1.0" encoding="utf-8"?> <rss version="2.0"> <channel> <title>Arch Linux: Recent news updates</title> <item> <title>Manual intervention required for foo package</title> <link>https://archlinux.org/news/manual-intervention-foo/</link> <pubDate>Mon, 10 Nov 2025 10:00:00 +0000</pubDate> <description><![CDATA[<p>Action required before upgrading foo package.</p>]]></description> </item> <item> <title>New kernel release 6.7</title> <link>https://archlinux.org/news/kernel-6-7/</link> <pubDate>Sun, 09 Nov 2025 14:00:00 +0000</pubDate> <description><![CDATA[<p>The Linux kernel has been updated to version 6.7.</p>]]></description> </item> <item> <title>Breaking change in systemd</title> <link>https://archlinux.org/news/systemd-breaking/</link> <pubDate>Sat, 08 Nov 2025 09:00:00 +0000</pubDate> <description><![CDATA[<p>Important changes in systemd configuration.</p>]]></description> </item> </channel> </rss> """ @pytest.mark.asyncio async def test_get_latest_news_success(self, sample_rss_feed): """Test successful news retrieval.""" mock_response = MagicMock() mock_response.content = sample_rss_feed.encode('utf-8') mock_response.raise_for_status = MagicMock() with patch("httpx.AsyncClient") as mock_client: mock_client.return_value.__aenter__.return_value.get = AsyncMock( return_value=mock_response ) result = await get_latest_news(limit=10) assert result["count"] == 3 assert len(result["news"]) == 3 assert result["news"][0]["title"] == "Manual intervention required for foo package" assert "archlinux.org" in result["news"][0]["link"] @pytest.mark.asyncio async def test_get_latest_news_with_limit(self, sample_rss_feed): """Test news retrieval with limit.""" mock_response = MagicMock() mock_response.content = sample_rss_feed.encode('utf-8') mock_response.raise_for_status = MagicMock() with patch("httpx.AsyncClient") as mock_client: mock_client.return_value.__aenter__.return_value.get = AsyncMock( return_value=mock_response ) result = await get_latest_news(limit=2) assert result["count"] == 2 assert len(result["news"]) == 2 @pytest.mark.asyncio async def test_get_latest_news_http_error(self): """Test news retrieval with HTTP error.""" mock_response = MagicMock() mock_response.status_code = 500 mock_response.raise_for_status.side_effect = httpx.HTTPStatusError( "HTTP 500", request=MagicMock(), response=mock_response ) with patch("httpx.AsyncClient") as mock_client: mock_client.return_value.__aenter__.return_value.get = AsyncMock( return_value=mock_response ) result = await get_latest_news() assert "error" in result assert result["error"] == "HTTPError" @pytest.mark.asyncio async def test_get_latest_news_timeout(self): """Test news retrieval with timeout.""" with patch("httpx.AsyncClient") as mock_client: mock_client.return_value.__aenter__.return_value.get = AsyncMock( side_effect=httpx.TimeoutException("Request timed out") ) result = await get_latest_news() assert "error" in result assert result["error"] == "Timeout" class TestCriticalNews: """Test critical news detection.""" @pytest.fixture def critical_rss_feed(self): """RSS feed with critical news items.""" return """<?xml version="1.0" encoding="utf-8"?> <rss version="2.0"> <channel> <item> <title>Manual intervention required for glibc</title> <link>https://archlinux.org/news/glibc-manual/</link> <pubDate>Mon, 10 Nov 2025 10:00:00 +0000</pubDate> <description><![CDATA[<p>Action required before upgrading.</p>]]></description> </item> <item> <title>Regular package update</title> <link>https://archlinux.org/news/regular/</link> <pubDate>Sun, 09 Nov 2025 14:00:00 +0000</pubDate> <description><![CDATA[<p>Normal update information.</p>]]></description> </item> </channel> </rss> """ @pytest.mark.asyncio async def test_check_critical_news_found(self, critical_rss_feed): """Test detection of critical news.""" mock_response = MagicMock() mock_response.content = critical_rss_feed.encode('utf-8') mock_response.raise_for_status = MagicMock() with patch("httpx.AsyncClient") as mock_client: mock_client.return_value.__aenter__.return_value.get = AsyncMock( return_value=mock_response ) result = await check_critical_news() assert result["has_critical"] is True assert result["critical_count"] == 1 assert len(result["critical_news"]) == 1 assert "manual intervention" in result["critical_news"][0]["title"].lower() assert "matched_keywords" in result["critical_news"][0] @pytest.mark.asyncio async def test_check_critical_news_none_found(self): """Test when no critical news is found.""" safe_feed = """<?xml version="1.0" encoding="utf-8"?> <rss version="2.0"> <channel> <item> <title>Regular update</title> <link>https://archlinux.org/news/regular/</link> <pubDate>Mon, 10 Nov 2025 10:00:00 +0000</pubDate> <description><![CDATA[<p>Normal update.</p>]]></description> </item> </channel> </rss> """ mock_response = MagicMock() mock_response.content = safe_feed.encode('utf-8') mock_response.raise_for_status = MagicMock() with patch("httpx.AsyncClient") as mock_client: mock_client.return_value.__aenter__.return_value.get = AsyncMock( return_value=mock_response ) result = await check_critical_news() assert result["has_critical"] is False assert result["critical_count"] == 0 class TestNewsSinceUpdate: """Test news since last update functionality.""" @pytest.fixture def sample_pacman_log(self): """Sample pacman log content.""" return """[2025-11-08 10:00] [PACMAN] Running 'pacman -Syu' [2025-11-08 10:01] [ALPM] upgraded linux (6.6.1-1 -> 6.6.2-1) [2025-11-08 10:02] [ALPM] upgraded systemd (255.1-1 -> 255.2-1) [2025-11-08 10:03] [PACMAN] synchronizing package lists [2025-11-09 15:30] [ALPM] installed test-package (1.0-1) """ @pytest.mark.asyncio @patch("arch_ops_server.news.IS_ARCH", True) async def test_get_news_since_last_update_success(self, sample_pacman_log): """Test getting news since last update.""" rss_feed = """<?xml version="1.0" encoding="utf-8"?> <rss version="2.0"> <channel> <item> <title>Recent news after update</title> <link>https://archlinux.org/news/recent/</link> <pubDate>Mon, 10 Nov 2025 10:00:00 +0000</pubDate> <description><![CDATA[<p>New announcement.</p>]]></description> </item> <item> <title>Old news before update</title> <link>https://archlinux.org/news/old/</link> <pubDate>Wed, 06 Nov 2025 10:00:00 +0000</pubDate> <description><![CDATA[<p>Old announcement.</p>]]></description> </item> </channel> </rss> """ mock_response = MagicMock() mock_response.content = rss_feed.encode('utf-8') mock_response.raise_for_status = MagicMock() with patch("httpx.AsyncClient") as mock_client, \ patch("builtins.open", mock_open(read_data=sample_pacman_log)): mock_client.return_value.__aenter__.return_value.get = AsyncMock( return_value=mock_response ) result = await get_news_since_last_update() assert result["has_news"] is True assert result["news_count"] >= 0 assert "last_update" in result @pytest.mark.asyncio @patch("arch_ops_server.news.IS_ARCH", False) async def test_get_news_since_last_update_not_arch(self): """Test on non-Arch system.""" result = await get_news_since_last_update() assert "error" in result assert result["error"] == "NotSupported" @pytest.mark.asyncio @patch("arch_ops_server.news.IS_ARCH", True) async def test_get_news_since_last_update_no_log(self): """Test when pacman log doesn't exist.""" with patch("pathlib.Path.exists", return_value=False): result = await get_news_since_last_update() assert "error" in result assert result["error"] == "NotFound"

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nihalxkumar/arch-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server