fix(crons): Fix re-opening closed check-ins on the consumer
diff --git a/src/sentry/monitors/consumers/monitor_consumer.py b/src/sentry/monitors/consumers/monitor_consumer.py
index ef498c3079c0a3..5d07b3bda98cae 100644
--- a/src/sentry/monitors/consumers/monitor_consumer.py
+++ b/src/sentry/monitors/consumers/monitor_consumer.py
@@ -171,6 +171,18 @@ def _process_message(wrapper: Dict) -> None:
                     monitor=monitor,
                 )
 
+                if check_in.status in CheckInStatus.FINISHED_VALUES:
+                    metrics.incr(
+                        "monitors.checkin.result",
+                        tags={"source": "consumer", "status": "checkin_finished"},
+                    )
+                    logger.debug(
+                        "check-in was finished: attempted update from %s to %s",
+                        check_in.status,
+                        status,
+                    )
+                    return
+
                 if duration is None:
                     duration = int((start_time - check_in.date_added).total_seconds() * 1000)
 
diff --git a/tests/sentry/monitors/test_monitor_consumer.py b/tests/sentry/monitors/test_monitor_consumer.py
index 7407dd7b45335c..265cf0c6a4df40 100644
--- a/tests/sentry/monitors/test_monitor_consumer.py
+++ b/tests/sentry/monitors/test_monitor_consumer.py
@@ -1,6 +1,6 @@
 import uuid
 from datetime import datetime
-from typing import Any, Dict
+from typing import Any, Dict, Optional
 from unittest import mock
 
 import msgpack
@@ -30,9 +30,11 @@
 
 
 class MonitorConsumerTest(TestCase):
-    def get_message(self, monitor_slug: str, **overrides: Any) -> Dict[str, Any]:
+    def get_message(
+        self, monitor_slug: str, guid: Optional[str] = None, **overrides: Any
+    ) -> Dict[str, Any]:
         now = datetime.now()
-        self.guid = uuid.uuid4().hex
+        self.guid = uuid.uuid4().hex if not guid else guid
         payload = {
             "monitor_slug": monitor_slug,
             "status": "ok",
@@ -164,13 +166,29 @@ def test_disabled(self):
     @pytest.mark.django_db
     def test_check_in_update(self):
         monitor = self._create_monitor(slug="my-monitor")
-        message = self.get_message(monitor.slug)
-        _process_message(message)
-        _process_message(message)
+        _process_message(self.get_message(monitor.slug, status="in_progress"))
+        _process_message(self.get_message(monitor.slug, guid=self.guid))
 
         checkin = MonitorCheckIn.objects.get(guid=self.guid)
         assert checkin.duration is not None
 
+    @pytest.mark.django_db
+    def test_check_in_update_terminal(self):
+        monitor = self._create_monitor(slug="my-monitor")
+        done_message = self.get_message(monitor.slug, duration=10.0)
+        _process_message(done_message)
+        _process_message(self.get_message(monitor.slug, guid=self.guid, status="in_progress"))
+
+        checkin = MonitorCheckIn.objects.get(guid=self.guid)
+        assert checkin.duration == int(10.0 * 1000)
+
+        error_message = self.get_message(monitor.slug, duration=20.0, status="error")
+        _process_message(error_message)
+        _process_message(self.get_message(monitor.slug, guid=self.guid, status="in_progress"))
+
+        checkin = MonitorCheckIn.objects.get(guid=self.guid)
+        assert checkin.duration == int(20.0 * 1000)
+
     @pytest.mark.django_db
     def test_monitor_environment(self):
         monitor = self._create_monitor(slug="my-monitor")

Fixes incorrectly "re-opening" of closed check-ins on the consumer. Validation logic now lines up with HTTP API