Coverage for tests/publisher/snaps/test_builds.py: 100%
46 statements
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-26 22:06 +0000
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-26 22:06 +0000
1import unittest
2from unittest.mock import patch
4from webapp.publisher.snaps.builds import map_build_and_upload_states
5from webapp.publisher.snaps.build_views import (
6 extract_github_repository,
7 get_builds,
8)
11class TestBuildStateMapper(unittest.TestCase):
12 def test_build_state_mappings(self):
13 combinations = [
14 ("Needs building", "Unscheduled", "building_soon"),
15 ("Needs building", "Pending", "building_soon"),
16 ("Needs building", "Failed to upload", "building_soon"),
17 (
18 "Needs building",
19 "Failed to release to channels",
20 "building_soon",
21 ),
22 ("Needs building", "Uploaded", "building_soon"),
23 ("Successfully built", "Unscheduled", "wont_release"),
24 ("Successfully built", "Pending", "releasing_soon"),
25 ("Successfully built", "Failed to upload", "release_failed"),
26 (
27 "Successfully built",
28 "Failed to release to channels",
29 "release_failed",
30 ),
31 ("Successfully built", "Uploaded", "released"),
32 ("Currently building", "Unscheduled", "in_progress"),
33 ("Currently building", "Pending", "in_progress"),
34 ("Currently building", "Failed to upload", "in_progress"),
35 (
36 "Currently building",
37 "Failed to release to channels",
38 "in_progress",
39 ),
40 ("Currently building", "Uploaded", "in_progress"),
41 ("Failed to build", "Unscheduled", "failed_to_build"),
42 ("Failed to build", "Pending", "failed_to_build"),
43 ("Failed to build", "Failed to upload", "failed_to_build"),
44 (
45 "Failed to build",
46 "Failed to release to channels",
47 "failed_to_build",
48 ),
49 ("Failed to build", "Uploaded", "failed_to_build"),
50 ("Dependency wait", "Unscheduled", "failed_to_build"),
51 ("Dependency wait", "Pending", "failed_to_build"),
52 ("Dependency wait", "Failed to upload", "failed_to_build"),
53 (
54 "Dependency wait",
55 "Failed to release to channels",
56 "failed_to_build",
57 ),
58 ("Dependency wait", "Uploaded", "failed_to_build"),
59 ("Chroot problem", "Unscheduled", "failed_to_build"),
60 ("Chroot problem", "Pending", "failed_to_build"),
61 ("Chroot problem", "Failed to upload", "failed_to_build"),
62 (
63 "Chroot problem",
64 "Failed to release to channels",
65 "failed_to_build",
66 ),
67 ("Chroot problem", "Uploaded", "failed_to_build"),
68 ("Build for superseded Source", "Unscheduled", "failed_to_build"),
69 ("Build for superseded Source", "Pending", "failed_to_build"),
70 (
71 "Build for superseded Source",
72 "Failed to upload",
73 "failed_to_build",
74 ),
75 (
76 "Build for superseded Source",
77 "Failed to release to channels",
78 "failed_to_build",
79 ),
80 ("Build for superseded Source", "Uploaded", "failed_to_build"),
81 ("Failed to upload", "Unscheduled", "failed_to_build"),
82 ("Failed to upload", "Pending", "failed_to_build"),
83 ("Failed to upload", "Failed to upload", "failed_to_build"),
84 (
85 "Failed to upload",
86 "Failed to release to channels",
87 "failed_to_build",
88 ),
89 ("Failed to upload", "Uploaded", "failed_to_build"),
90 ("Cancelling build", "Unscheduled", "cancelled"),
91 ("Cancelling build", "Pending", "cancelled"),
92 ("Cancelling build", "Cancelling build", "cancelled"),
93 (
94 "Cancelling build",
95 "Failed to release to channels",
96 "cancelled",
97 ),
98 ("Cancelling build", "Uploaded", "cancelled"),
99 ("Cancelled build", "Unscheduled", "cancelled"),
100 ("Cancelled build", "Pending", "cancelled"),
101 ("Cancelled build", "Cancelled build", "cancelled"),
102 (
103 "Cancelled build",
104 "Failed to release to channels",
105 "cancelled",
106 ),
107 ("Cancelled build", "Uploaded", "cancelled"),
108 ("Failed to upload", "Unscheduled", "failed_to_build"),
109 ("Failed to upload", "Pending", "failed_to_build"),
110 ("Failed to upload", "Failed to upload", "failed_to_build"),
111 (
112 "Failed to upload",
113 "Failed to release to channels",
114 "failed_to_build",
115 ),
116 ("Failed to upload", "Uploaded", "failed_to_build"),
117 ]
119 for build_state, upload_state, expected in combinations:
120 result = map_build_and_upload_states(build_state, upload_state)
121 self.assertEqual(result, expected)
124class TestGetBuilds(unittest.TestCase):
125 @patch("webapp.publisher.snaps.build_views.launchpad")
126 def test_get_builds_includes_github_repository(self, mock_launchpad):
127 """Test that get_builds includes GitHub repository information"""
128 # Mock Launchpad snap data with GitHub repository URL
129 lp_snap = {
130 "store_name": "test-snap",
131 "git_repository_url": "https://github.com/owner/repo",
132 }
134 # Mock build data from Launchpad
135 mock_builds = [
136 {
137 "self_link": (
138 "https://api.launchpad.net/devel/~owner/"
139 "+snap/test-snap/+build/123"
140 ),
141 "arch_tag": "amd64",
142 "datebuilt": "2023-01-01T12:00:00Z",
143 "duration": "00:05:30",
144 "build_log_url": (
145 "https://launchpad.net/~owner/+snap/test-snap/"
146 "+build/123/+files/buildlog.txt"
147 ),
148 "revision_id": "abcdef1234567890abcdef1234567890abcdef12",
149 "buildstate": "Successfully built",
150 "store_upload_status": "Uploaded",
151 "title": "Test build",
152 }
153 ]
155 mock_launchpad.get_snap_builds.return_value = mock_builds
157 # Call get_builds
158 result = get_builds(lp_snap, slice(0, 10))
160 # Verify the result includes GitHub repository information
161 self.assertEqual(result["total_builds"], 1)
162 self.assertEqual(len(result["snap_builds"]), 1)
164 build = result["snap_builds"][0]
165 self.assertEqual(build["id"], "123")
166 self.assertEqual(build["arch_tag"], "amd64")
167 self.assertEqual(
168 build["revision_id"], "abcdef1234567890abcdef1234567890abcdef12"
169 )
170 self.assertEqual(build["github_repository"], "owner/repo")
171 self.assertEqual(build["status"], "released")
173 @patch("webapp.publisher.snaps.build_views.launchpad")
174 def test_get_builds_without_github_repository(self, mock_launchpad):
175 """Test that get_builds handles snaps without GitHub repository"""
176 # Mock Launchpad snap data without GitHub repository URL
177 lp_snap = {"store_name": "test-snap"}
179 # Mock build data from Launchpad
180 mock_builds = [
181 {
182 "self_link": (
183 "https://api.launchpad.net/devel/~owner/"
184 "+snap/test-snap/+build/123"
185 ),
186 "arch_tag": "amd64",
187 "datebuilt": "2023-01-01T12:00:00Z",
188 "duration": "00:05:30",
189 "build_log_url": (
190 "https://launchpad.net/~owner/+snap/test-snap/"
191 "+build/123/+files/buildlog.txt"
192 ),
193 "revision_id": "abcdef1234567890abcdef1234567890abcdef12",
194 "buildstate": "Successfully built",
195 "store_upload_status": "Uploaded",
196 "title": "Test build",
197 }
198 ]
200 mock_launchpad.get_snap_builds.return_value = mock_builds
202 # Call get_builds
203 result = get_builds(lp_snap, slice(0, 10))
205 # Verify the result has None for GitHub repository
206 build = result["snap_builds"][0]
207 self.assertIsNone(build["github_repository"])
210class TestExtractGithubRepository(unittest.TestCase):
211 """Test the extract_github_repository helper function."""
213 def test_extract_valid_github_url(self):
214 """Test extracting owner/repo from valid GitHub URLs."""
215 test_cases = [
216 ("https://github.com/owner/repo", "owner/repo"),
217 ("https://github.com/owner/repo.git", "owner/repo"),
218 ("https://github.com/owner/repo/", "owner/repo"),
219 ("https://github.com/owner/repo.git/", "owner/repo"),
220 ("http://github.com/owner/repo", "owner/repo"),
221 ]
223 for url, expected in test_cases:
224 with self.subTest(url=url):
225 result = extract_github_repository(url)
226 self.assertEqual(result, expected)
228 def test_extract_invalid_urls(self):
229 """Test that invalid URLs return None."""
230 test_cases = [
231 None,
232 "",
233 "https://gitlab.com/owner/repo",
234 "https://bitbucket.org/owner/repo",
235 "not-a-url",
236 "https://github.com/",
237 "https://github.com/owner",
238 ]
240 for url in test_cases:
241 with self.subTest(url=url):
242 result = extract_github_repository(url)
243 self.assertIsNone(result)