82 lines
3.5 KiB
Python
82 lines
3.5 KiB
Python
|
|
"""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
|