from __future__ import annotations import unittest from unittest.mock import patch from defusedxml import ElementTree as SafeET from govoplan_connectors.backend.feeds import ConnectorFeedProvider, feed_rows from govoplan_core.core.feeds import ( FeedCapabilityError, FeedEntry, FeedRenderRequest, ) from govoplan_core.security.http_fetch import HttpFetchResponse RSS = b""" Decisionshttps://example.test/ Published decisions decision-1Decision one https://example.test/1 Fri, 31 Jul 2026 10:00:00 GMT planning """ ATOM = b""" https://example.test/feedUpdates 2026-07-31T10:00:00Z update-1Update one 2026-07-31T10:00:00Z """ class ConnectorFeedProviderTests(unittest.TestCase): def setUp(self) -> None: self.provider = ConnectorFeedProvider() def test_rss_and_atom_are_normalized_to_tabular_entries(self) -> None: rss = self.provider.parse(RSS, source_url="https://example.test/rss") atom = self.provider.parse(ATOM, source_url="https://example.test/atom") self.assertEqual("rss", rss.format) self.assertEqual("decision-1", rss.entries[0].id) self.assertEqual("atom", atom.format) self.assertEqual("https://example.test/update-1", atom.entries[0].url) self.assertEqual("decision-1", feed_rows(rss)[0]["id"]) self.assertEqual(64, len(rss.sha256)) def test_fetch_records_transport_freshness_and_provenance(self) -> None: with patch( "govoplan_connectors.backend.feeds.fetch_http", return_value=HttpFetchResponse( status=200, headers={ "Content-Type": "application/rss+xml", "Cache-Control": "public, max-age=600", "ETag": '"feed-1"', }, body=RSS, ), ): document = self.provider.fetch("https://example.test/rss") self.assertEqual('"feed-1"', document.etag) self.assertIsNotNone(document.acquired_at) self.assertEqual(600, int((document.fresh_until - document.acquired_at).total_seconds())) self.assertEqual(len(RSS), document.metadata["byte_count"]) def test_render_filters_entries_by_explicit_visibility(self) -> None: request = FeedRenderRequest( format="atom", title="Public updates", feed_url="https://example.test/feed.atom", home_url="https://example.test/", entries=( FeedEntry(id="public-1", title="Public", visibility="public"), FeedEntry(id="tenant-1", title="Tenant", visibility="tenant"), ), allowed_visibilities=frozenset({"public"}), ) result = self.provider.render(request) root = SafeET.fromstring(result.body) self.assertEqual(1, result.included_entries) self.assertEqual(1, result.excluded_entries) self.assertIn(b"Public", result.body) self.assertNotIn(b"Tenant", result.body) self.assertTrue(root.tag.endswith("feed")) def test_unsafe_xml_is_rejected(self) -> None: payload = b']>&y;' with self.assertRaisesRegex(FeedCapabilityError, "not safe or valid"): self.provider.parse(payload, source_url="https://example.test/rss") if __name__ == "__main__": unittest.main()