Skip to content

Commit 9c10626

Browse files
committed
add tests for subscriptions model
1 parent 790b415 commit 9c10626

File tree

8 files changed

+165
-0
lines changed

8 files changed

+165
-0
lines changed

castero/subscriptions.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ def load(self, path: str) -> None:
4848
SubscriptionsParseError: unable to parse text as an XML document
4949
SubscriptionsLoadError: an exception occurred when attempting to
5050
load the file
51+
SubscriptionsStructureError: the file data is not a properly
52+
structured OPML document
5153
"""
5254
self._tree = None
5355
try:
@@ -118,10 +120,24 @@ def generate(self, feeds: List[Feed]) -> None:
118120

119121
def _parse_feeds(self) -> None:
120122
"""Parse the XML tree into a list of feeds.
123+
124+
Raises:
125+
SubscriptionsStructureError: the file data is not a properly
126+
structured OPML document
121127
"""
128+
error_msg = "The file data is not a properly structured OPML document"
129+
130+
if self._tree is None:
131+
raise SubscriptionsStructureError(error_msg)
122132
body = self._tree.find('body')
133+
if body is None:
134+
raise SubscriptionsStructureError(error_msg)
123135
container = body.find('outline')
136+
if container is None:
137+
raise SubscriptionsStructureError(error_msg)
124138
entries = container.findall('outline')
139+
if entries is None:
140+
raise SubscriptionsStructureError(error_msg)
125141

126142
self._feeds = []
127143
for entry in entries:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
2+
<opml version="1.0">
3+
</opml>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
2+
<opml version="1.0">
3+
<head>
4+
<title>my feeds</title>
5+
</head>
6+
<body>
7+
<outline type="rss" text="feed1" xmlUrl="http://feed1" />
8+
<outline type="rss" text="feed2" xmlUrl="http://feed2" />
9+
</body>
10+
</opml>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
2+
<opml version="1.0">
3+
<head>
4+
<title>my feeds</title>
5+
</head>
6+
<body>
7+
<outline text="feeds">
8+
<outline type="rss" text="feed1" xmlUrl="http://feed1" />
9+
<outline type="rss" text="feed2" xmlUrl="http://feed2" />
10+
</outline>
11+
</body>
12+
</bad>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
2+
<opml version="1.0">
3+
<head>
4+
<title>my feeds</title>
5+
</head>
6+
<body>
7+
<outline text="feeds">
8+
<outline type="rss" text="feed1" xmlUrl="http://feed1" />
9+
<outline type="rss" text="feed2" xmlUrl="http://feed2" />
10+
</outline>
11+
</body>
12+
</opml>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
2+
<opml version="1.0">
3+
<body>
4+
<outline text="feeds">
5+
</outline>
6+
</body>
7+
</opml>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
2+
<opml version="1.0">
3+
<body>
4+
<outline text="feeds">
5+
<outline type="rss" text="feed1" xmlUrl="http://feed1" />
6+
<outline type="rss" text="feed2" xmlUrl="http://feed2" />
7+
</outline>
8+
</body>
9+
</opml>

tests/test_subscriptions.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import os
2+
from unittest import mock
3+
import xml.etree.ElementTree as ElementTree
4+
5+
import pytest
6+
7+
from castero.feed import Feed
8+
from castero.subscriptions import Subscriptions, SubscriptionsLoadError, \
9+
SubscriptionsParseError, SubscriptionsStructureError, SubscriptionsError
10+
11+
my_dir = os.path.dirname(os.path.realpath(__file__))
12+
13+
14+
def test_subscriptions_valid_complete():
15+
mysubscriptions = Subscriptions()
16+
Feed.__init__ = mock.MagicMock(return_value=None)
17+
mysubscriptions.load(my_dir + "/subscriptions/valid_complete.xml")
18+
assert isinstance(mysubscriptions, Subscriptions)
19+
Feed.__init__.assert_any_call(url="http://feed1")
20+
Feed.__init__.assert_any_call(url="http://feed2")
21+
assert Feed.__init__.call_count == 2
22+
assert len(mysubscriptions.feeds) == 2
23+
24+
25+
def test_subscriptions_valid_no_head():
26+
mysubscriptions = Subscriptions()
27+
Feed.__init__ = mock.MagicMock(return_value=None)
28+
mysubscriptions.load(my_dir + "/subscriptions/valid_no_head.xml")
29+
assert isinstance(mysubscriptions, Subscriptions)
30+
Feed.__init__.assert_any_call(url="http://feed1")
31+
Feed.__init__.assert_any_call(url="http://feed2")
32+
assert Feed.__init__.call_count == 2
33+
assert len(mysubscriptions.feeds) == 2
34+
35+
36+
def test_subscriptions_valid_minimal():
37+
mysubscriptions = Subscriptions()
38+
Feed.__init__ = mock.MagicMock(return_value=None)
39+
mysubscriptions.load(my_dir + "/subscriptions/valid_minimal.xml")
40+
assert isinstance(mysubscriptions, Subscriptions)
41+
assert len(mysubscriptions.feeds) == 0
42+
43+
44+
def test_subscriptions_broken_nonexistant():
45+
mysubscriptions = Subscriptions()
46+
Feed.__init__ = mock.MagicMock(return_value=None)
47+
with pytest.raises(SubscriptionsLoadError):
48+
mysubscriptions.load(my_dir + "/subscriptions/doesnt_exist")
49+
50+
51+
def test_subscriptions_broken_parse():
52+
mysubscriptions = Subscriptions()
53+
Feed.__init__ = mock.MagicMock(return_value=None)
54+
with pytest.raises(SubscriptionsParseError):
55+
mysubscriptions.load(my_dir + "/subscriptions/broken_parse.xml")
56+
57+
58+
def test_subscriptions_broken_no_body():
59+
mysubscriptions = Subscriptions()
60+
Feed.__init__ = mock.MagicMock(return_value=None)
61+
with pytest.raises(SubscriptionsStructureError):
62+
mysubscriptions.load(my_dir + "/subscriptions/broken_no_body.xml")
63+
64+
65+
def test_subscriptions_generate():
66+
feed1 = mock.MagicMock()
67+
feed2 = mock.MagicMock()
68+
mysubscriptions = Subscriptions()
69+
mysubscriptions.generate([feed1, feed2])
70+
71+
Feed.__init__ = mock.MagicMock(return_value=None)
72+
mysubscriptions._parse_feeds()
73+
assert len(mysubscriptions.feeds) == 2
74+
75+
76+
def test_subscriptions_save():
77+
temp_fname = my_dir + "/subscriptions/saved_temp.xml"
78+
Feed.__init__ = mock.MagicMock(return_value=None)
79+
80+
mysubscriptions1 = Subscriptions()
81+
mysubscriptions1.load(my_dir + "/subscriptions/valid_complete.xml")
82+
mysubscriptions1.save(temp_fname)
83+
84+
mysubscriptions2 = Subscriptions()
85+
mysubscriptions2.load(my_dir + "/subscriptions/saved_temp.xml")
86+
os.remove(temp_fname)
87+
88+
tree1 = ElementTree.tostring(mysubscriptions1._tree.getroot())
89+
tree2 = ElementTree.tostring(mysubscriptions2._tree.getroot())
90+
assert tree1 == tree2
91+
92+
93+
def test_subscriptions_save_before_create():
94+
mysubscriptions = Subscriptions()
95+
with pytest.raises(SubscriptionsError):
96+
mysubscriptions.save(my_dir + "/subscriptions/saved_bad_temp.xml")

0 commit comments

Comments
 (0)