Files
duty-teller/tests/test_app.py
Nikolay Tatarinov 4e6756025d Enhance database initialization and improve command handling
- Added `__all__` declaration in `db/__init__.py` for better module export management.
- Simplified command text formatting in `handlers/commands.py` for improved readability.
- Refactored error handler function signature in `handlers/errors.py` for better code style.
- Introduced comprehensive tests for API duties and Telegram authentication in new test files.
2026-02-17 20:05:49 +03:00

134 lines
4.7 KiB
Python

"""Tests for FastAPI app /api/duties."""
import time
from unittest.mock import patch
import pytest
from fastapi.testclient import TestClient
import config
from api.app import app
from tests.test_telegram_auth import _make_init_data
@pytest.fixture
def client():
return TestClient(app)
def test_duties_invalid_date_format(client):
r = client.get("/api/duties", params={"from": "2025-01-01", "to": "invalid"})
assert r.status_code == 400
assert "YYYY-MM-DD" in r.json()["detail"]
def test_duties_from_after_to(client):
r = client.get("/api/duties", params={"from": "2025-02-01", "to": "2025-01-01"})
assert r.status_code == 400
assert "from" in r.json()["detail"].lower() or "позже" in r.json()["detail"]
@patch("api.app._is_private_client")
@patch("api.app.config.MINI_APP_SKIP_AUTH", False)
def test_duties_403_without_init_data_from_public_client(mock_private, client):
"""Without initData and without private IP / skip-auth, should get 403."""
mock_private.return_value = False # simulate public client
r = client.get(
"/api/duties",
params={"from": "2025-01-01", "to": "2025-01-31"},
)
assert r.status_code == 403
@patch("api.app.config.MINI_APP_SKIP_AUTH", True)
@patch("api.app._fetch_duties_response")
def test_duties_200_when_skip_auth(mock_fetch, client):
mock_fetch.return_value = []
r = client.get(
"/api/duties",
params={"from": "2025-01-01", "to": "2025-01-31"},
)
assert r.status_code == 200
assert r.json() == []
mock_fetch.assert_called_once_with("2025-01-01", "2025-01-31")
@patch("api.app.validate_init_data_with_reason")
def test_duties_403_when_init_data_invalid(mock_validate, client):
mock_validate.return_value = (None, "hash_mismatch")
r = client.get(
"/api/duties",
params={"from": "2025-01-01", "to": "2025-01-31"},
headers={"X-Telegram-Init-Data": "some=data&hash=abc"},
)
assert r.status_code == 403
detail = r.json()["detail"]
assert "авторизации" in detail or "Неверные" in detail or "Неверная" in detail
@patch("api.app.validate_init_data_with_reason")
@patch("api.app.config.can_access_miniapp")
def test_duties_403_when_username_not_allowed(mock_can_access, mock_validate, client):
mock_validate.return_value = ("someuser", "ok")
mock_can_access.return_value = False
with patch("api.app._fetch_duties_response") as mock_fetch:
r = client.get(
"/api/duties",
params={"from": "2025-01-01", "to": "2025-01-31"},
headers={"X-Telegram-Init-Data": "user=xxx&hash=yyy"},
)
assert r.status_code == 403
assert "Доступ запрещён" in r.json()["detail"]
mock_fetch.assert_not_called()
@patch("api.app.validate_init_data_with_reason")
@patch("api.app.config.can_access_miniapp")
def test_duties_200_with_allowed_user(mock_can_access, mock_validate, client):
mock_validate.return_value = ("alloweduser", "ok")
mock_can_access.return_value = True
with patch("api.app._fetch_duties_response") as mock_fetch:
mock_fetch.return_value = [
{
"id": 1,
"user_id": 10,
"start_at": "2025-01-15T09:00:00",
"end_at": "2025-01-15T18:00:00",
"full_name": "Иван Иванов",
}
]
r = client.get(
"/api/duties",
params={"from": "2025-01-01", "to": "2025-01-31"},
headers={"X-Telegram-Init-Data": "user=xxx&hash=yyy"},
)
assert r.status_code == 200
assert len(r.json()) == 1
assert r.json()[0]["full_name"] == "Иван Иванов"
mock_fetch.assert_called_once_with("2025-01-01", "2025-01-31")
def test_duties_e2e_auth_real_validation(client, monkeypatch):
"""E2E: valid initData + allowlist, no mocks on validate_init_data_with_reason; full auth path."""
test_token = "123:ABC"
test_username = "e2euser"
monkeypatch.setattr(config, "BOT_TOKEN", test_token)
monkeypatch.setattr(config, "ALLOWED_USERNAMES", {test_username})
monkeypatch.setattr(config, "ADMIN_USERNAMES", set())
monkeypatch.setattr(config, "INIT_DATA_MAX_AGE_SECONDS", 0)
init_data = _make_init_data(
{"id": 1, "username": test_username},
test_token,
auth_date=int(time.time()),
)
with patch("api.app._fetch_duties_response") as mock_fetch:
mock_fetch.return_value = []
r = client.get(
"/api/duties",
params={"from": "2025-01-01", "to": "2025-01-31"},
headers={"X-Telegram-Init-Data": init_data},
)
assert r.status_code == 200
assert r.json() == []
mock_fetch.assert_called_once_with("2025-01-01", "2025-01-31")