File indexing completed on 2026-07-22 09:42:05
0001 from datetime import timedelta
0002
0003 from django.contrib.auth.models import User
0004 from django.urls import reverse
0005 from django.utils import timezone
0006 from rest_framework import status
0007 from rest_framework.test import APITestCase
0008
0009 from snapper_ai.models import CurrentComponent
0010 from monitor_app.workflow_models import WorkflowDefinition, WorkflowExecution
0011
0012
0013 class DatatakingPublicationTests(APITestCase):
0014 def setUp(self):
0015 self.user = User.objects.create_user(
0016 username="snapper-datataking-test",
0017 password="testpassword",
0018 )
0019 self.client.force_authenticate(user=self.user)
0020 self.initial_transition = timezone.now() - timedelta(minutes=1)
0021 self.workflow_definition = WorkflowDefinition.objects.create(
0022 workflow_name="state-history-test",
0023 version="1",
0024 workflow_type="test",
0025 definition="pass",
0026 parameter_values={},
0027 created_by=self.user.username,
0028 )
0029 self._create_execution("state-history-alice", "alice")
0030 self.run_data = {
0031 "run_number": 101,
0032 "phase": "initializing",
0033 "state": "imminent",
0034 "substate": "preparing",
0035 "target_worker_count": 2,
0036 "active_worker_count": 0,
0037 "stf_samples_received": 0,
0038 "slices_created": 0,
0039 "slices_queued": 0,
0040 "slices_processing": 0,
0041 "slices_completed": 0,
0042 "slices_failed": 0,
0043 "state_changed_at": self.initial_transition.isoformat(),
0044 "metadata": {"execution_id": "state-history-alice"},
0045 }
0046
0047 def _create_execution(self, execution_id, namespace):
0048 return WorkflowExecution.objects.create(
0049 execution_id=execution_id,
0050 workflow_definition=self.workflow_definition,
0051 namespace=namespace,
0052 parameter_values={},
0053 status="running",
0054 start_time=self.initial_transition,
0055 executed_by=self.user.username,
0056 )
0057
0058 def test_only_state_transitions_advance_datataking_revision(self):
0059 list_url = reverse("monitor_app:runstate-list")
0060 response = self.client.post(list_url, self.run_data, format="json")
0061 self.assertEqual(response.status_code, status.HTTP_201_CREATED)
0062
0063 component = CurrentComponent.objects.get(
0064 scope="testbed",
0065 name="datataking",
0066 )
0067 self.assertEqual(component.component_schema_version, 2)
0068 self.assertEqual(component.revision, 1)
0069 self.assertEqual(
0070 component.data["namespaces"]["alice"],
0071 {
0072 "run_number": 101,
0073 "phase": "initializing",
0074 "state": "imminent",
0075 "substate": "preparing",
0076 "last_transition_at": (
0077 self.initial_transition.isoformat().replace("+00:00", "Z")
0078 ),
0079 },
0080 )
0081
0082 detail_url = reverse("monitor_app:runstate-detail", kwargs={"pk": 101})
0083 counter_time = self.initial_transition + timedelta(seconds=10)
0084 response = self.client.patch(
0085 detail_url,
0086 {
0087 "slices_created": 4,
0088 "state_changed_at": counter_time.isoformat(),
0089 },
0090 format="json",
0091 )
0092 self.assertEqual(response.status_code, status.HTTP_200_OK)
0093 component.refresh_from_db()
0094 self.assertEqual(component.revision, 1)
0095 self.assertEqual(
0096 component.data["namespaces"]["alice"]["last_transition_at"],
0097 self.initial_transition.isoformat().replace("+00:00", "Z"),
0098 )
0099
0100 transition_time = counter_time + timedelta(seconds=10)
0101 response = self.client.patch(
0102 detail_url,
0103 {
0104 "phase": "physics",
0105 "state": "running",
0106 "substate": "physics",
0107 "state_changed_at": transition_time.isoformat(),
0108 },
0109 format="json",
0110 )
0111 self.assertEqual(response.status_code, status.HTTP_200_OK)
0112 component.refresh_from_db()
0113 self.assertEqual(component.revision, 2)
0114 alice = component.data["namespaces"]["alice"]
0115 self.assertEqual(alice["phase"], "physics")
0116 self.assertEqual(alice["state"], "running")
0117 self.assertEqual(alice["substate"], "physics")
0118 self.assertEqual(
0119 alice["last_transition_at"],
0120 transition_time.isoformat().replace("+00:00", "Z"),
0121 )
0122
0123 self._create_execution("state-history-bob", "bob")
0124 bob_data = {
0125 **self.run_data,
0126 "run_number": 102,
0127 "metadata": {"execution_id": "state-history-bob"},
0128 }
0129 response = self.client.post(list_url, bob_data, format="json")
0130 self.assertEqual(response.status_code, status.HTTP_201_CREATED)
0131 component.refresh_from_db()
0132 self.assertEqual(component.revision, 3)
0133 self.assertEqual(set(component.data["namespaces"]), {"alice", "bob"})
0134 self.assertEqual(
0135 component.data["namespaces"]["alice"]["run_number"],
0136 101,
0137 )
0138 self.assertEqual(
0139 component.data["namespaces"]["bob"]["run_number"],
0140 102,
0141 )