Testing more the base repository class

This commit is contained in:
Zoe Roux 2021-06-10 21:01:50 +02:00
parent 6cac991653
commit af39793b7c
8 changed files with 124 additions and 81 deletions

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -242,49 +241,13 @@ namespace Kyoo.Controllers
/// <param name="obj">The resource to delete</param> /// <param name="obj">The resource to delete</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception> /// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete([NotNull] T obj); Task Delete([NotNull] T obj);
/// <summary> /// <summary>
/// Delete a list of resources. /// Delete all resources that match the predicate.
/// </summary>
/// <param name="objs">One or multiple resources to delete</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(params T[] objs) => DeleteRange(objs.AsEnumerable());
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="objs">An enumerable of resources to delete</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(IEnumerable<T> objs);
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="ids">One or multiple resource's id</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(params int[] ids) => DeleteRange(ids.AsEnumerable());
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="ids">An enumerable of resource's id</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(IEnumerable<int> ids);
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="slugs">One or multiple resource's slug</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(params string[] slugs) => DeleteRange(slugs.AsEnumerable());
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="slugs">An enumerable of resource's slug</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(IEnumerable<string> slugs);
/// <summary>
/// Delete a list of resources.
/// </summary> /// </summary>
/// <param name="where">A predicate to filter resources to delete. Every resource that match this will be deleted.</param> /// <param name="where">A predicate to filter resources to delete. Every resource that match this will be deleted.</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception> /// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange([NotNull] Expression<Func<T, bool>> where); Task DeleteAll([NotNull] Expression<Func<T, bool>> where);
} }
/// <summary> /// <summary>

View File

@ -194,7 +194,7 @@ namespace Kyoo.CommonApi
{ {
try try
{ {
await _repository.DeleteRange(ApiHelper.ParseWhere<T>(where)); await _repository.DeleteAll(ApiHelper.ParseWhere<T>(where));
} }
catch (ItemNotFoundException) catch (ItemNotFoundException)
{ {

View File

@ -295,31 +295,10 @@ namespace Kyoo.Controllers
public abstract Task Delete(T obj); public abstract Task Delete(T obj);
/// <inheritdoc/> /// <inheritdoc/>
public virtual async Task DeleteRange(IEnumerable<T> objs) public async Task DeleteAll(Expression<Func<T, bool>> where)
{ {
foreach (T obj in objs) foreach (T resource in await GetAll(where))
await Delete(obj); await Delete(resource);
}
/// <inheritdoc/>
public virtual async Task DeleteRange(IEnumerable<int> ids)
{
foreach (int id in ids)
await Delete(id);
}
/// <inheritdoc/>
public virtual async Task DeleteRange(IEnumerable<string> slugs)
{
foreach (string slug in slugs)
await Delete(slug);
}
/// <inheritdoc/>
public async Task DeleteRange(Expression<Func<T, bool>> where)
{
ICollection<T> resources = await GetAll(where);
await DeleteRange(resources);
} }
} }
} }

View File

@ -25,8 +25,7 @@ namespace Kyoo.Tests
PeopleRepository people = new(_database, provider, PeopleRepository people = new(_database, provider,
new Lazy<IShowRepository>(() => LibraryManager.ShowRepository)); new Lazy<IShowRepository>(() => LibraryManager.ShowRepository));
ShowRepository show = new(_database, studio, people, genre, provider); ShowRepository show = new(_database, studio, people, genre, provider);
SeasonRepository season = new(_database, provider, show, SeasonRepository season = new(_database, provider, show);
new Lazy<IEpisodeRepository>(() => LibraryManager.EpisodeRepository));
LibraryItemRepository libraryItem = new(_database, LibraryItemRepository libraryItem = new(_database,
new Lazy<ILibraryRepository>(() => LibraryManager.LibraryRepository), new Lazy<ILibraryRepository>(() => LibraryManager.LibraryRepository),
new Lazy<IShowRepository>(() => LibraryManager.ShowRepository), new Lazy<IShowRepository>(() => LibraryManager.ShowRepository),

View File

@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Kyoo.Controllers; using Kyoo.Controllers;
using Kyoo.Models; using Kyoo.Models;
@ -8,7 +11,7 @@ using Xunit;
namespace Kyoo.Tests namespace Kyoo.Tests
{ {
public abstract class RepositoryTests<T> public abstract class RepositoryTests<T>
where T : class, IResource where T : class, IResource, new()
{ {
protected readonly RepositoryActivator Repositories; protected readonly RepositoryActivator Repositories;
private readonly IRepository<T> _repository; private readonly IRepository<T> _repository;
@ -86,5 +89,94 @@ namespace Kyoo.Tests
await _repository.Create(expected); await _repository.Create(expected);
KAssert.DeepEqual(expected, await _repository.Get(expected.Slug)); KAssert.DeepEqual(expected, await _repository.Get(expected.Slug));
} }
[Fact]
public async Task CreateNullTest()
{
await Assert.ThrowsAsync<ArgumentNullException>(() => _repository.Create(null!));
}
[Fact]
public async Task CreateIfNotExistNullTest()
{
await Assert.ThrowsAsync<ArgumentNullException>(() => _repository.CreateIfNotExists(null!));
}
[Fact]
public async Task CreateIfNotExistTest()
{
T expected = TestSample.Get<T>();
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(TestSample.Get<T>()));
await _repository.Delete(TestSample.Get<T>());
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(TestSample.Get<T>()));
}
[Fact]
public async Task EditNullTest()
{
await Assert.ThrowsAsync<ArgumentNullException>(() => _repository.Edit(null!, false));
}
[Fact]
public async Task EditNonExistingTest()
{
await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Edit(new T {ID = 56}, false));
}
[Fact]
public async Task GetExpressionIDTest()
{
KAssert.DeepEqual(TestSample.Get<T>(), await _repository.Get(x => x.ID == TestSample.Get<T>().ID));
}
[Fact]
public async Task GetExpressionSlugTest()
{
KAssert.DeepEqual(TestSample.Get<T>(), await _repository.Get(x => x.Slug == TestSample.Get<T>().Slug));
}
[Fact]
public async Task GetExpressionNotFoundTest()
{
await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Get(x => x.Slug == "non-existing"));
}
[Fact]
public async Task GetExpressionNullTest()
{
await Assert.ThrowsAsync<ArgumentNullException>(() => _repository.Get((Expression<Func<T, bool>>)null!));
}
[Fact]
public async Task GetOrDefaultTest()
{
Assert.Null(await _repository.GetOrDefault(56));
Assert.Null(await _repository.GetOrDefault("non-existing"));
Assert.Null(await _repository.GetOrDefault(x => x.Slug == "non-existing"));
}
[Fact]
public async Task GetCountWithFilterTest()
{
string slug = TestSample.Get<T>().Slug[2..4];
Assert.Equal(1, await _repository.GetCount(x => x.Slug.Contains(slug)));
}
[Fact]
public async Task GetAllTest()
{
string slug = TestSample.Get<T>().Slug[2..4];
ICollection<T> ret = await _repository.GetAll(x => x.Slug.Contains(slug));
Assert.Equal(1, ret.Count);
KAssert.DeepEqual(TestSample.Get<T>(), ret.First());
}
[Fact]
public async Task DeleteAllTest()
{
string slug = TestSample.Get<T>().Slug[2..4];
await _repository.DeleteAll(x => x.Slug.Contains(slug));
Assert.Equal(0, await _repository.GetCount());
}
} }
} }

