- pytest.ini для конфигурации тестов - tests/test_pogoda.py — тесты translate_weather, pressure_to_mmhg, wmo_to_russian (93 теста) - tests/test_fetch_cat.py — тесты fetch_cat (10 тестов) - tests/test_fetch_rss.py — тесты fetch_rss (20 тестов) - tests/test_format_articles.py — тесты truncate_title, parse_date, format_articles (24 теста) - tests/test_fetch_weather.py — тесты fetch_weather, fetch_open_meteo (20 тестов) - tests/test_commands_pogoda.py — тесты команды !pogoda (13 тестов) - Обновить AGENTS.md и requirements.txt
230 lines
11 KiB
Python
230 lines
11 KiB
Python
import pytest
|
||
from utils.news import format_articles, truncate_title, _parse_date
|
||
|
||
|
||
class TestTruncateTitle:
|
||
"""Тесты функции truncate_title() — обрезка заголовка."""
|
||
|
||
@pytest.mark.parametrize(
|
||
"title, max_len, expected",
|
||
[
|
||
("Короткий заголовок", 60, "Короткий заголовок"),
|
||
("Заголовок ровно в 60 символов1234567890", 60, "Заголовок ровно в 60 символов1234567890"),
|
||
("A" * 80, 60, "A" * 60 + "..."), # ASCII для надёжного сравнения
|
||
("", 60, ""),
|
||
("A" * 100, 100, "A" * 100),
|
||
("A" * 101, 100, "A" * 100 + "..."),
|
||
("A" * 50, 100, "A" * 50),
|
||
],
|
||
)
|
||
def test_truncate(self, title, max_len, expected):
|
||
"""Проверка обрезки заголовка."""
|
||
assert truncate_title(title, max_len) == expected
|
||
|
||
def test_truncate_default_max_len(self):
|
||
"""По умолчанию max_len=60."""
|
||
long_title = "A" * 61
|
||
result = truncate_title(long_title)
|
||
assert result == "A" * 60 + "..."
|
||
assert len(result) == 63 # 60 + "..."
|
||
|
||
|
||
class TestParseDate:
|
||
"""Тесты функции _parse_date() — парсинг даты из RSS."""
|
||
|
||
@pytest.mark.parametrize(
|
||
"pub_date, expected",
|
||
[
|
||
("Mon, 28 May 2026 10:00:00 +0000", "28.05.2026"),
|
||
("Mon, 28 May 2026 10:00:00 GMT", "28.05.2026"),
|
||
("2026-05-28T10:00:00Z", "2026.05.28"),
|
||
("2026-12-31T23:59:59Z", "2026.12.31"),
|
||
("2026-01-01T00:00:00Z", "2026.01.01"),
|
||
],
|
||
)
|
||
def test_parse_date_known(self, pub_date, expected):
|
||
"""Известные форматы даты должны парситься корректно."""
|
||
assert _parse_date(pub_date) == expected
|
||
|
||
@pytest.mark.parametrize(
|
||
"pub_date, expected",
|
||
[
|
||
("", ""),
|
||
(None, ""),
|
||
],
|
||
)
|
||
def test_parse_date_empty(self, pub_date, expected):
|
||
"""Пустая или None дата должна вернуть пустую строку."""
|
||
assert _parse_date(pub_date) == expected
|
||
|
||
def test_parse_date_invalid(self):
|
||
"""Невалидная дата должна вернуть первые 10 символов."""
|
||
result = _parse_date("invalid-date-string")
|
||
assert result == "invalid.da" # первые 10 символов: 'invalid-da' → 'invalid.da' (replace('-','.'))
|
||
|
||
|
||
class TestFormatArticles:
|
||
"""Тесты функции format_articles() — формирование строк для вывода."""
|
||
|
||
def test_format_articles_normal(self):
|
||
"""Нормальный список статей должен вернуть заголовок + 5 статей."""
|
||
articles = [
|
||
{
|
||
"title": "Статья 1",
|
||
"link": "https://habr.com/1",
|
||
"pub_date": "Mon, 28 May 2026 10:00:00 +0000",
|
||
"creator": "Автор 1",
|
||
"tags": ["AI"],
|
||
},
|
||
{
|
||
"title": "Статья 2",
|
||
"link": "https://habr.com/2",
|
||
"pub_date": "Tue, 29 May 2026 12:00:00 +0000",
|
||
"creator": "Автор 2",
|
||
"tags": ["ML"],
|
||
},
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert len(result) == 3 # заголовок + 2 статьи
|
||
assert result[0] == "**Заголовок**\n<https://habr.com/feed>"
|
||
assert result[1] == "Статья 1\n28.05.2026 <https://habr.com/1>"
|
||
assert result[2] == "Статья 2\n29.05.2026 <https://habr.com/2>"
|
||
|
||
def test_format_articles_limit_to_5(self):
|
||
"""Больше 5 статей должно быть обрезано до 5."""
|
||
articles = [
|
||
{"title": f"Статья {i}", "link": f"https://habr.com/{i}", "pub_date": "Mon, 28 May 2026 10:00:00 +0000", "creator": "", "tags": []}
|
||
for i in range(10)
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert len(result) == 6 # заголовок + 5 статей
|
||
assert result[-1] == "Статья 4\n28.05.2026 <https://habr.com/4>"
|
||
|
||
def test_format_articles_empty_list(self):
|
||
"""Пустой список должен вернуть только заголовок."""
|
||
result = format_articles([], "Заголовок", "https://habr.com/feed")
|
||
assert result == ["**Заголовок**\n<https://habr.com/feed>"]
|
||
assert len(result) == 1
|
||
|
||
def test_format_articles_none(self):
|
||
"""None должен вызвать TypeError (articles[:5] на None)."""
|
||
with pytest.raises(TypeError):
|
||
format_articles(None, "Заголовок", "https://habr.com/feed")
|
||
|
||
def test_format_articles_single_article(self):
|
||
"""Одна статья должна быть корректно отформатирована."""
|
||
articles = [
|
||
{
|
||
"title": "Единственная статья",
|
||
"link": "https://habr.com/1",
|
||
"pub_date": "Mon, 28 May 2026 10:00:00 +0000",
|
||
"creator": "Автор",
|
||
"tags": ["AI"],
|
||
},
|
||
]
|
||
result = format_articles(articles, "Новости AI", "https://habr.com/ai")
|
||
assert len(result) == 2
|
||
assert result[0] == "**Новости AI**\n<https://habr.com/ai>"
|
||
assert result[1] == "Единственная статья\n28.05.2026 <https://habr.com/1>"
|
||
|
||
def test_format_articles_long_title_truncated(self):
|
||
"""Длинный заголовок должен быть обрезан до 60 символов с '...'."""
|
||
long_title = "A" * 100
|
||
articles = [
|
||
{"title": long_title, "link": "https://habr.com/1", "pub_date": "Mon, 28 May 2026 10:00:00 +0000", "creator": "", "tags": []}
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert len(result[1].split("\n")[0]) == 63 # 60 + "..."
|
||
assert result[1].split("\n")[0].endswith("...")
|
||
|
||
def test_format_articles_short_title_unchanged(self):
|
||
"""Короткий заголовок должен остаться без изменений."""
|
||
short_title = "Кот"
|
||
articles = [
|
||
{"title": short_title, "link": "https://habr.com/1", "pub_date": "Mon, 28 May 2026 10:00:00 +0000", "creator": "", "tags": []}
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert result[1].split("\n")[0] == "Кот"
|
||
|
||
def test_format_articles_exact_60_chars(self):
|
||
"""Заголовок ровно 60 символов не должен обрезаться."""
|
||
exact_title = "A" * 60
|
||
articles = [
|
||
{"title": exact_title, "link": "https://habr.com/1", "pub_date": "Mon, 28 May 2026 10:00:00 +0000", "creator": "", "tags": []}
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert result[1].split("\n")[0] == exact_title
|
||
assert "..." not in result[1]
|
||
|
||
def test_format_articles_iso_date(self):
|
||
"""Дата в формате ISO должна парситься корректно."""
|
||
articles = [
|
||
{
|
||
"title": "Статья",
|
||
"link": "https://habr.com/1",
|
||
"pub_date": "2026-05-28T10:00:00Z",
|
||
"creator": "",
|
||
"tags": [],
|
||
},
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert result[1] == "Статья\n2026.05.28 <https://habr.com/1>"
|
||
|
||
def test_format_articles_empty_date(self):
|
||
"""Пустая дата должна быть пустой строкой."""
|
||
articles = [
|
||
{"title": "Статья", "link": "https://habr.com/1", "pub_date": "", "creator": "", "tags": []}
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert result[1] == "Статья\n <https://habr.com/1>"
|
||
|
||
def test_format_articles_none_date(self):
|
||
"""None дата должна быть пустой строкой."""
|
||
articles = [
|
||
{"title": "Статья", "link": "https://habr.com/1", "pub_date": None, "creator": "", "tags": []}
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert result[1] == "Статья\n <https://habr.com/1>"
|
||
|
||
def test_format_articles_empty_link(self):
|
||
"""Пустая ссылка должна быть пустой строкой в угловых скобках."""
|
||
articles = [
|
||
{"title": "Статья", "link": "", "pub_date": "Mon, 28 May 2026 10:00:00 +0000", "creator": "", "tags": []}
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert result[1].endswith(" <>")
|
||
|
||
def test_format_articles_russian_title(self):
|
||
"""Русские заголовки должны корректно отображаться."""
|
||
articles = [
|
||
{
|
||
"title": "Искусственный интеллект в медицине",
|
||
"link": "https://habr.com/1",
|
||
"pub_date": "Mon, 28 May 2026 10:00:00 +0000",
|
||
"creator": "Иван Иванов",
|
||
"tags": ["AI", "медицина"],
|
||
},
|
||
]
|
||
result = format_articles(articles, "Новости AI", "https://habr.com/ai")
|
||
assert "Искусственный интеллект в медицине" in result[1]
|
||
|
||
def test_format_articles_exact_5_articles(self):
|
||
"""Ровно 5 статей должно быть включено."""
|
||
articles = [
|
||
{"title": f"Статья {i}", "link": f"https://habr.com/{i}", "pub_date": "Mon, 28 May 2026 10:00:00 +0000", "creator": "", "tags": []}
|
||
for i in range(5)
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert len(result) == 6 # заголовок + 5 статей
|
||
assert result[-1] == "Статья 4\n28.05.2026 <https://habr.com/4>"
|
||
|
||
def test_format_articles_6th_article_excluded(self):
|
||
"""6-я статья должна быть исключена."""
|
||
articles = [
|
||
{"title": f"Статья {i}", "link": f"https://habr.com/{i}", "pub_date": "Mon, 28 May 2026 10:00:00 +0000", "creator": "", "tags": []}
|
||
for i in range(6)
|
||
]
|
||
result = format_articles(articles, "Заголовок", "https://habr.com/feed")
|
||
assert len(result) == 6 # заголовок + 5 статей
|
||
assert "Статья 5" not in result[5]
|