"""Session history keeps its string timestamp API while accepting DB datetimes.""" import asyncio from datetime import datetime, timezone import pytest from app.models.schemas import AnyDeskSessionDetail from app.routers import anydesk from app.services import anydesk as service @pytest.mark.parametrize('ended', [None, datetime(2026, 10, 7, 8, 0, tzinfo=timezone.utc)]) def test_contact_history_serializes_database_timestamps(monkeypatch, ended): timestamp = datetime(2026, 10, 7, 7, 29, tzinfo=timezone.utc) row = dict(id=1, anydesk_session_id='history-test', customer_id=18, contact_id=1565, status='completed' if ended else 'active', started_at=timestamp, ended_at=ended, created_at=timestamp, updated_at=timestamp, duration_minutes=26) def query(sql, params): assert params[0] == 1565 return [{'total': 1}] if 'COUNT(*)' in sql else [row] monkeypatch.setattr(service, 'execute_query', query) result = asyncio.run(anydesk.get_session_history(contact_id=1565)) payload = result.model_dump(mode='json') assert payload['total'] == 1 actual = payload['sessions'][0] for key in ('started_at', 'created_at', 'updated_at'): assert actual[key] == timestamp.isoformat() assert actual['ended_at'] == (ended.isoformat() if ended else None) assert actual['contact_id'] == 1565 def test_existing_string_timestamps_are_preserved(): row = AnyDeskSessionDetail(id=1, anydesk_session_id='legacy', customer_id=18, status='completed', started_at='2026-10-07T07:29:00Z', created_at='2026-10-07T07:29:00Z') assert row.started_at == '2026-10-07T07:29:00Z' def test_link_case_from_contact_table_changes_only_selected_session(monkeypatch): from contextlib import nullcontext import json updates = [] def query(sql, params): if 'FROM sag_sager' in sql: return [{'id': 123, 'customer_id': 21}] if 'SELECT customer_id FROM anydesk_sessions' in sql: return [{'customer_id': 21}] updates.append((sql, params)) return [] monkeypatch.setattr(anydesk, 'execute_query', query) monkeypatch.setattr(anydesk, 'atomic_transaction', nullcontext) response = asyncio.run(anydesk.link_session(551, sag_id=123, require_same_customer=True)) assert json.loads(response.body)['ok'] assert len(updates) == 1 sql, params = updates[0] assert 'sag_id = %s' in sql assert 'WHERE id = %s' in sql assert params == (123, 551) assert 'customer_id =' not in sql assert 'contact_id =' not in sql @pytest.mark.parametrize('case_rows,session_rows,status', [ ([], [{'customer_id':21}], 404), ([{'id':123,'customer_id':99}], [{'customer_id':21}], 409), ([{'id':123,'customer_id':21}], [], 404), ]) def test_case_picker_rejects_missing_or_other_customer_cases(monkeypatch, case_rows, session_rows, status): from contextlib import nullcontext from fastapi import HTTPException def query(sql, params): if 'FROM sag_sager' in sql: return case_rows if 'SELECT customer_id FROM anydesk_sessions' in sql: return session_rows raise AssertionError('Must not change session on rejected case selection') monkeypatch.setattr(anydesk, 'execute_query', query) monkeypatch.setattr(anydesk, 'atomic_transaction', nullcontext) with pytest.raises(HTTPException) as error: asyncio.run(anydesk.link_session(551, sag_id=123, require_same_customer=True)) assert error.value.status_code == status