Files
roa2web-service-auto/tests/backend/test_telegram_oracle_server_id.py
Marius Mutu e257fa5d5f feat(telegram): bot bonuri fiscale — OCR → preview → Oracle write
- US-001: mută queue_client.py în data_entry/services/ocr/
- US-002/003/004: oracle_receipt_writer + oracle_server_id în DB
- US-005: receipt_handlers.py (PDF/photo/callback flow)
- US-006: wire handlers în main.py, per-schema connect, seq_cod.nextval
- US-007: .gitignore secrets/*.oracle_pass
- US-008/009/010: teste unit + integration + E2E
- setup-secrets.sh helper + template
- docs/telegram/README.md actualizat cu arhitectura nouă

Testat E2E pe DB live (MARIUSM_AUTO). COD din seq_cod.nextval.
pypdfium2 fallback pentru PDF decode (fără poppler).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 09:26:58 +00:00

230 lines
8.3 KiB
Python

"""Unit tests for US-004: oracle_server_id in telegram_users.
Tests cover:
- link_user_to_oracle() persists server_id
- link_user_to_oracle() works without server_id (backwards compat)
- get_user() returns oracle_server_id from stored row
- get_user_auth_data() includes server_id in returned dict
- Round-trip: link with server_id → auth data returns same server_id
"""
import asyncio
import sys
import os
import pytest
import aiosqlite
from datetime import datetime, timedelta
from pathlib import Path
from unittest.mock import AsyncMock, patch
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../'))
# python-telegram-bot is not installed in the test environment; mock it so that
# linking.py can be imported (it has `from telegram import User` at top-level).
from unittest.mock import MagicMock
sys.modules.setdefault('telegram', MagicMock())
import backend.modules.telegram.auth.linking # noqa: F401 — registers module for patch() resolution
from backend.modules.telegram.db.operations import (
create_or_update_user,
link_user_to_oracle,
get_user,
)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
async def _make_test_db(tmp_path: Path) -> Path:
"""Create a minimal telegram_users table in a temp SQLite file."""
db_file = tmp_path / "test_app.db"
async with aiosqlite.connect(db_file) as db:
await db.execute("""CREATE TABLE telegram_users (
telegram_user_id INTEGER PRIMARY KEY,
username TEXT, first_name TEXT NOT NULL, last_name TEXT,
oracle_username TEXT, oracle_server_id TEXT,
jwt_token TEXT, jwt_refresh_token TEXT,
token_expires_at TIMESTAMP, linked_at TIMESTAMP,
last_active_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT 1
)""")
await db.commit()
return db_file
async def _insert_user(db_path: Path, telegram_user_id: int):
async with aiosqlite.connect(db_path) as db:
await db.execute(
"INSERT INTO telegram_users (telegram_user_id, first_name) VALUES (?, ?)",
(telegram_user_id, "Test")
)
await db.commit()
# ---------------------------------------------------------------------------
# Tests: link_user_to_oracle persists server_id
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_link_user_persists_server_id(tmp_path):
db_file = await _make_test_db(tmp_path)
await _insert_user(db_file, 12345)
with patch("backend.modules.telegram.db.operations.DB_PATH", db_file):
result = await link_user_to_oracle(
telegram_user_id=12345,
oracle_username="TESTUSER",
jwt_token="tok",
jwt_refresh_token="rtok",
token_expires_at=datetime.now() + timedelta(minutes=30),
server_id="SERVER1"
)
assert result is True
async with aiosqlite.connect(db_file) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"SELECT oracle_server_id FROM telegram_users WHERE telegram_user_id = ?",
(12345,)
)
row = await cursor.fetchone()
assert row["oracle_server_id"] == "SERVER1"
@pytest.mark.asyncio
async def test_link_user_without_server_id(tmp_path):
"""Backwards compat: omitting server_id stores NULL."""
db_file = await _make_test_db(tmp_path)
await _insert_user(db_file, 22222)
with patch("backend.modules.telegram.db.operations.DB_PATH", db_file):
result = await link_user_to_oracle(
telegram_user_id=22222,
oracle_username="TESTUSER2",
jwt_token="tok",
jwt_refresh_token="rtok",
token_expires_at=datetime.now() + timedelta(minutes=30),
)
assert result is True
async with aiosqlite.connect(db_file) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"SELECT oracle_server_id FROM telegram_users WHERE telegram_user_id = ?",
(22222,)
)
row = await cursor.fetchone()
assert row["oracle_server_id"] is None
# ---------------------------------------------------------------------------
# Tests: get_user returns oracle_server_id
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_get_user_returns_oracle_server_id(tmp_path):
db_file = await _make_test_db(tmp_path)
await _insert_user(db_file, 33333)
with patch("backend.modules.telegram.db.operations.DB_PATH", db_file):
await link_user_to_oracle(
telegram_user_id=33333,
oracle_username="TESTUSER3",
jwt_token="tok",
jwt_refresh_token="rtok",
token_expires_at=datetime.now() + timedelta(minutes=30),
server_id="PROD_SERVER"
)
user = await get_user(33333)
assert user is not None
assert user["oracle_server_id"] == "PROD_SERVER"
# ---------------------------------------------------------------------------
# Tests: get_user_auth_data returns server_id (round-trip)
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_get_user_auth_data_includes_server_id(tmp_path):
"""Round-trip: link with server_id → get_user_auth_data returns same server_id."""
db_file = await _make_test_db(tmp_path)
await _insert_user(db_file, 44444)
expires_at = datetime.now() + timedelta(hours=1)
with patch("backend.modules.telegram.db.operations.DB_PATH", db_file):
await link_user_to_oracle(
telegram_user_id=44444,
oracle_username="TESTUSER4",
jwt_token="valid_token",
jwt_refresh_token="rtoken",
token_expires_at=expires_at,
server_id="ALPHA_SERVER"
)
mock_companies = [{"id": 1, "name": "Test Co"}]
mock_backend = AsyncMock()
mock_backend.__aenter__ = AsyncMock(return_value=mock_backend)
mock_backend.__aexit__ = AsyncMock(return_value=False)
mock_backend.get_user_companies = AsyncMock(return_value=mock_companies)
with (
patch("backend.modules.telegram.db.operations.DB_PATH", db_file),
patch("backend.modules.telegram.auth.linking.get_user") as mock_get_user,
patch("backend.modules.telegram.auth.linking.get_backend_client", return_value=mock_backend),
):
# Return stored user row (simulating what get_user() would return)
mock_get_user.return_value = {
"telegram_user_id": 44444,
"oracle_username": "TESTUSER4",
"oracle_server_id": "ALPHA_SERVER",
"jwt_token": "valid_token",
"jwt_refresh_token": "rtoken",
"token_expires_at": expires_at.isoformat(),
}
from backend.modules.telegram.auth.linking import get_user_auth_data
auth_data = await get_user_auth_data(44444)
assert auth_data is not None
assert "server_id" in auth_data
assert auth_data["server_id"] == "ALPHA_SERVER"
assert auth_data["username"] == "TESTUSER4"
@pytest.mark.asyncio
async def test_get_user_auth_data_server_id_none_when_not_set(tmp_path):
"""server_id is None in auth data when user was linked without a server_id."""
mock_companies = []
mock_backend = AsyncMock()
mock_backend.__aenter__ = AsyncMock(return_value=mock_backend)
mock_backend.__aexit__ = AsyncMock(return_value=False)
mock_backend.get_user_companies = AsyncMock(return_value=mock_companies)
expires_at = datetime.now() + timedelta(hours=1)
with (
patch("backend.modules.telegram.auth.linking.get_user") as mock_get_user,
patch("backend.modules.telegram.auth.linking.get_backend_client", return_value=mock_backend),
):
mock_get_user.return_value = {
"telegram_user_id": 55555,
"oracle_username": "TESTUSER5",
"oracle_server_id": None,
"jwt_token": "valid_token",
"jwt_refresh_token": "rtoken",
"token_expires_at": expires_at.isoformat(),
}
from backend.modules.telegram.auth.linking import get_user_auth_data
auth_data = await get_user_auth_data(55555)
assert auth_data is not None
assert "server_id" in auth_data
assert auth_data["server_id"] is None