//go:build ignore // Legacy tests for monolithic cmd package are ignored after refactor to Clean Architecture. package main import ( "os" "path/filepath" "strings" "testing" ) // TestCleanSearchTitle тестирует функцию очистки названий func TestCleanSearchTitle(t *testing.T) { tests := []struct { name string input string expected string }{ { name: "Удаление круглых скобок", input: "Название книги (автор)", expected: "Название книги", }, { name: "Удаление квадратных скобок", input: "Название книги [жанр]", expected: "Название книги", }, { name: "Удаление множественных пробелов", input: "Название книги", expected: "Название книги", }, { name: "Комплексная очистка", input: "Название книги (автор) [жанр] ", expected: "Название книги", }, { name: "Пустая строка", input: "", expected: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := cleanSearchTitle(tt.input) if result != tt.expected { t.Errorf("cleanSearchTitle(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } // TestFindMP3Files тестирует поиск MP3 файлов func TestFindMP3Files(t *testing.T) { // Создаем временную директорию для теста tmpDir := t.TempDir() // Создаем тестовые файлы testFiles := []string{ "test1.mp3", "test2.MP3", "test3.txt", "test4.mp3", "notmp3.wav", } for _, file := range testFiles { filePath := filepath.Join(tmpDir, file) f, err := os.Create(filePath) if err != nil { t.Fatalf("Не удалось создать тестовый файл %s: %v", file, err) } f.Close() } // Тестируем функцию mp3Files, err := findMP3Files(tmpDir) if err != nil { t.Fatalf("findMP3Files вернула ошибку: %v", err) } // Проверяем результат expectedCount := 3 // test1.mp3, test2.MP3, test4.mp3 if len(mp3Files) != expectedCount { t.Errorf("Ожидалось %d MP3 файлов, найдено %d", expectedCount, len(mp3Files)) } // Проверяем, что найдены правильные файлы foundFiles := make(map[string]bool) for _, file := range mp3Files { foundFiles[filepath.Base(file)] = true } expectedFiles := []string{"test1.mp3", "test2.MP3", "test4.mp3"} for _, expected := range expectedFiles { if !foundFiles[expected] { t.Errorf("Файл %s не найден в результатах", expected) } } } // TestCreateChapters тестирует создание глав из MP3 файлов func TestCreateChapters(t *testing.T) { // Создаем временную директорию с тестовыми MP3 файлами tempDir := t.TempDir() // Создаем тестовые MP3 файлы testFiles := []string{ "01 - Введение.mp3", "02 - Первая глава.mp3", "03 - Вторая глава.mp3", "10 - Заключение.mp3", } var mp3FilePaths []string for _, filename := range testFiles { filePath := filepath.Join(tempDir, filename) // Создаем пустой файл file, err := os.Create(filePath) if err != nil { t.Fatalf("Не удалось создать тестовый файл %s: %v", filename, err) } file.Close() mp3FilePaths = append(mp3FilePaths, filePath) } // Тестируем создание глав chapters := createChapters(mp3FilePaths) // Проверяем количество созданных глав if len(chapters) != len(testFiles) { t.Errorf("Ожидалось %d глав, получено %d", len(testFiles), len(chapters)) } // Проверяем первую главу if chapters[0].ID != 0 { t.Errorf("Ожидался ID 0 для первой главы, получен %d", chapters[0].ID) } if chapters[0].Title != "01 - Введение" { t.Errorf("Ожидалось название '01 - Введение', получено '%s'", chapters[0].Title) } // Проверяем последнюю главу lastChapter := chapters[len(chapters)-1] if lastChapter.ID != len(chapters)-1 { t.Errorf("Ожидался ID %d для последней главы, получен %d", len(chapters)-1, lastChapter.ID) } // Проверяем, что главы отсортированы правильно expectedTitles := []string{"01 - Введение", "02 - Первая глава", "03 - Вторая глава", "10 - Заключение"} for i, expectedTitle := range expectedTitles { if chapters[i].Title != expectedTitle { t.Errorf("Глава %d: ожидалось название '%s', получено '%s'", i, expectedTitle, chapters[i].Title) } } } // TestFindCoverFile тестирует поиск файлов обложек func TestFindCoverFile(t *testing.T) { // Создаем временную директорию для теста tmpDir := t.TempDir() // Создаем тестовые файлы testFiles := []string{ "cover.jpg", "other.txt", "music.mp3", } for _, file := range testFiles { filePath := filepath.Join(tmpDir, file) f, err := os.Create(filePath) if err != nil { t.Fatalf("Не удалось создать тестовый файл %s: %v", file, err) } f.Close() } // Тестируем функцию coverFile := findCoverFile(tmpDir) expectedPath := filepath.Join(tmpDir, "cover.jpg") if coverFile != expectedPath { t.Errorf("Ожидался путь к обложке %q, получен %q", expectedPath, coverFile) } } // TestFindCoverFileNotFound тестирует случай, когда обложка не найдена func TestFindCoverFileNotFound(t *testing.T) { // Создаем временную директорию без файлов обложек tmpDir := t.TempDir() // Создаем файлы, которые не являются обложками testFiles := []string{ "music.mp3", "readme.txt", } for _, file := range testFiles { filePath := filepath.Join(tmpDir, file) f, err := os.Create(filePath) if err != nil { t.Fatalf("Не удалось создать тестовый файл %s: %v", file, err) } f.Close() } // Тестируем функцию coverFile := findCoverFile(tmpDir) if coverFile != "" { t.Errorf("Ожидалась пустая строка, получен %q", coverFile) } } // BenchmarkCleanSearchTitle тестирует производительность очистки названий func BenchmarkCleanSearchTitle(b *testing.B) { testTitle := "Очень длинное название книги с (автором) [жанром] и множественными пробелами" b.ResetTimer() for i := 0; i < b.N; i++ { cleanSearchTitle(testTitle) } } // TestCreateMetadata тестирует создание метаданных func TestCreateMetadata(t *testing.T) { // Подготавливаем тестовые данные book := AudioBookInfo{ Title: "Тестовая книга", Path: "/test/path", MP3Files: []string{"chapter1.mp3", "chapter2.mp3"}, Description: "Описание тестовой книги", } rutrackerData := &RuTrackerResult{ Title: "RuTracker название", Authors: []string{"Автор 1", "Автор 2"}, Narrators: []string{"Рассказчик 1"}, Genres: []string{"Фантастика", "Приключения"}, Description: "RuTracker описание", } // Тестируем функцию metadata := createMetadata(book, rutrackerData) // Проверяем результат if metadata.Title != rutrackerData.Title { t.Errorf("Ожидалось название %q, получено %q", rutrackerData.Title, metadata.Title) } if len(metadata.Authors) != len(rutrackerData.Authors) { t.Errorf("Ожидалось %d авторов, получено %d", len(rutrackerData.Authors), len(metadata.Authors)) } if len(metadata.Chapters) != len(book.MP3Files) { t.Errorf("Ожидалось %d глав, получено %d", len(book.MP3Files), len(metadata.Chapters)) } if metadata.Language != "ru" { t.Errorf("Ожидался язык 'ru', получен %q", metadata.Language) } } // TestParseSearchResults тестирует функцию парсинга результатов поиска RuTracker func TestParseSearchResults(t *testing.T) { // Пример HTML контента с результатами поиска (упрощенная версия) htmlContent := `
Download Тестовое название торрента 1.2 GB 15 3
Download Другой торрент 2.5 GB 8 1
` torrents, err := parseSearchResults(htmlContent) if err != nil { t.Fatalf("Ошибка парсинга: %v", err) } // Проверяем количество найденных торрентов expectedCount := 2 if len(torrents) != expectedCount { t.Errorf("Ожидалось %d торрентов, найдено %d", expectedCount, len(torrents)) } // Проверяем, что найдены торренты с правильными ID foundIDs := make(map[string]bool) for _, torrent := range torrents { foundIDs[torrent.ID] = true // Проверяем, что URL формируются правильно expectedTopicURL := "https://rutracker.org/forum/viewtopic.php?t=" + torrent.ID if torrent.TopicURL != expectedTopicURL { t.Errorf("Неправильный URL темы для ID %s: ожидался %q, получен %q", torrent.ID, expectedTopicURL, torrent.TopicURL) } expectedDownloadURL := "https://rutracker.org/forum/dl.php?t=" + torrent.ID if torrent.DownloadURL != expectedDownloadURL { t.Errorf("Неправильный URL скачивания для ID %s: ожидался %q, получен %q", torrent.ID, expectedDownloadURL, torrent.DownloadURL) } } // Проверяем наличие ожидаемых ID expectedIDs := []string{"123456", "789012"} for _, expectedID := range expectedIDs { if !foundIDs[expectedID] { t.Errorf("Не найден торрент с ID %s", expectedID) } } } // TestParseTopicMetadata тестирует функцию парсинга метаданных со страницы темы func TestParseTopicMetadata(t *testing.T) { // Реальный пример HTML контента страницы темы RuTracker с post-b структурой htmlContent := `
Год выпуска: 2025
Фамилия автора: Первухин
Имя автора: Андрей
Исполнитель: Парфенов Константин
Жанр: Боевое фэнтези
Издательство: ЛитРес
Описание: Простой парень попал в магический мир и стал наследником рода.
` torrent := TorrentInfo{ ID: "6643935", Title: "Первухин Андрей - Наследник. Книга 03", } metadata, err := parseTopicMetadata(htmlContent, torrent) if err != nil { t.Fatalf("Ошибка парсинга метаданных: %v", err) } // Проверяем извлеченные данные if metadata.Title != torrent.Title { t.Errorf("Неправильное название: ожидалось %q, получено %q", torrent.Title, metadata.Title) } // Проверяем автора (должен быть объединен из фамилии и имени) expectedAuthor := "Первухин Андрей" if len(metadata.Authors) != 1 || metadata.Authors[0] != expectedAuthor { t.Errorf("Неправильные авторы: ожидался ['%s'], получено %v", expectedAuthor, metadata.Authors) } // Проверяем исполнителя expectedNarrator := "Парфенов Константин" if len(metadata.Narrators) != 1 || metadata.Narrators[0] != expectedNarrator { t.Errorf("Неправильные чтецы: ожидался ['%s'], получено %v", expectedNarrator, metadata.Narrators) } // Проверяем год expectedYear := 2025 if metadata.PublishedYear == nil || *metadata.PublishedYear != expectedYear { t.Errorf("Неправильный год: ожидался %d, получено %v", expectedYear, metadata.PublishedYear) } // Проверяем издательство expectedPublisher := "ЛитРес" if metadata.Publisher == nil || *metadata.Publisher != expectedPublisher { t.Errorf("Неправильное издательство: ожидалось '%s', получено %v", expectedPublisher, metadata.Publisher) } // Проверяем жанры expectedGenres := []string{"Боевое фэнтези"} if len(metadata.Genres) != 1 || metadata.Genres[0] != expectedGenres[0] { t.Errorf("Неправильные жанры: ожидались %v, получено %v", expectedGenres, metadata.Genres) } // Проверяем описание expectedDesc := "Простой парень попал в магический мир и стал наследником рода." if metadata.Description != expectedDesc { t.Errorf("Неправильное описание: ожидалось '%s', получено '%s'", expectedDesc, metadata.Description) } if metadata.Language != "ru" { t.Errorf("Неправильный язык: ожидался 'ru', получен %q", metadata.Language) } } // TestParseTopicMetadataRealHTML тестирует парсинг с реальным HTML от RuTracker func TestParseTopicMetadataRealHTML(t *testing.T) { // Реальный фрагмент HTML со страницы RuTracker htmlContent := `
Наследник. Книга 03
Год выпуска: 2025
Фамилия автора: Первухин
Имя автора: Андрей
Исполнитель: Парфенов Константин
Жанр: Боевое фэнтези
Издательство: ЛитРес: чтец , Автор
Аудиокодек: MP3
Битрейт: 96 kbps
Вид битрейта: постоянный битрейт (CBR)
Частота дискретизации: 32 kHz
Количество каналов (моно-стерео): Стерео
Время звучания: 07:25:47
Описание: Простой парень, без каких-то особых талантов попал в магический мир. Вроде бы живи и радуйся, тем более, в новом мире ты не простой человек, а целый наследник рода, правда, не самого богатого и влиятельного. Только вот беда в том, что боги не обошли его своим вниманием.
Цикл «Наследник»
01. Наследник. Книга 01
02. Наследник. Книга 02
03. Наследник. Книга 03
03. Наследник. Книга 04
` torrent := TorrentInfo{ ID: "6643935", Title: "Первухин Андрей - Наследник. Книга 03 [Парфенов Константин, 2025, 96 kbps, MP3]", } metadata, err := parseTopicMetadata(htmlContent, torrent) if err != nil { t.Fatalf("Ошибка парсинга реального HTML: %v", err) } // Проверяем автора expectedAuthor := "Первухин Андрей" if len(metadata.Authors) != 1 || metadata.Authors[0] != expectedAuthor { t.Errorf("Неправильные авторы: ожидался ['%s'], получено %v", expectedAuthor, metadata.Authors) } // Проверяем исполнителя expectedNarrator := "Парфенов Константин" if len(metadata.Narrators) != 1 || metadata.Narrators[0] != expectedNarrator { t.Errorf("Неправильные чтецы: ожидался ['%s'], получено %v", expectedNarrator, metadata.Narrators) } // Проверяем жанр expectedGenre := "Боевое фэнтези" if len(metadata.Genres) != 1 || metadata.Genres[0] != expectedGenre { t.Errorf("Неправильные жанры: ожидался ['%s'], получено %v", expectedGenre, metadata.Genres) } // Проверяем год expectedYear := 2025 if metadata.PublishedYear == nil || *metadata.PublishedYear != expectedYear { t.Errorf("Неправильный год: ожидался %d, получено %v", expectedYear, metadata.PublishedYear) } // Проверяем издательство (должно очистить ": чтец , Автор") expectedPublisher := "ЛитРес" if metadata.Publisher == nil || *metadata.Publisher != expectedPublisher { t.Errorf("Неправильное издательство: ожидалось '%s', получено %v", expectedPublisher, metadata.Publisher) } // Проверяем описание if metadata.Description == "" { t.Error("Описание не найдено") } else if !strings.Contains(metadata.Description, "Простой парень") { t.Errorf("Описание не содержит ожидаемый текст: %s", metadata.Description) } // Проверяем информацию о серии (должна найтись в ссылке) if len(metadata.Series) == 0 { t.Error("Серия не найдена") } else if !strings.Contains(metadata.Series[0], "Наследник") { t.Errorf("Неправильная серия: %v", metadata.Series) } t.Logf("Успешно извлечены метаданные: автор=%v, чтец=%v, жанр=%v, год=%v", metadata.Authors, metadata.Narrators, metadata.Genres, *metadata.PublishedYear) } // TestExtractCoverURL тестирует извлечение URL обложки из HTML func TestExtractCoverURL(t *testing.T) { // HTML с var тегом как в реальном RuTracker htmlWithVar := `
Некоторый текст...
` coverURL := extractCoverURL(htmlWithVar) expectedURL := "https://i124.fastpic.org/big/2025/0209/7a/d953d7a9b39477e7da76e222993b1f7a.jpg" if coverURL != expectedURL { t.Errorf("Неправильный URL обложки: ожидался '%s', получен '%s'", expectedURL, coverURL) } // HTML с обычным img тегом htmlWithImg := `
Cover Текст...
` coverURL2 := extractCoverURL(htmlWithImg) expectedURL2 := "https://example.com/cover.png" if coverURL2 != expectedURL2 { t.Errorf("Неправильный URL обложки из img: ожидался '%s', получен '%s'", expectedURL2, coverURL2) } // HTML без изображений htmlEmpty := `
Только текст без изображений
` coverURL3 := extractCoverURL(htmlEmpty) if coverURL3 != "" { t.Errorf("Ожидался пустой URL, получен '%s'", coverURL3) } // HTML с неправильным URL (не изображение) htmlWrongURL := `
` coverURL4 := extractCoverURL(htmlWrongURL) if coverURL4 != "" { t.Errorf("Ожидалась пустая строка для не-изображения, получен '%s'", coverURL4) } } // BenchmarkParseSearchResults бенчмарк для функции парсинга результатов поиска func BenchmarkParseSearchResults(b *testing.B) { htmlContent := `
Download Тестовое название торрента 1.2 GB 15 3
` b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parseSearchResults(htmlContent) if err != nil { b.Fatal(err) } } } // BenchmarkParseTopicMetadata бенчмарк для функции парсинга метаданных страницы темы func BenchmarkParseTopicMetadata(b *testing.B) { htmlContent := `
Год выпуска: 2025
Фамилия автора: Первухин
Имя автора: Андрей
Исполнитель: Парфенов Константин
Жанр: Боевое фэнтези
Описание: Описание книги.
` torrent := TorrentInfo{ ID: "6643935", Title: "Первухин Андрей - Наследник. Книга 03", } b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parseTopicMetadata(htmlContent, torrent) if err != nil { b.Fatal(err) } } } // TestDownloadCoverValidation тестирует валидацию функции downloadCover func TestDownloadCoverValidation(t *testing.T) { // Создаем временную директорию для тестов tempDir, err := os.MkdirTemp("", "cover_test") if err != nil { t.Fatalf("Не удалось создать временную директорию: %v", err) } defer os.RemoveAll(tempDir) // Тест с пустым URL err = downloadCover("", tempDir) if err == nil { t.Error("Ожидалась ошибка для пустого URL") } // Тест с неправильным URL err = downloadCover("not-a-url", tempDir) if err == nil { t.Error("Ожидалась ошибка для неправильного URL") } // Примечание: полные HTTP-тесты требуют мокирования или реального сервера // Для production тестирования можно использовать httptest.Server } // TestExtractChapterTitle тестирует извлечение названий глав из имен файлов func TestExtractChapterTitle(t *testing.T) { testCases := []struct { input string expected string }{ {"01 - Введение", "01 - Введение"}, {"001_Первая_глава", "001 - Первая_глава"}, {"Chapter 05 - Название", "05 - Название"}, {"Глава 3 - Важная информация", "3 - Важная информация"}, {"Часть 10", "10"}, {"простое_название", "простое_название"}, {"15", "15"}, {"chapter_1_test", "1 - test"}, {"глава_2_описание", "2 - описание"}, {"", ""}, } for _, tc := range testCases { result := extractChapterTitle(tc.input) if result != tc.expected { t.Errorf("extractChapterTitle(%q) = %q, ожидалось %q", tc.input, result, tc.expected) } } }