Coverage for tests/store/tests_publisher.py: 100%
76 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-08 12:33 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-08 12:33 +0000
1import responses
2from urllib.parse import urlencode
3from flask_testing import TestCase
4from webapp.app import create_app
5from cache.cache_utility import redis_cache
7JETBRAINS_FIND_RESPONSE = {
8 "results": [
9 {
10 "name": "pycharm-community",
11 "snap": {
12 "media": [
13 {
14 "height": 400,
15 "type": "icon",
16 "url": (
17 "https://dashboard.snapcraft.io/site_media/"
18 "appmedia/2024/06/Avatar-7.png"
19 ),
20 "width": 400,
21 },
22 {
23 "height": 2095,
24 "type": "screenshot",
25 "url": (
26 "https://dashboard.snapcraft.io/site_media/"
27 "appmedia/2017/12/pycharm_com_general.png"
28 ),
29 "width": 3713,
30 },
31 ],
32 "publisher": {
33 "display-name": "jetbrains",
34 "id": "28zEonXNoBLvIB7xneRbltOsp0Nf7DwS",
35 "username": "jetbrains",
36 "validation": "verified",
37 },
38 "summary": "PyCharm Community Edition",
39 "title": "pycharm-community",
40 },
41 "snap-id": "Qo9GiW9eyzgN1tXmWpQ9gdstdFsj4K7E",
42 },
43 ]
44}
46LUKEWH_FIND_RESPONSE = {
47 "results": [
48 {
49 "name": "deluge-lukewh",
50 "snap": {
51 "media": [
52 {
53 "height": 300,
54 "type": "icon",
55 "url": (
56 "https://dashboard.snapcraft.io/site_media/"
57 "appmedia/2020/04/the-new-deluge-icon-300x300.png"
58 ),
59 "width": 300,
60 },
61 {
62 "height": 720,
63 "type": "banner",
64 "url": (
65 "https://dashboard.snapcraft.io/site_media/"
66 "appmedia/2020/05/deluge-banner1.png"
67 ),
68 "width": 2160,
69 },
70 ],
71 "publisher": {
72 "display-name": "LukeWH",
73 "id": "GMrEEEdGN4gN9BhRHjRRDCoJuUkyJJnm",
74 "username": "lukewh",
75 "validation": "unproven",
76 },
77 "summary": (
78 "Deluge is a fully-featured cross-platform "
79 "BitTorrent client"
80 ),
81 "title": "Deluge",
82 },
83 "snap-id": "5dm2RBhIIGdr8sAnA9WJZWco2MQgbni7",
84 },
85 ]
86}
89def _snap_result(name, title):
90 return {
91 "name": name,
92 "snap": {
93 "media": [
94 {
95 "height": 256,
96 "type": "icon",
97 "url": (
98 "https://dashboard.snapcraft.io/site_media/"
99 "appmedia/" + name + ".png"
100 ),
101 "width": 256,
102 }
103 ],
104 "publisher": {
105 "display-name": "Snapcrafters",
106 "id": "snapcrafters-id",
107 "username": "snapcrafters",
108 "validation": "starred",
109 },
110 "summary": title + " summary",
111 "title": title,
112 },
113 "snap-id": name + "-id",
114 }
117SNAPCRAFTERS_FIND_RESPONSE = {
118 "results": [
119 _snap_result("sublime-text", "Sublime Text"),
120 _snap_result("discord", "Discord"),
121 _snap_result("alacritty", "Alacritty"),
122 ]
123}
126class GetPublisherPageTest(TestCase):
127 def setUp(self):
128 redis_cache.fallback.clear()
129 self.api_url_publisher_items = "".join(
130 [
131 "https://api.snapcraft.io/api/v1/",
132 "snaps/search",
133 "?",
134 urlencode(
135 {
136 "q": "publisher:28zEonXNoBLvIB7xneRbltOsp0Nf7DwS",
137 "size": "500",
138 "page": "1",
139 "scope": "wide",
140 "confinement": "strict,classic",
141 "fields": ",".join(
142 [
143 "package_name",
144 "title",
145 "summary",
146 "architecture",
147 "media",
148 "developer_name",
149 "developer_id",
150 "developer_validation",
151 "origin",
152 "apps",
153 "sections",
154 ]
155 ),
156 "arch": "wide",
157 }
158 ),
159 ]
160 )
161 self.api_url_find = lambda publisher: "".join(
162 [
163 "https://api.snapcraft.io/v2/",
164 "snaps/find",
165 "?",
166 urlencode(
167 {
168 "publisher": publisher,
169 "fields": ",".join(
170 [
171 "title",
172 "summary",
173 "media",
174 "publisher",
175 ]
176 ),
177 }
178 ),
179 ]
180 )
182 def create_app(self):
183 app = create_app(testing=True)
184 app.secret_key = "secret_key"
185 app.config["WTF_CSRF_METHODS"] = []
187 return app
189 @responses.activate
190 def test_community_publisher(self):
191 responses.add(
192 responses.GET,
193 self.api_url_find("lukewh"),
194 json=LUKEWH_FIND_RESPONSE,
195 status=200,
196 )
197 response = self.client.get("/publisher/lukewh")
198 self.assertEqual(response.status_code, 200)
199 self.assert_template_used("store/community-publisher-details.html")
201 @responses.activate
202 def test_existant_publisher(self):
203 responses.add(
204 responses.GET,
205 self.api_url_find("jetbrains"),
206 json=JETBRAINS_FIND_RESPONSE,
207 status=200,
208 )
209 response = self.client.get("/publisher/jetbrains")
210 self.assertEqual(response.status_code, 200)
211 self.assert_template_used("store/publisher-details.html")
213 @responses.activate
214 def test_non_existant_publisher(self):
215 responses.add(
216 responses.GET,
217 self.api_url_find("toto"),
218 json={"results": []},
219 status=200,
220 )
221 response = self.client.get("/publisher/toto")
222 self.assertEqual(response.status_code, 200)
223 self.assert_template_used("store/community-publisher-details.html")
225 @responses.activate
226 def test_api_error(self):
227 responses.add(
228 responses.GET, self.api_url_find("jetbrains"), json={}, status=504
229 )
230 response = self.client.get("/publisher/jetbrains")
231 self.assertEqual(response.status_code, 200)
232 self.assert_template_used("store/publisher-details.html")
234 @responses.activate
235 def test_no_snaps_from_api(self):
236 responses.add(
237 responses.GET,
238 self.api_url_find("jetbrains"),
239 json={"results": []},
240 status=200,
241 )
242 response = self.client.get("/publisher/jetbrains")
243 self.assertEqual(response.status_code, 200)
244 self.assert_template_used("store/publisher-details.html")
246 @responses.activate
247 def test_featured_snaps_hydrated_from_api(self):
248 # jetbrains.yaml features "intellij-idea" and "pycharm". The API
249 # returns intellij-idea (listed) but not pycharm (unlisted/gone).
250 find_response = {
251 "results": [
252 _snap_result("intellij-idea", "IntelliJ IDEA"),
253 _snap_result("goland", "GoLand"),
254 ]
255 }
256 responses.add(
257 responses.GET,
258 self.api_url_find("jetbrains"),
259 json=find_response,
260 status=200,
261 )
262 response = self.client.get("/publisher/jetbrains")
263 self.assertEqual(response.status_code, 200)
265 featured = self.get_context_variable("featured_snaps")
266 names = [snap["package_name"] for snap in featured]
267 # pycharm is not in the API response, so it is dropped.
268 self.assertEqual(names, ["intellij-idea"])
270 snap = featured[0]
271 # title/summary/icon come from the API
272 self.assertEqual(snap["title"], "IntelliJ IDEA")
273 self.assertEqual(snap["summary"], "IntelliJ IDEA summary")
274 self.assertTrue(snap["icon_url"])
275 # background/description come from the YAML.
276 self.assertEqual(snap["background"], "#000000")
277 self.assertIn("IntelliJ IDEA", snap["description"])
279 @responses.activate
280 def test_popular_snaps_hydrated_from_api(self):
281 responses.add(
282 responses.GET,
283 self.api_url_find("snapcrafters"),
284 json=SNAPCRAFTERS_FIND_RESPONSE,
285 status=200,
286 )
287 response = self.client.get("/publisher/snapcrafters")
288 self.assertEqual(response.status_code, 200)
289 self.assert_template_used("store/publisher-details.html")
291 body = response.get_data(as_text=True)
292 # make sure we render only the snaps listed in snapcrafters-snaps.yaml
293 self.assertIn('href="/sublime-text"', body)
294 self.assertIn('href="/discord"', body)
295 self.assertNotIn('href="/eclipse"', body)
296 self.assertNotIn('href="/alacritty"', body)