using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data.Common; using System.IO; using System.Linq; using System.Threading.Tasks; using API.Data; using API.Entities; using API.Interfaces; using API.Interfaces.Services; using API.Parser; using API.Services; using API.Services.Tasks; using API.Tests.Helpers; using AutoMapper; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.Logging; using NSubstitute; using Xunit; using Xunit.Abstractions; namespace API.Tests.Services { public class ScannerServiceTests : IDisposable { private readonly ITestOutputHelper _testOutputHelper; private readonly ScannerService _scannerService; private readonly ILogger _logger = Substitute.For>(); private readonly IUnitOfWork _unitOfWork; private readonly IArchiveService _archiveService = Substitute.For(); private readonly IBookService _bookService = Substitute.For(); private readonly IMetadataService _metadataService; private readonly ILogger _metadataLogger = Substitute.For>(); private readonly DbConnection _connection; private readonly DataContext _context; public ScannerServiceTests(ITestOutputHelper testOutputHelper) { var contextOptions = new DbContextOptionsBuilder() .UseSqlite(CreateInMemoryDatabase()) .Options; _connection = RelationalOptionsExtension.Extract(contextOptions).Connection; _context = new DataContext(contextOptions); Task.Run(SeedDb).GetAwaiter().GetResult(); //BackgroundJob.Enqueue is what I need to mock or something (it's static...) // ICacheService cacheService, ILogger logger, IScannerService scannerService, // IUnitOfWork unitOfWork, IMetadataService metadataService, IBackupService backupService, ICleanupService cleanupService, // IBackgroundJobClient jobClient //var taskScheduler = new TaskScheduler(Substitute.For(), Substitute.For>(), Substitute.For<) // Substitute.For>() - Not needed because only for UserService _unitOfWork = new UnitOfWork(_context, Substitute.For(), null, Substitute.For>()); _testOutputHelper = testOutputHelper; _metadataService= Substitute.For(_unitOfWork, _metadataLogger, _archiveService, _bookService); _scannerService = new ScannerService(_unitOfWork, _logger, _archiveService, _metadataService, _bookService); } private async Task SeedDb() { await _context.Database.MigrateAsync(); await Seed.SeedSettings(_context); _context.Library.Add(new Library() { Name = "Manga", Folders = new List() { new FolderPath() { Path = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ScannerService/Manga") } } }); return await _context.SaveChangesAsync() > 0; } // [Fact] // public void Test() // { // _scannerService.ScanLibrary(1, false); // // var series = _unitOfWork.LibraryRepository.GetLibraryForIdAsync(1).Result.Series; // } [Fact] public void FindSeriesNotOnDisk_Should_RemoveNothing_Test() { var infos = new Dictionary>(); AddToParsedInfo(infos, new ParserInfo() {Series = "Darker than Black"}); AddToParsedInfo(infos, new ParserInfo() {Series = "Cage of Eden", Volumes = "1"}); AddToParsedInfo(infos, new ParserInfo() {Series = "Cage of Eden", Volumes = "10"}); var existingSeries = new List(); existingSeries.Add(new Series() { Name = "Cage of Eden", LocalizedName = "Cage of Eden", OriginalName = "Cage of Eden", NormalizedName = API.Parser.Parser.Normalize("Cage of Eden") }); existingSeries.Add(new Series() { Name = "Darker Than Black", LocalizedName = "Darker Than Black", OriginalName = "Darker Than Black", NormalizedName = API.Parser.Parser.Normalize("Darker Than Black") }); var expectedSeries = new List(); Assert.Empty(_scannerService.FindSeriesNotOnDisk(existingSeries, infos)); } [Theory] [InlineData(new [] {"Darker than Black"}, "Darker than Black", "Darker than Black")] [InlineData(new [] {"Darker than Black"}, "Darker Than Black", "Darker than Black")] [InlineData(new [] {"Darker than Black"}, "Darker Than Black!", "Darker than Black")] [InlineData(new [] {""}, "Runaway Jack", "Runaway Jack")] public void MergeNameTest(string[] existingSeriesNames, string parsedInfoName, string expected) { var collectedSeries = new ConcurrentDictionary>(); foreach (var seriesName in existingSeriesNames) { AddToParsedInfo(collectedSeries, new ParserInfo() {Series = seriesName}); } var actualName = _scannerService.MergeName(collectedSeries, new ParserInfo() { Series = parsedInfoName }); Assert.Equal(expected, actualName); } [Fact] public void RemoveMissingSeries_Should_RemoveSeries() { var existingSeries = new List() { EntityFactory.CreateSeries("Darker than Black Vol 1"), EntityFactory.CreateSeries("Darker than Black"), EntityFactory.CreateSeries("Beastars"), }; var missingSeries = new List() { EntityFactory.CreateSeries("Darker than Black Vol 1"), }; existingSeries = ScannerService.RemoveMissingSeries(existingSeries, missingSeries, out var removeCount).ToList(); Assert.DoesNotContain(missingSeries[0].Name, existingSeries.Select(s => s.Name)); Assert.Equal(missingSeries.Count, removeCount); } private void AddToParsedInfo(IDictionary> collectedSeries, ParserInfo info) { if (collectedSeries.GetType() == typeof(ConcurrentDictionary<,>)) { ((ConcurrentDictionary>) collectedSeries).AddOrUpdate(info.Series, new List() {info}, (_, oldValue) => { oldValue ??= new List(); if (!oldValue.Contains(info)) { oldValue.Add(info); } return oldValue; }); } else { if (!collectedSeries.ContainsKey(info.Series)) { collectedSeries.Add(info.Series, new List() {info}); } else { var list = collectedSeries[info.Series]; if (!list.Contains(info)) { list.Add(info); } collectedSeries[info.Series] = list; } } } // [Fact] // public void ExistingOrDefault_Should_BeFromLibrary() // { // var allSeries = new List() // { // new Series() {Id = 2, Name = "Darker Than Black"}, // new Series() {Id = 3, Name = "Darker Than Black - Some Extension"}, // new Series() {Id = 4, Name = "Akame Ga Kill"}, // }; // Assert.Equal(_libraryMock.Series.ElementAt(0).Id, ScannerService.ExistingOrDefault(_libraryMock, allSeries, "Darker Than Black").Id); // Assert.Equal(_libraryMock.Series.ElementAt(0).Id, ScannerService.ExistingOrDefault(_libraryMock, allSeries, "Darker than Black").Id); // } // // [Fact] // public void ExistingOrDefault_Should_BeFromAllSeries() // { // var allSeries = new List() // { // new Series() {Id = 2, Name = "Darker Than Black"}, // new Series() {Id = 3, Name = "Darker Than Black - Some Extension"}, // new Series() {Id = 4, Name = "Akame Ga Kill"}, // }; // Assert.Equal(3, ScannerService.ExistingOrDefault(_libraryMock, allSeries, "Darker Than Black - Some Extension").Id); // } // // [Fact] // public void ExistingOrDefault_Should_BeNull() // { // var allSeries = new List() // { // new Series() {Id = 2, Name = "Darker Than Black"}, // new Series() {Id = 3, Name = "Darker Than Black - Some Extension"}, // new Series() {Id = 4, Name = "Akame Ga Kill"}, // }; // Assert.Null(ScannerService.ExistingOrDefault(_libraryMock, allSeries, "Non existing series")); // } [Fact] public void Should_CreateSeries_Test() { // var allSeries = new List(); // var parsedSeries = new Dictionary>(); // // parsedSeries.Add("Darker Than Black", new List() // { // new ParserInfo() {Chapters = "0", Filename = "Something.cbz", Format = MangaFormat.Archive, FullFilePath = "E:/Manga/Something.cbz", Series = "Darker Than Black", Volumes = "1"}, // new ParserInfo() {Chapters = "0", Filename = "Something.cbz", Format = MangaFormat.Archive, FullFilePath = "E:/Manga/Something.cbz", Series = "Darker than Black", Volumes = "2"} // }); // // _scannerService.UpsertSeries(_libraryMock, parsedSeries, allSeries); // // Assert.Equal(1, _libraryMock.Series.Count); // Assert.Equal(2, _libraryMock.Series.ElementAt(0).Volumes.Count); // _testOutputHelper.WriteLine(_libraryMock.ToString()); Assert.True(true); } private static DbConnection CreateInMemoryDatabase() { var connection = new SqliteConnection("Filename=:memory:"); connection.Open(); return connection; } public void Dispose() => _connection.Dispose(); } }