77import pytest
88
99from durable_workflow import activity , workflow
10- from durable_workflow .client import Client
10+ from durable_workflow .client import (
11+ CONTROL_PLANE_REQUEST_CONTRACT_SCHEMA ,
12+ CONTROL_PLANE_REQUEST_CONTRACT_VERSION ,
13+ CONTROL_PLANE_VERSION ,
14+ PROTOCOL_VERSION ,
15+ Client ,
16+ )
1117from durable_workflow .worker import Worker
1218
1319
@@ -38,10 +44,29 @@ def mock_client() -> AsyncMock:
3844 client .complete_activity_task = AsyncMock (return_value = {"outcome" : "completed" })
3945 client .fail_workflow_task = AsyncMock (return_value = {"outcome" : "failed" })
4046 client .fail_activity_task = AsyncMock (return_value = {"outcome" : "failed" })
41- client .get_cluster_info = AsyncMock (return_value = { "version" : "2.0.0" } )
47+ client .get_cluster_info = AsyncMock (return_value = compatible_cluster_info () )
4248 return client
4349
4450
51+ def compatible_cluster_info (** overrides : object ) -> dict [str , object ]:
52+ info : dict [str , object ] = {
53+ "version" : "not-authoritative" ,
54+ "control_plane" : {
55+ "version" : CONTROL_PLANE_VERSION ,
56+ "request_contract" : {
57+ "schema" : CONTROL_PLANE_REQUEST_CONTRACT_SCHEMA ,
58+ "version" : CONTROL_PLANE_REQUEST_CONTRACT_VERSION ,
59+ "operations" : {},
60+ },
61+ },
62+ "worker_protocol" : {
63+ "version" : PROTOCOL_VERSION ,
64+ },
65+ }
66+ info .update (overrides )
67+ return info
68+
69+
4570class TestWorkerRegistration :
4671 @pytest .mark .asyncio
4772 async def test_register (self , mock_client : AsyncMock ) -> None :
@@ -66,48 +91,59 @@ async def test_register_calls_cluster_info(self, mock_client: AsyncMock) -> None
6691 mock_client .get_cluster_info .assert_awaited_once ()
6792
6893 @pytest .mark .asyncio
69- async def test_register_accepts_server_major_0 (self , mock_client : AsyncMock ) -> None :
70- mock_client .get_cluster_info = AsyncMock (return_value = {"version" : "0.1.9" })
94+ async def test_register_uses_protocol_manifests_not_top_level_app_version (
95+ self , mock_client : AsyncMock
96+ ) -> None :
97+ mock_client .get_cluster_info = AsyncMock (return_value = compatible_cluster_info (version = "3.0.0" ))
7198 worker = Worker (mock_client , task_queue = "q1" , workflows = [TestWorkflow ], activities = [])
7299 await worker ._register ()
73100 mock_client .register_worker .assert_awaited_once ()
74101
75102 @pytest .mark .asyncio
76- async def test_register_accepts_server_major_2 (self , mock_client : AsyncMock ) -> None :
77- mock_client .get_cluster_info = AsyncMock (return_value = { "version" : "2.3.4" } )
103+ async def test_register_rejects_missing_control_plane_manifest (self , mock_client : AsyncMock ) -> None :
104+ mock_client .get_cluster_info = AsyncMock (return_value = compatible_cluster_info ( control_plane = None ) )
78105 worker = Worker (mock_client , task_queue = "q1" , workflows = [TestWorkflow ], activities = [])
79- await worker ._register ()
80- mock_client .register_worker .assert_awaited_once ()
106+ with pytest .raises (RuntimeError , match = "missing control_plane manifest" ):
107+ await worker ._register ()
108+ mock_client .register_worker .assert_not_called ()
81109
82110 @pytest .mark .asyncio
83- async def test_register_rejects_incompatible_major_1 (self , mock_client : AsyncMock ) -> None :
84- mock_client .get_cluster_info = AsyncMock (return_value = {"version" : "1.4.0" })
111+ async def test_register_rejects_unsupported_control_plane_version (self , mock_client : AsyncMock ) -> None :
112+ mock_client .get_cluster_info = AsyncMock (
113+ return_value = compatible_cluster_info (control_plane = {"version" : "3" , "request_contract" : {}})
114+ )
85115 worker = Worker (mock_client , task_queue = "q1" , workflows = [TestWorkflow ], activities = [])
86- with pytest .raises (RuntimeError , match = "incompatible " ):
116+ with pytest .raises (RuntimeError , match = "unsupported control_plane.version " ):
87117 await worker ._register ()
88118 mock_client .register_worker .assert_not_called ()
89119
90120 @pytest .mark .asyncio
91- async def test_register_rejects_incompatible_major_3 (self , mock_client : AsyncMock ) -> None :
92- mock_client .get_cluster_info = AsyncMock (return_value = {"version" : "3.0.0" })
121+ async def test_register_rejects_missing_request_contract (self , mock_client : AsyncMock ) -> None :
122+ mock_client .get_cluster_info = AsyncMock (
123+ return_value = compatible_cluster_info (control_plane = {"version" : CONTROL_PLANE_VERSION })
124+ )
93125 worker = Worker (mock_client , task_queue = "q1" , workflows = [TestWorkflow ], activities = [])
94- with pytest .raises (RuntimeError , match = "incompatible " ):
126+ with pytest .raises (RuntimeError , match = "missing control_plane.request_contract " ):
95127 await worker ._register ()
96128 mock_client .register_worker .assert_not_called ()
97129
98130 @pytest .mark .asyncio
99- async def test_register_skips_check_on_unparseable_version (self , mock_client : AsyncMock ) -> None :
100- mock_client .get_cluster_info = AsyncMock (return_value = {"version" : "unknown" })
131+ async def test_register_rejects_unsupported_worker_protocol_version (self , mock_client : AsyncMock ) -> None :
132+ mock_client .get_cluster_info = AsyncMock (
133+ return_value = compatible_cluster_info (worker_protocol = {"version" : "2.0" })
134+ )
101135 worker = Worker (mock_client , task_queue = "q1" , workflows = [TestWorkflow ], activities = [])
102- await worker ._register ()
103- mock_client .register_worker .assert_awaited_once ()
136+ with pytest .raises (RuntimeError , match = "unsupported worker_protocol.version" ):
137+ await worker ._register ()
138+ mock_client .register_worker .assert_not_called ()
104139
105140 @pytest .mark .asyncio
106- async def test_register_continues_when_cluster_info_fails (self , mock_client : AsyncMock ) -> None :
141+ async def test_register_fails_closed_when_cluster_info_fails (self , mock_client : AsyncMock ) -> None :
107142 mock_client .get_cluster_info = AsyncMock (side_effect = RuntimeError ("network down" ))
108143 worker = Worker (mock_client , task_queue = "q1" , workflows = [TestWorkflow ], activities = [])
109- await worker ._register ()
110- mock_client .register_worker .assert_awaited_once ()
144+ with pytest .raises (RuntimeError , match = "unable to read /api/cluster/info" ):
145+ await worker ._register ()
146+ mock_client .register_worker .assert_not_called ()
111147
112148
113149class TestWorkflowTaskExecution :
0 commit comments