mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 04:04:21 -04:00
Fix tests compilation errors
This commit is contained in:
parent
93b36f1bd4
commit
5446dbce83
@ -389,11 +389,23 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// Edit a resource
|
/// Edit a resource
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The resource to edit, it's ID can't change.</param>
|
/// <param name="item">The resource to edit, it's ID can't change.</param>
|
||||||
/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
|
|
||||||
/// <typeparam name="T">The type of resources</typeparam>
|
/// <typeparam name="T">The type of resources</typeparam>
|
||||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||||
/// <returns>The resource edited and completed by database's information (related items and so on)</returns>
|
/// <returns>The resource edited and completed by database's information (related items and so on)</returns>
|
||||||
Task<T> Edit<T>(T item, bool resetOld)
|
Task<T> Edit<T>(T item)
|
||||||
|
where T : class, IResource;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Edit only specific properties of a resource
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The id of the resource to edit</param>
|
||||||
|
/// <param name="patch">
|
||||||
|
/// A method that will be called when you need to update every properties that you want to
|
||||||
|
/// persist. It can return false to abort the process via an ArgumentException
|
||||||
|
/// </param>
|
||||||
|
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||||
|
/// <returns>The resource edited and completed by database's information (related items and so on)</returns>
|
||||||
|
Task<T> Patch<T>(int id, Func<T, Task<bool>> patch)
|
||||||
where T : class, IResource;
|
where T : class, IResource;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -129,13 +129,24 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
event ResourceEventHandler OnCreated;
|
event ResourceEventHandler OnCreated;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Edit a resource
|
/// Edit a resource and replace every property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="edited">The resource to edit, it's ID can't change.</param>
|
/// <param name="edited">The resource to edit, it's ID can't change.</param>
|
||||||
/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
|
|
||||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||||
/// <returns>The resource edited and completed by database's information (related items and so on)</returns>
|
/// <returns>The resource edited and completed by database's information (related items and so on)</returns>
|
||||||
Task<T> Edit(T edited, bool resetOld);
|
Task<T> Edit(T edited);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Edit only specific properties of a resource
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The id of the resource to edit</param>
|
||||||
|
/// <param name="patch">
|
||||||
|
/// A method that will be called when you need to update every properties that you want to
|
||||||
|
/// persist. It can return false to abort the process via an ArgumentException
|
||||||
|
/// </param>
|
||||||
|
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||||
|
/// <returns>The resource edited and completed by database's information (related items and so on)</returns>
|
||||||
|
Task<T> Patch(int id, Func<T, Task<bool>> patch);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when a resource has been edited.
|
/// Called when a resource has been edited.
|
||||||
|
@ -16,33 +16,11 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
namespace Kyoo.Models;
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Xunit;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace Kyoo.Tests.Database
|
public class PartialResource
|
||||||
{
|
{
|
||||||
namespace PostgreSQL
|
public int? Id { get; set; }
|
||||||
{
|
|
||||||
[Collection(nameof(Postgresql))]
|
|
||||||
public class GenreTests : AGenreTests
|
|
||||||
{
|
|
||||||
public GenreTests(PostgresFixture postgres, ITestOutputHelper output)
|
|
||||||
: base(new RepositoryActivator(output, postgres)) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class AGenreTests : RepositoryTests<Genre>
|
public string? Slug { get; set; }
|
||||||
{
|
|
||||||
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
|
|
||||||
private readonly IGenreRepository _repository;
|
|
||||||
|
|
||||||
protected AGenreTests(RepositoryActivator repositories)
|
|
||||||
: base(repositories)
|
|
||||||
{
|
|
||||||
_repository = Repositories.LibraryManager.GenreRepository;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -43,7 +43,7 @@ namespace Kyoo.Abstractions.Models
|
|||||||
{
|
{
|
||||||
if (ShowSlug != null || Show?.Slug != null)
|
if (ShowSlug != null || Show?.Slug != null)
|
||||||
return GetSlug(ShowSlug ?? Show.Slug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
return GetSlug(ShowSlug ?? Show.Slug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||||
return GetSlug(ShowID.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
return GetSlug(ShowId.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
@ -81,17 +81,17 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ID of the Show containing this episode.
|
/// The ID of the Show containing this episode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializeIgnore] public int ShowID { get; set; }
|
[SerializeIgnore] public int ShowId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The show that contains this episode. This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
/// The show that contains this episode. This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[LoadableRelation(nameof(ShowID))] public Show? Show { get; set; }
|
[LoadableRelation(nameof(ShowId))] public Show? Show { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ID of the Season containing this episode.
|
/// The ID of the Season containing this episode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializeIgnore] public int? SeasonID { get; set; }
|
[SerializeIgnore] public int? SeasonId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The season that contains this episode.
|
/// The season that contains this episode.
|
||||||
@ -101,7 +101,7 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// This can be null if the season is unknown and the episode is only identified
|
/// This can be null if the season is unknown and the episode is only identified
|
||||||
/// by it's <see cref="AbsoluteNumber"/>.
|
/// by it's <see cref="AbsoluteNumber"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[LoadableRelation(nameof(SeasonID))] public Season? Season { get; set; }
|
[LoadableRelation(nameof(SeasonId))] public Season? Season { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The season in witch this episode is in.
|
/// The season in witch this episode is in.
|
||||||
|
@ -42,7 +42,7 @@ namespace Kyoo.Abstractions.Models
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (ShowSlug == null && Show == null)
|
if (ShowSlug == null && Show == null)
|
||||||
return $"{ShowID}-s{SeasonNumber}";
|
return $"{ShowId}-s{SeasonNumber}";
|
||||||
return $"{ShowSlug ?? Show?.Slug}-s{SeasonNumber}";
|
return $"{ShowSlug ?? Show?.Slug}-s{SeasonNumber}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,13 +67,13 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ID of the Show containing this season.
|
/// The ID of the Show containing this season.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializeIgnore] public int ShowID { get; set; }
|
[SerializeIgnore] public int ShowId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The show that contains this season.
|
/// The show that contains this season.
|
||||||
/// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
/// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[LoadableRelation(nameof(ShowID))] public Show? Show { get; set; }
|
[LoadableRelation(nameof(ShowId))] public Show? Show { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of this season. This can be set to 0 to indicate specials.
|
/// The number of this season. This can be set to 0 to indicate specials.
|
||||||
|
@ -51,7 +51,7 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The list of alternative titles of this show.
|
/// The list of alternative titles of this show.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] Aliases { get; set; } = Array.Empty<string>();
|
public List<string> Aliases { get; set; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The summary of this show.
|
/// The summary of this show.
|
||||||
@ -61,12 +61,12 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of tags that match this movie.
|
/// A list of tags that match this movie.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] Tags { get; set; } = Array.Empty<string>();
|
public List<string> Tags { get; set; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The list of genres (themes) this show has.
|
/// The list of genres (themes) this show has.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Genre[] Genres { get; set; } = Array.Empty<Genre>();
|
public List<Genre> Genres { get; set; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is this show airing, not aired yet or finished?
|
/// Is this show airing, not aired yet or finished?
|
||||||
@ -106,13 +106,13 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ID of the Studio that made this show.
|
/// The ID of the Studio that made this show.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializeIgnore] public int? StudioID { get; set; }
|
[SerializeIgnore] public int? StudioId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Studio that made this show.
|
/// The Studio that made this show.
|
||||||
/// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
/// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[LoadableRelation(nameof(StudioID))][EditableRelation] public Studio? Studio { get; set; }
|
[LoadableRelation(nameof(StudioId))][EditableRelation] public Studio? Studio { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The list of people that made this show.
|
/// The list of people that made this show.
|
||||||
|
@ -17,9 +17,7 @@
|
|||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace Kyoo.Utils
|
namespace Kyoo.Utils
|
||||||
@ -29,125 +27,6 @@ namespace Kyoo.Utils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class EnumerableExtensions
|
public static class EnumerableExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// A Select where the index of the item can be used.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The IEnumerable to map. If self is null, an empty list is returned</param>
|
|
||||||
/// <param name="mapper">The function that will map each items</param>
|
|
||||||
/// <typeparam name="T">The type of items in <paramref name="self"/></typeparam>
|
|
||||||
/// <typeparam name="T2">The type of items in the returned list</typeparam>
|
|
||||||
/// <returns>The list mapped.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The list or the mapper can't be null</exception>
|
|
||||||
[LinqTunnel]
|
|
||||||
public static IEnumerable<T2> Map<T, T2>(this IEnumerable<T> self,
|
|
||||||
Func<T, int, T2> mapper)
|
|
||||||
{
|
|
||||||
if (self == null)
|
|
||||||
throw new ArgumentNullException(nameof(self));
|
|
||||||
if (mapper == null)
|
|
||||||
throw new ArgumentNullException(nameof(mapper));
|
|
||||||
|
|
||||||
static IEnumerable<T2> Generator(IEnumerable<T> self, Func<T, int, T2> mapper)
|
|
||||||
{
|
|
||||||
using IEnumerator<T> enumerator = self.GetEnumerator();
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
while (enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
yield return mapper(enumerator.Current, index);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Generator(self, mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A map where the mapping function is asynchronous.
|
|
||||||
/// Note: <see cref="SelectAsync{T,T2}"/> might interest you.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The IEnumerable to map.</param>
|
|
||||||
/// <param name="mapper">The asynchronous function that will map each items.</param>
|
|
||||||
/// <typeparam name="T">The type of items in <paramref name="self"/>.</typeparam>
|
|
||||||
/// <typeparam name="T2">The type of items in the returned list.</typeparam>
|
|
||||||
/// <returns>The list mapped as an AsyncEnumerable.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The list or the mapper can't be null.</exception>
|
|
||||||
[LinqTunnel]
|
|
||||||
public static IAsyncEnumerable<T2> MapAsync<T, T2>(this IEnumerable<T> self,
|
|
||||||
Func<T, int, Task<T2>> mapper)
|
|
||||||
{
|
|
||||||
if (self == null)
|
|
||||||
throw new ArgumentNullException(nameof(self));
|
|
||||||
if (mapper == null)
|
|
||||||
throw new ArgumentNullException(nameof(mapper));
|
|
||||||
|
|
||||||
static async IAsyncEnumerable<T2> Generator(IEnumerable<T> self, Func<T, int, Task<T2>> mapper)
|
|
||||||
{
|
|
||||||
using IEnumerator<T> enumerator = self.GetEnumerator();
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
while (enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
yield return await mapper(enumerator.Current, index);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Generator(self, mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An asynchronous version of Select.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The IEnumerable to map</param>
|
|
||||||
/// <param name="mapper">The asynchronous function that will map each items</param>
|
|
||||||
/// <typeparam name="T">The type of items in <paramref name="self"/></typeparam>
|
|
||||||
/// <typeparam name="T2">The type of items in the returned list</typeparam>
|
|
||||||
/// <returns>The list mapped as an AsyncEnumerable</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The list or the mapper can't be null</exception>
|
|
||||||
[LinqTunnel]
|
|
||||||
public static IAsyncEnumerable<T2> SelectAsync<T, T2>(this IEnumerable<T> self,
|
|
||||||
Func<T, Task<T2>> mapper)
|
|
||||||
{
|
|
||||||
if (self == null)
|
|
||||||
throw new ArgumentNullException(nameof(self));
|
|
||||||
if (mapper == null)
|
|
||||||
throw new ArgumentNullException(nameof(mapper));
|
|
||||||
|
|
||||||
static async IAsyncEnumerable<T2> Generator(IEnumerable<T> self, Func<T, Task<T2>> mapper)
|
|
||||||
{
|
|
||||||
using IEnumerator<T> enumerator = self.GetEnumerator();
|
|
||||||
|
|
||||||
while (enumerator.MoveNext())
|
|
||||||
yield return await mapper(enumerator.Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Generator(self, mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert an AsyncEnumerable to a List by waiting for every item.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The async list</param>
|
|
||||||
/// <typeparam name="T">The type of items in the async list and in the returned list.</typeparam>
|
|
||||||
/// <returns>A task that will return a simple list</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The list can't be null</exception>
|
|
||||||
[LinqTunnel]
|
|
||||||
public static Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> self)
|
|
||||||
{
|
|
||||||
if (self == null)
|
|
||||||
throw new ArgumentNullException(nameof(self));
|
|
||||||
|
|
||||||
static async Task<List<T>> ToList(IAsyncEnumerable<T> self)
|
|
||||||
{
|
|
||||||
List<T> ret = new();
|
|
||||||
await foreach (T i in self)
|
|
||||||
ret.Add(i);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ToList(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the enumerable is empty, execute an action.
|
/// If the enumerable is empty, execute an action.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -197,104 +76,5 @@ namespace Kyoo.Utils
|
|||||||
foreach (T i in self)
|
foreach (T i in self)
|
||||||
action(i);
|
action(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A foreach used as a function with a little specificity: the list can be null.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The list to enumerate. If this is null, the function result in a no-op</param>
|
|
||||||
/// <param name="action">The action to execute for each arguments</param>
|
|
||||||
public static void ForEach(this IEnumerable? self, Action<object> action)
|
|
||||||
{
|
|
||||||
if (self == null)
|
|
||||||
return;
|
|
||||||
foreach (object i in self)
|
|
||||||
action(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A foreach used as a function with a little specificity: the list can be null.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The list to enumerate. If this is null, the function result in a no-op</param>
|
|
||||||
/// <param name="action">The action to execute for each arguments</param>
|
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
|
||||||
public static async Task ForEachAsync(this IEnumerable? self, Func<object, Task> action)
|
|
||||||
{
|
|
||||||
if (self == null)
|
|
||||||
return;
|
|
||||||
foreach (object i in self)
|
|
||||||
await action(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A foreach used as a function with a little specificity: the list can be null.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The list to enumerate. If this is null, the function result in a no-op</param>
|
|
||||||
/// <param name="action">The asynchronous action to execute for each arguments</param>
|
|
||||||
/// <typeparam name="T">The type of items in the list.</typeparam>
|
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
|
||||||
public static async Task ForEachAsync<T>(this IEnumerable<T>? self, Func<T, Task> action)
|
|
||||||
{
|
|
||||||
if (self == null)
|
|
||||||
return;
|
|
||||||
foreach (T i in self)
|
|
||||||
await action(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A foreach used as a function with a little specificity: the list can be null.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The async list to enumerate. If this is null, the function result in a no-op</param>
|
|
||||||
/// <param name="action">The action to execute for each arguments</param>
|
|
||||||
/// <typeparam name="T">The type of items in the list.</typeparam>
|
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
|
||||||
public static async Task ForEachAsync<T>(this IAsyncEnumerable<T>? self, Action<T> action)
|
|
||||||
{
|
|
||||||
if (self == null)
|
|
||||||
return;
|
|
||||||
await foreach (T i in self)
|
|
||||||
action(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Split a list in a small chunk of data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="list">The list to split</param>
|
|
||||||
/// <param name="countPerList">The number of items in each chunk</param>
|
|
||||||
/// <typeparam name="T">The type of data in the initial list.</typeparam>
|
|
||||||
/// <returns>A list of chunks</returns>
|
|
||||||
[LinqTunnel]
|
|
||||||
public static IEnumerable<List<T>> BatchBy<T>(this List<T> list, int countPerList)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < list.Count; i += countPerList)
|
|
||||||
yield return list.GetRange(i, Math.Min(list.Count - i, countPerList));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Split a list in a small chunk of data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="list">The list to split</param>
|
|
||||||
/// <param name="countPerList">The number of items in each chunk</param>
|
|
||||||
/// <typeparam name="T">The type of data in the initial list.</typeparam>
|
|
||||||
/// <returns>A list of chunks</returns>
|
|
||||||
[LinqTunnel]
|
|
||||||
public static IEnumerable<T[]> BatchBy<T>(this IEnumerable<T> list, int countPerList)
|
|
||||||
{
|
|
||||||
T[] ret = new T[countPerList];
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
using IEnumerator<T> enumerator = list.GetEnumerator();
|
|
||||||
while (enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
ret[i] = enumerator.Current;
|
|
||||||
i++;
|
|
||||||
if (i < countPerList)
|
|
||||||
continue;
|
|
||||||
i = 0;
|
|
||||||
yield return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array.Resize(ref ret, i);
|
|
||||||
yield return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,10 @@
|
|||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Utils
|
namespace Kyoo.Utils
|
||||||
@ -33,99 +30,9 @@ namespace Kyoo.Utils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Merger
|
public static class Merger
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Merge two lists, can keep duplicates or remove them.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="first">The first enumerable to merge</param>
|
|
||||||
/// <param name="second">The second enumerable to merge, if items from this list are equals to one from the first, they are not kept</param>
|
|
||||||
/// <param name="isEqual">Equality function to compare items. If this is null, duplicated elements are kept</param>
|
|
||||||
/// <typeparam name="T">The type of items in the lists to merge.</typeparam>
|
|
||||||
/// <returns>The two list merged as an array</returns>
|
|
||||||
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
|
||||||
public static T[] MergeLists<T>(IEnumerable<T>? first,
|
|
||||||
IEnumerable<T>? second,
|
|
||||||
Func<T, T, bool>? isEqual = null)
|
|
||||||
{
|
|
||||||
if (first == null)
|
|
||||||
return second?.ToArray();
|
|
||||||
if (second == null)
|
|
||||||
return first.ToArray();
|
|
||||||
if (isEqual == null)
|
|
||||||
return first.Concat(second).ToArray();
|
|
||||||
List<T> list = first.ToList();
|
|
||||||
return list.Concat(second.Where(x => !list.Any(y => isEqual(x, y)))).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Merge two dictionary, if the same key is found on both dictionary, the values of the first one is kept.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="first">The first dictionary to merge</param>
|
|
||||||
/// <param name="second">The second dictionary to merge</param>
|
|
||||||
/// <typeparam name="T">The type of the keys in dictionaries</typeparam>
|
|
||||||
/// <typeparam name="T2">The type of values in the dictionaries</typeparam>
|
|
||||||
/// <returns>The first dictionary with the missing elements of <paramref name="second"/>.</returns>
|
|
||||||
/// <seealso cref="MergeDictionaries{T,T2}(System.Collections.Generic.IDictionary{T,T2},System.Collections.Generic.IDictionary{T,T2},out bool)"/>
|
|
||||||
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
|
||||||
public static IDictionary<T, T2> MergeDictionaries<T, T2>(IDictionary<T, T2>? first,
|
|
||||||
IDictionary<T, T2>? second)
|
|
||||||
{
|
|
||||||
return MergeDictionaries(first, second, out bool _);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Merge two dictionary, if the same key is found on both dictionary, the values of the first one is kept.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="first">The first dictionary to merge</param>
|
|
||||||
/// <param name="second">The second dictionary to merge</param>
|
|
||||||
/// <param name="hasChanged">
|
|
||||||
/// <c>true</c> if a new items has been added to the dictionary, <c>false</c> otherwise.
|
|
||||||
/// </param>
|
|
||||||
/// <typeparam name="T">The type of the keys in dictionaries</typeparam>
|
|
||||||
/// <typeparam name="T2">The type of values in the dictionaries</typeparam>
|
|
||||||
/// <returns>The first dictionary with the missing elements of <paramref name="second"/>.</returns>
|
|
||||||
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
|
||||||
public static IDictionary<T, T2> MergeDictionaries<T, T2>(IDictionary<T, T2>? first,
|
|
||||||
IDictionary<T, T2>? second,
|
|
||||||
out bool hasChanged)
|
|
||||||
{
|
|
||||||
if (first == null)
|
|
||||||
{
|
|
||||||
hasChanged = true;
|
|
||||||
return second;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasChanged = false;
|
|
||||||
if (second == null)
|
|
||||||
return first;
|
|
||||||
foreach ((T key, T2 value) in second)
|
|
||||||
{
|
|
||||||
bool success = first.TryAdd(key, value);
|
|
||||||
hasChanged |= success;
|
|
||||||
|
|
||||||
if (success || first[key]?.Equals(default) == false || value?.Equals(default) != false)
|
|
||||||
continue;
|
|
||||||
first[key] = value;
|
|
||||||
hasChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Merge two dictionary, if the same key is found on both dictionary, the values of the second one is kept.
|
/// Merge two dictionary, if the same key is found on both dictionary, the values of the second one is kept.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
|
||||||
/// The only difference in this function compared to
|
|
||||||
/// <see cref="MergeDictionaries{T,T2}(System.Collections.Generic.IDictionary{T,T2},System.Collections.Generic.IDictionary{T,T2}, out bool)"/>
|
|
||||||
/// is the way <paramref name="hasChanged"/> is calculated and the order of the arguments.
|
|
||||||
/// <code>
|
|
||||||
/// MergeDictionaries(first, second);
|
|
||||||
/// </code>
|
|
||||||
/// will do the same thing as
|
|
||||||
/// <code>
|
|
||||||
/// CompleteDictionaries(second, first, out bool _);
|
|
||||||
/// </code>
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="first">The first dictionary to merge</param>
|
/// <param name="first">The first dictionary to merge</param>
|
||||||
/// <param name="second">The second dictionary to merge</param>
|
/// <param name="second">The second dictionary to merge</param>
|
||||||
/// <param name="hasChanged">
|
/// <param name="hasChanged">
|
||||||
@ -138,7 +45,7 @@ namespace Kyoo.Utils
|
|||||||
/// set to those of <paramref name="first"/>.
|
/// set to those of <paramref name="first"/>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
||||||
public static IDictionary<T, T2> CompleteDictionaries<T, T2>(IDictionary<T, T2>? first,
|
public static IDictionary<T, T2>? CompleteDictionaries<T, T2>(IDictionary<T, T2>? first,
|
||||||
IDictionary<T, T2>? second,
|
IDictionary<T, T2>? second,
|
||||||
out bool hasChanged)
|
out bool hasChanged)
|
||||||
{
|
{
|
||||||
@ -160,14 +67,8 @@ namespace Kyoo.Utils
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set every non-default values of seconds to the corresponding property of second.
|
/// Set every non-default values of seconds to the corresponding property of second.
|
||||||
/// Dictionaries are handled like anonymous objects with a property per key/pair value
|
/// Dictionaries are handled like anonymous objects with a property per key/pair value
|
||||||
/// (see
|
|
||||||
/// <see cref="MergeDictionaries{T,T2}(System.Collections.Generic.IDictionary{T,T2},System.Collections.Generic.IDictionary{T,T2})"/>
|
|
||||||
/// for more details).
|
|
||||||
/// At the end, the OnMerge method of first will be called if first is a <see cref="IOnMerge"/>
|
/// At the end, the OnMerge method of first will be called if first is a <see cref="IOnMerge"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
|
||||||
/// This does the opposite of <see cref="Merge{T}"/>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <example>
|
/// <example>
|
||||||
/// {id: 0, slug: "test"}, {id: 4, slug: "foo"} -> {id: 4, slug: "foo"}
|
/// {id: 0, slug: "test"}, {id: 4, slug: "foo"} -> {id: 4, slug: "foo"}
|
||||||
/// </example>
|
/// </example>
|
||||||
@ -182,19 +83,16 @@ namespace Kyoo.Utils
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <typeparam name="T">Fields of T will be completed</typeparam>
|
/// <typeparam name="T">Fields of T will be completed</typeparam>
|
||||||
/// <returns><paramref name="first"/></returns>
|
/// <returns><paramref name="first"/></returns>
|
||||||
/// <exception cref="ArgumentNullException">If first is null</exception>
|
public static T Complete<T>(T first,
|
||||||
public static T Complete<T>([NotNull] T first,
|
|
||||||
T? second,
|
T? second,
|
||||||
[InstantHandle] Func<PropertyInfo, bool>? where = null)
|
[InstantHandle] Func<PropertyInfo, bool>? where = null)
|
||||||
{
|
{
|
||||||
if (first == null)
|
|
||||||
throw new ArgumentNullException(nameof(first));
|
|
||||||
if (second == null)
|
if (second == null)
|
||||||
return first;
|
return first;
|
||||||
|
|
||||||
Type type = typeof(T);
|
Type type = typeof(T);
|
||||||
IEnumerable<PropertyInfo> properties = type.GetProperties()
|
IEnumerable<PropertyInfo> properties = type.GetProperties()
|
||||||
.Where(x => x.CanRead && x.CanWrite
|
.Where(x => x is { CanRead: true, CanWrite: true }
|
||||||
&& Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
|
&& Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
|
||||||
|
|
||||||
if (where != null)
|
if (where != null)
|
||||||
@ -202,17 +100,16 @@ namespace Kyoo.Utils
|
|||||||
|
|
||||||
foreach (PropertyInfo property in properties)
|
foreach (PropertyInfo property in properties)
|
||||||
{
|
{
|
||||||
object value = property.GetValue(second);
|
object? value = property.GetValue(second);
|
||||||
object defaultValue = property.GetCustomAttribute<DefaultValueAttribute>()?.Value
|
|
||||||
?? property.PropertyType.GetClrDefault();
|
|
||||||
|
|
||||||
if (value?.Equals(defaultValue) != false || value.Equals(property.GetValue(first)))
|
if (value?.Equals(property.GetValue(first)) == true)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (Utility.IsOfGenericType(property.PropertyType, typeof(IDictionary<,>)))
|
if (Utility.IsOfGenericType(property.PropertyType, typeof(IDictionary<,>)))
|
||||||
{
|
{
|
||||||
Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
|
Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
|
||||||
.GenericTypeArguments;
|
.GenericTypeArguments;
|
||||||
object[] parameters =
|
object?[] parameters =
|
||||||
{
|
{
|
||||||
property.GetValue(first),
|
property.GetValue(first),
|
||||||
value,
|
value,
|
||||||
@ -222,8 +119,8 @@ namespace Kyoo.Utils
|
|||||||
typeof(Merger),
|
typeof(Merger),
|
||||||
nameof(CompleteDictionaries),
|
nameof(CompleteDictionaries),
|
||||||
dictionaryTypes,
|
dictionaryTypes,
|
||||||
parameters);
|
parameters)!;
|
||||||
if ((bool)parameters[2])
|
if ((bool)parameters[2]!)
|
||||||
property.SetValue(first, newDictionary);
|
property.SetValue(first, newDictionary);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -234,109 +131,5 @@ namespace Kyoo.Utils
|
|||||||
merge.OnMerge(second);
|
merge.OnMerge(second);
|
||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This will set missing values of <paramref name="first"/> to the corresponding values of <paramref name="second"/>.
|
|
||||||
/// Enumerable will be merged (concatenated) and Dictionaries too.
|
|
||||||
/// At the end, the OnMerge method of first will be called if first is a <see cref="IOnMerge"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <example>
|
|
||||||
/// {id: 0, slug: "test"}, {id: 4, slug: "foo"} -> {id: 4, slug: "test"}
|
|
||||||
/// </example>
|
|
||||||
/// <param name="first">
|
|
||||||
/// The object to complete
|
|
||||||
/// </param>
|
|
||||||
/// <param name="second">
|
|
||||||
/// Missing fields of first will be completed by fields of this item. If second is null, the function no-op.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="where">
|
|
||||||
/// Filter fields that will be merged
|
|
||||||
/// </param>
|
|
||||||
/// <typeparam name="T">Fields of T will be merged</typeparam>
|
|
||||||
/// <returns><paramref name="first"/></returns>
|
|
||||||
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
|
||||||
public static T Merge<T>(T? first,
|
|
||||||
T? second,
|
|
||||||
[InstantHandle] Func<PropertyInfo, bool>? where = null)
|
|
||||||
{
|
|
||||||
if (first == null)
|
|
||||||
return second;
|
|
||||||
if (second == null)
|
|
||||||
return first;
|
|
||||||
|
|
||||||
Type type = typeof(T);
|
|
||||||
IEnumerable<PropertyInfo> properties = type.GetProperties()
|
|
||||||
.Where(x => x.CanRead && x.CanWrite
|
|
||||||
&& Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
|
|
||||||
|
|
||||||
if (where != null)
|
|
||||||
properties = properties.Where(where);
|
|
||||||
|
|
||||||
foreach (PropertyInfo property in properties)
|
|
||||||
{
|
|
||||||
object oldValue = property.GetValue(first);
|
|
||||||
object newValue = property.GetValue(second);
|
|
||||||
object defaultValue = property.PropertyType.GetClrDefault();
|
|
||||||
|
|
||||||
if (oldValue?.Equals(defaultValue) != false)
|
|
||||||
property.SetValue(first, newValue);
|
|
||||||
else if (Utility.IsOfGenericType(property.PropertyType, typeof(IDictionary<,>)))
|
|
||||||
{
|
|
||||||
Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
|
|
||||||
.GenericTypeArguments;
|
|
||||||
object[] parameters =
|
|
||||||
{
|
|
||||||
oldValue,
|
|
||||||
newValue,
|
|
||||||
false
|
|
||||||
};
|
|
||||||
object newDictionary = Utility.RunGenericMethod<object>(
|
|
||||||
typeof(Merger),
|
|
||||||
nameof(MergeDictionaries),
|
|
||||||
dictionaryTypes,
|
|
||||||
parameters);
|
|
||||||
if ((bool)parameters[2])
|
|
||||||
property.SetValue(first, newDictionary);
|
|
||||||
}
|
|
||||||
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType)
|
|
||||||
&& property.PropertyType != typeof(string))
|
|
||||||
{
|
|
||||||
Type enumerableType = Utility.GetGenericDefinition(property.PropertyType, typeof(IEnumerable<>))
|
|
||||||
.GenericTypeArguments
|
|
||||||
.First();
|
|
||||||
Func<IResource, IResource, bool> equalityComparer = enumerableType.IsAssignableTo(typeof(IResource))
|
|
||||||
? (x, y) => x.Slug == y.Slug
|
|
||||||
: null;
|
|
||||||
property.SetValue(first, Utility.RunGenericMethod<object>(
|
|
||||||
typeof(Merger),
|
|
||||||
nameof(MergeLists),
|
|
||||||
enumerableType,
|
|
||||||
oldValue, newValue, equalityComparer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first is IOnMerge merge)
|
|
||||||
merge.OnMerge(second);
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set every fields of <paramref name="obj"/> to the default value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">The object to nullify</param>
|
|
||||||
/// <typeparam name="T">Fields of T will be nullified</typeparam>
|
|
||||||
/// <returns><paramref name="obj"/></returns>
|
|
||||||
public static T Nullify<T>(T obj)
|
|
||||||
{
|
|
||||||
Type type = typeof(T);
|
|
||||||
foreach (PropertyInfo property in type.GetProperties())
|
|
||||||
{
|
|
||||||
if (!property.CanWrite || property.GetCustomAttribute<ComputedAttribute>() != null)
|
|
||||||
continue;
|
|
||||||
property.SetValue(obj, property.PropertyType.GetClrDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
|
||||||
|
|
||||||
namespace Kyoo.Utils
|
namespace Kyoo.Utils
|
||||||
{
|
{
|
||||||
@ -49,37 +48,5 @@ namespace Kyoo.Utils
|
|||||||
return x.Result;
|
return x.Result;
|
||||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Map the result of a task to another result.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="task">The task to map.</param>
|
|
||||||
/// <param name="map">The mapper method, it take the task's result as a parameter and should return the new result.</param>
|
|
||||||
/// <typeparam name="T">The type of returns of the given task</typeparam>
|
|
||||||
/// <typeparam name="TResult">The resulting task after the mapping method</typeparam>
|
|
||||||
/// <returns>A task wrapping the initial task and mapping the initial result.</returns>
|
|
||||||
/// <exception cref="TaskCanceledException">The source task has been canceled.</exception>
|
|
||||||
public static Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> map)
|
|
||||||
{
|
|
||||||
return task.ContinueWith(x =>
|
|
||||||
{
|
|
||||||
if (x.IsFaulted)
|
|
||||||
x.Exception!.InnerException!.ReThrow();
|
|
||||||
if (x.IsCanceled)
|
|
||||||
throw new TaskCanceledException();
|
|
||||||
return map(x.Result);
|
|
||||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A method to return the a default value from a task if the initial task is null.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The initial task</param>
|
|
||||||
/// <typeparam name="T">The type that the task will return</typeparam>
|
|
||||||
/// <returns>A non-null task.</returns>
|
|
||||||
public static Task<T> DefaultIfNull<T>(Task<T>? value)
|
|
||||||
{
|
|
||||||
return value ?? Task.FromResult<T>(default);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,6 @@ namespace Kyoo.Utils
|
|||||||
/// <returns>True if the expression is a member, false otherwise</returns>
|
/// <returns>True if the expression is a member, false otherwise</returns>
|
||||||
public static bool IsPropertyExpression(LambdaExpression ex)
|
public static bool IsPropertyExpression(LambdaExpression ex)
|
||||||
{
|
{
|
||||||
if (ex == null)
|
|
||||||
return false;
|
|
||||||
return ex.Body is MemberExpression
|
return ex.Body is MemberExpression
|
||||||
|| (ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression);
|
|| (ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression);
|
||||||
}
|
}
|
||||||
@ -57,7 +55,7 @@ namespace Kyoo.Utils
|
|||||||
{
|
{
|
||||||
if (!IsPropertyExpression(ex))
|
if (!IsPropertyExpression(ex))
|
||||||
throw new ArgumentException($"{ex} is not a property expression.");
|
throw new ArgumentException($"{ex} is not a property expression.");
|
||||||
MemberExpression member = ex.Body.NodeType == ExpressionType.Convert
|
MemberExpression? member = ex.Body.NodeType == ExpressionType.Convert
|
||||||
? ((UnaryExpression)ex.Body).Operand as MemberExpression
|
? ((UnaryExpression)ex.Body).Operand as MemberExpression
|
||||||
: ex.Body as MemberExpression;
|
: ex.Body as MemberExpression;
|
||||||
return member!.Member.Name;
|
return member!.Member.Name;
|
||||||
@ -92,18 +90,6 @@ namespace Kyoo.Utils
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the default value of a type.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The type to get the default value</param>
|
|
||||||
/// <returns>The default value of the given type.</returns>
|
|
||||||
public static object GetClrDefault(this Type type)
|
|
||||||
{
|
|
||||||
return type.IsValueType
|
|
||||||
? Activator.CreateInstance(type)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return every <see cref="Type"/> in the inheritance tree of the parameter (interfaces are not returned)
|
/// Return every <see cref="Type"/> in the inheritance tree of the parameter (interfaces are not returned)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -194,13 +180,6 @@ namespace Kyoo.Utils
|
|||||||
Type[] generics,
|
Type[] generics,
|
||||||
object[] args)
|
object[] args)
|
||||||
{
|
{
|
||||||
if (type == null)
|
|
||||||
throw new ArgumentNullException(nameof(type));
|
|
||||||
if (generics == null)
|
|
||||||
throw new ArgumentNullException(nameof(generics));
|
|
||||||
if (args == null)
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
|
||||||
|
|
||||||
MethodInfo[] methods = type.GetMethods(flag | BindingFlags.Public)
|
MethodInfo[] methods = type.GetMethods(flag | BindingFlags.Public)
|
||||||
.Where(x => x.Name == name)
|
.Where(x => x.Name == name)
|
||||||
.Where(x => x.GetGenericArguments().Length == generics.Length)
|
.Where(x => x.GetGenericArguments().Length == generics.Length)
|
||||||
@ -295,12 +274,11 @@ namespace Kyoo.Utils
|
|||||||
/// <returns>The return of the method you wanted to run.</returns>
|
/// <returns>The return of the method you wanted to run.</returns>
|
||||||
/// <seealso cref="RunGenericMethod{T}(object,string,System.Type[],object[])"/>
|
/// <seealso cref="RunGenericMethod{T}(object,string,System.Type[],object[])"/>
|
||||||
/// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type,object[])"/>
|
/// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type,object[])"/>
|
||||||
[PublicAPI]
|
public static T? RunGenericMethod<T>(
|
||||||
public static T RunGenericMethod<T>(
|
|
||||||
Type owner,
|
Type owner,
|
||||||
string methodName,
|
string methodName,
|
||||||
Type[] types,
|
Type[] types,
|
||||||
params object[] args)
|
params object?[] args)
|
||||||
{
|
{
|
||||||
if (owner == null)
|
if (owner == null)
|
||||||
throw new ArgumentNullException(nameof(owner));
|
throw new ArgumentNullException(nameof(owner));
|
||||||
@ -311,7 +289,7 @@ namespace Kyoo.Utils
|
|||||||
if (types.Length < 1)
|
if (types.Length < 1)
|
||||||
throw new ArgumentException($"The {nameof(types)} array is empty. At least one type is needed.");
|
throw new ArgumentException($"The {nameof(types)} array is empty. At least one type is needed.");
|
||||||
MethodInfo method = GetMethod(owner, BindingFlags.Static, methodName, types, args);
|
MethodInfo method = GetMethod(owner, BindingFlags.Static, methodName, types, args);
|
||||||
return (T)method.MakeGenericMethod(types).Invoke(null, args);
|
return (T?)method.MakeGenericMethod(types).Invoke(null, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -27,6 +27,7 @@ using Kyoo.Abstractions.Models.Permissions;
|
|||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Authentication.Models;
|
using Kyoo.Authentication.Models;
|
||||||
using Kyoo.Authentication.Models.DTO;
|
using Kyoo.Authentication.Models.DTO;
|
||||||
|
using Kyoo.Models;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
@ -229,7 +230,7 @@ namespace Kyoo.Authentication.Views
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
user.Id = userID;
|
user.Id = userID;
|
||||||
return await _users.Edit(user, true);
|
return await _users.Edit(user);
|
||||||
}
|
}
|
||||||
catch (ItemNotFoundException)
|
catch (ItemNotFoundException)
|
||||||
{
|
{
|
||||||
@ -252,14 +253,15 @@ namespace Kyoo.Authentication.Views
|
|||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))]
|
[ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))]
|
[ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))]
|
||||||
public async Task<ActionResult<User>> PatchMe(User user)
|
public async Task<ActionResult<User>> PatchMe(PartialResource user)
|
||||||
{
|
{
|
||||||
if (!int.TryParse(User.FindFirstValue(Claims.Id), out int userID))
|
if (!int.TryParse(User.FindFirstValue(Claims.Id), out int userID))
|
||||||
return Unauthorized(new RequestError("User not authenticated or token invalid."));
|
return Unauthorized(new RequestError("User not authenticated or token invalid."));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
user.Id = userID;
|
if (user.Id.HasValue && user.Id != userID)
|
||||||
return await _users.Edit(user, false);
|
throw new ArgumentException("Can't edit your user id.");
|
||||||
|
return await _users.Patch(userID, TryUpdateModelAsync);
|
||||||
}
|
}
|
||||||
catch (ItemNotFoundException)
|
catch (ItemNotFoundException)
|
||||||
{
|
{
|
||||||
|
@ -284,12 +284,12 @@ namespace Kyoo.Core.Controllers
|
|||||||
(Show s, nameof(Show.Seasons)) => _SetRelation(s,
|
(Show s, nameof(Show.Seasons)) => _SetRelation(s,
|
||||||
SeasonRepository.GetAll(x => x.Show.Id == obj.Id),
|
SeasonRepository.GetAll(x => x.Show.Id == obj.Id),
|
||||||
(x, y) => x.Seasons = y,
|
(x, y) => x.Seasons = y,
|
||||||
(x, y) => { x.Show = y; x.ShowID = y.Id; }),
|
(x, y) => { x.Show = y; x.ShowId = y.Id; }),
|
||||||
|
|
||||||
(Show s, nameof(Show.Episodes)) => _SetRelation(s,
|
(Show s, nameof(Show.Episodes)) => _SetRelation(s,
|
||||||
EpisodeRepository.GetAll(x => x.Show.Id == obj.Id),
|
EpisodeRepository.GetAll(x => x.Show.Id == obj.Id),
|
||||||
(x, y) => x.Episodes = y,
|
(x, y) => x.Episodes = y,
|
||||||
(x, y) => { x.Show = y; x.ShowID = y.Id; }),
|
(x, y) => { x.Show = y; x.ShowId = y.Id; }),
|
||||||
|
|
||||||
(Show s, nameof(Show.Collections)) => CollectionRepository
|
(Show s, nameof(Show.Collections)) => CollectionRepository
|
||||||
.GetAll(x => x.Shows.Any(y => y.Id == obj.Id))
|
.GetAll(x => x.Shows.Any(y => y.Id == obj.Id))
|
||||||
@ -300,21 +300,21 @@ namespace Kyoo.Core.Controllers
|
|||||||
.Then(x =>
|
.Then(x =>
|
||||||
{
|
{
|
||||||
s.Studio = x;
|
s.Studio = x;
|
||||||
s.StudioID = x?.Id ?? 0;
|
s.StudioId = x?.Id ?? 0;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
||||||
(Season s, nameof(Season.Episodes)) => _SetRelation(s,
|
(Season s, nameof(Season.Episodes)) => _SetRelation(s,
|
||||||
EpisodeRepository.GetAll(x => x.Season.Id == obj.Id),
|
EpisodeRepository.GetAll(x => x.Season.Id == obj.Id),
|
||||||
(x, y) => x.Episodes = y,
|
(x, y) => x.Episodes = y,
|
||||||
(x, y) => { x.Season = y; x.SeasonID = y.Id; }),
|
(x, y) => { x.Season = y; x.SeasonId = y.Id; }),
|
||||||
|
|
||||||
(Season s, nameof(Season.Show)) => ShowRepository
|
(Season s, nameof(Season.Show)) => ShowRepository
|
||||||
.GetOrDefault(x => x.Seasons.Any(y => y.Id == obj.Id))
|
.GetOrDefault(x => x.Seasons.Any(y => y.Id == obj.Id))
|
||||||
.Then(x =>
|
.Then(x =>
|
||||||
{
|
{
|
||||||
s.Show = x;
|
s.Show = x;
|
||||||
s.ShowID = x?.Id ?? 0;
|
s.ShowId = x?.Id ?? 0;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
||||||
@ -323,7 +323,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
.Then(x =>
|
.Then(x =>
|
||||||
{
|
{
|
||||||
e.Show = x;
|
e.Show = x;
|
||||||
e.ShowID = x?.Id ?? 0;
|
e.ShowId = x?.Id ?? 0;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
(Episode e, nameof(Episode.Season)) => SeasonRepository
|
(Episode e, nameof(Episode.Season)) => SeasonRepository
|
||||||
@ -331,18 +331,18 @@ namespace Kyoo.Core.Controllers
|
|||||||
.Then(x =>
|
.Then(x =>
|
||||||
{
|
{
|
||||||
e.Season = x;
|
e.Season = x;
|
||||||
e.SeasonID = x?.Id ?? 0;
|
e.SeasonId = x?.Id ?? 0;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
(Episode e, nameof(Episode.PreviousEpisode)) => EpisodeRepository
|
(Episode e, nameof(Episode.PreviousEpisode)) => EpisodeRepository
|
||||||
.GetAll(
|
.GetAll(
|
||||||
where: x => x.ShowID == e.ShowID,
|
where: x => x.ShowId == e.ShowId,
|
||||||
limit: new Pagination(1, e.Id, true)
|
limit: new Pagination(1, e.Id, true)
|
||||||
).Then(x => e.PreviousEpisode = x.FirstOrDefault()),
|
).Then(x => e.PreviousEpisode = x.FirstOrDefault()),
|
||||||
|
|
||||||
(Episode e, nameof(Episode.NextEpisode)) => EpisodeRepository
|
(Episode e, nameof(Episode.NextEpisode)) => EpisodeRepository
|
||||||
.GetAll(
|
.GetAll(
|
||||||
where: x => x.ShowID == e.ShowID,
|
where: x => x.ShowId == e.ShowId,
|
||||||
limit: new Pagination(1, e.Id)
|
limit: new Pagination(1, e.Id)
|
||||||
).Then(x => e.NextEpisode = x.FirstOrDefault()),
|
).Then(x => e.NextEpisode = x.FirstOrDefault()),
|
||||||
|
|
||||||
@ -438,10 +438,17 @@ namespace Kyoo.Core.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<T> Edit<T>(T item, bool resetOld)
|
public Task<T> Edit<T>(T item)
|
||||||
where T : class, IResource
|
where T : class, IResource
|
||||||
{
|
{
|
||||||
return GetRepository<T>().Edit(item, resetOld);
|
return GetRepository<T>().Edit(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<T> Patch<T>(int id, Func<T, Task<bool>> patch)
|
||||||
|
where T : class, IResource
|
||||||
|
{
|
||||||
|
return GetRepository<T>().Patch(id, patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -64,7 +64,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
// Edit episode slugs when the show's slug changes.
|
// Edit episode slugs when the show's slug changes.
|
||||||
shows.OnEdited += (show) =>
|
shows.OnEdited += (show) =>
|
||||||
{
|
{
|
||||||
List<Episode> episodes = _database.Episodes.AsTracking().Where(x => x.ShowID == show.Id).ToList();
|
List<Episode> episodes = _database.Episodes.AsTracking().Where(x => x.ShowId == show.Id).ToList();
|
||||||
foreach (Episode ep in episodes)
|
foreach (Episode ep in episodes)
|
||||||
{
|
{
|
||||||
ep.ShowSlug = show.Slug;
|
ep.ShowSlug = show.Slug;
|
||||||
@ -77,7 +77,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
|
public Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
|
||||||
{
|
{
|
||||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID
|
||||||
&& x.SeasonNumber == seasonNumber
|
&& x.SeasonNumber == seasonNumber
|
||||||
&& x.EpisodeNumber == episodeNumber);
|
&& x.EpisodeNumber == episodeNumber);
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<Episode> GetAbsolute(int showID, int absoluteNumber)
|
public Task<Episode> GetAbsolute(int showID, int absoluteNumber)
|
||||||
{
|
{
|
||||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID
|
||||||
&& x.AbsoluteNumber == absoluteNumber);
|
&& x.AbsoluteNumber == absoluteNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,12 +142,12 @@ namespace Kyoo.Core.Controllers
|
|||||||
public override async Task<Episode> Create(Episode obj)
|
public override async Task<Episode> Create(Episode obj)
|
||||||
{
|
{
|
||||||
await base.Create(obj);
|
await base.Create(obj);
|
||||||
obj.ShowSlug = obj.Show?.Slug ?? _database.Shows.First(x => x.Id == obj.ShowID).Slug;
|
obj.ShowSlug = obj.Show?.Slug ?? _database.Shows.First(x => x.Id == obj.ShowId).Slug;
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
_database.Entry(obj).State = EntityState.Added;
|
||||||
await _database.SaveChangesAsync(() =>
|
await _database.SaveChangesAsync(() =>
|
||||||
obj.SeasonNumber != null && obj.EpisodeNumber != null
|
obj.SeasonNumber != null && obj.EpisodeNumber != null
|
||||||
? Get(obj.ShowID, obj.SeasonNumber.Value, obj.EpisodeNumber.Value)
|
? Get(obj.ShowId, obj.SeasonNumber.Value, obj.EpisodeNumber.Value)
|
||||||
: GetAbsolute(obj.ShowID, obj.AbsoluteNumber.Value));
|
: GetAbsolute(obj.ShowId, obj.AbsoluteNumber.Value));
|
||||||
OnResourceCreated(obj);
|
OnResourceCreated(obj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -156,14 +156,14 @@ namespace Kyoo.Core.Controllers
|
|||||||
protected override async Task Validate(Episode resource)
|
protected override async Task Validate(Episode resource)
|
||||||
{
|
{
|
||||||
await base.Validate(resource);
|
await base.Validate(resource);
|
||||||
if (resource.ShowID <= 0)
|
if (resource.ShowId <= 0)
|
||||||
{
|
{
|
||||||
if (resource.Show == null)
|
if (resource.Show == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Can't store an episode not related " +
|
throw new ArgumentException($"Can't store an episode not related " +
|
||||||
$"to any show (showID: {resource.ShowID}).");
|
$"to any show (showID: {resource.ShowId}).");
|
||||||
}
|
}
|
||||||
resource.ShowID = resource.Show.Id;
|
resource.ShowId = resource.Show.Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,12 +173,12 @@ namespace Kyoo.Core.Controllers
|
|||||||
if (obj == null)
|
if (obj == null)
|
||||||
throw new ArgumentNullException(nameof(obj));
|
throw new ArgumentNullException(nameof(obj));
|
||||||
|
|
||||||
int epCount = await _database.Episodes.Where(x => x.ShowID == obj.ShowID).Take(2).CountAsync();
|
int epCount = await _database.Episodes.Where(x => x.ShowId == obj.ShowId).Take(2).CountAsync();
|
||||||
_database.Entry(obj).State = EntityState.Deleted;
|
_database.Entry(obj).State = EntityState.Deleted;
|
||||||
await _database.SaveChangesAsync();
|
await _database.SaveChangesAsync();
|
||||||
await base.Delete(obj);
|
await base.Delete(obj);
|
||||||
if (epCount == 1)
|
if (epCount == 1)
|
||||||
await _shows.Delete(obj.ShowID);
|
await _shows.Delete(obj.ShowId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,11 @@ namespace Kyoo.Core.Controllers
|
|||||||
=> throw new InvalidOperationException();
|
=> throw new InvalidOperationException();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task<ILibraryItem> Edit(ILibraryItem obj, bool resetOld)
|
public override Task<ILibraryItem> Edit(ILibraryItem obj)
|
||||||
|
=> throw new InvalidOperationException();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<ILibraryItem> Patch(int id, Func<ILibraryItem, Task<bool>> patch)
|
||||||
=> throw new InvalidOperationException();
|
=> throw new InvalidOperationException();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -144,7 +144,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <param name="reference">The reference item (the AfterID query)</param>
|
/// <param name="reference">The reference item (the AfterID query)</param>
|
||||||
/// <param name="next">True if the following page should be returned, false for the previous.</param>
|
/// <param name="next">True if the following page should be returned, false for the previous.</param>
|
||||||
/// <returns>An expression ready to be added to a Where close of a sorted query to handle the AfterID</returns>
|
/// <returns>An expression ready to be added to a Where close of a sorted query to handle the AfterID</returns>
|
||||||
protected Expression<Func<T, bool>> KeysetPaginatate(
|
protected Expression<Func<T, bool>> KeysetPaginate(
|
||||||
Sort<T> sort,
|
Sort<T> sort,
|
||||||
T reference,
|
T reference,
|
||||||
bool next = true)
|
bool next = true)
|
||||||
@ -155,22 +155,22 @@ namespace Kyoo.Core.Controllers
|
|||||||
ParameterExpression x = Expression.Parameter(typeof(T), "x");
|
ParameterExpression x = Expression.Parameter(typeof(T), "x");
|
||||||
ConstantExpression referenceC = Expression.Constant(reference, typeof(T));
|
ConstantExpression referenceC = Expression.Constant(reference, typeof(T));
|
||||||
|
|
||||||
IEnumerable<Sort<T>.By> _GetSortsBy(Sort<T> sort)
|
IEnumerable<Sort<T>.By> GetSortsBy(Sort<T> sort)
|
||||||
{
|
{
|
||||||
return sort switch
|
return sort switch
|
||||||
{
|
{
|
||||||
Sort<T>.Default => _GetSortsBy(DefaultSort),
|
Sort<T>.Default => GetSortsBy(DefaultSort),
|
||||||
Sort<T>.By @sortBy => new[] { sortBy },
|
Sort<T>.By @sortBy => new[] { sortBy },
|
||||||
Sort<T>.Conglomerate(var list) => list.SelectMany(_GetSortsBy),
|
Sort<T>.Conglomerate(var list) => list.SelectMany(GetSortsBy),
|
||||||
_ => Array.Empty<Sort<T>.By>(),
|
_ => Array.Empty<Sort<T>.By>(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't forget that every sorts must end with a ID sort (to differenciate equalities).
|
// Don't forget that every sorts must end with a ID sort (to differentiate equalities).
|
||||||
Sort<T>.By id = new(x => x.Id);
|
Sort<T>.By id = new(x => x.Id);
|
||||||
IEnumerable<Sort<T>.By> sorts = _GetSortsBy(sort).Append(id);
|
IEnumerable<Sort<T>.By> sorts = GetSortsBy(sort).Append(id);
|
||||||
|
|
||||||
BinaryExpression filter = null;
|
BinaryExpression? filter = null;
|
||||||
List<Sort<T>.By> previousSteps = new();
|
List<Sort<T>.By> previousSteps = new();
|
||||||
// TODO: Add an outer query >= for perf
|
// TODO: Add an outer query >= for perf
|
||||||
// PERF: See https://use-the-index-luke.com/sql/partial-results/fetch-next-page#sb-equivalent-logic
|
// PERF: See https://use-the-index-luke.com/sql/partial-results/fetch-next-page#sb-equivalent-logic
|
||||||
@ -180,9 +180,9 @@ namespace Kyoo.Core.Controllers
|
|||||||
PropertyInfo property = typeof(T).GetProperty(key);
|
PropertyInfo property = typeof(T).GetProperty(key);
|
||||||
|
|
||||||
// Comparing a value with null always return false so we short opt < > comparisons with null.
|
// Comparing a value with null always return false so we short opt < > comparisons with null.
|
||||||
if (property.GetValue(reference) == null)
|
if (property!.GetValue(reference) == null)
|
||||||
{
|
{
|
||||||
previousSteps.Add(new(key, desc));
|
previousSteps.Add(new Sort<T>.By(key, desc));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
|
|
||||||
// Comparing a value with null always return false for nulls so we must add nulls to the results manually.
|
// Comparing a value with null always return false for nulls so we must add nulls to the results manually.
|
||||||
// Postgres sorts them after values so we will do the same
|
// Postgres sorts them after values so we will do the same
|
||||||
// We only add this condition if the collumn type is nullable
|
// We only add this condition if the column type is nullable
|
||||||
if (Nullable.GetUnderlyingType(property.PropertyType) != null)
|
if (Nullable.GetUnderlyingType(property.PropertyType) != null)
|
||||||
{
|
{
|
||||||
BinaryExpression equalNull = Expression.Equal(xkey, Expression.Constant(null));
|
BinaryExpression equalNull = Expression.Equal(xkey, Expression.Constant(null));
|
||||||
@ -223,7 +223,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
|
|
||||||
previousSteps.Add(new(key, desc));
|
previousSteps.Add(new(key, desc));
|
||||||
}
|
}
|
||||||
return Expression.Lambda<Func<T, bool>>(filter, x);
|
return Expression.Lambda<Func<T, bool>>(filter!, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -316,7 +316,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
if (limit?.AfterID != null)
|
if (limit?.AfterID != null)
|
||||||
{
|
{
|
||||||
T reference = await Get(limit.AfterID.Value);
|
T reference = await Get(limit.AfterID.Value);
|
||||||
query = query.Where(KeysetPaginatate(sort, reference, !limit.Reverse));
|
query = query.Where(KeysetPaginate(sort, reference, !limit.Reverse));
|
||||||
}
|
}
|
||||||
if (limit?.Reverse == true)
|
if (limit?.Reverse == true)
|
||||||
query = query.Reverse();
|
query = query.Reverse();
|
||||||
@ -343,12 +343,9 @@ namespace Kyoo.Core.Controllers
|
|||||||
await Validate(obj);
|
await Validate(obj);
|
||||||
if (obj is IThumbnails thumbs)
|
if (obj is IThumbnails thumbs)
|
||||||
{
|
{
|
||||||
if (thumbs.Poster != null)
|
Database.Entry(thumbs).Reference(x => x.Poster).IsModified = thumbs.Poster != null;
|
||||||
Database.Entry(thumbs).Reference(x => x.Poster).TargetEntry.State = EntityState.Added;
|
Database.Entry(thumbs).Reference(x => x.Thumbnail).IsModified = thumbs.Thumbnail != null;
|
||||||
if (thumbs.Thumbnail != null)
|
Database.Entry(thumbs).Reference(x => x.Logo).IsModified = thumbs.Logo != null;
|
||||||
Database.Entry(thumbs).Reference(x => x.Thumbnail).TargetEntry.State = EntityState.Added;
|
|
||||||
if (thumbs.Logo != null)
|
|
||||||
Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry.State = EntityState.Added;
|
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -376,9 +373,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (obj == null)
|
|
||||||
throw new ArgumentNullException(nameof(obj));
|
|
||||||
|
|
||||||
T old = await GetOrDefault(obj.Slug);
|
T old = await GetOrDefault(obj.Slug);
|
||||||
if (old != null)
|
if (old != null)
|
||||||
return old;
|
return old;
|
||||||
@ -392,21 +386,16 @@ namespace Kyoo.Core.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task<T> Edit(T edited, bool resetOld)
|
public virtual async Task<T> Edit(T edited)
|
||||||
{
|
{
|
||||||
if (edited == null)
|
|
||||||
throw new ArgumentNullException(nameof(edited));
|
|
||||||
|
|
||||||
bool lazyLoading = Database.ChangeTracker.LazyLoadingEnabled;
|
bool lazyLoading = Database.ChangeTracker.LazyLoadingEnabled;
|
||||||
Database.ChangeTracker.LazyLoadingEnabled = false;
|
Database.ChangeTracker.LazyLoadingEnabled = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
T old = await GetWithTracking(edited.Id);
|
T old = await GetWithTracking(edited.Id);
|
||||||
|
|
||||||
if (resetOld)
|
|
||||||
old = Merger.Nullify(old);
|
|
||||||
Merger.Complete(old, edited, x => x.GetCustomAttribute<LoadableRelationAttribute>() == null);
|
Merger.Complete(old, edited, x => x.GetCustomAttribute<LoadableRelationAttribute>() == null);
|
||||||
await EditRelations(old, edited, resetOld);
|
await EditRelations(old, edited, true);
|
||||||
await Database.SaveChangesAsync();
|
await Database.SaveChangesAsync();
|
||||||
OnEdited?.Invoke(old);
|
OnEdited?.Invoke(old);
|
||||||
return old;
|
return old;
|
||||||
@ -418,6 +407,28 @@ namespace Kyoo.Core.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public virtual async Task<T> Patch(int id, Func<T, Task<bool>> patch)
|
||||||
|
{
|
||||||
|
bool lazyLoading = Database.ChangeTracker.LazyLoadingEnabled;
|
||||||
|
Database.ChangeTracker.LazyLoadingEnabled = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
T resource = await GetWithTracking(id);
|
||||||
|
|
||||||
|
if (!await patch(resource))
|
||||||
|
throw new ArgumentException("Could not patch resource");
|
||||||
|
await Database.SaveChangesAsync();
|
||||||
|
OnEdited?.Invoke(resource);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Database.ChangeTracker.LazyLoadingEnabled = lazyLoading;
|
||||||
|
Database.ChangeTracker.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An overridable method to edit relation of a resource.
|
/// An overridable method to edit relation of a resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -434,12 +445,11 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
protected virtual Task EditRelations(T resource, T changed, bool resetOld)
|
protected virtual Task EditRelations(T resource, T changed, bool resetOld)
|
||||||
{
|
{
|
||||||
if (resource is IThumbnails thumbs)
|
if (resource is IThumbnails thumbs && changed is IThumbnails chng)
|
||||||
{
|
{
|
||||||
// FIXME: I think this throws if poster is set to null.
|
Database.Entry(thumbs).Reference(x => x.Poster).IsModified = thumbs.Poster != chng.Poster;
|
||||||
Database.Entry(thumbs).Reference(x => x.Poster).TargetEntry.State = EntityState.Modified;
|
Database.Entry(thumbs).Reference(x => x.Thumbnail).IsModified = thumbs.Thumbnail != chng.Thumbnail;
|
||||||
Database.Entry(thumbs).Reference(x => x.Thumbnail).TargetEntry.State = EntityState.Modified;
|
Database.Entry(thumbs).Reference(x => x.Logo).IsModified = thumbs.Logo != chng.Logo;
|
||||||
Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry.State = EntityState.Modified;
|
|
||||||
}
|
}
|
||||||
return Validate(resource);
|
return Validate(resource);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
// Edit seasons slugs when the show's slug changes.
|
// Edit seasons slugs when the show's slug changes.
|
||||||
shows.OnEdited += (show) =>
|
shows.OnEdited += (show) =>
|
||||||
{
|
{
|
||||||
List<Season> seasons = _database.Seasons.AsTracking().Where(x => x.ShowID == show.Id).ToList();
|
List<Season> seasons = _database.Seasons.AsTracking().Where(x => x.ShowId == show.Id).ToList();
|
||||||
foreach (Season season in seasons)
|
foreach (Season season in seasons)
|
||||||
{
|
{
|
||||||
season.ShowSlug = show.Slug;
|
season.ShowSlug = show.Slug;
|
||||||
@ -86,7 +86,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Task<Season> GetOrDefault(int showID, int seasonNumber)
|
public Task<Season> GetOrDefault(int showID, int seasonNumber)
|
||||||
{
|
{
|
||||||
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
|
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowId == showID
|
||||||
&& x.SeasonNumber == seasonNumber);
|
&& x.SeasonNumber == seasonNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,9 +112,9 @@ namespace Kyoo.Core.Controllers
|
|||||||
public override async Task<Season> Create(Season obj)
|
public override async Task<Season> Create(Season obj)
|
||||||
{
|
{
|
||||||
await base.Create(obj);
|
await base.Create(obj);
|
||||||
obj.ShowSlug = _database.Shows.First(x => x.Id == obj.ShowID).Slug;
|
obj.ShowSlug = _database.Shows.First(x => x.Id == obj.ShowId).Slug;
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
_database.Entry(obj).State = EntityState.Added;
|
||||||
await _database.SaveChangesAsync(() => Get(obj.ShowID, obj.SeasonNumber));
|
await _database.SaveChangesAsync(() => Get(obj.ShowId, obj.SeasonNumber));
|
||||||
OnResourceCreated(obj);
|
OnResourceCreated(obj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -123,14 +123,14 @@ namespace Kyoo.Core.Controllers
|
|||||||
protected override async Task Validate(Season resource)
|
protected override async Task Validate(Season resource)
|
||||||
{
|
{
|
||||||
await base.Validate(resource);
|
await base.Validate(resource);
|
||||||
if (resource.ShowID <= 0)
|
if (resource.ShowId <= 0)
|
||||||
{
|
{
|
||||||
if (resource.Show == null)
|
if (resource.Show == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Can't store a season not related to any show " +
|
throw new ArgumentException($"Can't store a season not related to any show " +
|
||||||
$"(showID: {resource.ShowID}).");
|
$"(showID: {resource.ShowId}).");
|
||||||
}
|
}
|
||||||
resource.ShowID = resource.Show.Id;
|
resource.ShowId = resource.Show.Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
if (resource.Studio != null)
|
if (resource.Studio != null)
|
||||||
{
|
{
|
||||||
resource.Studio = await _studios.CreateIfNotExists(resource.Studio);
|
resource.Studio = await _studios.CreateIfNotExists(resource.Studio);
|
||||||
resource.StudioID = resource.Studio.Id;
|
resource.StudioId = resource.Studio.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource.People != null)
|
if (resource.People != null)
|
||||||
|
@ -16,12 +16,14 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
|
using Kyoo.Models;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@ -162,11 +164,11 @@ namespace Kyoo.Core.Api
|
|||||||
public async Task<ActionResult<T>> Edit([FromBody] T resource)
|
public async Task<ActionResult<T>> Edit([FromBody] T resource)
|
||||||
{
|
{
|
||||||
if (resource.Id > 0)
|
if (resource.Id > 0)
|
||||||
return await Repository.Edit(resource, true);
|
return await Repository.Edit(resource);
|
||||||
|
|
||||||
T old = await Repository.Get(resource.Slug);
|
T old = await Repository.Get(resource.Slug);
|
||||||
resource.Id = old.Id;
|
resource.Id = old.Id;
|
||||||
return await Repository.Edit(resource, true);
|
return await Repository.Edit(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -185,14 +187,15 @@ namespace Kyoo.Core.Api
|
|||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<T>> Patch([FromBody] T resource)
|
public async Task<ActionResult<T>> Patch([FromBody] PartialResource resource)
|
||||||
{
|
{
|
||||||
if (resource.Id > 0)
|
if (resource.Id.HasValue)
|
||||||
return await Repository.Edit(resource, false);
|
return await Repository.Patch(resource.Id.Value, TryUpdateModelAsync);
|
||||||
|
if (resource.Slug == null)
|
||||||
|
throw new ArgumentException("Either the Id or the slug of the resource has to be defined to edit it.");
|
||||||
|
|
||||||
T old = await Repository.Get(resource.Slug);
|
T old = await Repository.Get(resource.Slug);
|
||||||
resource.Id = old.Id;
|
return await Repository.Patch(old.Id, TryUpdateModelAsync);
|
||||||
return await Repository.Edit(resource, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -83,7 +83,7 @@ namespace Kyoo.Core.Api
|
|||||||
[FromQuery] Pagination pagination)
|
[FromQuery] Pagination pagination)
|
||||||
{
|
{
|
||||||
ICollection<Show> resources = await _libraryManager.GetAll(
|
ICollection<Show> resources = await _libraryManager.GetAll(
|
||||||
ApiHelper.ParseWhere(where, identifier.Matcher<Show>(x => x.StudioID, x => x.Studio.Slug)),
|
ApiHelper.ParseWhere(where, identifier.Matcher<Show>(x => x.StudioId, x => x.Studio.Slug)),
|
||||||
Sort<Show>.From(sortBy),
|
Sort<Show>.From(sortBy),
|
||||||
pagination
|
pagination
|
||||||
);
|
);
|
||||||
|
@ -84,7 +84,7 @@ namespace Kyoo.Core.Api
|
|||||||
[FromQuery] Pagination pagination)
|
[FromQuery] Pagination pagination)
|
||||||
{
|
{
|
||||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
ICollection<Episode> resources = await _libraryManager.GetAll(
|
||||||
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.SeasonID, x => x.Season.Slug)),
|
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.SeasonId, x => x.Season.Slug)),
|
||||||
Sort<Episode>.From(sortBy),
|
Sort<Episode>.From(sortBy),
|
||||||
pagination
|
pagination
|
||||||
);
|
);
|
||||||
|
@ -86,7 +86,7 @@ namespace Kyoo.Core.Api
|
|||||||
[FromQuery] Pagination pagination)
|
[FromQuery] Pagination pagination)
|
||||||
{
|
{
|
||||||
ICollection<Season> resources = await _libraryManager.GetAll(
|
ICollection<Season> resources = await _libraryManager.GetAll(
|
||||||
ApiHelper.ParseWhere(where, identifier.Matcher<Season>(x => x.ShowID, x => x.Show.Slug)),
|
ApiHelper.ParseWhere(where, identifier.Matcher<Season>(x => x.ShowId, x => x.Show.Slug)),
|
||||||
Sort<Season>.From(sortBy),
|
Sort<Season>.From(sortBy),
|
||||||
pagination
|
pagination
|
||||||
);
|
);
|
||||||
@ -121,7 +121,7 @@ namespace Kyoo.Core.Api
|
|||||||
[FromQuery] Pagination pagination)
|
[FromQuery] Pagination pagination)
|
||||||
{
|
{
|
||||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
ICollection<Episode> resources = await _libraryManager.GetAll(
|
||||||
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.ShowID, x => x.Show.Slug)),
|
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.ShowId, x => x.Show.Slug)),
|
||||||
Sort<Episode>.From(sortBy),
|
Sort<Episode>.From(sortBy),
|
||||||
pagination
|
pagination
|
||||||
);
|
);
|
||||||
|
@ -306,13 +306,13 @@ namespace Kyoo.Postgresql
|
|||||||
.HasIndex(x => x.Slug)
|
.HasIndex(x => x.Slug)
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
modelBuilder.Entity<Season>()
|
modelBuilder.Entity<Season>()
|
||||||
.HasIndex(x => new { x.ShowID, x.SeasonNumber })
|
.HasIndex(x => new { ShowID = x.ShowId, x.SeasonNumber })
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
modelBuilder.Entity<Season>()
|
modelBuilder.Entity<Season>()
|
||||||
.HasIndex(x => x.Slug)
|
.HasIndex(x => x.Slug)
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
modelBuilder.Entity<Episode>()
|
modelBuilder.Entity<Episode>()
|
||||||
.HasIndex(x => new { x.ShowID, x.SeasonNumber, x.EpisodeNumber, x.AbsoluteNumber })
|
.HasIndex(x => new { ShowID = x.ShowId, x.SeasonNumber, x.EpisodeNumber, x.AbsoluteNumber })
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
modelBuilder.Entity<Episode>()
|
modelBuilder.Entity<Episode>()
|
||||||
.HasIndex(x => x.Slug)
|
.HasIndex(x => x.Slug)
|
||||||
|
@ -140,16 +140,10 @@ namespace Kyoo.Tests.Database
|
|||||||
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(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]
|
[Fact]
|
||||||
public async Task EditNonExistingTest()
|
public async Task EditNonExistingTest()
|
||||||
{
|
{
|
||||||
await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Edit(new T { Id = 56 }, false));
|
await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Edit(new T { Id = 56 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -170,12 +164,6 @@ namespace Kyoo.Tests.Database
|
|||||||
await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Get(x => x.Slug == "non-existing"));
|
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]
|
[Fact]
|
||||||
public async Task GetOrDefaultTest()
|
public async Task GetOrDefaultTest()
|
||||||
{
|
{
|
||||||
|
@ -66,29 +66,19 @@ namespace Kyoo.Tests.Database
|
|||||||
Assert.Equal("2!", ret.Slug);
|
Assert.Equal("2!", ret.Slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task CreateWithoutNameTest()
|
|
||||||
{
|
|
||||||
Collection collection = TestSample.GetNew<Collection>();
|
|
||||||
collection.Name = null;
|
|
||||||
await Assert.ThrowsAsync<ArgumentException>(() => _repository.Create(collection));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateWithExternalIdTest()
|
public async Task CreateWithExternalIdTest()
|
||||||
{
|
{
|
||||||
Collection collection = TestSample.GetNew<Collection>();
|
Collection collection = TestSample.GetNew<Collection>();
|
||||||
collection.ExternalId = new[]
|
collection.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new MetadataId
|
["1"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
new MetadataId
|
["2"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.GetNew<Provider>(),
|
|
||||||
Link = "new-provider-link",
|
Link = "new-provider-link",
|
||||||
DataId = "new-id"
|
DataId = "new-id"
|
||||||
}
|
}
|
||||||
@ -96,7 +86,6 @@ namespace Kyoo.Tests.Database
|
|||||||
await _repository.Create(collection);
|
await _repository.Create(collection);
|
||||||
|
|
||||||
Collection retrieved = await _repository.Get(2);
|
Collection retrieved = await _repository.Get(2);
|
||||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
|
||||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||||
KAssert.DeepEqual(collection.ExternalId.First(), retrieved.ExternalId.First());
|
KAssert.DeepEqual(collection.ExternalId.First(), retrieved.ExternalId.First());
|
||||||
KAssert.DeepEqual(collection.ExternalId.Last(), retrieved.ExternalId.Last());
|
KAssert.DeepEqual(collection.ExternalId.Last(), retrieved.ExternalId.Last());
|
||||||
@ -107,11 +96,8 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
||||||
value.Name = "New Title";
|
value.Name = "New Title";
|
||||||
value.Images = new Dictionary<int, string>
|
value.Poster = new Image("new-poster");
|
||||||
{
|
await _repository.Edit(value);
|
||||||
[Images.Poster] = "new-poster"
|
|
||||||
};
|
|
||||||
await _repository.Edit(value, false);
|
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Collection retrieved = await database.Collections.FirstAsync();
|
Collection retrieved = await database.Collections.FirstAsync();
|
||||||
@ -123,21 +109,19 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task EditMetadataTest()
|
public async Task EditMetadataTest()
|
||||||
{
|
{
|
||||||
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
||||||
value.ExternalId = new[]
|
value.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new MetadataId
|
["test"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Collection retrieved = await database.Collections
|
Collection retrieved = await database.Collections
|
||||||
.Include(x => x.ExternalId)
|
.Include(x => x.ExternalId)
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
@ -147,40 +131,36 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task AddMetadataTest()
|
public async Task AddMetadataTest()
|
||||||
{
|
{
|
||||||
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
||||||
value.ExternalId = new List<MetadataId>
|
value.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new()
|
["toto"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
{
|
{
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Collection retrieved = await database.Collections
|
Collection retrieved = await database.Collections
|
||||||
.Include(x => x.ExternalId)
|
.Include(x => x.ExternalId)
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
}
|
}
|
||||||
|
|
||||||
value.ExternalId.Add(new MetadataId
|
value.ExternalId.Add("test", new MetadataId
|
||||||
{
|
{
|
||||||
Provider = TestSample.GetNew<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
});
|
});
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
{
|
{
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Collection retrieved = await database.Collections
|
Collection retrieved = await database.Collections
|
||||||
.Include(x => x.ExternalId)
|
.Include(x => x.ExternalId)
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
|
@ -57,10 +57,10 @@ namespace Kyoo.Tests.Database
|
|||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
|
||||||
Show show = new()
|
Show show = new()
|
||||||
{
|
{
|
||||||
Id = episode.ShowID,
|
Id = episode.ShowId,
|
||||||
Slug = "new-slug"
|
Slug = "new-slug"
|
||||||
};
|
};
|
||||||
await Repositories.LibraryManager.ShowRepository.Edit(show, false);
|
await Repositories.LibraryManager.ShowRepository.Edit(show);
|
||||||
episode = await _repository.Get(1);
|
episode = await _repository.Get(1);
|
||||||
Assert.Equal("new-slug-s1e1", episode.Slug);
|
Assert.Equal("new-slug-s1e1", episode.Slug);
|
||||||
}
|
}
|
||||||
@ -74,8 +74,8 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
SeasonNumber = 2,
|
SeasonNumber = 2,
|
||||||
ShowID = 1
|
ShowId = 1
|
||||||
}, false);
|
});
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e1", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e1", episode.Slug);
|
||||||
episode = await _repository.Get(1);
|
episode = await _repository.Get(1);
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e1", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e1", episode.Slug);
|
||||||
@ -90,8 +90,8 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
EpisodeNumber = 2,
|
EpisodeNumber = 2,
|
||||||
ShowID = 1
|
ShowId = 1
|
||||||
}, false);
|
});
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
||||||
episode = await _repository.Get(1);
|
episode = await _repository.Get(1);
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
||||||
@ -102,7 +102,7 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
Episode episode = await _repository.Create(new Episode
|
Episode episode = await _repository.Create(new Episode
|
||||||
{
|
{
|
||||||
ShowID = TestSample.Get<Show>().Id,
|
ShowId = TestSample.Get<Show>().Id,
|
||||||
SeasonNumber = 2,
|
SeasonNumber = 2,
|
||||||
EpisodeNumber = 4
|
EpisodeNumber = 4
|
||||||
});
|
});
|
||||||
@ -129,10 +129,10 @@ namespace Kyoo.Tests.Database
|
|||||||
Episode episode = await _repository.Create(TestSample.GetAbsoluteEpisode());
|
Episode episode = await _repository.Create(TestSample.GetAbsoluteEpisode());
|
||||||
Show show = new()
|
Show show = new()
|
||||||
{
|
{
|
||||||
Id = episode.ShowID,
|
Id = episode.ShowId,
|
||||||
Slug = "new-slug"
|
Slug = "new-slug"
|
||||||
};
|
};
|
||||||
await Repositories.LibraryManager.ShowRepository.Edit(show, false);
|
await Repositories.LibraryManager.ShowRepository.Edit(show);
|
||||||
episode = await _repository.Get(2);
|
episode = await _repository.Get(2);
|
||||||
Assert.Equal($"new-slug-3", episode.Slug);
|
Assert.Equal($"new-slug-3", episode.Slug);
|
||||||
}
|
}
|
||||||
@ -145,8 +145,8 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2,
|
||||||
AbsoluteNumber = 56,
|
AbsoluteNumber = 56,
|
||||||
ShowID = 1
|
ShowId = 1
|
||||||
}, false);
|
});
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-56", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-56", episode.Slug);
|
||||||
episode = await _repository.Get(2);
|
episode = await _repository.Get(2);
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-56", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-56", episode.Slug);
|
||||||
@ -161,8 +161,8 @@ namespace Kyoo.Tests.Database
|
|||||||
Id = 2,
|
Id = 2,
|
||||||
SeasonNumber = 1,
|
SeasonNumber = 1,
|
||||||
EpisodeNumber = 2,
|
EpisodeNumber = 2,
|
||||||
ShowID = 1
|
ShowId = 1
|
||||||
}, false);
|
});
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
||||||
episode = await _repository.Get(2);
|
episode = await _repository.Get(2);
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
||||||
@ -174,49 +174,25 @@ namespace Kyoo.Tests.Database
|
|||||||
Episode episode = await _repository.Get(1);
|
Episode episode = await _repository.Get(1);
|
||||||
episode.SeasonNumber = null;
|
episode.SeasonNumber = null;
|
||||||
episode.AbsoluteNumber = 12;
|
episode.AbsoluteNumber = 12;
|
||||||
episode = await _repository.Edit(episode, true);
|
episode = await _repository.Edit(episode);
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-12", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-12", episode.Slug);
|
||||||
episode = await _repository.Get(1);
|
episode = await _repository.Get(1);
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-12", episode.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-12", episode.Slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MovieEpisodeTest()
|
|
||||||
{
|
|
||||||
Episode episode = await _repository.Create(TestSample.GetMovieEpisode());
|
|
||||||
Assert.Equal(TestSample.Get<Show>().Slug, episode.Slug);
|
|
||||||
episode = await _repository.Get(3);
|
|
||||||
Assert.Equal(TestSample.Get<Show>().Slug, episode.Slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MovieEpisodeEditTest()
|
|
||||||
{
|
|
||||||
await _repository.Create(TestSample.GetMovieEpisode());
|
|
||||||
await Repositories.LibraryManager.Edit(new Show
|
|
||||||
{
|
|
||||||
Id = 1,
|
|
||||||
Slug = "john-wick"
|
|
||||||
}, false);
|
|
||||||
Episode episode = await _repository.Get(3);
|
|
||||||
Assert.Equal("john-wick", episode.Slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateWithExternalIdTest()
|
public async Task CreateWithExternalIdTest()
|
||||||
{
|
{
|
||||||
Episode value = TestSample.GetNew<Episode>();
|
Episode value = TestSample.GetNew<Episode>();
|
||||||
value.ExternalId = new[]
|
value.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new MetadataId
|
["2"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
new MetadataId
|
["3"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.GetNew<Provider>(),
|
|
||||||
Link = "new-provider-link",
|
Link = "new-provider-link",
|
||||||
DataId = "new-id"
|
DataId = "new-id"
|
||||||
}
|
}
|
||||||
@ -224,7 +200,6 @@ namespace Kyoo.Tests.Database
|
|||||||
await _repository.Create(value);
|
await _repository.Create(value);
|
||||||
|
|
||||||
Episode retrieved = await _repository.Get(2);
|
Episode retrieved = await _repository.Get(2);
|
||||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
|
||||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||||
KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
|
KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
|
||||||
KAssert.DeepEqual(value.ExternalId.Last(), retrieved.ExternalId.Last());
|
KAssert.DeepEqual(value.ExternalId.Last(), retrieved.ExternalId.Last());
|
||||||
@ -235,11 +210,8 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
|
Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
|
||||||
value.Name = "New Title";
|
value.Name = "New Title";
|
||||||
value.Images = new Dictionary<int, string>
|
value.Poster = new Image("poster");
|
||||||
{
|
await _repository.Edit(value);
|
||||||
[Images.Poster] = "new-poster"
|
|
||||||
};
|
|
||||||
await _repository.Edit(value, false);
|
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Episode retrieved = await database.Episodes.FirstAsync();
|
Episode retrieved = await database.Episodes.FirstAsync();
|
||||||
@ -251,22 +223,18 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task EditMetadataTest()
|
public async Task EditMetadataTest()
|
||||||
{
|
{
|
||||||
Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
|
Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
|
||||||
value.ExternalId = new[]
|
value.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new MetadataId
|
["1"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Episode retrieved = await database.Episodes
|
Episode retrieved = await database.Episodes.FirstAsync();
|
||||||
.Include(x => x.ExternalId)
|
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
}
|
}
|
||||||
@ -275,41 +243,33 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task AddMetadataTest()
|
public async Task AddMetadataTest()
|
||||||
{
|
{
|
||||||
Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
|
Episode value = await _repository.Get(TestSample.Get<Episode>().Slug);
|
||||||
value.ExternalId = new List<MetadataId>
|
value.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new()
|
["toto"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
{
|
{
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Episode retrieved = await database.Episodes
|
Episode retrieved = await database.Episodes.FirstAsync();
|
||||||
.Include(x => x.ExternalId)
|
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
}
|
}
|
||||||
|
|
||||||
value.ExternalId.Add(new MetadataId
|
value.ExternalId.Add("test", new MetadataId
|
||||||
{
|
{
|
||||||
Provider = TestSample.GetNew<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
});
|
});
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
{
|
{
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Episode retrieved = await database.Episodes
|
Episode retrieved = await database.Episodes.FirstAsync();
|
||||||
.Include(x => x.ExternalId)
|
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
}
|
}
|
||||||
@ -326,7 +286,7 @@ namespace Kyoo.Tests.Database
|
|||||||
Episode value = new()
|
Episode value = new()
|
||||||
{
|
{
|
||||||
Name = "This is a test super title",
|
Name = "This is a test super title",
|
||||||
ShowID = 1,
|
ShowId = 1,
|
||||||
AbsoluteNumber = 2
|
AbsoluteNumber = 2
|
||||||
};
|
};
|
||||||
await _repository.Create(value);
|
await _repository.Create(value);
|
||||||
@ -343,8 +303,8 @@ namespace Kyoo.Tests.Database
|
|||||||
|
|
||||||
Episode expected = TestSample.Get<Episode>();
|
Episode expected = TestSample.Get<Episode>();
|
||||||
expected.Id = 0;
|
expected.Id = 0;
|
||||||
expected.ShowID = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get<Show>())).Id;
|
expected.ShowId = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get<Show>())).Id;
|
||||||
expected.SeasonID = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get<Season>())).Id;
|
expected.SeasonId = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get<Season>())).Id;
|
||||||
await _repository.Create(expected);
|
await _repository.Create(expected);
|
||||||
KAssert.DeepEqual(expected, await _repository.Get(expected.Slug));
|
KAssert.DeepEqual(expected, await _repository.Get(expected.Slug));
|
||||||
}
|
}
|
||||||
@ -355,8 +315,8 @@ namespace Kyoo.Tests.Database
|
|||||||
Episode expected = TestSample.Get<Episode>();
|
Episode expected = TestSample.Get<Episode>();
|
||||||
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(TestSample.Get<Episode>()));
|
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(TestSample.Get<Episode>()));
|
||||||
await _repository.Delete(TestSample.Get<Episode>());
|
await _repository.Delete(TestSample.Get<Episode>());
|
||||||
expected.ShowID = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get<Show>())).Id;
|
expected.ShowId = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get<Show>())).Id;
|
||||||
expected.SeasonID = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get<Season>())).Id;
|
expected.SeasonId = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get<Season>())).Id;
|
||||||
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(expected));
|
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(expected));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Xunit;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace Kyoo.Tests.Database
|
|
||||||
{
|
|
||||||
namespace PostgreSQL
|
|
||||||
{
|
|
||||||
[Collection(nameof(Postgresql))]
|
|
||||||
public class LibraryItemTest : ALibraryItemTest
|
|
||||||
{
|
|
||||||
public LibraryItemTest(PostgresFixture postgres, ITestOutputHelper output)
|
|
||||||
: base(new RepositoryActivator(output, postgres)) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class ALibraryItemTest
|
|
||||||
{
|
|
||||||
private readonly ILibraryItemRepository _repository;
|
|
||||||
private readonly RepositoryActivator _repositories;
|
|
||||||
|
|
||||||
protected ALibraryItemTest(RepositoryActivator repositories)
|
|
||||||
{
|
|
||||||
_repositories = repositories;
|
|
||||||
_repository = repositories.LibraryManager.LibraryItemRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task CountTest()
|
|
||||||
{
|
|
||||||
Assert.Equal(2, await _repository.GetCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetShowTests()
|
|
||||||
{
|
|
||||||
LibraryItem expected = new(TestSample.Get<Show>());
|
|
||||||
LibraryItem actual = await _repository.Get(1);
|
|
||||||
KAssert.DeepEqual(expected, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetCollectionTests()
|
|
||||||
{
|
|
||||||
LibraryItem expected = new(TestSample.Get<Collection>());
|
|
||||||
LibraryItem actual = await _repository.Get(-1);
|
|
||||||
KAssert.DeepEqual(expected, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetShowSlugTests()
|
|
||||||
{
|
|
||||||
LibraryItem expected = new(TestSample.Get<Show>());
|
|
||||||
LibraryItem actual = await _repository.Get(TestSample.Get<Show>().Slug);
|
|
||||||
KAssert.DeepEqual(expected, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetCollectionSlugTests()
|
|
||||||
{
|
|
||||||
LibraryItem expected = new(TestSample.Get<Collection>());
|
|
||||||
LibraryItem actual = await _repository.Get(TestSample.Get<Collection>().Slug);
|
|
||||||
KAssert.DeepEqual(expected, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetDuplicatedSlugTests()
|
|
||||||
{
|
|
||||||
await _repositories.LibraryManager.Create(new Collection
|
|
||||||
{
|
|
||||||
Slug = TestSample.Get<Show>().Slug,
|
|
||||||
Name = "name"
|
|
||||||
});
|
|
||||||
await Assert.ThrowsAsync<InvalidOperationException>(() => _repository.Get(TestSample.Get<Show>().Slug));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,169 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Postgresql;
|
|
||||||
using Kyoo.Utils;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Xunit;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace Kyoo.Tests.Database
|
|
||||||
{
|
|
||||||
namespace PostgreSQL
|
|
||||||
{
|
|
||||||
[Collection(nameof(Postgresql))]
|
|
||||||
public class LibraryTests : ALibraryTests
|
|
||||||
{
|
|
||||||
public LibraryTests(PostgresFixture postgres, ITestOutputHelper output)
|
|
||||||
: base(new RepositoryActivator(output, postgres)) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class ALibraryTests : RepositoryTests<Library>
|
|
||||||
{
|
|
||||||
private readonly ILibraryRepository _repository;
|
|
||||||
|
|
||||||
protected ALibraryTests(RepositoryActivator repositories)
|
|
||||||
: base(repositories)
|
|
||||||
{
|
|
||||||
_repository = Repositories.LibraryManager.LibraryRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task CreateWithoutPathTest()
|
|
||||||
{
|
|
||||||
Library library = TestSample.GetNew<Library>();
|
|
||||||
library.Paths = null;
|
|
||||||
await Assert.ThrowsAsync<ArgumentException>(() => _repository.Create(library));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task CreateWithEmptySlugTest()
|
|
||||||
{
|
|
||||||
Library library = TestSample.GetNew<Library>();
|
|
||||||
library.Slug = string.Empty;
|
|
||||||
await Assert.ThrowsAsync<ArgumentException>(() => _repository.Create(library));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task CreateWithNumberSlugTest()
|
|
||||||
{
|
|
||||||
Library library = TestSample.GetNew<Library>();
|
|
||||||
library.Slug = "2";
|
|
||||||
Library ret = await _repository.Create(library);
|
|
||||||
Assert.Equal("2!", ret.Slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task CreateWithoutNameTest()
|
|
||||||
{
|
|
||||||
Library library = TestSample.GetNew<Library>();
|
|
||||||
library.Name = null;
|
|
||||||
await Assert.ThrowsAsync<ArgumentException>(() => _repository.Create(library));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task CreateWithProvider()
|
|
||||||
{
|
|
||||||
Library library = TestSample.GetNew<Library>();
|
|
||||||
library.Providers = new[] { TestSample.Get<Provider>() };
|
|
||||||
await _repository.Create(library);
|
|
||||||
Library retrieved = await _repository.Get(2);
|
|
||||||
await Repositories.LibraryManager.Load(retrieved, x => x.Providers);
|
|
||||||
Assert.Single(retrieved.Providers);
|
|
||||||
Assert.Equal(TestSample.Get<Provider>().Slug, retrieved.Providers.First().Slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task EditTest()
|
|
||||||
{
|
|
||||||
Library value = await _repository.Get(TestSample.Get<Library>().Slug);
|
|
||||||
value.Paths = new[] { "/super", "/test" };
|
|
||||||
value.Name = "New Title";
|
|
||||||
Library edited = await _repository.Edit(value, false);
|
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
|
||||||
Library show = await database.Libraries.FirstAsync();
|
|
||||||
|
|
||||||
KAssert.DeepEqual(show, edited);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task EditProvidersTest()
|
|
||||||
{
|
|
||||||
Library value = await _repository.Get(TestSample.Get<Library>().Slug);
|
|
||||||
value.Providers = new[]
|
|
||||||
{
|
|
||||||
TestSample.GetNew<Provider>()
|
|
||||||
};
|
|
||||||
Library edited = await _repository.Edit(value, false);
|
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
|
||||||
Library show = await database.Libraries
|
|
||||||
.Include(x => x.Providers)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
show.Providers.ForEach(x => x.Libraries = null);
|
|
||||||
edited.Providers.ForEach(x => x.Libraries = null);
|
|
||||||
KAssert.DeepEqual(show, edited);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task AddProvidersTest()
|
|
||||||
{
|
|
||||||
Library value = await _repository.Get(TestSample.Get<Library>().Slug);
|
|
||||||
await Repositories.LibraryManager.Load(value, x => x.Providers);
|
|
||||||
value.Providers.Add(TestSample.GetNew<Provider>());
|
|
||||||
Library edited = await _repository.Edit(value, false);
|
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
|
||||||
Library show = await database.Libraries
|
|
||||||
.Include(x => x.Providers)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
show.Providers.ForEach(x => x.Libraries = null);
|
|
||||||
edited.Providers.ForEach(x => x.Libraries = null);
|
|
||||||
KAssert.DeepEqual(show, edited);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("test")]
|
|
||||||
[InlineData("super")]
|
|
||||||
[InlineData("title")]
|
|
||||||
[InlineData("TiTlE")]
|
|
||||||
[InlineData("SuPeR")]
|
|
||||||
public async Task SearchTest(string query)
|
|
||||||
{
|
|
||||||
Library value = new()
|
|
||||||
{
|
|
||||||
Slug = "super-test",
|
|
||||||
Name = "This is a test title",
|
|
||||||
Paths = new[] { "path" }
|
|
||||||
};
|
|
||||||
await _repository.Create(value);
|
|
||||||
ICollection<Library> ret = await _repository.Search(query);
|
|
||||||
KAssert.DeepEqual(value, ret.First());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -52,17 +52,15 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task CreateWithExternalIdTest()
|
public async Task CreateWithExternalIdTest()
|
||||||
{
|
{
|
||||||
People value = TestSample.GetNew<People>();
|
People value = TestSample.GetNew<People>();
|
||||||
value.ExternalId = new[]
|
value.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new MetadataId
|
["2"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
new MetadataId
|
["1"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.GetNew<Provider>(),
|
|
||||||
Link = "new-provider-link",
|
Link = "new-provider-link",
|
||||||
DataId = "new-id"
|
DataId = "new-id"
|
||||||
}
|
}
|
||||||
@ -70,7 +68,6 @@ namespace Kyoo.Tests.Database
|
|||||||
await _repository.Create(value);
|
await _repository.Create(value);
|
||||||
|
|
||||||
People retrieved = await _repository.Get(2);
|
People retrieved = await _repository.Get(2);
|
||||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
|
||||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||||
KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
|
KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
|
||||||
KAssert.DeepEqual(value.ExternalId.Last(), retrieved.ExternalId.Last());
|
KAssert.DeepEqual(value.ExternalId.Last(), retrieved.ExternalId.Last());
|
||||||
@ -81,11 +78,8 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
People value = await _repository.Get(TestSample.Get<People>().Slug);
|
People value = await _repository.Get(TestSample.Get<People>().Slug);
|
||||||
value.Name = "New Name";
|
value.Name = "New Name";
|
||||||
value.Images = new Dictionary<int, string>
|
value.Poster = new Image("poster");
|
||||||
{
|
await _repository.Edit(value);
|
||||||
[Images.Poster] = "new-poster"
|
|
||||||
};
|
|
||||||
await _repository.Edit(value, false);
|
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
People retrieved = await database.People.FirstAsync();
|
People retrieved = await database.People.FirstAsync();
|
||||||
@ -97,22 +91,18 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task EditMetadataTest()
|
public async Task EditMetadataTest()
|
||||||
{
|
{
|
||||||
People value = await _repository.Get(TestSample.Get<People>().Slug);
|
People value = await _repository.Get(TestSample.Get<People>().Slug);
|
||||||
value.ExternalId = new[]
|
value.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new MetadataId
|
["toto"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
People retrieved = await database.People
|
People retrieved = await database.People.FirstAsync();
|
||||||
.Include(x => x.ExternalId)
|
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
}
|
}
|
||||||
@ -121,41 +111,32 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task AddMetadataTest()
|
public async Task AddMetadataTest()
|
||||||
{
|
{
|
||||||
People value = await _repository.Get(TestSample.Get<People>().Slug);
|
People value = await _repository.Get(TestSample.Get<People>().Slug);
|
||||||
value.ExternalId = new List<MetadataId>
|
value.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new()
|
["1"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
{
|
{
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
People retrieved = await database.People
|
People retrieved = await database.People.FirstAsync();
|
||||||
.Include(x => x.ExternalId)
|
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
}
|
}
|
||||||
|
|
||||||
value.ExternalId.Add(new MetadataId
|
value.ExternalId.Add("toto", new MetadataId
|
||||||
{
|
{
|
||||||
Provider = TestSample.GetNew<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
});
|
});
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
{
|
{
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
People retrieved = await database.People
|
People retrieved = await database.People.FirstAsync();
|
||||||
.Include(x => x.ExternalId)
|
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
}
|
}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Xunit;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace Kyoo.Tests.Database
|
|
||||||
{
|
|
||||||
namespace PostgreSQL
|
|
||||||
{
|
|
||||||
[Collection(nameof(Postgresql))]
|
|
||||||
public class ProviderTests : AProviderTests
|
|
||||||
{
|
|
||||||
public ProviderTests(PostgresFixture postgres, ITestOutputHelper output)
|
|
||||||
: base(new RepositoryActivator(output, postgres)) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class AProviderTests : RepositoryTests<Provider>
|
|
||||||
{
|
|
||||||
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
|
|
||||||
private readonly IProviderRepository _repository;
|
|
||||||
|
|
||||||
protected AProviderTests(RepositoryActivator repositories)
|
|
||||||
: base(repositories)
|
|
||||||
{
|
|
||||||
_repository = Repositories.LibraryManager.ProviderRepository;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -55,10 +55,10 @@ namespace Kyoo.Tests.Database
|
|||||||
Assert.Equal("anohana-s1", season.Slug);
|
Assert.Equal("anohana-s1", season.Slug);
|
||||||
Show show = new()
|
Show show = new()
|
||||||
{
|
{
|
||||||
Id = season.ShowID,
|
Id = season.ShowId,
|
||||||
Slug = "new-slug"
|
Slug = "new-slug"
|
||||||
};
|
};
|
||||||
await Repositories.LibraryManager.ShowRepository.Edit(show, false);
|
await Repositories.LibraryManager.ShowRepository.Edit(show);
|
||||||
season = await _repository.Get(1);
|
season = await _repository.Get(1);
|
||||||
Assert.Equal("new-slug-s1", season.Slug);
|
Assert.Equal("new-slug-s1", season.Slug);
|
||||||
}
|
}
|
||||||
@ -72,8 +72,8 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
SeasonNumber = 2,
|
SeasonNumber = 2,
|
||||||
ShowID = 1
|
ShowId = 1
|
||||||
}, false);
|
});
|
||||||
season = await _repository.Get(1);
|
season = await _repository.Get(1);
|
||||||
Assert.Equal("anohana-s2", season.Slug);
|
Assert.Equal("anohana-s2", season.Slug);
|
||||||
}
|
}
|
||||||
@ -83,7 +83,7 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
Season season = await _repository.Create(new Season
|
Season season = await _repository.Create(new Season
|
||||||
{
|
{
|
||||||
ShowID = TestSample.Get<Show>().Id,
|
ShowId = TestSample.Get<Show>().Id,
|
||||||
SeasonNumber = 2
|
SeasonNumber = 2
|
||||||
});
|
});
|
||||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2", season.Slug);
|
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2", season.Slug);
|
||||||
@ -93,17 +93,15 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task CreateWithExternalIdTest()
|
public async Task CreateWithExternalIdTest()
|
||||||
{
|
{
|
||||||
Season season = TestSample.GetNew<Season>();
|
Season season = TestSample.GetNew<Season>();
|
||||||
season.ExternalId = new[]
|
season.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new MetadataId
|
["2"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
new MetadataId
|
["1"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.GetNew<Provider>(),
|
|
||||||
Link = "new-provider-link",
|
Link = "new-provider-link",
|
||||||
DataId = "new-id"
|
DataId = "new-id"
|
||||||
}
|
}
|
||||||
@ -111,7 +109,6 @@ namespace Kyoo.Tests.Database
|
|||||||
await _repository.Create(season);
|
await _repository.Create(season);
|
||||||
|
|
||||||
Season retrieved = await _repository.Get(2);
|
Season retrieved = await _repository.Get(2);
|
||||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
|
||||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||||
KAssert.DeepEqual(season.ExternalId.First(), retrieved.ExternalId.First());
|
KAssert.DeepEqual(season.ExternalId.First(), retrieved.ExternalId.First());
|
||||||
KAssert.DeepEqual(season.ExternalId.Last(), retrieved.ExternalId.Last());
|
KAssert.DeepEqual(season.ExternalId.Last(), retrieved.ExternalId.Last());
|
||||||
@ -122,11 +119,8 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
Season value = await _repository.Get(TestSample.Get<Season>().Slug);
|
Season value = await _repository.Get(TestSample.Get<Season>().Slug);
|
||||||
value.Name = "New Title";
|
value.Name = "New Title";
|
||||||
value.Images = new Dictionary<int, string>
|
value.Poster = new Image("test");
|
||||||
{
|
await _repository.Edit(value);
|
||||||
[Images.Poster] = "new-poster"
|
|
||||||
};
|
|
||||||
await _repository.Edit(value, false);
|
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Season retrieved = await database.Seasons.FirstAsync();
|
Season retrieved = await database.Seasons.FirstAsync();
|
||||||
@ -138,22 +132,18 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task EditMetadataTest()
|
public async Task EditMetadataTest()
|
||||||
{
|
{
|
||||||
Season value = await _repository.Get(TestSample.Get<Season>().Slug);
|
Season value = await _repository.Get(TestSample.Get<Season>().Slug);
|
||||||
value.ExternalId = new[]
|
value.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new MetadataId
|
["toto"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Season retrieved = await database.Seasons
|
Season retrieved = await database.Seasons.FirstAsync();
|
||||||
.Include(x => x.ExternalId)
|
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
}
|
}
|
||||||
@ -162,41 +152,33 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task AddMetadataTest()
|
public async Task AddMetadataTest()
|
||||||
{
|
{
|
||||||
Season value = await _repository.Get(TestSample.Get<Season>().Slug);
|
Season value = await _repository.Get(TestSample.Get<Season>().Slug);
|
||||||
value.ExternalId = new List<MetadataId>
|
value.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new()
|
["1"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
{
|
{
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Season retrieved = await database.Seasons
|
Season retrieved = await database.Seasons.FirstAsync();
|
||||||
.Include(x => x.ExternalId)
|
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
}
|
}
|
||||||
|
|
||||||
value.ExternalId.Add(new MetadataId
|
value.ExternalId.Add("toto", new MetadataId
|
||||||
{
|
{
|
||||||
Provider = TestSample.GetNew<Provider>(),
|
|
||||||
Link = "link",
|
Link = "link",
|
||||||
DataId = "id"
|
DataId = "id"
|
||||||
});
|
});
|
||||||
await _repository.Edit(value, false);
|
await _repository.Edit(value);
|
||||||
|
|
||||||
{
|
{
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Season retrieved = await database.Seasons
|
Season retrieved = await database.Seasons.FirstAsync();
|
||||||
.Include(x => x.ExternalId)
|
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
KAssert.DeepEqual(value, retrieved);
|
KAssert.DeepEqual(value, retrieved);
|
||||||
}
|
}
|
||||||
@ -213,7 +195,7 @@ namespace Kyoo.Tests.Database
|
|||||||
Season value = new()
|
Season value = new()
|
||||||
{
|
{
|
||||||
Name = "This is a test super title",
|
Name = "This is a test super title",
|
||||||
ShowID = 1
|
ShowId = 1
|
||||||
};
|
};
|
||||||
await _repository.Create(value);
|
await _repository.Create(value);
|
||||||
ICollection<Season> ret = await _repository.Search(query);
|
ICollection<Season> ret = await _repository.Search(query);
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -55,9 +54,8 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task EditTest()
|
public async Task EditTest()
|
||||||
{
|
{
|
||||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||||
value.Path = "/super";
|
|
||||||
value.Name = "New Title";
|
value.Name = "New Title";
|
||||||
Show edited = await _repository.Edit(value, false);
|
Show edited = await _repository.Edit(value);
|
||||||
KAssert.DeepEqual(value, edited);
|
KAssert.DeepEqual(value, edited);
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
@ -70,11 +68,11 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task EditGenreTest()
|
public async Task EditGenreTest()
|
||||||
{
|
{
|
||||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||||
value.Genres = new[] { new Genre("test") };
|
value.Genres = new List<Genre> { Genre.Action };
|
||||||
Show edited = await _repository.Edit(value, false);
|
Show edited = await _repository.Edit(value);
|
||||||
|
|
||||||
Assert.Equal(value.Slug, edited.Slug);
|
Assert.Equal(value.Slug, edited.Slug);
|
||||||
Assert.Equal(value.Genres.Select(x => new { x.Slug, x.Name }), edited.Genres.Select(x => new { x.Slug, x.Name }));
|
Assert.Equal(value.Genres, edited.Genres);
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Show show = await database.Shows
|
Show show = await database.Shows
|
||||||
@ -82,19 +80,18 @@ namespace Kyoo.Tests.Database
|
|||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
Assert.Equal(value.Slug, show.Slug);
|
Assert.Equal(value.Slug, show.Slug);
|
||||||
Assert.Equal(value.Genres.Select(x => new { x.Slug, x.Name }), show.Genres.Select(x => new { x.Slug, x.Name }));
|
Assert.Equal(value.Genres, show.Genres);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AddGenreTest()
|
public async Task AddGenreTest()
|
||||||
{
|
{
|
||||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||||
await Repositories.LibraryManager.Load(value, x => x.Genres);
|
value.Genres.Add(Genre.Drama);
|
||||||
value.Genres.Add(new Genre("test"));
|
Show edited = await _repository.Edit(value);
|
||||||
Show edited = await _repository.Edit(value, false);
|
|
||||||
|
|
||||||
Assert.Equal(value.Slug, edited.Slug);
|
Assert.Equal(value.Slug, edited.Slug);
|
||||||
Assert.Equal(value.Genres.Select(x => new { x.Slug, x.Name }), edited.Genres.Select(x => new { x.Slug, x.Name }));
|
Assert.Equal(value.Genres, edited.Genres);
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Show show = await database.Shows
|
Show show = await database.Shows
|
||||||
@ -102,7 +99,7 @@ namespace Kyoo.Tests.Database
|
|||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
Assert.Equal(value.Slug, show.Slug);
|
Assert.Equal(value.Slug, show.Slug);
|
||||||
Assert.Equal(value.Genres.Select(x => new { x.Slug, x.Name }), show.Genres.Select(x => new { x.Slug, x.Name }));
|
Assert.Equal(value.Genres, show.Genres);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -110,10 +107,10 @@ namespace Kyoo.Tests.Database
|
|||||||
{
|
{
|
||||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||||
value.Studio = new Studio("studio");
|
value.Studio = new Studio("studio");
|
||||||
Show edited = await _repository.Edit(value, false);
|
Show edited = await _repository.Edit(value);
|
||||||
|
|
||||||
Assert.Equal(value.Slug, edited.Slug);
|
Assert.Equal(value.Slug, edited.Slug);
|
||||||
Assert.Equal("studio", edited.Studio.Slug);
|
Assert.Equal("studio", edited.Studio!.Slug);
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Show show = await database.Shows
|
Show show = await database.Shows
|
||||||
@ -121,15 +118,15 @@ namespace Kyoo.Tests.Database
|
|||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
Assert.Equal(value.Slug, show.Slug);
|
Assert.Equal(value.Slug, show.Slug);
|
||||||
Assert.Equal("studio", show.Studio.Slug);
|
Assert.Equal("studio", show.Studio!.Slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task EditAliasesTest()
|
public async Task EditAliasesTest()
|
||||||
{
|
{
|
||||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||||
value.Aliases = new[] { "NiceNewAlias", "SecondAlias" };
|
value.Aliases = new List<string>() { "NiceNewAlias", "SecondAlias" };
|
||||||
Show edited = await _repository.Edit(value, false);
|
Show edited = await _repository.Edit(value);
|
||||||
|
|
||||||
Assert.Equal(value.Slug, edited.Slug);
|
Assert.Equal(value.Slug, edited.Slug);
|
||||||
Assert.Equal(value.Aliases, edited.Aliases);
|
Assert.Equal(value.Aliases, edited.Aliases);
|
||||||
@ -156,10 +153,10 @@ namespace Kyoo.Tests.Database
|
|||||||
Role = "NiceCharacter"
|
Role = "NiceCharacter"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Show edited = await _repository.Edit(value, false);
|
Show edited = await _repository.Edit(value);
|
||||||
|
|
||||||
Assert.Equal(value.Slug, edited.Slug);
|
Assert.Equal(value.Slug, edited.Slug);
|
||||||
Assert.Equal(edited.People.First().ShowID, value.Id);
|
Assert.Equal(edited.People!.First().ShowID, value.Id);
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
value.People.Select(x => new { x.Role, x.Slug, x.People.Name }),
|
value.People.Select(x => new { x.Role, x.Slug, x.People.Name }),
|
||||||
edited.People.Select(x => new { x.Role, x.Slug, x.People.Name }));
|
edited.People.Select(x => new { x.Role, x.Slug, x.People.Name }));
|
||||||
@ -173,62 +170,30 @@ namespace Kyoo.Tests.Database
|
|||||||
Assert.Equal(value.Slug, show.Slug);
|
Assert.Equal(value.Slug, show.Slug);
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
value.People.Select(x => new { x.Role, x.Slug, x.People.Name }),
|
value.People.Select(x => new { x.Role, x.Slug, x.People.Name }),
|
||||||
show.People.Select(x => new { x.Role, x.Slug, x.People.Name }));
|
show.People!.Select(x => new { x.Role, x.Slug, x.People.Name }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task EditExternalIDsTest()
|
public async Task EditExternalIDsTest()
|
||||||
{
|
{
|
||||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||||
value.ExternalId = new[]
|
value.ExternalId = new Dictionary<string, MetadataId>()
|
||||||
{
|
{
|
||||||
new MetadataId
|
["test"] = new()
|
||||||
{
|
{
|
||||||
Provider = new Provider("test", "test.png"),
|
|
||||||
DataId = "1234"
|
DataId = "1234"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Show edited = await _repository.Edit(value, false);
|
Show edited = await _repository.Edit(value);
|
||||||
|
|
||||||
Assert.Equal(value.Slug, edited.Slug);
|
Assert.Equal(value.Slug, edited.Slug);
|
||||||
Assert.Equal(
|
Assert.Equal(value.ExternalId, edited.ExternalId);
|
||||||
value.ExternalId.Select(x => new { x.DataID, x.Provider.Slug }),
|
|
||||||
edited.ExternalId.Select(x => new { x.DataID, x.Provider.Slug }));
|
|
||||||
|
|
||||||
await using DatabaseContext database = Repositories.Context.New();
|
await using DatabaseContext database = Repositories.Context.New();
|
||||||
Show show = await database.Shows
|
Show show = await database.Shows.FirstAsync();
|
||||||
.Include(x => x.ExternalId)
|
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
Assert.Equal(value.Slug, show.Slug);
|
Assert.Equal(value.Slug, show.Slug);
|
||||||
Assert.Equal(
|
Assert.Equal(value.ExternalId, show.ExternalId);
|
||||||
value.ExternalId.Select(x => new { x.DataID, x.Provider.Slug }),
|
|
||||||
show.ExternalId.Select(x => new { x.DataID, x.Provider.Slug }));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task EditResetOldTest()
|
|
||||||
{
|
|
||||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
|
||||||
Show newValue = new()
|
|
||||||
{
|
|
||||||
Id = value.Id,
|
|
||||||
Slug = "reset",
|
|
||||||
Name = "Reset"
|
|
||||||
};
|
|
||||||
|
|
||||||
Show edited = await _repository.Edit(newValue, true);
|
|
||||||
|
|
||||||
Assert.Equal(value.Id, edited.Id);
|
|
||||||
Assert.Null(edited.Overview);
|
|
||||||
Assert.Equal("reset", edited.Slug);
|
|
||||||
Assert.Equal("Reset", edited.Name);
|
|
||||||
Assert.Null(edited.Aliases);
|
|
||||||
Assert.Null(edited.ExternalId);
|
|
||||||
Assert.Null(edited.People);
|
|
||||||
Assert.Null(edited.Genres);
|
|
||||||
Assert.Null(edited.Studio);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -237,22 +202,14 @@ namespace Kyoo.Tests.Database
|
|||||||
Show expected = TestSample.Get<Show>();
|
Show expected = TestSample.Get<Show>();
|
||||||
expected.Id = 0;
|
expected.Id = 0;
|
||||||
expected.Slug = "created-relation-test";
|
expected.Slug = "created-relation-test";
|
||||||
expected.ExternalId = new[]
|
expected.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new MetadataId
|
["test"] = new()
|
||||||
{
|
{
|
||||||
Provider = new Provider("provider", "provider.png"),
|
|
||||||
DataId = "ID"
|
DataId = "ID"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
expected.Genres = new[]
|
expected.Genres = new List<Genre>() { Genre.Action };
|
||||||
{
|
|
||||||
new Genre
|
|
||||||
{
|
|
||||||
Name = "Genre",
|
|
||||||
Slug = "genre"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
expected.People = new[]
|
expected.People = new[]
|
||||||
{
|
{
|
||||||
new PeopleRole
|
new PeopleRole
|
||||||
@ -270,7 +227,6 @@ namespace Kyoo.Tests.Database
|
|||||||
await using DatabaseContext context = Repositories.Context.New();
|
await using DatabaseContext context = Repositories.Context.New();
|
||||||
Show retrieved = await context.Shows
|
Show retrieved = await context.Shows
|
||||||
.Include(x => x.ExternalId)
|
.Include(x => x.ExternalId)
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.Include(x => x.Genres)
|
.Include(x => x.Genres)
|
||||||
.Include(x => x.People)
|
.Include(x => x.People)
|
||||||
.ThenInclude(x => x.People)
|
.ThenInclude(x => x.People)
|
||||||
@ -281,10 +237,7 @@ namespace Kyoo.Tests.Database
|
|||||||
x.Show = null;
|
x.Show = null;
|
||||||
x.People.Roles = null;
|
x.People.Roles = null;
|
||||||
});
|
});
|
||||||
retrieved.Studio.Shows = null;
|
retrieved.Studio!.Shows = null;
|
||||||
retrieved.Genres.ForEach(x => x.Shows = null);
|
|
||||||
|
|
||||||
expected.Genres.ForEach(x => x.Shows = null);
|
|
||||||
expected.People.ForEach(x =>
|
expected.People.ForEach(x =>
|
||||||
{
|
{
|
||||||
x.Show = null;
|
x.Show = null;
|
||||||
@ -300,11 +253,10 @@ namespace Kyoo.Tests.Database
|
|||||||
Show expected = TestSample.Get<Show>();
|
Show expected = TestSample.Get<Show>();
|
||||||
expected.Id = 0;
|
expected.Id = 0;
|
||||||
expected.Slug = "created-relation-test";
|
expected.Slug = "created-relation-test";
|
||||||
expected.ExternalId = new[]
|
expected.ExternalId = new Dictionary<string, MetadataId>
|
||||||
{
|
{
|
||||||
new MetadataId
|
["test"] = new()
|
||||||
{
|
{
|
||||||
Provider = TestSample.Get<Provider>(),
|
|
||||||
DataId = "ID"
|
DataId = "ID"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -313,11 +265,10 @@ namespace Kyoo.Tests.Database
|
|||||||
await using DatabaseContext context = Repositories.Context.New();
|
await using DatabaseContext context = Repositories.Context.New();
|
||||||
Show retrieved = await context.Shows
|
Show retrieved = await context.Shows
|
||||||
.Include(x => x.ExternalId)
|
.Include(x => x.ExternalId)
|
||||||
.ThenInclude(x => x.Provider)
|
|
||||||
.FirstAsync(x => x.Id == created.Id);
|
.FirstAsync(x => x.Id == created.Id);
|
||||||
KAssert.DeepEqual(expected, retrieved);
|
KAssert.DeepEqual(expected, retrieved);
|
||||||
Assert.Single(retrieved.ExternalId);
|
Assert.Single(retrieved.ExternalId);
|
||||||
Assert.Equal("ID", retrieved.ExternalId.First().DataID);
|
Assert.Equal("ID", retrieved.ExternalId["test"].DataId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -362,25 +313,12 @@ namespace Kyoo.Tests.Database
|
|||||||
await Repositories.LibraryManager.Load(show, x => x.Seasons);
|
await Repositories.LibraryManager.Load(show, x => x.Seasons);
|
||||||
await Repositories.LibraryManager.Load(show, x => x.Episodes);
|
await Repositories.LibraryManager.Load(show, x => x.Episodes);
|
||||||
Assert.Equal(1, await _repository.GetCount());
|
Assert.Equal(1, await _repository.GetCount());
|
||||||
Assert.Single(show.Seasons);
|
Assert.Single(show.Seasons!);
|
||||||
Assert.Single(show.Episodes);
|
Assert.Single(show.Episodes!);
|
||||||
await _repository.Delete(show);
|
await _repository.Delete(show);
|
||||||
Assert.Equal(0, await Repositories.LibraryManager.ShowRepository.GetCount());
|
Assert.Equal(0, await Repositories.LibraryManager.ShowRepository.GetCount());
|
||||||
Assert.Equal(0, await Repositories.LibraryManager.SeasonRepository.GetCount());
|
Assert.Equal(0, await Repositories.LibraryManager.SeasonRepository.GetCount());
|
||||||
Assert.Equal(0, await Repositories.LibraryManager.EpisodeRepository.GetCount());
|
Assert.Equal(0, await Repositories.LibraryManager.EpisodeRepository.GetCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task AddShowLinkTest()
|
|
||||||
{
|
|
||||||
await Repositories.LibraryManager.Create(TestSample.GetNew<Library>());
|
|
||||||
await _repository.AddShowLink(1, 2, null);
|
|
||||||
|
|
||||||
await using DatabaseContext context = Repositories.Context.New();
|
|
||||||
Show show = context.Shows
|
|
||||||
.Include(x => x.Libraries)
|
|
||||||
.First(x => x.ID == 1);
|
|
||||||
Assert.Contains(2, show.Libraries.Select(x => x.ID));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,7 @@ namespace Kyoo.Tests
|
|||||||
Slug = "new-collection",
|
Slug = "new-collection",
|
||||||
Name = "New Collection",
|
Name = "New Collection",
|
||||||
Overview = "A collection created by new sample",
|
Overview = "A collection created by new sample",
|
||||||
Images = new Dictionary<int, string>
|
Thumbnail = new Image("thumbnail")
|
||||||
{
|
|
||||||
[Images.Thumbnail] = "thumbnail"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -52,13 +49,9 @@ namespace Kyoo.Tests
|
|||||||
Status = Status.Planned,
|
Status = Status.Planned,
|
||||||
StartAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
StartAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
||||||
EndAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
EndAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
||||||
Images = new Dictionary<int, string>
|
Poster = new Image("Poster"),
|
||||||
{
|
Logo = new Image("Logo"),
|
||||||
[Images.Poster] = "Poster",
|
Thumbnail = new Image("Thumbnail"),
|
||||||
[Images.Logo] = "Logo",
|
|
||||||
[Images.Thumbnail] = "Thumbnail"
|
|
||||||
},
|
|
||||||
IsMovie = false,
|
|
||||||
Studio = null
|
Studio = null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -67,17 +60,14 @@ namespace Kyoo.Tests
|
|||||||
() => new Season
|
() => new Season
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2,
|
||||||
ShowID = 1,
|
ShowId = 1,
|
||||||
ShowSlug = Get<Show>().Slug,
|
ShowSlug = Get<Show>().Slug,
|
||||||
Name = "New season",
|
Name = "New season",
|
||||||
Overview = "New overview",
|
Overview = "New overview",
|
||||||
EndDate = new DateTime(2000, 10, 10).ToUniversalTime(),
|
EndDate = new DateTime(2000, 10, 10).ToUniversalTime(),
|
||||||
SeasonNumber = 2,
|
SeasonNumber = 2,
|
||||||
StartDate = new DateTime(2010, 10, 10).ToUniversalTime(),
|
StartDate = new DateTime(2010, 10, 10).ToUniversalTime(),
|
||||||
Images = new Dictionary<int, string>
|
Logo = new Image("logo")
|
||||||
{
|
|
||||||
[Images.Logo] = "logo"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -85,9 +75,9 @@ namespace Kyoo.Tests
|
|||||||
() => new Episode
|
() => new Episode
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2,
|
||||||
ShowID = 1,
|
ShowId = 1,
|
||||||
ShowSlug = Get<Show>().Slug,
|
ShowSlug = Get<Show>().Slug,
|
||||||
SeasonID = 1,
|
SeasonId = 1,
|
||||||
SeasonNumber = Get<Season>().SeasonNumber,
|
SeasonNumber = Get<Season>().SeasonNumber,
|
||||||
EpisodeNumber = 3,
|
EpisodeNumber = 3,
|
||||||
AbsoluteNumber = 4,
|
AbsoluteNumber = 4,
|
||||||
@ -95,23 +85,7 @@ namespace Kyoo.Tests
|
|||||||
Name = "New Episode Title",
|
Name = "New Episode Title",
|
||||||
ReleaseDate = new DateTime(2000, 10, 10).ToUniversalTime(),
|
ReleaseDate = new DateTime(2000, 10, 10).ToUniversalTime(),
|
||||||
Overview = "new episode overview",
|
Overview = "new episode overview",
|
||||||
Images = new Dictionary<int, string>
|
Logo = new Image("new episode logo")
|
||||||
{
|
|
||||||
[Images.Logo] = "new episode logo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
typeof(Provider),
|
|
||||||
() => new Provider
|
|
||||||
{
|
|
||||||
ID = 2,
|
|
||||||
Slug = "new-provider",
|
|
||||||
Name = "Provider NewSample",
|
|
||||||
Images = new Dictionary<int, string>
|
|
||||||
{
|
|
||||||
[Images.Logo] = "logo"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -121,27 +95,14 @@ namespace Kyoo.Tests
|
|||||||
Id = 2,
|
Id = 2,
|
||||||
Slug = "new-person-name",
|
Slug = "new-person-name",
|
||||||
Name = "New person name",
|
Name = "New person name",
|
||||||
Images = new Dictionary<int, string>
|
Logo = new Image("Old Logo"),
|
||||||
{
|
Poster = new Image("Old poster")
|
||||||
[Images.Logo] = "Old Logo",
|
|
||||||
[Images.Poster] = "Old poster"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Dictionary<Type, Func<object>> Samples = new()
|
private static readonly Dictionary<Type, Func<object>> Samples = new()
|
||||||
{
|
{
|
||||||
{
|
|
||||||
typeof(Library),
|
|
||||||
() => new Library
|
|
||||||
{
|
|
||||||
ID = 1,
|
|
||||||
Slug = "deck",
|
|
||||||
Name = "Deck",
|
|
||||||
Paths = new[] { "/path/to/deck" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
typeof(Collection),
|
typeof(Collection),
|
||||||
() => new Collection
|
() => new Collection
|
||||||
@ -150,10 +111,7 @@ namespace Kyoo.Tests
|
|||||||
Slug = "collection",
|
Slug = "collection",
|
||||||
Name = "Collection",
|
Name = "Collection",
|
||||||
Overview = "A nice collection for tests",
|
Overview = "A nice collection for tests",
|
||||||
Images = new Dictionary<int, string>
|
Poster = new Image("Poster")
|
||||||
{
|
|
||||||
[Images.Poster] = "Poster"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -163,7 +121,7 @@ namespace Kyoo.Tests
|
|||||||
Id = 1,
|
Id = 1,
|
||||||
Slug = "anohana",
|
Slug = "anohana",
|
||||||
Name = "Anohana: The Flower We Saw That Day",
|
Name = "Anohana: The Flower We Saw That Day",
|
||||||
Aliases = new[]
|
Aliases = new List<string>
|
||||||
{
|
{
|
||||||
"Ano Hi Mita Hana no Namae o Bokutachi wa Mada Shiranai.",
|
"Ano Hi Mita Hana no Namae o Bokutachi wa Mada Shiranai.",
|
||||||
"AnoHana",
|
"AnoHana",
|
||||||
@ -173,16 +131,12 @@ namespace Kyoo.Tests
|
|||||||
"In time, however, these childhood friends drifted apart, and when they became high " +
|
"In time, however, these childhood friends drifted apart, and when they became high " +
|
||||||
"school students, they had long ceased to think of each other as friends.",
|
"school students, they had long ceased to think of each other as friends.",
|
||||||
Status = Status.Finished,
|
Status = Status.Finished,
|
||||||
StudioID = 1,
|
StudioId = 1,
|
||||||
StartAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
StartAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
||||||
EndAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
EndAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
||||||
Images = new Dictionary<int, string>
|
Poster = new Image("Poster"),
|
||||||
{
|
Logo = new Image("Logo"),
|
||||||
[Images.Poster] = "Poster",
|
Thumbnail = new Image("Thumbnail"),
|
||||||
[Images.Logo] = "Logo",
|
|
||||||
[Images.Thumbnail] = "Thumbnail"
|
|
||||||
},
|
|
||||||
IsMovie = false,
|
|
||||||
Studio = null
|
Studio = null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -192,18 +146,15 @@ namespace Kyoo.Tests
|
|||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
ShowSlug = "anohana",
|
ShowSlug = "anohana",
|
||||||
ShowID = 1,
|
ShowId = 1,
|
||||||
SeasonNumber = 1,
|
SeasonNumber = 1,
|
||||||
Name = "Season 1",
|
Name = "Season 1",
|
||||||
Overview = "The first season",
|
Overview = "The first season",
|
||||||
StartDate = new DateTime(2020, 06, 05).ToUniversalTime(),
|
StartDate = new DateTime(2020, 06, 05).ToUniversalTime(),
|
||||||
EndDate = new DateTime(2020, 07, 05).ToUniversalTime(),
|
EndDate = new DateTime(2020, 07, 05).ToUniversalTime(),
|
||||||
Images = new Dictionary<int, string>
|
Poster = new Image("Poster"),
|
||||||
{
|
Logo = new Image("Logo"),
|
||||||
[Images.Poster] = "Poster",
|
Thumbnail = new Image("Thumbnail")
|
||||||
[Images.Logo] = "Logo",
|
|
||||||
[Images.Thumbnail] = "Thumbnail"
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -212,18 +163,15 @@ namespace Kyoo.Tests
|
|||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
ShowSlug = "anohana",
|
ShowSlug = "anohana",
|
||||||
ShowID = 1,
|
ShowId = 1,
|
||||||
SeasonID = 1,
|
SeasonId = 1,
|
||||||
SeasonNumber = 1,
|
SeasonNumber = 1,
|
||||||
EpisodeNumber = 1,
|
EpisodeNumber = 1,
|
||||||
AbsoluteNumber = 1,
|
AbsoluteNumber = 1,
|
||||||
Path = "/home/kyoo/anohana-s1e1",
|
Path = "/home/kyoo/anohana-s1e1",
|
||||||
Images = new Dictionary<int, string>
|
Poster = new Image("Poster"),
|
||||||
{
|
Logo = new Image("Logo"),
|
||||||
[Images.Poster] = "Poster",
|
Thumbnail = new Image("Thumbnail"),
|
||||||
[Images.Logo] = "Logo",
|
|
||||||
[Images.Thumbnail] = "Thumbnail"
|
|
||||||
},
|
|
||||||
Name = "Episode 1",
|
Name = "Episode 1",
|
||||||
Overview = "Summary of the first episode",
|
Overview = "Summary of the first episode",
|
||||||
ReleaseDate = new DateTime(2020, 06, 05).ToUniversalTime()
|
ReleaseDate = new DateTime(2020, 06, 05).ToUniversalTime()
|
||||||
@ -236,12 +184,9 @@ namespace Kyoo.Tests
|
|||||||
Id = 1,
|
Id = 1,
|
||||||
Slug = "the-actor",
|
Slug = "the-actor",
|
||||||
Name = "The Actor",
|
Name = "The Actor",
|
||||||
Images = new Dictionary<int, string>
|
Poster = new Image("Poster"),
|
||||||
{
|
Logo = new Image("Logo"),
|
||||||
[Images.Poster] = "Poster",
|
Thumbnail = new Image("Thumbnail")
|
||||||
[Images.Logo] = "Logo",
|
|
||||||
[Images.Thumbnail] = "Thumbnail"
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -253,30 +198,6 @@ namespace Kyoo.Tests
|
|||||||
Name = "Hyper studio",
|
Name = "Hyper studio",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
typeof(Genre),
|
|
||||||
() => new Genre
|
|
||||||
{
|
|
||||||
ID = 1,
|
|
||||||
Slug = "action",
|
|
||||||
Name = "Action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
typeof(Provider),
|
|
||||||
() => new Provider
|
|
||||||
{
|
|
||||||
ID = 1,
|
|
||||||
Slug = "tvdb",
|
|
||||||
Name = "The TVDB",
|
|
||||||
Images = new Dictionary<int, string>
|
|
||||||
{
|
|
||||||
[Images.Poster] = "Poster",
|
|
||||||
[Images.Logo] = "path/tvdb.svg",
|
|
||||||
[Images.Thumbnail] = "Thumbnail"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
typeof(User),
|
typeof(User),
|
||||||
() => new User
|
() => new User
|
||||||
@ -309,20 +230,20 @@ namespace Kyoo.Tests
|
|||||||
|
|
||||||
Show show = Get<Show>();
|
Show show = Get<Show>();
|
||||||
show.Id = 0;
|
show.Id = 0;
|
||||||
show.StudioID = 0;
|
show.StudioId = 0;
|
||||||
context.Shows.Add(show);
|
context.Shows.Add(show);
|
||||||
|
|
||||||
Season season = Get<Season>();
|
Season season = Get<Season>();
|
||||||
season.Id = 0;
|
season.Id = 0;
|
||||||
season.ShowID = 0;
|
season.ShowId = 0;
|
||||||
season.Show = show;
|
season.Show = show;
|
||||||
context.Seasons.Add(season);
|
context.Seasons.Add(season);
|
||||||
|
|
||||||
Episode episode = Get<Episode>();
|
Episode episode = Get<Episode>();
|
||||||
episode.Id = 0;
|
episode.Id = 0;
|
||||||
episode.ShowID = 0;
|
episode.ShowId = 0;
|
||||||
episode.Show = show;
|
episode.Show = show;
|
||||||
episode.SeasonID = 0;
|
episode.SeasonId = 0;
|
||||||
episode.Season = season;
|
episode.Season = season;
|
||||||
context.Episodes.Add(episode);
|
context.Episodes.Add(episode);
|
||||||
|
|
||||||
@ -331,20 +252,10 @@ namespace Kyoo.Tests
|
|||||||
studio.Shows = new List<Show> { show };
|
studio.Shows = new List<Show> { show };
|
||||||
context.Studios.Add(studio);
|
context.Studios.Add(studio);
|
||||||
|
|
||||||
Genre genre = Get<Genre>();
|
|
||||||
genre.ID = 0;
|
|
||||||
genre.Shows = new List<Show> { show };
|
|
||||||
context.Genres.Add(genre);
|
|
||||||
|
|
||||||
People people = Get<People>();
|
People people = Get<People>();
|
||||||
people.Id = 0;
|
people.Id = 0;
|
||||||
context.People.Add(people);
|
context.People.Add(people);
|
||||||
|
|
||||||
Library library = Get<Library>();
|
|
||||||
library.ID = 0;
|
|
||||||
library.Collections = new List<Collection> { collection };
|
|
||||||
context.Libraries.Add(library);
|
|
||||||
|
|
||||||
User user = Get<User>();
|
User user = Get<User>();
|
||||||
user.Id = 0;
|
user.Id = 0;
|
||||||
context.Users.Add(user);
|
context.Users.Add(user);
|
||||||
@ -358,41 +269,18 @@ namespace Kyoo.Tests
|
|||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2,
|
||||||
ShowSlug = "anohana",
|
ShowSlug = "anohana",
|
||||||
ShowID = 1,
|
ShowId = 1,
|
||||||
SeasonNumber = null,
|
SeasonNumber = null,
|
||||||
EpisodeNumber = null,
|
EpisodeNumber = null,
|
||||||
AbsoluteNumber = 3,
|
AbsoluteNumber = 3,
|
||||||
Path = "/home/kyoo/anohana-3",
|
Path = "/home/kyoo/anohana-3",
|
||||||
Images = new Dictionary<int, string>
|
Poster = new Image("Poster"),
|
||||||
{
|
Logo = new Image("Logo"),
|
||||||
[Images.Poster] = "Poster",
|
Thumbnail = new Image("Thumbnail"),
|
||||||
[Images.Logo] = "Logo",
|
|
||||||
[Images.Thumbnail] = "Thumbnail"
|
|
||||||
},
|
|
||||||
Name = "Episode 3",
|
Name = "Episode 3",
|
||||||
Overview = "Summary of the third absolute episode",
|
Overview = "Summary of the third absolute episode",
|
||||||
ReleaseDate = new DateTime(2020, 06, 05).ToUniversalTime()
|
ReleaseDate = new DateTime(2020, 06, 05).ToUniversalTime()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Episode GetMovieEpisode()
|
|
||||||
{
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
Id = 3,
|
|
||||||
ShowSlug = "anohana",
|
|
||||||
ShowID = 1,
|
|
||||||
Path = "/home/kyoo/john-wick",
|
|
||||||
Images = new Dictionary<int, string>
|
|
||||||
{
|
|
||||||
[Images.Poster] = "Poster",
|
|
||||||
[Images.Logo] = "Logo",
|
|
||||||
[Images.Thumbnail] = "Thumbnail"
|
|
||||||
},
|
|
||||||
Name = "John wick",
|
|
||||||
Overview = "A movie episode test",
|
|
||||||
ReleaseDate = new DateTime(1595, 05, 12).ToUniversalTime()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,7 @@
|
|||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -27,53 +25,6 @@ namespace Kyoo.Tests.Utility
|
|||||||
{
|
{
|
||||||
public class EnumerableTests
|
public class EnumerableTests
|
||||||
{
|
{
|
||||||
[Fact]
|
|
||||||
public void MapTest()
|
|
||||||
{
|
|
||||||
int[] list = { 1, 2, 3, 4 };
|
|
||||||
Assert.All(list.Map((x, i) => (x, i)), x => Assert.Equal(x.x - 1, x.i));
|
|
||||||
Assert.Throws<ArgumentNullException>(() => list.Map(((Func<int, int, int>)null)!));
|
|
||||||
list = null;
|
|
||||||
Assert.Throws<ArgumentNullException>(() => list!.Map((x, _) => x + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MapAsyncTest()
|
|
||||||
{
|
|
||||||
int[] list = { 1, 2, 3, 4 };
|
|
||||||
await foreach ((int x, int i) in list.MapAsync((x, i) => Task.FromResult((x, i))))
|
|
||||||
{
|
|
||||||
Assert.Equal(x - 1, i);
|
|
||||||
}
|
|
||||||
Assert.Throws<ArgumentNullException>(() => list.MapAsync(((Func<int, int, Task<int>>)null)!));
|
|
||||||
list = null;
|
|
||||||
Assert.Throws<ArgumentNullException>(() => list!.MapAsync((x, _) => Task.FromResult(x + 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SelectAsyncTest()
|
|
||||||
{
|
|
||||||
int[] list = { 1, 2, 3, 4 };
|
|
||||||
int i = 2;
|
|
||||||
await foreach (int x in list.SelectAsync(x => Task.FromResult(x + 1)))
|
|
||||||
{
|
|
||||||
Assert.Equal(i++, x);
|
|
||||||
}
|
|
||||||
Assert.Throws<ArgumentNullException>(() => list.SelectAsync(((Func<int, Task<int>>)null)!));
|
|
||||||
list = null;
|
|
||||||
Assert.Throws<ArgumentNullException>(() => list!.SelectAsync(x => Task.FromResult(x + 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ToListAsyncTest()
|
|
||||||
{
|
|
||||||
int[] expected = { 1, 2, 3, 4 };
|
|
||||||
IAsyncEnumerable<int> list = expected.SelectAsync(Task.FromResult);
|
|
||||||
Assert.Equal(expected, await list.ToListAsync());
|
|
||||||
list = null;
|
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>(() => list!.ToListAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IfEmptyTest()
|
public void IfEmptyTest()
|
||||||
{
|
{
|
||||||
|
@ -16,13 +16,9 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -30,318 +26,21 @@ namespace Kyoo.Tests.Utility
|
|||||||
{
|
{
|
||||||
public class MergerTests
|
public class MergerTests
|
||||||
{
|
{
|
||||||
[Fact]
|
|
||||||
public void NullifyTest()
|
|
||||||
{
|
|
||||||
Genre genre = new("test")
|
|
||||||
{
|
|
||||||
ID = 5
|
|
||||||
};
|
|
||||||
Merger.Nullify(genre);
|
|
||||||
Assert.Equal(0, genre.ID);
|
|
||||||
Assert.Null(genre.Name);
|
|
||||||
Assert.Null(genre.Slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MergeTest()
|
|
||||||
{
|
|
||||||
Genre genre = new()
|
|
||||||
{
|
|
||||||
ID = 5
|
|
||||||
};
|
|
||||||
Genre genre2 = new()
|
|
||||||
{
|
|
||||||
Name = "test"
|
|
||||||
};
|
|
||||||
Genre ret = Merger.Merge(genre, genre2);
|
|
||||||
Assert.True(ReferenceEquals(genre, ret));
|
|
||||||
Assert.Equal(5, ret.ID);
|
|
||||||
Assert.Equal("test", genre.Name);
|
|
||||||
Assert.Null(genre.Slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[SuppressMessage("ReSharper", "ExpressionIsAlwaysNull")]
|
|
||||||
public void MergeNullTests()
|
|
||||||
{
|
|
||||||
Genre genre = new()
|
|
||||||
{
|
|
||||||
ID = 5
|
|
||||||
};
|
|
||||||
Assert.True(ReferenceEquals(genre, Merger.Merge(genre, null)));
|
|
||||||
Assert.True(ReferenceEquals(genre, Merger.Merge(null, genre)));
|
|
||||||
Assert.Null(Merger.Merge<Genre>(null, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestIOnMerge : IOnMerge
|
|
||||||
{
|
|
||||||
public void OnMerge(object other)
|
|
||||||
{
|
|
||||||
Exception exception = new();
|
|
||||||
exception.Data[0] = other;
|
|
||||||
throw exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void OnMergeTest()
|
|
||||||
{
|
|
||||||
TestIOnMerge test = new();
|
|
||||||
TestIOnMerge test2 = new();
|
|
||||||
Assert.Throws<Exception>(() => Merger.Merge(test, test2));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Merger.Merge(test, test2);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Assert.True(ReferenceEquals(test2, ex.Data[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Test
|
|
||||||
{
|
|
||||||
public int ID { get; set; }
|
|
||||||
|
|
||||||
public int[] Numbers { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GlobalMergeListTest()
|
|
||||||
{
|
|
||||||
Test test = new()
|
|
||||||
{
|
|
||||||
ID = 5,
|
|
||||||
Numbers = new[] { 1 }
|
|
||||||
};
|
|
||||||
Test test2 = new()
|
|
||||||
{
|
|
||||||
Numbers = new[] { 3 }
|
|
||||||
};
|
|
||||||
Test ret = Merger.Merge(test, test2);
|
|
||||||
Assert.True(ReferenceEquals(test, ret));
|
|
||||||
Assert.Equal(5, ret.ID);
|
|
||||||
|
|
||||||
Assert.Equal(2, ret.Numbers.Length);
|
|
||||||
Assert.Equal(1, ret.Numbers[0]);
|
|
||||||
Assert.Equal(3, ret.Numbers[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GlobalMergeListDuplicatesTest()
|
|
||||||
{
|
|
||||||
Test test = new()
|
|
||||||
{
|
|
||||||
ID = 5,
|
|
||||||
Numbers = new[] { 1 }
|
|
||||||
};
|
|
||||||
Test test2 = new()
|
|
||||||
{
|
|
||||||
Numbers = new[]
|
|
||||||
{
|
|
||||||
1,
|
|
||||||
3,
|
|
||||||
3
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Test ret = Merger.Merge(test, test2);
|
|
||||||
Assert.True(ReferenceEquals(test, ret));
|
|
||||||
Assert.Equal(5, ret.ID);
|
|
||||||
|
|
||||||
Assert.Equal(4, ret.Numbers.Length);
|
|
||||||
Assert.Equal(1, ret.Numbers[0]);
|
|
||||||
Assert.Equal(1, ret.Numbers[1]);
|
|
||||||
Assert.Equal(3, ret.Numbers[2]);
|
|
||||||
Assert.Equal(3, ret.Numbers[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MergeDictionaryTest
|
|
||||||
{
|
|
||||||
public int ID { get; set; }
|
|
||||||
|
|
||||||
public Dictionary<int, string> Dictionary { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GlobalMergeDictionariesTest()
|
|
||||||
{
|
|
||||||
MergeDictionaryTest test = new()
|
|
||||||
{
|
|
||||||
ID = 5,
|
|
||||||
Dictionary = new Dictionary<int, string>
|
|
||||||
{
|
|
||||||
[2] = "two"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
MergeDictionaryTest test2 = new()
|
|
||||||
{
|
|
||||||
Dictionary = new Dictionary<int, string>
|
|
||||||
{
|
|
||||||
[3] = "third"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
MergeDictionaryTest ret = Merger.Merge(test, test2);
|
|
||||||
Assert.True(ReferenceEquals(test, ret));
|
|
||||||
Assert.Equal(5, ret.ID);
|
|
||||||
|
|
||||||
Assert.Equal(2, ret.Dictionary.Count);
|
|
||||||
Assert.Equal("two", ret.Dictionary[2]);
|
|
||||||
Assert.Equal("third", ret.Dictionary[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GlobalMergeDictionariesDuplicatesTest()
|
|
||||||
{
|
|
||||||
MergeDictionaryTest test = new()
|
|
||||||
{
|
|
||||||
ID = 5,
|
|
||||||
Dictionary = new Dictionary<int, string>
|
|
||||||
{
|
|
||||||
[2] = "two"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
MergeDictionaryTest test2 = new()
|
|
||||||
{
|
|
||||||
Dictionary = new Dictionary<int, string>
|
|
||||||
{
|
|
||||||
[2] = "nope",
|
|
||||||
[3] = "third"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
MergeDictionaryTest ret = Merger.Merge(test, test2);
|
|
||||||
Assert.True(ReferenceEquals(test, ret));
|
|
||||||
Assert.Equal(5, ret.ID);
|
|
||||||
|
|
||||||
Assert.Equal(2, ret.Dictionary.Count);
|
|
||||||
Assert.Equal("two", ret.Dictionary[2]);
|
|
||||||
Assert.Equal("third", ret.Dictionary[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GlobalMergeListDuplicatesResourcesTest()
|
|
||||||
{
|
|
||||||
Show test = new()
|
|
||||||
{
|
|
||||||
Id = 5,
|
|
||||||
Genres = new[] { new Genre("test") }
|
|
||||||
};
|
|
||||||
Show test2 = new()
|
|
||||||
{
|
|
||||||
Genres = new[]
|
|
||||||
{
|
|
||||||
new Genre("test"),
|
|
||||||
new Genre("test2")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Show ret = Merger.Merge(test, test2);
|
|
||||||
Assert.True(ReferenceEquals(test, ret));
|
|
||||||
Assert.Equal(5, ret.Id);
|
|
||||||
|
|
||||||
Assert.Equal(2, ret.Genres.Count);
|
|
||||||
Assert.Equal("test", ret.Genres.ToArray()[0].Slug);
|
|
||||||
Assert.Equal("test2", ret.Genres.ToArray()[1].Slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MergeListTest()
|
|
||||||
{
|
|
||||||
int[] first = { 1 };
|
|
||||||
int[] second = { 3, 3 };
|
|
||||||
int[] ret = Merger.MergeLists(first, second);
|
|
||||||
|
|
||||||
Assert.Equal(3, ret.Length);
|
|
||||||
Assert.Equal(1, ret[0]);
|
|
||||||
Assert.Equal(3, ret[1]);
|
|
||||||
Assert.Equal(3, ret[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MergeListDuplicateTest()
|
|
||||||
{
|
|
||||||
int[] first = { 1 };
|
|
||||||
int[] second = {
|
|
||||||
1,
|
|
||||||
3,
|
|
||||||
3
|
|
||||||
};
|
|
||||||
int[] ret = Merger.MergeLists(first, second);
|
|
||||||
|
|
||||||
Assert.Equal(4, ret.Length);
|
|
||||||
Assert.Equal(1, ret[0]);
|
|
||||||
Assert.Equal(1, ret[1]);
|
|
||||||
Assert.Equal(3, ret[2]);
|
|
||||||
Assert.Equal(3, ret[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MergeListDuplicateCustomEqualityTest()
|
|
||||||
{
|
|
||||||
int[] first = { 1 };
|
|
||||||
int[] second = { 3, 2 };
|
|
||||||
int[] ret = Merger.MergeLists(first, second, (x, y) => x % 2 == y % 2);
|
|
||||||
|
|
||||||
Assert.Equal(2, ret.Length);
|
|
||||||
Assert.Equal(1, ret[0]);
|
|
||||||
Assert.Equal(2, ret[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MergeDictionariesTest()
|
|
||||||
{
|
|
||||||
Dictionary<int, string> first = new()
|
|
||||||
{
|
|
||||||
[1] = "test",
|
|
||||||
[5] = "value"
|
|
||||||
};
|
|
||||||
Dictionary<int, string> second = new()
|
|
||||||
{
|
|
||||||
[3] = "third",
|
|
||||||
};
|
|
||||||
IDictionary<int, string> ret = Merger.MergeDictionaries(first, second);
|
|
||||||
|
|
||||||
Assert.Equal(3, ret.Count);
|
|
||||||
Assert.Equal("test", ret[1]);
|
|
||||||
Assert.Equal("value", ret[5]);
|
|
||||||
Assert.Equal("third", ret[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MergeDictionariesDuplicateTest()
|
|
||||||
{
|
|
||||||
Dictionary<int, string> first = new()
|
|
||||||
{
|
|
||||||
[1] = "test",
|
|
||||||
[5] = "value"
|
|
||||||
};
|
|
||||||
Dictionary<int, string> second = new()
|
|
||||||
{
|
|
||||||
[3] = "third",
|
|
||||||
[5] = "new-value",
|
|
||||||
};
|
|
||||||
IDictionary<int, string> ret = Merger.MergeDictionaries(first, second);
|
|
||||||
|
|
||||||
Assert.Equal(3, ret.Count);
|
|
||||||
Assert.Equal("test", ret[1]);
|
|
||||||
Assert.Equal("value", ret[5]);
|
|
||||||
Assert.Equal("third", ret[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CompleteTest()
|
public void CompleteTest()
|
||||||
{
|
{
|
||||||
Genre genre = new()
|
Studio genre = new()
|
||||||
{
|
{
|
||||||
ID = 5,
|
Id = 5,
|
||||||
Name = "merged"
|
Name = "merged"
|
||||||
};
|
};
|
||||||
Genre genre2 = new()
|
Studio genre2 = new()
|
||||||
{
|
{
|
||||||
Name = "test"
|
Name = "test"
|
||||||
};
|
};
|
||||||
Genre ret = Merger.Complete(genre, genre2);
|
Studio ret = Merger.Complete(genre, genre2);
|
||||||
Assert.True(ReferenceEquals(genre, ret));
|
Assert.True(ReferenceEquals(genre, ret));
|
||||||
Assert.Equal(5, ret.ID);
|
Assert.Equal(5, ret.Id);
|
||||||
Assert.Equal("test", genre.Name);
|
Assert.Equal("test", genre.Name);
|
||||||
Assert.Null(genre.Slug);
|
Assert.Null(genre.Slug);
|
||||||
}
|
}
|
||||||
@ -437,64 +136,6 @@ namespace Kyoo.Tests.Utility
|
|||||||
// This should no call the setter of first so the test should pass.
|
// This should no call the setter of first so the test should pass.
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MergeDictionaryNoChangeNoSetTest()
|
|
||||||
{
|
|
||||||
TestMergeSetter first = new()
|
|
||||||
{
|
|
||||||
Backing = new Dictionary<int, int>
|
|
||||||
{
|
|
||||||
[2] = 3
|
|
||||||
}
|
|
||||||
};
|
|
||||||
TestMergeSetter second = new()
|
|
||||||
{
|
|
||||||
Backing = new Dictionary<int, int>()
|
|
||||||
};
|
|
||||||
Merger.Merge(first, second);
|
|
||||||
// This should no call the setter of first so the test should pass.
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MergeDictionaryNullValue()
|
|
||||||
{
|
|
||||||
Dictionary<string, string> first = new()
|
|
||||||
{
|
|
||||||
["logo"] = "logo",
|
|
||||||
["poster"] = null
|
|
||||||
};
|
|
||||||
Dictionary<string, string> second = new()
|
|
||||||
{
|
|
||||||
["poster"] = "new-poster",
|
|
||||||
["thumbnail"] = "thumbnails"
|
|
||||||
};
|
|
||||||
IDictionary<string, string> ret = Merger.MergeDictionaries(first, second, out bool changed);
|
|
||||||
Assert.True(changed);
|
|
||||||
Assert.Equal(3, ret.Count);
|
|
||||||
Assert.Equal("new-poster", ret["poster"]);
|
|
||||||
Assert.Equal("thumbnails", ret["thumbnail"]);
|
|
||||||
Assert.Equal("logo", ret["logo"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MergeDictionaryNullValueNoChange()
|
|
||||||
{
|
|
||||||
Dictionary<string, string> first = new()
|
|
||||||
{
|
|
||||||
["logo"] = "logo",
|
|
||||||
["poster"] = null
|
|
||||||
};
|
|
||||||
Dictionary<string, string> second = new()
|
|
||||||
{
|
|
||||||
["poster"] = null,
|
|
||||||
};
|
|
||||||
IDictionary<string, string> ret = Merger.MergeDictionaries(first, second, out bool changed);
|
|
||||||
Assert.False(changed);
|
|
||||||
Assert.Equal(2, ret.Count);
|
|
||||||
Assert.Null(ret["poster"]);
|
|
||||||
Assert.Equal("logo", ret["logo"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CompleteDictionaryNullValue()
|
public void CompleteDictionaryNullValue()
|
||||||
{
|
{
|
||||||
|
@ -26,13 +26,6 @@ namespace Kyoo.Tests.Utility
|
|||||||
{
|
{
|
||||||
public class TaskTests
|
public class TaskTests
|
||||||
{
|
{
|
||||||
[Fact]
|
|
||||||
public async Task DefaultIfNullTest()
|
|
||||||
{
|
|
||||||
Assert.Equal(0, await TaskUtils.DefaultIfNull<int>(null));
|
|
||||||
Assert.Equal(1, await TaskUtils.DefaultIfNull(Task.FromResult(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ThenTest()
|
public async Task ThenTest()
|
||||||
{
|
{
|
||||||
@ -59,37 +52,5 @@ namespace Kyoo.Tests.Utility
|
|||||||
await Assert.ThrowsAsync<TaskCanceledException>(() => Task.Run(Infinite, token.Token)
|
await Assert.ThrowsAsync<TaskCanceledException>(() => Task.Run(Infinite, token.Token)
|
||||||
.Then(_ => { }));
|
.Then(_ => { }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MapTest()
|
|
||||||
{
|
|
||||||
await Assert.ThrowsAsync<ArgumentException>(() => Task.FromResult(1)
|
|
||||||
.Map<int, int>(_ => throw new ArgumentException()));
|
|
||||||
Assert.Equal(2, await Task.FromResult(1)
|
|
||||||
.Map(x => x + 1));
|
|
||||||
|
|
||||||
static async Task<int> Faulted()
|
|
||||||
{
|
|
||||||
await Task.Delay(1);
|
|
||||||
throw new ArgumentException();
|
|
||||||
}
|
|
||||||
await Assert.ThrowsAsync<ArgumentException>(() => Faulted()
|
|
||||||
.Map(x =>
|
|
||||||
{
|
|
||||||
KAssert.Fail();
|
|
||||||
return x;
|
|
||||||
}));
|
|
||||||
|
|
||||||
static async Task<int> Infinite()
|
|
||||||
{
|
|
||||||
await Task.Delay(100000);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
CancellationTokenSource token = new();
|
|
||||||
token.Cancel();
|
|
||||||
await Assert.ThrowsAsync<TaskCanceledException>(() => Task.Run(Infinite, token.Token)
|
|
||||||
.Map(x => x));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ using System;
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Utils;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
using KUtility = Kyoo.Utils.Utility;
|
using KUtility = Kyoo.Utils.Utility;
|
||||||
@ -35,7 +34,6 @@ namespace Kyoo.Tests.Utility
|
|||||||
Expression<Func<Show, int>> member = x => x.Id;
|
Expression<Func<Show, int>> member = x => x.Id;
|
||||||
Expression<Func<Show, object>> memberCast = x => x.Id;
|
Expression<Func<Show, object>> memberCast = x => x.Id;
|
||||||
|
|
||||||
Assert.False(KUtility.IsPropertyExpression(null));
|
|
||||||
Assert.True(KUtility.IsPropertyExpression(member));
|
Assert.True(KUtility.IsPropertyExpression(member));
|
||||||
Assert.True(KUtility.IsPropertyExpression(memberCast));
|
Assert.True(KUtility.IsPropertyExpression(memberCast));
|
||||||
|
|
||||||
@ -51,7 +49,6 @@ namespace Kyoo.Tests.Utility
|
|||||||
|
|
||||||
Assert.Equal("ID", KUtility.GetPropertyName(member));
|
Assert.Equal("ID", KUtility.GetPropertyName(member));
|
||||||
Assert.Equal("ID", KUtility.GetPropertyName(memberCast));
|
Assert.Equal("ID", KUtility.GetPropertyName(memberCast));
|
||||||
Assert.Throws<ArgumentException>(() => KUtility.GetPropertyName(null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -84,16 +81,5 @@ namespace Kyoo.Tests.Utility
|
|||||||
Array.Empty<Type>(),
|
Array.Empty<Type>(),
|
||||||
new object[] { this }));
|
new object[] { this }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetMethodTest2()
|
|
||||||
{
|
|
||||||
MethodInfo method = KUtility.GetMethod(typeof(Merger),
|
|
||||||
BindingFlags.Static | BindingFlags.Public,
|
|
||||||
nameof(Merger.MergeLists),
|
|
||||||
new[] { typeof(string) },
|
|
||||||
new object[] { "string", "string2", null });
|
|
||||||
Assert.Equal(nameof(Merger.MergeLists), method.Name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user