77 execute_component_action ,
88 get_component_actions ,
99 get_device_status ,
10+ get_firmware_releases ,
1011 scan_devices ,
1112 update_device ,
1213)
2021from core .use_cases .check_device_status import CheckDeviceStatusUseCase
2122from core .use_cases .execute_component_action import ExecuteComponentActionUseCase
2223from core .use_cases .get_component_actions import GetComponentActionsUseCase
24+ from core .use_cases .get_local_firmware_releases import GetLocalFirmwareReleases
2325from core .use_cases .scan_devices import ScanDevicesUseCase
2426from core .use_cases .update_device_from_local import UpdateDeviceFromLocal
2527from litestar .di import Provide
@@ -188,8 +190,9 @@ class MockUpdateDeviceFromLocal(UpdateDeviceFromLocal):
188190 def __init__ (self ):
189191 pass
190192
191- async def execute (self , request ):
193+ async def execute (self , request , channel = "stable" ):
192194 captured ["request" ] = request
195+ captured ["channel" ] = channel
193196 return ActionResult (
194197 device_ip = request .device_ip ,
195198 success = True ,
@@ -213,27 +216,24 @@ async def execute(self, request):
213216 assert data ["source" ] == "local"
214217 assert data ["channel" ] == "stable"
215218 assert captured ["request" ].device_ip == "192.168.1.100"
219+ assert captured ["channel" ] == "stable"
216220
217- def test_update_device_rejects_an_unknown_source (self ):
218- with create_test_client (route_handlers = [update_device ]) as client :
219- response = client .post ("/192.168.1.100/update" , json = {"source" : "usb" })
220-
221- assert response .status_code == 400
222-
223- def test_update_device_rejects_an_unknown_channel (self ):
224- with create_test_client (
225- route_handlers = [update_device ],
226- exception_handlers = EXCEPTION_HANDLERS ,
227- ) as client :
228- response = client .post ("/192.168.1.100/update" , json = {"channel" : "nightly" })
229-
230- assert response .status_code == 400
221+ def test_update_device_from_local_source_passes_the_channel (self ):
222+ captured = {}
231223
232- def test_update_device_rejects_a_beta_local_update (self ):
233224 class MockUpdateDeviceFromLocal (UpdateDeviceFromLocal ):
234225 def __init__ (self ):
235226 pass
236227
228+ async def execute (self , request , channel = "stable" ):
229+ captured ["channel" ] = channel
230+ return ActionResult (
231+ device_ip = request .device_ip ,
232+ success = True ,
233+ message = "Update executed successfully on shelly" ,
234+ action_type = "shelly.Update" ,
235+ )
236+
237237 with create_test_client (
238238 route_handlers = [update_device ],
239239 dependencies = {
@@ -247,6 +247,81 @@ def __init__(self):
247247 json = {"source" : "local" , "channel" : "beta" },
248248 )
249249
250+ assert response .status_code == 200
251+ assert response .json ()["channel" ] == "beta"
252+ assert captured ["channel" ] == "beta"
253+
254+ def test_get_firmware_releases_reports_each_channel (self ):
255+ from core .domain .value_objects .firmware_release import FirmwareRelease
256+
257+ class MockGetLocalFirmwareReleases (GetLocalFirmwareReleases ):
258+ def __init__ (self ):
259+ pass
260+
261+ async def execute (self , request ):
262+ return {
263+ "stable" : FirmwareRelease (
264+ app_name = "Plus2PM" ,
265+ version = "1.8.0" ,
266+ build_id = "20250611-100000/1.8.0-g1234567" ,
267+ download_url = "https://fwcdn.example.test/Plus2PM.zip" ,
268+ ),
269+ "beta" : None ,
270+ }
271+
272+ with create_test_client (
273+ route_handlers = [get_firmware_releases ],
274+ dependencies = {
275+ "local_firmware_releases_interactor" : Provide (
276+ lambda : MockGetLocalFirmwareReleases (), sync_to_thread = False
277+ )
278+ },
279+ ) as client :
280+ response = client .get ("/192.168.1.100/firmware-releases" )
281+
282+ assert response .status_code == 200
283+ assert response .json () == {
284+ "stable" : {
285+ "version" : "1.8.0" ,
286+ "build_id" : "20250611-100000/1.8.0-g1234567" ,
287+ },
288+ "beta" : None ,
289+ }
290+
291+ def test_get_firmware_releases_maps_an_index_failure_to_422 (self ):
292+ class MockGetLocalFirmwareReleases (GetLocalFirmwareReleases ):
293+ def __init__ (self ):
294+ pass
295+
296+ async def execute (self , request ):
297+ raise FirmwareError ("Firmware index request failed for Plus2PM" )
298+
299+ with create_test_client (
300+ route_handlers = [get_firmware_releases ],
301+ dependencies = {
302+ "local_firmware_releases_interactor" : Provide (
303+ lambda : MockGetLocalFirmwareReleases (), sync_to_thread = False
304+ )
305+ },
306+ exception_handlers = EXCEPTION_HANDLERS ,
307+ ) as client :
308+ response = client .get ("/192.168.1.100/firmware-releases" )
309+
310+ assert response .status_code == 422
311+
312+ def test_update_device_rejects_an_unknown_source (self ):
313+ with create_test_client (route_handlers = [update_device ]) as client :
314+ response = client .post ("/192.168.1.100/update" , json = {"source" : "usb" })
315+
316+ assert response .status_code == 400
317+
318+ def test_update_device_rejects_an_unknown_channel (self ):
319+ with create_test_client (
320+ route_handlers = [update_device ],
321+ exception_handlers = EXCEPTION_HANDLERS ,
322+ ) as client :
323+ response = client .post ("/192.168.1.100/update" , json = {"channel" : "nightly" })
324+
250325 assert response .status_code == 400
251326
252327 def test_update_device_maps_firmware_misconfiguration_to_500 (self ):
@@ -256,7 +331,7 @@ class MockUpdateDeviceFromLocal(UpdateDeviceFromLocal):
256331 def __init__ (self ):
257332 pass
258333
259- async def execute (self , request ):
334+ async def execute (self , request , channel = "stable" ):
260335 raise FirmwareConfigurationError (
261336 "Local updates need SHELLY_FIRMWARE_ADVERTISED_BASE_URL set"
262337 )
@@ -280,8 +355,8 @@ class MockUpdateDeviceFromLocal(UpdateDeviceFromLocal):
280355 def __init__ (self ):
281356 pass
282357
283- async def execute (self , request ):
284- raise FirmwareError ("No firmware published for app 'Plus2PM'" )
358+ async def execute (self , request , channel = "stable" ):
359+ raise FirmwareError ("No stable firmware published for app 'Plus2PM'" )
285360
286361 with create_test_client (
287362 route_handlers = [update_device ],
0 commit comments