11from __future__ import annotations
22
3+ import inspect
34from unittest import mock
45
56import pytest
1213 BoundAction ,
1314 ResourceActionsClient ,
1415)
16+ from hcloud .certificates import BoundCertificate , CertificatesClient
17+ from hcloud .core import BoundModelBase , ResourceClientBase
18+ from hcloud .firewalls import BoundFirewall , FirewallsClient
19+ from hcloud .floating_ips import BoundFloatingIP , FloatingIPsClient
20+ from hcloud .images import BoundImage , ImagesClient
21+ from hcloud .load_balancers import BoundLoadBalancer , LoadBalancersClient
22+ from hcloud .networks import BoundNetwork , NetworksClient
23+ from hcloud .primary_ips import BoundPrimaryIP , PrimaryIPsClient
24+ from hcloud .servers import BoundServer , ServersClient
25+ from hcloud .volumes import BoundVolume , VolumesClient
1526
1627from ..conftest import assert_bound_action1 , assert_bound_action2
1728
29+ resources_with_actions : dict [str , tuple [ResourceClientBase , BoundModelBase ]] = {
30+ "certificates" : (CertificatesClient , BoundCertificate ),
31+ "firewalls" : (FirewallsClient , BoundFirewall ),
32+ "floating_ips" : (FloatingIPsClient , BoundFloatingIP ),
33+ "images" : (ImagesClient , BoundImage ),
34+ "load_balancers" : (LoadBalancersClient , BoundLoadBalancer ),
35+ "networks" : (NetworksClient , BoundNetwork ),
36+ "primary_ips" : (PrimaryIPsClient , BoundPrimaryIP ),
37+ "servers" : (ServersClient , BoundServer ),
38+ "volumes" : (VolumesClient , BoundVolume ),
39+ }
40+
41+
42+ def test_resources_with_actions (client : Client ):
43+ """
44+ Ensure that the list of resource clients above is up to date.
45+ """
46+ members = inspect .getmembers (
47+ client ,
48+ predicate = lambda p : isinstance (p , ResourceClientBase ) and hasattr (p , "actions" ),
49+ )
50+ for name , member in members :
51+ assert name in resources_with_actions
52+
53+ resource_client_class , _ = resources_with_actions [name ]
54+ assert member .__class__ is resource_client_class
55+
56+ assert len (members ) == len (resources_with_actions )
57+
1858
1959class TestBoundAction :
2060 @pytest .fixture ()
@@ -90,85 +130,262 @@ def test_wait_until_finished_max_retries(
90130
91131
92132class TestResourceActionsClient :
133+ """
134+ /<resource>/actions
135+ /<resource>/actions/<id>
136+ """
137+
138+ @pytest .fixture (params = resources_with_actions .keys ())
139+ def resource (self , request ) -> str :
140+ return request .param
141+
93142 @pytest .fixture ()
94- def actions_client (self , client : Client ):
95- return ResourceActionsClient (client , resource = "/resource" )
143+ def resource_client (self , client : Client , resource : str ) -> ResourceActionsClient :
144+ """
145+ Extract the resource actions client from the client.
146+ """
147+ return getattr (client , resource ).actions
96148
97149 def test_get_by_id (
98150 self ,
99151 request_mock : mock .MagicMock ,
100- actions_client : ActionsClient ,
152+ resource_client : ResourceActionsClient ,
153+ resource : str ,
101154 action_response ,
102155 ):
103156 request_mock .return_value = action_response
104157
105- action = actions_client .get_by_id (1 )
158+ action = resource_client .get_by_id (1 )
106159
107160 request_mock .assert_called_with (
108161 method = "GET" ,
109- url = "/ resource/actions/1" ,
162+ url = f"/ { resource } /actions/1" ,
110163 )
111164
112- assert_bound_action1 (action , actions_client ._parent .actions )
165+ assert_bound_action1 (action , resource_client ._parent .actions )
113166
114167 @pytest .mark .parametrize (
115168 "params" ,
116169 [
117170 {},
118- {"status" : ["active " ], "sort" : ["status" ], "page" : 2 , "per_page" : 10 },
171+ {"status" : ["running " ], "sort" : ["status" ], "page" : 2 , "per_page" : 10 },
119172 ],
120173 )
121174 def test_get_list (
122175 self ,
123176 request_mock : mock .MagicMock ,
124- actions_client : ActionsClient ,
177+ resource_client : ResourceActionsClient ,
178+ resource : str ,
125179 action_list_response ,
126180 params ,
127181 ):
128182 request_mock .return_value = action_list_response
129183
130- result = actions_client .get_list (** params )
184+ result = resource_client .get_list (** params )
131185
132186 request_mock .assert_called_with (
133187 method = "GET" ,
134- url = "/ resource/actions" ,
188+ url = f"/ { resource } /actions" ,
135189 params = params ,
136190 )
137191
138192 assert result .meta is not None
139193
140194 actions = result .actions
141195 assert len (actions ) == 2
142- assert_bound_action1 (actions [0 ], actions_client ._parent .actions )
143- assert_bound_action2 (actions [1 ], actions_client ._parent .actions )
196+ assert_bound_action1 (actions [0 ], resource_client ._parent .actions )
197+ assert_bound_action2 (actions [1 ], resource_client ._parent .actions )
144198
145- @pytest .mark .parametrize ("params" , [{}, {"status" : ["active" ], "sort" : ["status" ]}])
199+ @pytest .mark .parametrize (
200+ "params" ,
201+ [
202+ {},
203+ {"status" : ["running" ], "sort" : ["status" ]},
204+ ],
205+ )
146206 def test_get_all (
147207 self ,
148208 request_mock : mock .MagicMock ,
149- actions_client : ActionsClient ,
209+ resource_client : ResourceActionsClient ,
210+ resource : str ,
211+ action_list_response ,
212+ params ,
213+ ):
214+ request_mock .return_value = action_list_response
215+
216+ actions = resource_client .get_all (** params )
217+
218+ request_mock .assert_called_with (
219+ method = "GET" ,
220+ url = f"/{ resource } /actions" ,
221+ params = {** params , "page" : 1 , "per_page" : 50 },
222+ )
223+
224+ assert len (actions ) == 2
225+ assert_bound_action1 (actions [0 ], resource_client ._parent .actions )
226+ assert_bound_action2 (actions [1 ], resource_client ._parent .actions )
227+
228+
229+ class TestResourceObjectActionsClient :
230+ """
231+ /<resource>/<id>/actions
232+ """
233+
234+ @pytest .fixture (params = resources_with_actions .keys ())
235+ def resource (self , request ):
236+ if request .param == "primary_ips" :
237+ pytest .skip ("not implemented yet" )
238+ return request .param
239+
240+ @pytest .fixture ()
241+ def resource_client (self , client : Client , resource : str ) -> ResourceClientBase :
242+ return getattr (client , resource )
243+
244+ @pytest .mark .parametrize (
245+ "params" ,
246+ [
247+ {},
248+ {"status" : ["running" ], "sort" : ["status" ], "page" : 2 , "per_page" : 10 },
249+ ],
250+ )
251+ def test_get_actions_list (
252+ self ,
253+ request_mock : mock .MagicMock ,
254+ resource_client : ResourceClientBase ,
255+ resource : str ,
256+ action_list_response ,
257+ params ,
258+ ):
259+ request_mock .return_value = action_list_response
260+
261+ result = resource_client .get_actions_list (mock .MagicMock (id = 1 ), ** params )
262+
263+ request_mock .assert_called_with (
264+ method = "GET" ,
265+ url = f"/{ resource } /1/actions" ,
266+ params = params ,
267+ )
268+
269+ assert result .meta is not None
270+
271+ actions = result .actions
272+ assert len (actions ) == 2
273+ assert_bound_action1 (actions [0 ], resource_client ._parent .actions )
274+ assert_bound_action2 (actions [1 ], resource_client ._parent .actions )
275+
276+ @pytest .mark .parametrize (
277+ "params" ,
278+ [
279+ {},
280+ {"status" : ["running" ], "sort" : ["status" ]},
281+ ],
282+ )
283+ def test_get_actions (
284+ self ,
285+ request_mock : mock .MagicMock ,
286+ resource_client : ResourceClientBase ,
287+ resource : str ,
150288 action_list_response ,
151289 params ,
152290 ):
153291 request_mock .return_value = action_list_response
154292
155- actions = actions_client . get_all ( ** params )
293+ actions = resource_client . get_actions ( mock . MagicMock ( id = 1 ), ** params )
156294
157295 request_mock .assert_called_with (
158296 method = "GET" ,
159- url = "/ resource/actions" ,
297+ url = f"/ { resource } /1 /actions" ,
160298 params = {** params , "page" : 1 , "per_page" : 50 },
161299 )
162300
163301 assert len (actions ) == 2
164- assert_bound_action1 (actions [0 ], actions_client ._parent .actions )
165- assert_bound_action2 (actions [1 ], actions_client ._parent .actions )
302+ assert_bound_action1 (actions [0 ], resource_client ._parent .actions )
303+ assert_bound_action2 (actions [1 ], resource_client ._parent .actions )
304+
305+
306+ class TestBoundModelActions :
307+ """
308+ /<resource>/<id>/actions
309+ """
310+
311+ @pytest .fixture (params = resources_with_actions .keys ())
312+ def resource (self , request ):
313+ if request .param == "primary_ips" :
314+ pytest .skip ("not implemented yet" )
315+ return request .param
316+
317+ @pytest .fixture ()
318+ def bound_model (self , client : Client , resource : str ) -> ResourceClientBase :
319+ _ , bound_model_class = resources_with_actions [resource ]
320+ resource_client = getattr (client , resource )
321+ return bound_model_class (resource_client , data = {"id" : 1 })
322+
323+ @pytest .mark .parametrize (
324+ "params" ,
325+ [
326+ {},
327+ {"status" : ["running" ], "sort" : ["status" ], "page" : 2 , "per_page" : 10 },
328+ ],
329+ )
330+ def test_get_actions_list (
331+ self ,
332+ request_mock : mock .MagicMock ,
333+ bound_model : BoundModelBase ,
334+ resource : str ,
335+ action_list_response ,
336+ params ,
337+ ):
338+ request_mock .return_value = action_list_response
339+
340+ result = bound_model .get_actions_list (** params )
341+
342+ request_mock .assert_called_with (
343+ method = "GET" ,
344+ url = f"/{ resource } /1/actions" ,
345+ params = params ,
346+ )
347+
348+ assert result .meta is not None
349+
350+ actions = result .actions
351+ assert len (actions ) == 2
352+ assert_bound_action1 (actions [0 ], bound_model ._client ._parent .actions )
353+ assert_bound_action2 (actions [1 ], bound_model ._client ._parent .actions )
354+
355+ @pytest .mark .parametrize (
356+ "params" ,
357+ [
358+ {},
359+ {"status" : ["running" ], "sort" : ["status" ]},
360+ ],
361+ )
362+ def test_get_actions (
363+ self ,
364+ request_mock : mock .MagicMock ,
365+ bound_model : BoundModelBase ,
366+ resource : str ,
367+ action_list_response ,
368+ params ,
369+ ):
370+ request_mock .return_value = action_list_response
371+
372+ actions = bound_model .get_actions (** params )
373+
374+ request_mock .assert_called_with (
375+ method = "GET" ,
376+ url = f"/{ resource } /1/actions" ,
377+ params = {** params , "page" : 1 , "per_page" : 50 },
378+ )
379+
380+ assert len (actions ) == 2
381+ assert_bound_action1 (actions [0 ], bound_model ._client ._parent .actions )
382+ assert_bound_action2 (actions [1 ], bound_model ._client ._parent .actions )
166383
167384
168385class TestActionsClient :
169386 @pytest .fixture ()
170- def actions_client (self , client : Client ):
171- return ActionsClient ( client )
387+ def actions_client (self , client : Client ) -> ActionsClient :
388+ return client . actions
172389
173390 def test_get_by_id (
174391 self ,
@@ -184,13 +401,13 @@ def test_get_by_id(
184401 method = "GET" ,
185402 url = "/actions/1" ,
186403 )
187- assert_bound_action1 (action , actions_client . _parent . actions )
404+ assert_bound_action1 (action , actions_client )
188405
189406 @pytest .mark .parametrize (
190407 "params" ,
191408 [
192409 {},
193- {"status" : ["active " ], "sort" : ["status" ], "page" : 2 , "per_page" : 10 },
410+ {"status" : ["running " ], "sort" : ["status" ], "page" : 2 , "per_page" : 10 },
194411 ],
195412 )
196413 def test_get_list (
@@ -215,10 +432,16 @@ def test_get_list(
215432
216433 actions = result .actions
217434 assert len (actions ) == 2
218- assert_bound_action1 (actions [0 ], actions_client . _parent . actions )
219- assert_bound_action2 (actions [1 ], actions_client . _parent . actions )
435+ assert_bound_action1 (actions [0 ], actions_client )
436+ assert_bound_action2 (actions [1 ], actions_client )
220437
221- @pytest .mark .parametrize ("params" , [{}, {"status" : ["active" ], "sort" : ["status" ]}])
438+ @pytest .mark .parametrize (
439+ "params" ,
440+ [
441+ {},
442+ {"status" : ["running" ], "sort" : ["status" ]},
443+ ],
444+ )
222445 def test_get_all (
223446 self ,
224447 request_mock : mock .MagicMock ,
@@ -238,5 +461,5 @@ def test_get_all(
238461 )
239462
240463 assert len (actions ) == 2
241- assert_bound_action1 (actions [0 ], actions_client . _parent . actions )
242- assert_bound_action2 (actions [1 ], actions_client . _parent . actions )
464+ assert_bound_action1 (actions [0 ], actions_client )
465+ assert_bound_action2 (actions [1 ], actions_client )
0 commit comments