|
16 | 16 |
|
17 | 17 | from kernel_patches_daemon.branch_worker import NewPRWithNoChangeException |
18 | 18 | from kernel_patches_daemon.config import KPDConfig |
19 | | -from kernel_patches_daemon.github_sync import GithubSync |
| 19 | +from kernel_patches_daemon.github_sync import GithubSync, HEAD_BASE_SEPARATOR |
20 | 20 | from tests.common.patchwork_mock import init_pw_responses, load_test_data, PatchworkMock |
21 | 21 |
|
22 | 22 | TEST_BRANCH = "test-branch" |
|
47 | 47 | }, |
48 | 48 | "tag_to_branch_mapping": { |
49 | 49 | "bpf-next": [TEST_BPF_NEXT_BRANCH], |
| 50 | + "multibranch-tag": [TEST_BRANCH, TEST_BPF_NEXT_BRANCH], |
50 | 51 | "__DEFAULT__": [TEST_BRANCH], |
51 | 52 | }, |
52 | 53 | "base_directory": "/tmp", |
@@ -178,6 +179,105 @@ async def test_checkout_and_patch_safe(self) -> None: |
178 | 179 | ) |
179 | 180 | ) |
180 | 181 |
|
| 182 | + def _setup_sync_relevant_subject_mocks(self): |
| 183 | + """Helper method to set up common mocks for sync_relevant_subject tests.""" |
| 184 | + series_mock = MagicMock() |
| 185 | + series_mock.id = 12345 |
| 186 | + series_mock.all_tags = AsyncMock(return_value=["tag"]) |
| 187 | + subject_mock = MagicMock() |
| 188 | + subject_mock.subject = "Test subject" |
| 189 | + subject_mock.latest_series = AsyncMock(return_value=series_mock)() |
| 190 | + |
| 191 | + return subject_mock, series_mock |
| 192 | + |
| 193 | + async def test_sync_relevant_subject_no_mapped_branches(self) -> None: |
| 194 | + subject_mock, series_mock = self._setup_sync_relevant_subject_mocks() |
| 195 | + self._gh.get_mapped_branches = AsyncMock(return_value=[]) |
| 196 | + self._gh.checkout_and_patch_safe = AsyncMock() |
| 197 | + |
| 198 | + await self._gh.sync_relevant_subject(subject_mock) |
| 199 | + |
| 200 | + self._gh.get_mapped_branches.assert_called_once_with(series_mock) |
| 201 | + self._gh.checkout_and_patch_safe.assert_not_called() |
| 202 | + |
| 203 | + async def test_sync_relevant_subject_success_first_branch(self) -> None: |
| 204 | + series_prefix = "series/987654" |
| 205 | + series_branch_name = f"{series_prefix}{HEAD_BASE_SEPARATOR}{TEST_BRANCH}" |
| 206 | + |
| 207 | + subject_mock, series_mock = self._setup_sync_relevant_subject_mocks() |
| 208 | + series_mock.all_tags = AsyncMock(return_value=["multibranch-tag"]) |
| 209 | + subject_mock.branch = AsyncMock(return_value=series_prefix)() |
| 210 | + |
| 211 | + pr_mock = MagicMock() |
| 212 | + pr_mock.head.ref = series_branch_name |
| 213 | + |
| 214 | + self._gh.checkout_and_patch_safe = AsyncMock(return_value=pr_mock) |
| 215 | + self._gh.close_existing_prs_with_same_base = MagicMock() |
| 216 | + |
| 217 | + worker_mock = self._gh.workers[TEST_BRANCH] |
| 218 | + worker_mock.sync_checks = AsyncMock() |
| 219 | + worker_mock.try_apply_mailbox_series = AsyncMock( |
| 220 | + return_value=(True, None, None) |
| 221 | + ) |
| 222 | + |
| 223 | + await self._gh.sync_relevant_subject(subject_mock) |
| 224 | + |
| 225 | + worker_mock.try_apply_mailbox_series.assert_called_once_with( |
| 226 | + series_branch_name, series_mock |
| 227 | + ) |
| 228 | + self._gh.checkout_and_patch_safe.assert_called_once_with( |
| 229 | + worker_mock, series_branch_name, series_mock |
| 230 | + ) |
| 231 | + worker_mock.sync_checks.assert_called_once_with(pr_mock, series_mock) |
| 232 | + self._gh.close_existing_prs_with_same_base.assert_called_once_with( |
| 233 | + list(self._gh.workers.values()), pr_mock |
| 234 | + ) |
| 235 | + |
| 236 | + async def test_sync_relevant_subject_success_second_branch(self) -> None: |
| 237 | + """Test sync_relevant_subject when series fails on first branch but succeeds on second.""" |
| 238 | + series_prefix = "series/333333" |
| 239 | + bad_branch_name = f"{series_prefix}{HEAD_BASE_SEPARATOR}{TEST_BRANCH}" |
| 240 | + good_branch_name = f"{series_prefix}{HEAD_BASE_SEPARATOR}{TEST_BPF_NEXT_BRANCH}" |
| 241 | + |
| 242 | + subject_mock, series_mock = self._setup_sync_relevant_subject_mocks() |
| 243 | + series_mock.all_tags = AsyncMock(return_value=["multibranch-tag"]) |
| 244 | + |
| 245 | + pr_mock = MagicMock() |
| 246 | + pr_mock.head.ref = good_branch_name |
| 247 | + |
| 248 | + self._gh.checkout_and_patch_safe = AsyncMock(return_value=pr_mock) |
| 249 | + self._gh.close_existing_prs_with_same_base = MagicMock() |
| 250 | + |
| 251 | + bad_worker_mock = self._gh.workers[TEST_BRANCH] |
| 252 | + bad_worker_mock.sync_checks = AsyncMock() |
| 253 | + bad_worker_mock.subject_to_branch = AsyncMock(return_value=bad_branch_name) |
| 254 | + bad_worker_mock.try_apply_mailbox_series = AsyncMock( |
| 255 | + return_value=(False, None, None) |
| 256 | + ) |
| 257 | + |
| 258 | + good_worker_mock = self._gh.workers[TEST_BPF_NEXT_BRANCH] |
| 259 | + good_worker_mock.sync_checks = AsyncMock() |
| 260 | + good_worker_mock.subject_to_branch = AsyncMock(return_value=good_branch_name) |
| 261 | + good_worker_mock.try_apply_mailbox_series = AsyncMock( |
| 262 | + return_value=(True, None, None) |
| 263 | + ) |
| 264 | + |
| 265 | + await self._gh.sync_relevant_subject(subject_mock) |
| 266 | + |
| 267 | + bad_worker_mock.try_apply_mailbox_series.assert_called_once_with( |
| 268 | + bad_branch_name, series_mock |
| 269 | + ) |
| 270 | + good_worker_mock.try_apply_mailbox_series.assert_called_once_with( |
| 271 | + good_branch_name, series_mock |
| 272 | + ) |
| 273 | + self._gh.checkout_and_patch_safe.assert_called_once_with( |
| 274 | + good_worker_mock, good_branch_name, series_mock |
| 275 | + ) |
| 276 | + good_worker_mock.sync_checks.assert_called_once_with(pr_mock, series_mock) |
| 277 | + self._gh.close_existing_prs_with_same_base.assert_called_once_with( |
| 278 | + list(self._gh.workers.values()), pr_mock |
| 279 | + ) |
| 280 | + |
181 | 281 | @aioresponses() |
182 | 282 | async def test_sync_patches_pr_summary_success(self, m: aioresponses) -> None: |
183 | 283 | """ |
|
0 commit comments