@@ -155,6 +155,69 @@ def test_add_state_rejects_oversized_uploads(
155155 assert response .status_code == status .HTTP_413_CONTENT_TOO_LARGE
156156
157157
158+ @mock .patch (
159+ "endpoints.states.fs_asset_handler.remove_file" , new_callable = mock .AsyncMock
160+ )
161+ @mock .patch ("endpoints.states.fs_asset_handler.write_file" , new_callable = mock .AsyncMock )
162+ @mock .patch ("endpoints.states.scan_state" , new_callable = mock .AsyncMock )
163+ def test_reupload_updates_file_path_and_emulator (
164+ mock_scan ,
165+ _mock_write ,
166+ mock_remove ,
167+ client ,
168+ access_token : str ,
169+ rom : Rom ,
170+ platform : Platform ,
171+ admin_user : User ,
172+ ):
173+ """Re-uploading the same filename under a different emulator must move the
174+ row's file_path/emulator to where the new bytes landed, so the row never
175+ serves the previous emulator's state."""
176+ existing = db_state_handler .add_state (
177+ State (
178+ file_name = "game.state" ,
179+ file_name_no_tags = "game" ,
180+ file_name_no_ext = "game" ,
181+ file_extension = "state" ,
182+ file_path = f"{ platform .slug } /states/old_emu" ,
183+ file_size_bytes = 100 ,
184+ emulator = "old_emu" ,
185+ rom_id = rom .id ,
186+ user_id = admin_user .id ,
187+ )
188+ )
189+
190+ new_path = f"{ platform .slug } /states/new_emu"
191+ mock_scan .return_value = State (
192+ file_name = "game.state" ,
193+ file_name_no_tags = "game" ,
194+ file_name_no_ext = "game" ,
195+ file_extension = "state" ,
196+ file_path = new_path ,
197+ file_size_bytes = 200 ,
198+ rom_id = rom .id ,
199+ user_id = admin_user .id ,
200+ )
201+
202+ response = client .post (
203+ f"/api/states?rom_id={ rom .id } &emulator=new_emu" ,
204+ files = {"stateFile" : ("game.state" , b"NEW STATE" , "application/octet-stream" )},
205+ headers = _auth (access_token ),
206+ )
207+
208+ assert response .status_code == status .HTTP_200_OK
209+
210+ updated = db_state_handler .get_state (user_id = admin_user .id , id = existing .id )
211+ assert updated is not None
212+ assert updated .file_path == new_path
213+ assert updated .emulator == "new_emu"
214+ assert updated .file_size_bytes == 200
215+ # full_path now points at the freshly written bytes, not the stale ones.
216+ assert updated .full_path == f"{ new_path } /game.state"
217+ # The orphaned bytes at the old location are cleaned up.
218+ mock_remove .assert_awaited_once_with (f"{ platform .slug } /states/old_emu/game.state" )
219+
220+
158221@contextmanager
159222def _kiosk_mode ():
160223 """Both call sites of the setting, as a real KIOSK_MODE=true deploy sees it."""
0 commit comments