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" assert result[1] == "Статья 1\n28.05.2026 " assert result[2] == "Статья 2\n29.05.2026 " 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 " def test_format_articles_empty_list(self): """Пустой список должен вернуть только заголовок.""" result = format_articles([], "Заголовок", "https://habr.com/feed") assert result == ["**Заголовок**\n"] 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" assert result[1] == "Единственная статья\n28.05.2026 " 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 " 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 " 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 " 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 " 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]