mirror of
https://github.com/rommapp/romm.git
synced 2026-06-28 14:56:01 +00:00
saves responses now include one device_syncs entry per device that has synced a save, not just the caller's, so clients can tell which devices hold a save. is_current is computed per entry and the caller's own entry is ordered first for backward compatibility. add a saves.origin_device_id column (migration 0081) recording the device that created a save, set on initial upload only, surfaced as origin_device_id on the save schema.
272 lines
10 KiB
Python
272 lines
10 KiB
Python
from datetime import datetime, timezone
|
|
|
|
from handler.database import (
|
|
db_device_handler,
|
|
db_device_save_sync_handler,
|
|
db_save_handler,
|
|
)
|
|
from models.assets import Save
|
|
from models.device import Device
|
|
from models.rom import Rom
|
|
from models.user import User
|
|
|
|
|
|
class TestGetSync:
|
|
def test_get_existing_sync(self, admin_user: User, rom: Rom, save: Save):
|
|
device = db_device_handler.add_device(
|
|
Device(id="sync-dev-1", user_id=admin_user.id)
|
|
)
|
|
db_device_save_sync_handler.upsert_sync(device.id, save.id)
|
|
|
|
result = db_device_save_sync_handler.get_sync(device.id, save.id)
|
|
assert result is not None
|
|
assert result.device_id == device.id
|
|
assert result.save_id == save.id
|
|
|
|
def test_get_nonexistent_sync(self, admin_user: User):
|
|
result = db_device_save_sync_handler.get_sync("no-device", 999)
|
|
assert result is None
|
|
|
|
|
|
class TestGetSyncsForDeviceAndSaves:
|
|
def test_returns_matching_syncs(self, admin_user: User, rom: Rom):
|
|
device = db_device_handler.add_device(
|
|
Device(id="bulk-dev-1", user_id=admin_user.id)
|
|
)
|
|
saves = []
|
|
for i in range(3):
|
|
s = db_save_handler.add_save(
|
|
Save(
|
|
rom_id=rom.id,
|
|
user_id=admin_user.id,
|
|
file_name=f"bulk_{i}.sav",
|
|
file_name_no_tags=f"bulk_{i}",
|
|
file_name_no_ext=f"bulk_{i}",
|
|
file_extension="sav",
|
|
emulator="emu",
|
|
file_path=f"{rom.platform_slug}/saves",
|
|
file_size_bytes=100,
|
|
)
|
|
)
|
|
saves.append(s)
|
|
db_device_save_sync_handler.upsert_sync(device.id, s.id)
|
|
|
|
result = db_device_save_sync_handler.get_syncs_for_device_and_saves(
|
|
device.id, [saves[0].id, saves[2].id]
|
|
)
|
|
assert len(result) == 2
|
|
ids = {r.save_id for r in result}
|
|
assert saves[0].id in ids
|
|
assert saves[2].id in ids
|
|
|
|
def test_empty_save_ids_returns_empty(self, admin_user: User):
|
|
result = db_device_save_sync_handler.get_syncs_for_device_and_saves(
|
|
"any-dev", []
|
|
)
|
|
assert result == []
|
|
|
|
|
|
class TestGetSyncsForSaves:
|
|
def test_returns_syncs_across_devices_with_name(
|
|
self, admin_user: User, rom: Rom, save: Save
|
|
):
|
|
dev_a = db_device_handler.add_device(
|
|
Device(id="multi-dev-a", user_id=admin_user.id, name="Device A")
|
|
)
|
|
dev_b = db_device_handler.add_device(
|
|
Device(id="multi-dev-b", user_id=admin_user.id, name="Device B")
|
|
)
|
|
db_device_save_sync_handler.upsert_sync(dev_a.id, save.id)
|
|
db_device_save_sync_handler.upsert_sync(dev_b.id, save.id)
|
|
|
|
result = db_device_save_sync_handler.get_syncs_for_saves([save.id])
|
|
|
|
assert set(result.keys()) == {save.id}
|
|
by_device = {sync.device_id: name for sync, name in result[save.id]}
|
|
assert by_device == {dev_a.id: "Device A", dev_b.id: "Device B"}
|
|
|
|
def test_filters_to_requested_saves(self, admin_user: User, rom: Rom):
|
|
device = db_device_handler.add_device(
|
|
Device(id="multi-dev-c", user_id=admin_user.id, name="Device C")
|
|
)
|
|
saves = []
|
|
for i in range(3):
|
|
s = db_save_handler.add_save(
|
|
Save(
|
|
rom_id=rom.id,
|
|
user_id=admin_user.id,
|
|
file_name=f"multi_{i}.sav",
|
|
file_name_no_tags=f"multi_{i}",
|
|
file_name_no_ext=f"multi_{i}",
|
|
file_extension="sav",
|
|
emulator="emu",
|
|
file_path=f"{rom.platform_slug}/saves",
|
|
file_size_bytes=100,
|
|
)
|
|
)
|
|
saves.append(s)
|
|
db_device_save_sync_handler.upsert_sync(device.id, s.id)
|
|
|
|
result = db_device_save_sync_handler.get_syncs_for_saves(
|
|
[saves[0].id, saves[2].id]
|
|
)
|
|
|
|
assert set(result.keys()) == {saves[0].id, saves[2].id}
|
|
|
|
def test_empty_save_ids_returns_empty(self, admin_user: User):
|
|
result = db_device_save_sync_handler.get_syncs_for_saves([])
|
|
assert result == {}
|
|
|
|
|
|
class TestOriginDeviceCascade:
|
|
def test_origin_device_id_nulled_on_device_delete(self, admin_user: User, rom: Rom):
|
|
device = db_device_handler.add_device(
|
|
Device(id="origin-dev", user_id=admin_user.id, name="Origin")
|
|
)
|
|
save = db_save_handler.add_save(
|
|
Save(
|
|
rom_id=rom.id,
|
|
user_id=admin_user.id,
|
|
file_name="origin_cascade.sav",
|
|
file_name_no_tags="origin_cascade",
|
|
file_name_no_ext="origin_cascade",
|
|
file_extension="sav",
|
|
emulator="emu",
|
|
file_path=f"{rom.platform_slug}/saves",
|
|
file_size_bytes=100,
|
|
origin_device_id=device.id,
|
|
)
|
|
)
|
|
assert save.origin_device_id == device.id
|
|
|
|
db_device_handler.delete_device(device.id, admin_user.id)
|
|
|
|
refreshed = db_save_handler.get_save(user_id=admin_user.id, id=save.id)
|
|
assert refreshed is not None
|
|
assert refreshed.origin_device_id is None
|
|
|
|
|
|
class TestUpsertSync:
|
|
def test_creates_new_sync(self, admin_user: User, rom: Rom, save: Save):
|
|
device = db_device_handler.add_device(
|
|
Device(id="upsert-dev-1", user_id=admin_user.id)
|
|
)
|
|
result = db_device_save_sync_handler.upsert_sync(device.id, save.id)
|
|
|
|
assert result.device_id == device.id
|
|
assert result.save_id == save.id
|
|
assert result.is_untracked is False
|
|
assert result.last_synced_at is not None
|
|
|
|
def test_updates_existing_sync(self, admin_user: User, rom: Rom, save: Save):
|
|
device = db_device_handler.add_device(
|
|
Device(id="upsert-dev-2", user_id=admin_user.id)
|
|
)
|
|
earlier = datetime(2020, 1, 1, tzinfo=timezone.utc)
|
|
db_device_save_sync_handler.upsert_sync(device.id, save.id, synced_at=earlier)
|
|
|
|
later = datetime(2025, 6, 1, tzinfo=timezone.utc)
|
|
result = db_device_save_sync_handler.upsert_sync(
|
|
device.id, save.id, synced_at=later
|
|
)
|
|
|
|
assert result.last_synced_at == later
|
|
assert result.is_untracked is False
|
|
|
|
def test_upsert_clears_untracked(self, admin_user: User, rom: Rom, save: Save):
|
|
device = db_device_handler.add_device(
|
|
Device(id="upsert-dev-3", user_id=admin_user.id)
|
|
)
|
|
db_device_save_sync_handler.set_untracked(device.id, save.id, True)
|
|
sync = db_device_save_sync_handler.get_sync(device.id, save.id)
|
|
assert sync is not None
|
|
assert sync.is_untracked is True
|
|
|
|
db_device_save_sync_handler.upsert_sync(device.id, save.id)
|
|
sync = db_device_save_sync_handler.get_sync(device.id, save.id)
|
|
assert sync is not None
|
|
assert sync.is_untracked is False
|
|
|
|
def test_upsert_with_custom_timestamp(self, admin_user: User, rom: Rom, save: Save):
|
|
device = db_device_handler.add_device(
|
|
Device(id="upsert-dev-4", user_id=admin_user.id)
|
|
)
|
|
ts = datetime(2024, 3, 15, 12, 0, 0, tzinfo=timezone.utc)
|
|
result = db_device_save_sync_handler.upsert_sync(
|
|
device.id, save.id, synced_at=ts
|
|
)
|
|
assert result.last_synced_at == ts
|
|
|
|
|
|
class TestSetUntracked:
|
|
def test_set_untracked_on_existing(self, admin_user: User, rom: Rom, save: Save):
|
|
device = db_device_handler.add_device(
|
|
Device(id="untrack-dev-1", user_id=admin_user.id)
|
|
)
|
|
db_device_save_sync_handler.upsert_sync(device.id, save.id)
|
|
|
|
result = db_device_save_sync_handler.set_untracked(device.id, save.id, True)
|
|
assert result is not None
|
|
assert result.is_untracked is True
|
|
|
|
def test_set_tracked_on_existing(self, admin_user: User, rom: Rom, save: Save):
|
|
device = db_device_handler.add_device(
|
|
Device(id="untrack-dev-2", user_id=admin_user.id)
|
|
)
|
|
db_device_save_sync_handler.upsert_sync(device.id, save.id)
|
|
db_device_save_sync_handler.set_untracked(device.id, save.id, True)
|
|
|
|
result = db_device_save_sync_handler.set_untracked(device.id, save.id, False)
|
|
assert result is not None
|
|
assert result.is_untracked is False
|
|
|
|
def test_set_untracked_creates_new_record(
|
|
self, admin_user: User, rom: Rom, save: Save
|
|
):
|
|
device = db_device_handler.add_device(
|
|
Device(id="untrack-dev-3", user_id=admin_user.id)
|
|
)
|
|
result = db_device_save_sync_handler.set_untracked(device.id, save.id, True)
|
|
assert result is not None
|
|
assert result.is_untracked is True
|
|
assert result.device_id == device.id
|
|
|
|
def test_set_tracked_nonexistent_returns_none(self, admin_user: User):
|
|
device = db_device_handler.add_device(
|
|
Device(id="untrack-dev-4", user_id=admin_user.id)
|
|
)
|
|
result = db_device_save_sync_handler.set_untracked(device.id, 999999, False)
|
|
assert result is None
|
|
|
|
|
|
class TestDeleteSyncsForDevice:
|
|
def test_deletes_all_syncs(self, admin_user: User, rom: Rom):
|
|
device = db_device_handler.add_device(
|
|
Device(id="del-dev-1", user_id=admin_user.id)
|
|
)
|
|
for i in range(3):
|
|
s = db_save_handler.add_save(
|
|
Save(
|
|
rom_id=rom.id,
|
|
user_id=admin_user.id,
|
|
file_name=f"del_{i}.sav",
|
|
file_name_no_tags=f"del_{i}",
|
|
file_name_no_ext=f"del_{i}",
|
|
file_extension="sav",
|
|
emulator="emu",
|
|
file_path=f"{rom.platform_slug}/saves",
|
|
file_size_bytes=100,
|
|
)
|
|
)
|
|
db_device_save_sync_handler.upsert_sync(device.id, s.id)
|
|
|
|
db_device_save_sync_handler.delete_syncs_for_device(device.id)
|
|
|
|
result = db_device_save_sync_handler.get_syncs_for_device_and_saves(
|
|
device.id, [1, 2, 3]
|
|
)
|
|
assert len(result) == 0
|
|
|
|
def test_delete_nonexistent_device_no_error(self):
|
|
db_device_save_sync_handler.delete_syncs_for_device("nonexistent-device")
|