using FluentAssertions; using PageManager.Api.Data.Models; using PageManager.Api.Services; using PageManager.Api.Tests.Helpers; namespace PageManager.Api.Tests.Unit.Services; public class FileScannerServiceTests { // ── GetFormatFromExtension ──────────────────────────────────────────────── [Theory] [InlineData(".epub", FileFormat.Epub)] [InlineData(".mobi", FileFormat.Mobi)] [InlineData(".pdf", FileFormat.Pdf)] [InlineData(".m4b", FileFormat.M4b)] [InlineData(".mp3", FileFormat.Mp3)] [InlineData(".aac", FileFormat.Aac)] [InlineData(".flac", FileFormat.Flac)] [InlineData(".EPUB", FileFormat.Epub)] // case-insensitive [InlineData(".PDF", FileFormat.Pdf)] public void GetFormatFromExtension_KnownExtension_ReturnsFormat(string ext, FileFormat expected) { FileScannerService.GetFormatFromExtension(ext).Should().Be(expected); } [Theory] [InlineData(".txt")] [InlineData(".docx")] [InlineData(".zip")] [InlineData("")] public void GetFormatFromExtension_UnknownExtension_ReturnsNull(string ext) { FileScannerService.GetFormatFromExtension(ext).Should().BeNull(); } // ── FindMatch ───────────────────────────────────────────────────────────── [Fact] public void FindMatch_FilenameContainsBookTitle_ReturnsBook() { var books = new[] { BookFactory.Create(id: 1, title: "Dune") }; var file = new BookFile { Filename = "Dune - Frank Herbert.epub" }; FileScannerService.FindMatch(file, books)!.Id.Should().Be(1); } [Fact] public void FindMatch_CaseInsensitive_ReturnsBook() { var books = new[] { BookFactory.Create(id: 2, title: "The Way of Kings") }; var file = new BookFile { Filename = "the way of kings.epub" }; FileScannerService.FindMatch(file, books)!.Id.Should().Be(2); } [Fact] public void FindMatch_FilenameContainsIsbn_ReturnsBook() { var books = new[] { BookFactory.Create(id: 3, title: "Foundation", isbn: "9780553293357") }; var file = new BookFile { Filename = "9780553293357.epub" }; FileScannerService.FindMatch(file, books)!.Id.Should().Be(3); } [Fact] public void FindMatch_NoMatch_ReturnsNull() { var books = new[] { BookFactory.Create(id: 1, title: "Dune") }; var file = new BookFile { Filename = "Foundation - Isaac Asimov.epub" }; FileScannerService.FindMatch(file, books).Should().BeNull(); } [Fact] public void FindMatch_EmptyBookList_ReturnsNull() { FileScannerService.FindMatch(new BookFile { Filename = "anything.epub" }, []).Should().BeNull(); } [Fact] public void FindMatch_BookWithNullIsbn_DoesNotThrow() { var books = new[] { BookFactory.Create(id: 1, title: "Dune", isbn: null) }; var file = new BookFile { Filename = "some-file.epub" }; var act = () => FileScannerService.FindMatch(file, books); act.Should().NotThrow(); } [Fact] public void FindMatch_YearPrefixedFilename_StillMatchesTitle() { var books = new[] { BookFactory.Create(id: 1, title: "Project Hail Mary") }; var file = new BookFile { Filename = "2021 - Project Hail Mary.epub", Path = "2021 - Project Hail Mary.epub" }; FileScannerService.FindMatch(file, books)!.Id.Should().Be(1); } [Fact] public void FindMatch_AudiobookInSubfolder_MatchesByParentFolderName() { var books = new[] { BookFactory.Create(id: 2, title: "Project Hail Mary") }; // m4b lives at "Andy Weir/2021 - Project Hail Mary/Project Hail Mary.m4b" var file = new BookFile { Filename = "Project Hail Mary.m4b", Path = "Andy Weir/2021 - Project Hail Mary/Project Hail Mary.m4b", }; FileScannerService.FindMatch(file, books)!.Id.Should().Be(2); } // ── StripYearPrefix ─────────────────────────────────────────────────────── [Theory] [InlineData("2021 - project hail mary", "project hail mary")] [InlineData("2015 - artemis", "artemis")] [InlineData("project hail mary", "project hail mary")] // no prefix → unchanged [InlineData("diary of an asscan", "diary of an asscan")] public void StripYearPrefix_ReturnsExpected(string input, string expected) { FileScannerService.StripYearPrefix(input).Should().Be(expected); } }