|
5 | 5 |
|
6 | 6 | from sentry.models.commitcomparison import CommitComparison |
7 | 7 | from sentry.preprod.models import PreprodArtifact |
8 | | -from sentry.preprod.snapshots.models import PreprodSnapshotMetrics |
| 8 | +from sentry.preprod.snapshots.models import PreprodSnapshotComparison, PreprodSnapshotMetrics |
9 | 9 | from sentry.testutils.cases import APITestCase |
10 | 10 |
|
11 | 11 |
|
@@ -250,6 +250,88 @@ def test_snapshot_invalid_sha_format(self) -> None: |
250 | 250 |
|
251 | 251 | assert response.status_code == 400 |
252 | 252 |
|
| 253 | + @patch("sentry.preprod.api.endpoints.preprod_artifact_snapshot.get_preprod_session") |
| 254 | + @patch("sentry.preprod.api.endpoints.preprod_artifact_snapshot.compare_snapshots") |
| 255 | + def test_base_upload_triggers_comparison_for_waiting_head( |
| 256 | + self, mock_compare_snapshots, mock_get_session |
| 257 | + ) -> None: |
| 258 | + """ |
| 259 | + When a head snapshot is uploaded before its base, uploading the base should |
| 260 | + retroactively trigger a comparison for the waiting head. |
| 261 | + """ |
| 262 | + head_sha = "a" * 40 |
| 263 | + base_sha = "b" * 40 |
| 264 | + repo_name = "owner/repo" |
| 265 | + app_id = "com.example.app" |
| 266 | + |
| 267 | + # Simulate a head artifact that was uploaded before its base was available. |
| 268 | + # It has a commit_comparison with base_sha pointing to the not-yet-uploaded base. |
| 269 | + head_commit_comparison = CommitComparison.objects.create( |
| 270 | + organization_id=self.org.id, |
| 271 | + head_repo_name=repo_name, |
| 272 | + head_sha=head_sha, |
| 273 | + base_sha=base_sha, |
| 274 | + provider="github", |
| 275 | + head_ref="feature-branch", |
| 276 | + base_repo_name=repo_name, |
| 277 | + ) |
| 278 | + head_artifact = PreprodArtifact.objects.create( |
| 279 | + project=self.project, |
| 280 | + state=PreprodArtifact.ArtifactState.UPLOADED, |
| 281 | + app_id=app_id, |
| 282 | + commit_comparison=head_commit_comparison, |
| 283 | + ) |
| 284 | + head_metrics = PreprodSnapshotMetrics.objects.create( |
| 285 | + preprod_artifact=head_artifact, |
| 286 | + image_count=1, |
| 287 | + extras={ |
| 288 | + "manifest_key": f"{self.org.id}/{self.project.id}/{head_artifact.id}/manifest.json" |
| 289 | + }, |
| 290 | + ) |
| 291 | + |
| 292 | + # No comparison exists yet — the base was missing when the head was uploaded. |
| 293 | + assert not PreprodSnapshotComparison.objects.filter( |
| 294 | + head_snapshot_metrics=head_metrics |
| 295 | + ).exists() |
| 296 | + |
| 297 | + # Upload the base snapshot. Its head_sha matches the head artifact's base_sha. |
| 298 | + url = self._get_create_url() |
| 299 | + data = { |
| 300 | + "app_id": app_id, |
| 301 | + "head_sha": base_sha, |
| 302 | + "provider": "github", |
| 303 | + "head_repo_name": repo_name, |
| 304 | + "head_ref": "main", |
| 305 | + "images": { |
| 306 | + "img1": {"display_name": "Screen 1", "width": 375, "height": 812}, |
| 307 | + }, |
| 308 | + } |
| 309 | + |
| 310 | + with self.feature("organizations:preprod-snapshots"): |
| 311 | + response = self.client.post(url, data, format="json") |
| 312 | + |
| 313 | + assert response.status_code == 200 |
| 314 | + |
| 315 | + base_artifact = PreprodArtifact.objects.get(id=response.data["artifactId"]) |
| 316 | + base_metrics = PreprodSnapshotMetrics.objects.get(preprod_artifact=base_artifact) |
| 317 | + |
| 318 | + # A pending comparison record should have been created linking head to base. |
| 319 | + comparison = PreprodSnapshotComparison.objects.get( |
| 320 | + head_snapshot_metrics=head_metrics, |
| 321 | + base_snapshot_metrics=base_metrics, |
| 322 | + ) |
| 323 | + assert comparison.state == PreprodSnapshotComparison.State.PENDING |
| 324 | + |
| 325 | + # The comparison task should have been queued for the waiting head. |
| 326 | + mock_compare_snapshots.apply_async.assert_called_once_with( |
| 327 | + kwargs={ |
| 328 | + "project_id": self.project.id, |
| 329 | + "org_id": self.org.id, |
| 330 | + "head_artifact_id": head_artifact.id, |
| 331 | + "base_artifact_id": base_artifact.id, |
| 332 | + } |
| 333 | + ) |
| 334 | + |
253 | 335 |
|
254 | 336 | class ProjectPreprodSnapshotGetTest(APITestCase): |
255 | 337 | def setUp(self) -> None: |
|
0 commit comments