Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ private sealed class StubEditionService : IEditionService
public Edition GetEditionByGoogleBooksEditionId(string googleBooksEditionId) => throw new NotImplementedException();
public Edition GetEditionByProviderAndId(string providerPrefix, string providerId) => throw new NotImplementedException();
public System.Collections.Generic.List<Edition> GetEditionsByProviderAndId(string providerPrefix, string providerId) => new System.Collections.Generic.List<Edition>();
public List<Edition> GetAllMonitoredEditions() => throw new NotImplementedException();
public List<Edition> GetMonitoredEditionsForBookList(BookMediaType? mediaType) => throw new NotImplementedException();
public void InsertMany(List<Edition> editions) => throw new NotImplementedException();
public void InsertMany(List<Edition> editions, IDbConnection connection, IDbTransaction transaction) => throw new NotImplementedException();
public void UpdateMany(List<Edition> editions) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ public List<Edition> SetMonitored(Edition edition, bool isManualSelection = fals
public Edition GetEditionByOpenLibraryEditionId(string openLibraryEditionId) => throw new NotImplementedException();
public Edition GetEditionByProviderAndId(string providerPrefix, string providerId) => throw new NotImplementedException();
public System.Collections.Generic.List<Edition> GetEditionsByProviderAndId(string providerPrefix, string providerId) => new System.Collections.Generic.List<Edition>();
public List<Edition> GetAllMonitoredEditions() => throw new NotImplementedException();
public List<Edition> GetMonitoredEditionsForBookList(BookMediaType? mediaType) => throw new NotImplementedException();
public void InsertMany(List<Edition> editions) => throw new NotImplementedException();
public void InsertMany(List<Edition> editions, System.Data.IDbConnection connection, System.Data.IDbTransaction transaction) => throw new NotImplementedException();
public void UpdateMany(List<Edition> editions) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public void UpdateMany(List<Edition> editions)
public Edition GetEditionByOpenLibraryEditionId(string openLibraryEditionId) => throw new NotImplementedException();
public Edition GetEditionByProviderAndId(string providerPrefix, string providerId) => throw new NotImplementedException();
public System.Collections.Generic.List<Edition> GetEditionsByProviderAndId(string providerPrefix, string providerId) => new System.Collections.Generic.List<Edition>();
public List<Edition> GetAllMonitoredEditions() => throw new NotImplementedException();
public List<Edition> GetMonitoredEditionsForBookList(BookMediaType? mediaType) => throw new NotImplementedException();
public void InsertMany(List<Edition> editions) => throw new NotImplementedException();
public void InsertMany(List<Edition> editions, System.Data.IDbConnection connection, System.Data.IDbTransaction transaction) => throw new NotImplementedException();
public void DeleteMany(List<Edition> editions) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ public void UpdateMany(List<Edition> editions)
public Edition GetEditionByGoogleBooksEditionId(string googleBooksEditionId) => throw new NotImplementedException();
public Edition GetEditionByProviderAndId(string providerPrefix, string providerId) => throw new NotImplementedException();
public System.Collections.Generic.List<Edition> GetEditionsByProviderAndId(string providerPrefix, string providerId) => new System.Collections.Generic.List<Edition>();
public List<Edition> GetAllMonitoredEditions() => throw new NotImplementedException();
public List<Edition> GetMonitoredEditionsForBookList(BookMediaType? mediaType) => throw new NotImplementedException();
public void InsertMany(List<Edition> editions, IDbConnection connection, IDbTransaction transaction) => throw new NotImplementedException();
public void DeleteMany(List<Edition> editions) => throw new NotImplementedException();
public List<Edition> GetEditionsForRefresh(int bookId) => throw new NotImplementedException();
Expand Down
176 changes: 176 additions & 0 deletions src/Chaptarr.Core.Test/Books/BookListQueriesFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using System;
using System.IO;
using System.Linq;
using Dapper;
using Microsoft.Data.Sqlite;
using NUnit.Framework;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Books;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;

namespace Chaptarr.Core.Test.Books
{
[TestFixture]
public class BookListQueriesFixture
{
private sealed class StubEventAggregator : IEventAggregator
{
public void PublishEvent<TEvent>(TEvent @event)
where TEvent : class, IEvent
{
}
}

[OneTimeSetUp]
public void OneTimeSetUp()
{
if (TableMapping.Mapper.TableMap.Count == 0)
{
TableMapping.Map();
}
}

[TestCase(BookMediaType.Audiobook, new[] { 1 })]
[TestCase(BookMediaType.Ebook, new[] { 2 })]
public void book_list_editions_should_be_the_monitored_editions_of_the_requested_media_type(BookMediaType mediaType, int[] expectedEditionIds)
{
WithDatabase(database =>
{
var repository = new EditionRepository(database, new StubEventAggregator());

var editions = repository.GetMonitoredEditionsForBookList(mediaType);

Assert.That(editions.Select(e => e.Id).OrderBy(id => id), Is.EqualTo(expectedEditionIds));
});
}

[Test]
public void book_list_editions_without_media_type_should_include_both_media_types()
{
WithDatabase(database =>
{
var repository = new EditionRepository(database, new StubEventAggregator());

var editions = repository.GetMonitoredEditionsForBookList(null);

Assert.That(editions.Select(e => e.Id).OrderBy(id => id), Is.EqualTo(new[] { 1, 2 }));
});
}

[Test]
public void book_list_editions_should_not_load_chapters()
{
WithDatabase(database =>
{
var repository = new EditionRepository(database, new StubEventAggregator());

var edition = repository.GetMonitoredEditionsForBookList(BookMediaType.Audiobook).Single();

Assert.That(edition.Title, Is.EqualTo("Audiobook Edition"));
Assert.That(edition.BookId, Is.EqualTo(1));
Assert.That(edition.Monitored, Is.True);
Assert.That(edition.Chapters, Is.Null.Or.Empty);
});
}

[TestCase(BookMediaType.Audiobook, new[] { 1 })]
[TestCase(BookMediaType.Ebook, new[] { 2 })]
public void books_by_media_type_should_return_only_that_media_type(BookMediaType mediaType, int[] expectedBookIds)
{
WithDatabase(database =>
{
var repository = new BookRepository(database, new StubEventAggregator());

var books = repository.GetBooksByMediaType(mediaType);

Assert.That(books.Select(b => b.Id), Is.EqualTo(expectedBookIds));
});
}

private static void WithDatabase(Action<MainDatabase> action)
{
var databasePath = Path.Combine(TestContext.CurrentContext.WorkDirectory, $"book_list_queries_{Guid.NewGuid():N}.db");
var connectionString = new SqliteConnectionStringBuilder
{
DataSource = databasePath,
Mode = SqliteOpenMode.ReadWriteCreate
}.ToString();

try
{
using (var connection = new SqliteConnection(connectionString))
{
connection.Open();
CreateSchema<Author>(connection);
CreateSchema<Book>(connection);
CreateSchema<Edition>(connection);

connection.Execute("INSERT INTO \"Authors\" (\"Id\") VALUES (1);");
connection.Execute("INSERT INTO \"Books\" (\"Id\", \"AuthorId\", \"Title\", \"MediaType\") VALUES " +
"(1, 1, 'Foundation', 0), " +
"(2, 1, 'Foundation', 1);");
connection.Execute("INSERT INTO \"Editions\" (\"Id\", \"BookId\", \"Title\", \"Monitored\", \"Chapters\") VALUES " +
"(1, 1, 'Audiobook Edition', 1, '[{\"title\":\"Chapter 1\"}]'), " +
"(2, 2, 'Ebook Edition', 1, '[{\"title\":\"Chapter 1\"}]'), " +
"(3, 2, 'Unmonitored Ebook Edition', 0, NULL);");
}

var database = new Database("main", () =>
{
var conn = new SqliteConnection(connectionString);
conn.Open();
return conn;
});

action(new MainDatabase(database));
}
finally
{
try
{
if (File.Exists(databasePath))
{
File.Delete(databasePath);
}
}
catch
{
}
}
}

private static void CreateSchema<T>(SqliteConnection connection)
where T : ModelBase
{
var excluded = TableMapping.Mapper.ExcludeProperties(typeof(T))
.Select(property => property.Name)
.ToHashSet(StringComparer.OrdinalIgnoreCase);

var tableName = TableMapping.Mapper.TableNameMapping(typeof(T));
var columns = typeof(T)
.GetProperties()
.Where(property => property.Name == nameof(ModelBase.Id) || (property.IsMappableProperty() && !excluded.Contains(property.Name)))
.GroupBy(property => property.Name, StringComparer.OrdinalIgnoreCase)
.Select(group => group.First())
.Select(property => property.Name == nameof(ModelBase.Id)
? "\"Id\" INTEGER PRIMARY KEY AUTOINCREMENT"
: $"\"{property.Name}\" {GetSqliteColumnType(property)} NULL")
.ToList();

connection.Execute($"CREATE TABLE \"{tableName}\" ({string.Join(", ", columns)});");
}

private static string GetSqliteColumnType(System.Reflection.PropertyInfo property)
{
var type = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;

if (type.IsEnum || type == typeof(int) || type == typeof(long) || type == typeof(bool))
{
return "INTEGER";
}

return "TEXT";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public List<Edition> SetMonitored(Edition edition, bool isManualSelection = fals
public Edition GetEditionByGoogleBooksEditionId(string googleBooksEditionId) => throw new NotImplementedException();
public Edition GetEditionByProviderAndId(string providerPrefix, string providerId) => throw new NotImplementedException();
public System.Collections.Generic.List<Edition> GetEditionsByProviderAndId(string providerPrefix, string providerId) => new System.Collections.Generic.List<Edition>();
public List<Edition> GetAllMonitoredEditions()
public List<Edition> GetMonitoredEditionsForBookList(BookMediaType? mediaType)
{
LoadedAllMonitoredEditions = true;
return _editions.Where(e => e.Monitored).ToList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public List<Edition> GetEditionsByBook(IEnumerable<int> bookIds)
public Edition GetEditionByOpenLibraryEditionId(string openLibraryEditionId) => throw new NotImplementedException();
public Edition GetEditionByProviderAndId(string providerPrefix, string providerId) => throw new NotImplementedException();
public System.Collections.Generic.List<Edition> GetEditionsByProviderAndId(string providerPrefix, string providerId) => new System.Collections.Generic.List<Edition>();
public List<Edition> GetAllMonitoredEditions() => throw new NotImplementedException();
public List<Edition> GetMonitoredEditionsForBookList(BookMediaType? mediaType) => throw new NotImplementedException();
public void InsertMany(List<Edition> editions) => throw new NotImplementedException();
public void InsertMany(List<Edition> editions, IDbConnection connection, IDbTransaction transaction) => throw new NotImplementedException();
public void UpdateMany(List<Edition> editions) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.RootFolders;

namespace Chaptarr.Core.Test.Books
{
Expand Down Expand Up @@ -100,6 +101,11 @@ public IEnumerable<Book> FindExisting(IEnumerable<int> ids)
public void UpdateMonitoringByAuthorAndMediaType(int authorId, BookMediaType mediaType, bool monitored, IEnumerable<int> exceptBookIds = null) => throw new NotImplementedException();
public BookBucketResource GetBookBuckets(string sortKey, string sortDirection, bool includeUnmonitored = false, string mediaType = null, bool? downloaded = null) => throw new NotImplementedException();
public PagedBookResource GetBooksPaged(int offset, int pageSize, string sortKey, string sortDirection, bool includeUnmonitored = false, string mediaType = null, bool? downloaded = null) => throw new NotImplementedException();

public List<Book> GetBooksByMediaType(BookMediaType mediaType)
{
return _booksById.Values.Where(b => b.MediaType == mediaType).ToList();
}
}

private sealed class StubEditionService : IEditionService
Expand All @@ -126,7 +132,13 @@ public List<Edition> GetEditionsByBook(IEnumerable<int> bookIds)
public Edition GetEditionByOpenLibraryEditionId(string openLibraryEditionId) => throw new NotImplementedException();
public Edition GetEditionByProviderAndId(string providerPrefix, string providerId) => throw new NotImplementedException();
public System.Collections.Generic.List<Edition> GetEditionsByProviderAndId(string providerPrefix, string providerId) => new System.Collections.Generic.List<Edition>();
public List<Edition> GetAllMonitoredEditions() => throw new NotImplementedException();
public BookMediaType? BookListMediaType { get; private set; }

public List<Edition> GetMonitoredEditionsForBookList(BookMediaType? mediaType)
{
BookListMediaType = mediaType;
return _editions.Where(e => e.Monitored).ToList();
}
public void InsertMany(List<Edition> editions) => throw new NotImplementedException();
public void InsertMany(List<Edition> editions, IDbConnection connection, IDbTransaction transaction) => throw new NotImplementedException();
public void UpdateMany(List<Edition> editions) => throw new NotImplementedException();
Expand All @@ -140,6 +152,28 @@ public List<Edition> GetEditionsByBook(IEnumerable<int> bookIds)
public List<Edition> SetMonitored(Edition edition, bool isManualSelection = false) => throw new NotImplementedException();
}

private sealed class StubRootFolderService : IRootFolderService
{
private readonly List<RootFolder> _rootFolders;

public StubRootFolderService(params RootFolder[] rootFolders)
{
_rootFolders = rootFolders.ToList();
}

public List<RootFolder> All() => _rootFolders;
public List<RootFolder> AllWithSpaceStats() => throw new NotImplementedException();
public RootFolder Add(RootFolder rootFolder) => throw new NotImplementedException();
public RootFolder Update(RootFolder rootFolder) => throw new NotImplementedException();
public void Remove(int id) => throw new NotImplementedException();
public RootFolder Get(int id) => throw new NotImplementedException();
public List<RootFolder> AllForTag(int tagId) => throw new NotImplementedException();
public RootFolder GetBestRootFolder(string path) => throw new NotImplementedException();
public RootFolder GetBestRootFolder(string path, List<RootFolder> allRootFolders) => throw new NotImplementedException();
public string GetBestRootFolderPath(string path) => throw new NotImplementedException();
public string GetBestRootFolderPath(string path, List<RootFolder> allRootFolders) => throw new NotImplementedException();
}

private sealed class StubAuthorService : IAuthorService
{
private readonly Dictionary<int, Author> _authors;
Expand Down Expand Up @@ -350,5 +384,42 @@ public void should_batch_load_files_for_book_in_edition_iteration_order()
Assert.That(result.Editions[1].BookFiles.Select(file => file.Id), Is.EqualTo(new[] { 300 }));
Assert.That(result.Editions[2].BookFiles, Is.Null);
}

[TestCase("audiobook", BookMediaType.Audiobook, 1, 10)]
[TestCase("ebook", BookMediaType.Ebook, 2, 20)]
public void general_display_list_should_load_only_the_requested_media_type(string mediaType, BookMediaType expectedMediaType, int expectedBookId, int expectedEditionId)
{
var author = new Author { Id = 10, Name = "Isaac Asimov" };
var books = new[]
{
new Book { Id = 1, AuthorId = 10, Title = "Foundation", MediaType = BookMediaType.Audiobook },
new Book { Id = 2, AuthorId = 10, Title = "Foundation", MediaType = BookMediaType.Ebook }
};
var editions = new[]
{
new Edition { Id = 10, BookId = 1, Title = "Foundation", Monitored = true },
new Edition { Id = 20, BookId = 2, Title = "Foundation", Monitored = true },
new Edition { Id = 21, BookId = 2, Title = "Foundation", Monitored = false }
};
var editionService = new StubEditionService(editions);

// The stub repository throws from All(), so loading the whole library fails the test.
var service = new BookService(
new StubBookRepository(books),
editionService,
eventAggregator: null,
authorService: new StubAuthorService(new[] { author }),
mediaFileService: null,
rootFolderService: new StubRootFolderService(new RootFolder { FolderType = FolderType.Mixed }),
new StubSeriesBookLinkRepository(),
multiCopySeriesService: null,
logger: LogManager.GetCurrentClassLogger());

var results = service.GetBooksForDisplay(authorId: null, mediaType: mediaType);

Assert.That(results.Select(book => book.Id), Is.EqualTo(new[] { expectedBookId }));
Assert.That(results[0].Editions.Select(edition => edition.Id), Is.EqualTo(new[] { expectedEditionId }));
Assert.That(editionService.BookListMediaType, Is.EqualTo(expectedMediaType));
}
}
}
Loading
Loading