View File

@ -216,5 +216,22 @@ namespace Kyoo.Tests.SpecificTests
Show created = await _repository.Create(expected); Show created = await _repository.Create(expected);
KAssert.DeepEqual(expected, created); KAssert.DeepEqual(expected, created);
} }
[Fact]
public async Task SlugDuplicationTest()
{
Show test = TestSample.Get<Show>();
test.ID = 0;
test.Slug = "300";
Show created = await _repository.Create(test);
Assert.Equal("300!", created.Slug);
}
[Fact]
public async Task GetSlugTest()
{
Show reference = TestSample.Get<Show>();
Assert.Equal(reference.Slug, await _repository.GetSlug(reference.ID));
}
} }
} }

View File

@ -195,7 +195,7 @@ namespace Kyoo.Controllers
if (changed.Tracks != null || resetOld) if (changed.Tracks != null || resetOld)
{ {
await _tracks.DeleteRange(x => x.EpisodeID == resource.ID); await _tracks.DeleteAll(x => x.EpisodeID == resource.ID);
resource.Tracks = changed.Tracks; resource.Tracks = changed.Tracks;
await ValidateTracks(resource); await ValidateTracks(resource);
} }

View File

@ -27,11 +27,7 @@ namespace Kyoo.Controllers
/// A show repository to get show's slug from their ID and keep the slug in each episode. /// A show repository to get show's slug from their ID and keep the slug in each episode.
/// </summary> /// </summary>
private readonly IShowRepository _shows; private readonly IShowRepository _shows;
/// <summary>
/// A lazilly loaded episode repository to handle deletion of episodes with the season.
/// </summary>
private readonly Lazy<IEpisodeRepository> _episodes;
/// <inheritdoc/> /// <inheritdoc/>
protected override Expression<Func<Season, object>> DefaultSort => x => x.SeasonNumber; protected override Expression<Func<Season, object>> DefaultSort => x => x.SeasonNumber;
@ -43,17 +39,14 @@ namespace Kyoo.Controllers
/// <param name="database">The database handle that will be used</param> /// <param name="database">The database handle that will be used</param>
/// <param name="providers">A provider repository</param> /// <param name="providers">A provider repository</param>
/// <param name="shows">A show repository</param> /// <param name="shows">A show repository</param>
/// <param name="episodes">A lazy loaded episode repository.</param>
public SeasonRepository(DatabaseContext database, public SeasonRepository(DatabaseContext database,
IProviderRepository providers, IProviderRepository providers,
IShowRepository shows, IShowRepository shows)
Lazy<IEpisodeRepository> episodes)
: base(database) : base(database)
{ {
_database = database; _database = database;
_providers = providers; _providers = providers;
_shows = shows; _shows = shows;
_episodes = episodes;
} }
@ -186,9 +179,9 @@ namespace Kyoo.Controllers
_database.Entry(obj).State = EntityState.Deleted; _database.Entry(obj).State = EntityState.Deleted;
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted); obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
//
if (obj.Episodes != null) // if (obj.Episodes != null)
await _episodes.Value.DeleteRange(obj.Episodes); // await _episodes.Value.DeleteRange(obj.Episodes);
} }
} }
} }