""" Unit tests for feeds reading and parsing. """ import logging import os import sys from unittest.mock import patch from courlan import get_hostinfo from trafilatura.cli import main from trafilatura.feeds import ( FeedParameters, determine_feed, extract_links, find_links, find_feed_urls, handle_link_list, probe_gnews, ) logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) TEST_DIR = os.path.abspath(os.path.dirname(__file__)) RESOURCES_DIR = os.path.join(TEST_DIR, "resources") XMLDECL = '\n' def test_atom_extraction(): """Test link extraction from an Atom feed""" params = FeedParameters("https://example.org", "example.org", "") assert not extract_links(None, params) assert len(extract_links("", params)) == 0 filepath = os.path.join(RESOURCES_DIR, "feed1.atom") with open(filepath, "r", encoding="utf-8") as f: teststring = f.read() assert len(extract_links(teststring, params)) > 0 params = FeedParameters("https://www.dwds.de", "dwds.de", "") assert ( len( extract_links( f'{XMLDECL}', params, ) ) == 0 ) params = FeedParameters("http://example.org", "example.org", "http://example.org") assert ( len( extract_links( f'{XMLDECL}', params, ) ) == 0 ) params = FeedParameters("https://example.org", "example.org", "") assert ( len( extract_links( f'{XMLDECL}', params, ) ) == 0 ) params = FeedParameters("http://example.org/", "example.org", "http://example.org") assert extract_links( f'{XMLDECL}', params ) == [ "http://example.org/article1/" ] # TODO: remove slash? def test_rss_extraction(): """Test link extraction from a RSS feed""" params = FeedParameters("http://example.org/", "example.org", "") assert ( len( extract_links(f"{XMLDECL}http://example.org/article1/", params) ) == 1 ) # CDATA assert extract_links( f"{XMLDECL}", params ) == [ "http://example.org/article1/" ] # TODO: remove slash? # spaces params = FeedParameters("https://www.ak-kurier.de/", "ak-kurier.de", "") assert ( len( extract_links( XMLDECL + "\r\n https://www.ak-kurier.de/akkurier/www/artikel/108815-sinfonisches-blasorchester-spielt-1500-euro-fuer-kinder-in-drk-krankenhaus-kirchen-ein ", params, ) ) == 1 ) params = FeedParameters("http://example.org", "example.org", "http://example.org") assert len(extract_links(f"{XMLDECL}http://example.org/", params)) == 0 params = FeedParameters("http://example.org", "example.org", "") assert len(extract_links(f"{XMLDECL}https://example.org", params)) == 0 params = FeedParameters("https://www.dwds.de", "dwds.de", "https://www.dwds.de") assert extract_links( f"{XMLDECL}/api/feed/themenglossar/Corona", params ) == ["https://www.dwds.de/api/feed/themenglossar/Corona"] params = FeedParameters("https://example.org", "example.org", "") filepath = os.path.join(RESOURCES_DIR, "feed2.rss") with open(filepath, "r", encoding="utf-8") as f: teststring = f.read() assert len(extract_links(teststring, params)) > 0 def test_json_extraction(): """Test link extraction from a JSON feed""" # find link params = FeedParameters("https://www.jsonfeed.org", "jsonfeed.org", "") assert ( len( determine_feed( '>', params, ) ) == 1 ) # extract data assert not extract_links("{/}", params) filepath = os.path.join(RESOURCES_DIR, "feed.json") with open(filepath, "r", encoding="utf-8") as f: teststring = f.read() params = FeedParameters("https://npr.org", "npr.org", "") links = extract_links(teststring, params) assert len(links) == 25 # id as a backup params = FeedParameters("https://example.org", "example.org", "") links = extract_links( r'{"version":"https:\/\/jsonfeed.org\/version\/1","items":[{"id":"https://www.example.org/1","title":"Test"}]}', params, ) assert len(links) == 1 def test_feeds_helpers(): """Test helper functions for feed extraction""" params = FeedParameters("https://example.org", "example.org", "https://example.org") domainname, baseurl = get_hostinfo("https://example.org") assert domainname == params.domain and baseurl == params.base # empty page assert find_links("", params) == [] # nothing useful assert len(determine_feed("", params)) == 0 assert ( len( determine_feed( '', params, ) ) == 0 ) # useful assert ( len( determine_feed( '', params, ) ) == 1 ) assert ( len( determine_feed( '', params, ) ) == 1 ) assert ( len( determine_feed( '', params, ) ) == 1 ) assert ( len( determine_feed( '', params, ) ) == 1 ) assert ( len( determine_feed( '', params, ) ) == 1 ) # no comments wanted assert ( len( determine_feed( '', params, ) ) == 0 ) # invalid links params = FeedParameters("example.org", "example.org", "https://example.org") # fix assert ( len( determine_feed( '', params, ) ) == 0 ) # detecting in -elements params = FeedParameters("https://example.org", "example.org", "https://example.org") assert determine_feed( '', params ) == ["https://example.org/feed.xml"] assert determine_feed( '', params ) == ["https://example.org/feed.atom"] assert determine_feed( '', params ) == ["https://example.org/rss"] assert determine_feed( '', params, ) == ["https://example.org/feeds/posts/default/"] assert ( len( determine_feed( '', params, ) ) == 1 ) assert determine_feed( '', params, ) == ["https://example.org/?feed=rss"] # feed discovery assert not find_feed_urls("http://") assert not find_feed_urls("https://httpbun.com/status/404") # feed download and processing links = find_feed_urls("https://www.w3.org/blog/feed/") assert len(links) > 0 # Feedburner/Google links assert handle_link_list(["https://feedproxy.google.com/ABCD"], params) == [ "https://feedproxy.google.com/ABCD" ] # override failed checks assert handle_link_list(["https://feedburner.com/kat/1"], params) == [ "https://feedburner.com/kat/1" ] # diverging domain names assert not handle_link_list(["https://www.software.info/1"], params) # Gnews params = FeedParameters( "https://www.handelsblatt.com", "handelsblatt.com", "https://www.handelsblatt.com", False, "de", ) assert probe_gnews(params, None) is not None def test_cli_behavior(): """Test command-line interface with respect to feeds""" testargs = ["", "--list", "--feed", "https://httpbun.com/xml"] with patch.object(sys, "argv", testargs): assert main() is None if __name__ == "__main__": test_atom_extraction() test_rss_extraction() test_json_extraction() test_feeds_helpers() test_cli_behavior()