"""Tests for duty_teller.api.dependencies (lang, auth error, date validation, private client).""" from unittest.mock import patch import pytest from fastapi import HTTPException import duty_teller.api.dependencies as deps import duty_teller.config as config class TestLangFromAcceptLanguage: """Tests for _lang_from_accept_language: always returns config.DEFAULT_LANGUAGE.""" def test_always_returns_default_language(self): """Header is ignored; result is always config.DEFAULT_LANGUAGE.""" assert deps._lang_from_accept_language(None) == config.DEFAULT_LANGUAGE assert deps._lang_from_accept_language("") == config.DEFAULT_LANGUAGE assert deps._lang_from_accept_language(" ") == config.DEFAULT_LANGUAGE assert ( deps._lang_from_accept_language("ru-RU,ru;q=0.9") == config.DEFAULT_LANGUAGE ) assert deps._lang_from_accept_language("en-US") == config.DEFAULT_LANGUAGE assert deps._lang_from_accept_language("zz") == config.DEFAULT_LANGUAGE assert deps._lang_from_accept_language("x") == config.DEFAULT_LANGUAGE def test_returns_ru_when_default_language_is_ru(self): with patch.object(config, "DEFAULT_LANGUAGE", "ru"): assert deps._lang_from_accept_language("en-US") == "ru" assert deps._lang_from_accept_language(None) == "ru" def test_returns_en_when_default_language_is_en(self): with patch.object(config, "DEFAULT_LANGUAGE", "en"): assert deps._lang_from_accept_language("ru-RU") == "en" assert deps._lang_from_accept_language(None) == "en" class TestAuthErrorDetail: """Tests for _auth_error_detail.""" def test_hash_mismatch_uses_bad_signature_key(self): with patch("duty_teller.api.dependencies.t") as mock_t: mock_t.return_value = "Bad signature" result = deps._auth_error_detail("hash_mismatch", "en") assert result == "Bad signature" mock_t.assert_called_once_with("en", "api.auth_bad_signature") def test_other_reason_uses_auth_invalid_key(self): with patch("duty_teller.api.dependencies.t") as mock_t: mock_t.return_value = "Invalid auth" result = deps._auth_error_detail("expired", "ru") assert result == "Invalid auth" mock_t.assert_called_once_with("ru", "api.auth_invalid") class TestValidateDutyDates: """Tests for _validate_duty_dates.""" def test_valid_range_no_exception(self): deps._validate_duty_dates("2025-01-01", "2025-01-31", "en") def test_bad_format_raises_400_with_i18n(self): with patch("duty_teller.api.dependencies.t") as mock_t: mock_t.return_value = "Bad format message" with pytest.raises(HTTPException) as exc_info: deps._validate_duty_dates("01-01-2025", "2025-01-31", "en") assert exc_info.value.status_code == 400 assert exc_info.value.detail == "Bad format message" mock_t.assert_called_with("en", "dates.bad_format") def test_from_after_to_raises_400_with_i18n(self): with patch("duty_teller.api.dependencies.t") as mock_t: mock_t.return_value = "From after to message" with pytest.raises(HTTPException) as exc_info: deps._validate_duty_dates("2025-02-01", "2025-01-01", "ru") assert exc_info.value.status_code == 400 assert exc_info.value.detail == "From after to message" mock_t.assert_called_with("ru", "dates.from_after_to") class TestFetchDutiesResponse: """Tests for fetch_duties_response (DutyWithUser list with phone, username).""" def test_fetch_duties_response_includes_phone_and_username(self): """get_duties returns (Duty, full_name, phone, username); response has phone, username.""" from types import SimpleNamespace from duty_teller.db.schemas import DutyWithUser duty = SimpleNamespace( id=1, user_id=10, start_at="2025-01-15T09:00:00Z", end_at="2025-01-15T18:00:00Z", event_type="duty", ) rows = [(duty, "Alice", "+79001234567", "alice_dev")] with patch.object(deps, "get_duties", return_value=rows): result = deps.fetch_duties_response( type("Session", (), {})(), "2025-01-01", "2025-01-31" ) assert len(result) == 1 assert isinstance(result[0], DutyWithUser) assert result[0].full_name == "Alice" assert result[0].phone == "+79001234567" assert result[0].username == "alice_dev"