Files
romm/backend/tests/handler/database/test_sync_sessions_handler.py
nendo c59ea7790c test: add coverage for sync session handler, sync watcher, device masking, and auth utils
- test_sync_sessions_handler: increment_operations_completed (atomic
  counter, no-op on missing), NoResultFound on update/complete/fail
  with nonexistent session
- test_sync_watcher: _extract_device_and_platform path parsing (valid,
  non-incoming, too few parts, nested, outside base), _ensure_conflicts_dir
  creation and idempotency, process_sync_changes empty/disabled
- test_sync (endpoints): negotiate with untracked saves (no_op),
  server saves not mentioned by client (download), deleted-by-client
  detection (skip), complete on FAILED/CANCELLED session (400),
  trigger_push_pull passes session_id in enqueue kwargs
- test_device (endpoints): sync_config SSH credential masking
  (ssh_password/ssh_key_path -> "********"), null config passthrough,
  config without sensitive fields
- test_utils_auth: _get_device_name UA parsing (browser+OS, browser
  only, OS only, neither), create_or_find_web_device (creates new,
  returns existing on fingerprint match, updates last_seen)
2026-03-16 11:19:30 +09:00

260 lines
10 KiB
Python

import pytest
from sqlalchemy.exc import NoResultFound
from handler.database import db_device_handler, db_sync_session_handler
from models.device import Device
from models.sync_session import SyncSessionStatus
from models.user import User
class TestCreateSession:
def test_creates_pending_session(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="sess-dev-1", user_id=admin_user.id)
)
session = db_sync_session_handler.create_session(device.id, admin_user.id)
assert session.id is not None
assert session.device_id == device.id
assert session.user_id == admin_user.id
assert session.status == SyncSessionStatus.PENDING
assert session.initiated_at is not None
assert session.completed_at is None
class TestGetSession:
def test_get_existing_session(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="get-dev-1", user_id=admin_user.id)
)
created = db_sync_session_handler.create_session(device.id, admin_user.id)
result = db_sync_session_handler.get_session(created.id, admin_user.id)
assert result is not None
assert result.id == created.id
def test_get_session_wrong_user(self, admin_user: User, editor_user: User):
device = db_device_handler.add_device(
Device(id="get-dev-2", user_id=admin_user.id)
)
created = db_sync_session_handler.create_session(device.id, admin_user.id)
result = db_sync_session_handler.get_session(created.id, editor_user.id)
assert result is None
def test_get_nonexistent_session(self, admin_user: User):
result = db_sync_session_handler.get_session(999999, admin_user.id)
assert result is None
class TestGetActiveSession:
def test_returns_pending_session(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="active-dev-1", user_id=admin_user.id)
)
created = db_sync_session_handler.create_session(device.id, admin_user.id)
result = db_sync_session_handler.get_active_session(device.id, admin_user.id)
assert result is not None
assert result.id == created.id
def test_returns_in_progress_session(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="active-dev-2", user_id=admin_user.id)
)
created = db_sync_session_handler.create_session(device.id, admin_user.id)
db_sync_session_handler.update_session(
created.id, {"status": SyncSessionStatus.IN_PROGRESS}
)
result = db_sync_session_handler.get_active_session(device.id, admin_user.id)
assert result is not None
assert result.status == SyncSessionStatus.IN_PROGRESS
def test_does_not_return_completed_session(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="active-dev-3", user_id=admin_user.id)
)
created = db_sync_session_handler.create_session(device.id, admin_user.id)
db_sync_session_handler.complete_session(created.id)
result = db_sync_session_handler.get_active_session(device.id, admin_user.id)
assert result is None
def test_no_active_session(self, admin_user: User):
result = db_sync_session_handler.get_active_session(
"nonexistent", admin_user.id
)
assert result is None
class TestCompleteSession:
def test_marks_completed_with_counts(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="comp-dev-1", user_id=admin_user.id)
)
created = db_sync_session_handler.create_session(device.id, admin_user.id)
result = db_sync_session_handler.complete_session(
created.id, operations_completed=10, operations_failed=2
)
assert result.status == SyncSessionStatus.COMPLETED
assert result.operations_completed == 10
assert result.operations_failed == 2
assert result.completed_at is not None
class TestFailSession:
def test_marks_failed_with_error(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="fail-dev-1", user_id=admin_user.id)
)
created = db_sync_session_handler.create_session(device.id, admin_user.id)
result = db_sync_session_handler.fail_session(
created.id, error_message="Connection lost"
)
assert result.status == SyncSessionStatus.FAILED
assert result.error_message == "Connection lost"
assert result.completed_at is not None
def test_marks_failed_without_error(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="fail-dev-2", user_id=admin_user.id)
)
created = db_sync_session_handler.create_session(device.id, admin_user.id)
result = db_sync_session_handler.fail_session(created.id)
assert result.status == SyncSessionStatus.FAILED
assert result.error_message is None
class TestIncrementOperationsCompleted:
def test_increments_counter(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="inc-dev-1", user_id=admin_user.id)
)
session = db_sync_session_handler.create_session(device.id, admin_user.id)
assert session.operations_completed == 0
db_sync_session_handler.increment_operations_completed(session.id)
db_sync_session_handler.increment_operations_completed(session.id)
db_sync_session_handler.increment_operations_completed(session.id)
result = db_sync_session_handler.get_session(session.id, admin_user.id)
assert result.operations_completed == 3
def test_noop_on_nonexistent_session(self, admin_user: User):
db_sync_session_handler.increment_operations_completed(999999)
class TestNoResultFoundOnMissingSession:
def test_update_session_raises(self, admin_user: User):
with pytest.raises(NoResultFound):
db_sync_session_handler.update_session(
999999, {"status": SyncSessionStatus.IN_PROGRESS}
)
def test_complete_session_raises(self, admin_user: User):
with pytest.raises(NoResultFound):
db_sync_session_handler.complete_session(999999)
def test_fail_session_raises(self, admin_user: User):
with pytest.raises(NoResultFound):
db_sync_session_handler.fail_session(999999, error_message="test")
class TestCancelActiveSessions:
def test_cancels_active_sessions(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="cancel-dev-1", user_id=admin_user.id)
)
db_sync_session_handler.create_session(device.id, admin_user.id)
s2 = db_sync_session_handler.create_session(device.id, admin_user.id)
db_sync_session_handler.update_session(
s2.id, {"status": SyncSessionStatus.IN_PROGRESS}
)
count = db_sync_session_handler.cancel_active_sessions(device.id, admin_user.id)
assert count == 2
active = db_sync_session_handler.get_active_session(device.id, admin_user.id)
assert active is None
def test_does_not_cancel_completed(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="cancel-dev-2", user_id=admin_user.id)
)
created = db_sync_session_handler.create_session(device.id, admin_user.id)
db_sync_session_handler.complete_session(created.id)
count = db_sync_session_handler.cancel_active_sessions(device.id, admin_user.id)
assert count == 0
class TestGetSessions:
def test_list_sessions_for_user(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="list-dev-1", user_id=admin_user.id)
)
for _ in range(3):
db_sync_session_handler.create_session(device.id, admin_user.id)
sessions = db_sync_session_handler.get_sessions(admin_user.id)
assert len(sessions) == 3
def test_filter_by_device(self, admin_user: User):
d1 = db_device_handler.add_device(
Device(id="list-dev-2a", user_id=admin_user.id)
)
d2 = db_device_handler.add_device(
Device(id="list-dev-2b", user_id=admin_user.id)
)
db_sync_session_handler.create_session(d1.id, admin_user.id)
db_sync_session_handler.create_session(d2.id, admin_user.id)
db_sync_session_handler.create_session(d2.id, admin_user.id)
sessions = db_sync_session_handler.get_sessions(admin_user.id, device_id=d2.id)
assert len(sessions) == 2
assert all(s.device_id == d2.id for s in sessions)
def test_filter_by_status(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="list-dev-3", user_id=admin_user.id)
)
s1 = db_sync_session_handler.create_session(device.id, admin_user.id)
db_sync_session_handler.create_session(device.id, admin_user.id)
db_sync_session_handler.complete_session(s1.id)
sessions = db_sync_session_handler.get_sessions(
admin_user.id, status=SyncSessionStatus.COMPLETED
)
assert len(sessions) == 1
assert sessions[0].status == SyncSessionStatus.COMPLETED
def test_respects_limit(self, admin_user: User):
device = db_device_handler.add_device(
Device(id="list-dev-4", user_id=admin_user.id)
)
for _ in range(5):
db_sync_session_handler.create_session(device.id, admin_user.id)
sessions = db_sync_session_handler.get_sessions(admin_user.id, limit=2)
assert len(sessions) == 2
def test_user_isolation(self, admin_user: User, editor_user: User):
d1 = db_device_handler.add_device(Device(id="iso-dev-1", user_id=admin_user.id))
d2 = db_device_handler.add_device(
Device(id="iso-dev-2", user_id=editor_user.id)
)
db_sync_session_handler.create_session(d1.id, admin_user.id)
db_sync_session_handler.create_session(d2.id, editor_user.id)
admin_sessions = db_sync_session_handler.get_sessions(admin_user.id)
assert len(admin_sessions) == 1
assert admin_sessions[0].user_id == admin_user.id
editor_sessions = db_sync_session_handler.get_sessions(editor_user.id)
assert len(editor_sessions) == 1
assert editor_sessions[0].user_id == editor_user.id