mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -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
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <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> 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;
|
||||
|
||||
/// <summary>
|
||||
|
@ -129,13 +129,24 @@ namespace Kyoo.Abstractions.Controllers
|
||||
event ResourceEventHandler OnCreated;
|
||||
|
||||
/// <summary>
|
||||
/// Edit a resource
|
||||
/// Edit a resource and replace every property
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <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>
|
||||
/// Called when a resource has been edited.
|
||||
|
@ -16,33 +16,11 @@
|
||||
// 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.Models;
|
||||
|
||||
namespace Kyoo.Tests.Database
|
||||
public class PartialResource
|
||||
{
|
||||
namespace PostgreSQL
|
||||
{
|
||||
[Collection(nameof(Postgresql))]
|
||||
public class GenreTests : AGenreTests
|
||||
{
|
||||
public GenreTests(PostgresFixture postgres, ITestOutputHelper output)
|
||||
: base(new RepositoryActivator(output, postgres)) { }
|
||||
}
|
||||
}
|
||||
public int? Id { get; set; }
|
||||
|
||||
public abstract class AGenreTests : RepositoryTests<Genre>
|
||||
{
|
||||
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
|
||||
private readonly IGenreRepository _repository;
|
||||
|
||||
protected AGenreTests(RepositoryActivator repositories)
|
||||
: base(repositories)
|
||||
{
|
||||
_repository = Repositories.LibraryManager.GenreRepository;
|
||||
}
|
||||
}
|
||||
public string? Slug { get; set; }
|
||||
}
|
@ -43,7 +43,7 @@ namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
if (ShowSlug != null || Show?.Slug != null)
|
||||
return GetSlug(ShowSlug ?? Show.Slug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||
return GetSlug(ShowID.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||
return GetSlug(ShowId.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
@ -81,17 +81,17 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The ID of the Show containing this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int ShowID { get; set; }
|
||||
[SerializeIgnore] public int ShowId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The show that contains this episode. This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(ShowID))] public Show? Show { get; set; }
|
||||
[LoadableRelation(nameof(ShowId))] public Show? Show { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the Season containing this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int? SeasonID { get; set; }
|
||||
[SerializeIgnore] public int? SeasonId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// by it's <see cref="AbsoluteNumber"/>.
|
||||
/// </remarks>
|
||||
[LoadableRelation(nameof(SeasonID))] public Season? Season { get; set; }
|
||||
[LoadableRelation(nameof(SeasonId))] public Season? Season { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The season in witch this episode is in.
|
||||
|
@ -42,7 +42,7 @@ namespace Kyoo.Abstractions.Models
|
||||
get
|
||||
{
|
||||
if (ShowSlug == null && Show == null)
|
||||
return $"{ShowID}-s{SeasonNumber}";
|
||||
return $"{ShowId}-s{SeasonNumber}";
|
||||
return $"{ShowSlug ?? Show?.Slug}-s{SeasonNumber}";
|
||||
}
|
||||
|
||||
@ -67,13 +67,13 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The ID of the Show containing this season.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int ShowID { get; set; }
|
||||
[SerializeIgnore] public int ShowId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The show that contains this season.
|
||||
/// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(ShowID))] public Show? Show { get; set; }
|
||||
[LoadableRelation(nameof(ShowId))] public Show? Show { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of this season. This can be set to 0 to indicate specials.
|
||||
|
@ -51,7 +51,7 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The list of alternative titles of this show.
|
||||
/// </summary>
|
||||
public string[] Aliases { get; set; } = Array.Empty<string>();
|
||||
public List<string> Aliases { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The summary of this show.
|
||||
@ -61,12 +61,12 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// A list of tags that match this movie.
|
||||
/// </summary>
|
||||
public string[] Tags { get; set; } = Array.Empty<string>();
|
||||
public List<string> Tags { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The list of genres (themes) this show has.
|
||||
/// </summary>
|
||||
public Genre[] Genres { get; set; } = Array.Empty<Genre>();
|
||||
public List<Genre> Genres { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Is this show airing, not aired yet or finished?
|
||||
@ -106,13 +106,13 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The ID of the Studio that made this show.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int? StudioID { get; set; }
|
||||
[SerializeIgnore] public int? StudioId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Studio that made this show.
|
||||
/// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(StudioID))][EditableRelation] public Studio? Studio { get; set; }
|
||||
[LoadableRelation(nameof(StudioId))][EditableRelation] public Studio? Studio { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of people that made this show.
|
||||
|
@ -17,9 +17,7 @@
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Kyoo.Utils
|
||||
@ -29,125 +27,6 @@ namespace Kyoo.Utils
|
||||
/// </summary>
|
||||
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>
|
||||
/// If the enumerable is empty, execute an action.
|
||||
/// </summary>
|
||||
@ -197,104 +76,5 @@ namespace Kyoo.Utils
|
||||
foreach (T 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>
|
||||
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/>.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Utils
|
||||
@ -33,99 +30,9 @@ namespace Kyoo.Utils
|
||||
/// </summary>
|
||||
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>
|
||||
/// Merge two dictionary, if the same key is found on both dictionary, the values of the second one is kept.
|
||||
/// </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="second">The second dictionary to merge</param>
|
||||
/// <param name="hasChanged">
|
||||
@ -138,7 +45,7 @@ namespace Kyoo.Utils
|
||||
/// set to those of <paramref name="first"/>.
|
||||
/// </returns>
|
||||
[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,
|
||||
out bool hasChanged)
|
||||
{
|
||||
@ -160,14 +67,8 @@ namespace Kyoo.Utils
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// (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"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This does the opposite of <see cref="Merge{T}"/>.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// {id: 0, slug: "test"}, {id: 4, slug: "foo"} -> {id: 4, slug: "foo"}
|
||||
/// </example>
|
||||
@ -182,19 +83,16 @@ namespace Kyoo.Utils
|
||||
/// </param>
|
||||
/// <typeparam name="T">Fields of T will be completed</typeparam>
|
||||
/// <returns><paramref name="first"/></returns>
|
||||
/// <exception cref="ArgumentNullException">If first is null</exception>
|
||||
public static T Complete<T>([NotNull] T first,
|
||||
public static T Complete<T>(T first,
|
||||
T? second,
|
||||
[InstantHandle] Func<PropertyInfo, bool>? where = null)
|
||||
{
|
||||
if (first == null)
|
||||
throw new ArgumentNullException(nameof(first));
|
||||
if (second == null)
|
||||
return first;
|
||||
|
||||
Type type = typeof(T);
|
||||
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);
|
||||
|
||||
if (where != null)
|
||||
@ -202,17 +100,16 @@ namespace Kyoo.Utils
|
||||
|
||||
foreach (PropertyInfo property in properties)
|
||||
{
|
||||
object value = property.GetValue(second);
|
||||
object defaultValue = property.GetCustomAttribute<DefaultValueAttribute>()?.Value
|
||||
?? property.PropertyType.GetClrDefault();
|
||||
object? value = property.GetValue(second);
|
||||
|
||||
if (value?.Equals(defaultValue) != false || value.Equals(property.GetValue(first)))
|
||||
if (value?.Equals(property.GetValue(first)) == true)
|
||||
continue;
|
||||
|
||||
if (Utility.IsOfGenericType(property.PropertyType, typeof(IDictionary<,>)))
|
||||
{
|
||||
Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
|
||||
.GenericTypeArguments;
|
||||
object[] parameters =
|
||||
object?[] parameters =
|
||||
{
|
||||
property.GetValue(first),
|
||||
value,
|
||||
@ -222,8 +119,8 @@ namespace Kyoo.Utils
|
||||
typeof(Merger),
|
||||
nameof(CompleteDictionaries),
|
||||
dictionaryTypes,
|
||||
parameters);
|
||||
if ((bool)parameters[2])
|
||||
parameters)!;
|
||||
if ((bool)parameters[2]!)
|
||||
property.SetValue(first, newDictionary);
|
||||
}
|
||||
else
|
||||
@ -234,109 +131,5 @@ namespace Kyoo.Utils
|
||||
merge.OnMerge(second);
|
||||
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.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Kyoo.Utils
|
||||
{
|
||||
@ -49,37 +48,5 @@ namespace Kyoo.Utils
|
||||
return x.Result;
|
||||
}, 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>
|
||||
public static bool IsPropertyExpression(LambdaExpression ex)
|
||||
{
|
||||
if (ex == null)
|
||||
return false;
|
||||
return ex.Body is MemberExpression
|
||||
|| (ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression);
|
||||
}
|
||||
@ -57,7 +55,7 @@ namespace Kyoo.Utils
|
||||
{
|
||||
if (!IsPropertyExpression(ex))
|
||||
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
|
||||
: ex.Body as MemberExpression;
|
||||
return member!.Member.Name;
|
||||
@ -92,18 +90,6 @@ namespace Kyoo.Utils
|
||||
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>
|
||||
/// Return every <see cref="Type"/> in the inheritance tree of the parameter (interfaces are not returned)
|
||||
/// </summary>
|
||||
@ -194,13 +180,6 @@ namespace Kyoo.Utils
|
||||
Type[] generics,
|
||||
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)
|
||||
.Where(x => x.Name == name)
|
||||
.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>
|
||||
/// <seealso cref="RunGenericMethod{T}(object,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,
|
||||
string methodName,
|
||||
Type[] types,
|
||||
params object[] args)
|
||||
params object?[] args)
|
||||
{
|
||||
if (owner == null)
|
||||
throw new ArgumentNullException(nameof(owner));
|
||||
@ -311,7 +289,7 @@ namespace Kyoo.Utils
|
||||
if (types.Length < 1)
|
||||
throw new ArgumentException($"The {nameof(types)} array is empty. At least one type is needed.");
|
||||
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>
|
||||
|
@ -27,6 +27,7 @@ using Kyoo.Abstractions.Models.Permissions;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
using Kyoo.Authentication.Models;
|
||||
using Kyoo.Authentication.Models.DTO;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
@ -229,7 +230,7 @@ namespace Kyoo.Authentication.Views
|
||||
try
|
||||
{
|
||||
user.Id = userID;
|
||||
return await _users.Edit(user, true);
|
||||
return await _users.Edit(user);
|
||||
}
|
||||
catch (ItemNotFoundException)
|
||||
{
|
||||
@ -252,14 +253,15 @@ namespace Kyoo.Authentication.Views
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized, 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))
|
||||
return Unauthorized(new RequestError("User not authenticated or token invalid."));
|
||||
try
|
||||
{
|
||||
user.Id = userID;
|
||||
return await _users.Edit(user, false);
|
||||
if (user.Id.HasValue && user.Id != userID)
|
||||
throw new ArgumentException("Can't edit your user id.");
|
||||
return await _users.Patch(userID, TryUpdateModelAsync);
|
||||
}
|
||||
catch (ItemNotFoundException)
|
||||
{
|
||||
|
@ -284,12 +284,12 @@ namespace Kyoo.Core.Controllers
|
||||
(Show s, nameof(Show.Seasons)) => _SetRelation(s,
|
||||
SeasonRepository.GetAll(x => x.Show.Id == obj.Id),
|
||||
(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,
|
||||
EpisodeRepository.GetAll(x => x.Show.Id == obj.Id),
|
||||
(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
|
||||
.GetAll(x => x.Shows.Any(y => y.Id == obj.Id))
|
||||
@ -300,21 +300,21 @@ namespace Kyoo.Core.Controllers
|
||||
.Then(x =>
|
||||
{
|
||||
s.Studio = x;
|
||||
s.StudioID = x?.Id ?? 0;
|
||||
s.StudioId = x?.Id ?? 0;
|
||||
}),
|
||||
|
||||
|
||||
(Season s, nameof(Season.Episodes)) => _SetRelation(s,
|
||||
EpisodeRepository.GetAll(x => x.Season.Id == obj.Id),
|
||||
(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
|
||||
.GetOrDefault(x => x.Seasons.Any(y => y.Id == obj.Id))
|
||||
.Then(x =>
|
||||
{
|
||||
s.Show = x;
|
||||
s.ShowID = x?.Id ?? 0;
|
||||
s.ShowId = x?.Id ?? 0;
|
||||
}),
|
||||
|
||||
|
||||
@ -323,7 +323,7 @@ namespace Kyoo.Core.Controllers
|
||||
.Then(x =>
|
||||
{
|
||||
e.Show = x;
|
||||
e.ShowID = x?.Id ?? 0;
|
||||
e.ShowId = x?.Id ?? 0;
|
||||
}),
|
||||
|
||||
(Episode e, nameof(Episode.Season)) => SeasonRepository
|
||||
@ -331,18 +331,18 @@ namespace Kyoo.Core.Controllers
|
||||
.Then(x =>
|
||||
{
|
||||
e.Season = x;
|
||||
e.SeasonID = x?.Id ?? 0;
|
||||
e.SeasonId = x?.Id ?? 0;
|
||||
}),
|
||||
|
||||
(Episode e, nameof(Episode.PreviousEpisode)) => EpisodeRepository
|
||||
.GetAll(
|
||||
where: x => x.ShowID == e.ShowID,
|
||||
where: x => x.ShowId == e.ShowId,
|
||||
limit: new Pagination(1, e.Id, true)
|
||||
).Then(x => e.PreviousEpisode = x.FirstOrDefault()),
|
||||
|
||||
(Episode e, nameof(Episode.NextEpisode)) => EpisodeRepository
|
||||
.GetAll(
|
||||
where: x => x.ShowID == e.ShowID,
|
||||
where: x => x.ShowId == e.ShowId,
|
||||
limit: new Pagination(1, e.Id)
|
||||
).Then(x => e.NextEpisode = x.FirstOrDefault()),
|
||||
|
||||
@ -438,10 +438,17 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<T> Edit<T>(T item, bool resetOld)
|
||||
public Task<T> Edit<T>(T item)
|
||||
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 />
|
||||
|
@ -64,7 +64,7 @@ namespace Kyoo.Core.Controllers
|
||||
// Edit episode slugs when the show's slug changes.
|
||||
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)
|
||||
{
|
||||
ep.ShowSlug = show.Slug;
|
||||
@ -77,7 +77,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc />
|
||||
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.EpisodeNumber == episodeNumber);
|
||||
}
|
||||
@ -111,7 +111,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
|
||||
@ -142,12 +142,12 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<Episode> Create(Episode 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;
|
||||
await _database.SaveChangesAsync(() =>
|
||||
obj.SeasonNumber != null && obj.EpisodeNumber != null
|
||||
? Get(obj.ShowID, obj.SeasonNumber.Value, obj.EpisodeNumber.Value)
|
||||
: GetAbsolute(obj.ShowID, obj.AbsoluteNumber.Value));
|
||||
? Get(obj.ShowId, obj.SeasonNumber.Value, obj.EpisodeNumber.Value)
|
||||
: GetAbsolute(obj.ShowId, obj.AbsoluteNumber.Value));
|
||||
OnResourceCreated(obj);
|
||||
return obj;
|
||||
}
|
||||
@ -156,14 +156,14 @@ namespace Kyoo.Core.Controllers
|
||||
protected override async Task Validate(Episode resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
if (resource.ShowID <= 0)
|
||||
if (resource.ShowId <= 0)
|
||||
{
|
||||
if (resource.Show == null)
|
||||
{
|
||||
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)
|
||||
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;
|
||||
await _database.SaveChangesAsync();
|
||||
await base.Delete(obj);
|
||||
if (epCount == 1)
|
||||
await _shows.Delete(obj.ShowID);
|
||||
await _shows.Delete(obj.ShowId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,11 @@ namespace Kyoo.Core.Controllers
|
||||
=> throw new InvalidOperationException();
|
||||
|
||||
/// <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();
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -144,7 +144,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <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>
|
||||
/// <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,
|
||||
T reference,
|
||||
bool next = true)
|
||||
@ -155,22 +155,22 @@ namespace Kyoo.Core.Controllers
|
||||
ParameterExpression x = Expression.Parameter(typeof(T), "x");
|
||||
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
|
||||
{
|
||||
Sort<T>.Default => _GetSortsBy(DefaultSort),
|
||||
Sort<T>.Default => GetSortsBy(DefaultSort),
|
||||
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>(),
|
||||
};
|
||||
}
|
||||
|
||||
// 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);
|
||||
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();
|
||||
// TODO: Add an outer query >= for perf
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -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.
|
||||
// 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)
|
||||
{
|
||||
BinaryExpression equalNull = Expression.Equal(xkey, Expression.Constant(null));
|
||||
@ -223,7 +223,7 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
previousSteps.Add(new(key, desc));
|
||||
}
|
||||
return Expression.Lambda<Func<T, bool>>(filter, x);
|
||||
return Expression.Lambda<Func<T, bool>>(filter!, x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -316,7 +316,7 @@ namespace Kyoo.Core.Controllers
|
||||
if (limit?.AfterID != null)
|
||||
{
|
||||
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)
|
||||
query = query.Reverse();
|
||||
@ -343,12 +343,9 @@ namespace Kyoo.Core.Controllers
|
||||
await Validate(obj);
|
||||
if (obj is IThumbnails thumbs)
|
||||
{
|
||||
if (thumbs.Poster != null)
|
||||
Database.Entry(thumbs).Reference(x => x.Poster).TargetEntry.State = EntityState.Added;
|
||||
if (thumbs.Thumbnail != 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;
|
||||
Database.Entry(thumbs).Reference(x => x.Poster).IsModified = thumbs.Poster != null;
|
||||
Database.Entry(thumbs).Reference(x => x.Thumbnail).IsModified = thumbs.Thumbnail != null;
|
||||
Database.Entry(thumbs).Reference(x => x.Logo).IsModified = thumbs.Logo != null;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
@ -376,9 +373,6 @@ namespace Kyoo.Core.Controllers
|
||||
{
|
||||
try
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
T old = await GetOrDefault(obj.Slug);
|
||||
if (old != null)
|
||||
return old;
|
||||
@ -392,21 +386,16 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <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;
|
||||
Database.ChangeTracker.LazyLoadingEnabled = false;
|
||||
try
|
||||
{
|
||||
T old = await GetWithTracking(edited.Id);
|
||||
|
||||
if (resetOld)
|
||||
old = Merger.Nullify(old);
|
||||
Merger.Complete(old, edited, x => x.GetCustomAttribute<LoadableRelationAttribute>() == null);
|
||||
await EditRelations(old, edited, resetOld);
|
||||
await EditRelations(old, edited, true);
|
||||
await Database.SaveChangesAsync();
|
||||
OnEdited?.Invoke(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>
|
||||
/// An overridable method to edit relation of a resource.
|
||||
/// </summary>
|
||||
@ -434,12 +445,11 @@ namespace Kyoo.Core.Controllers
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
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).TargetEntry.State = EntityState.Modified;
|
||||
Database.Entry(thumbs).Reference(x => x.Thumbnail).TargetEntry.State = EntityState.Modified;
|
||||
Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry.State = EntityState.Modified;
|
||||
Database.Entry(thumbs).Reference(x => x.Poster).IsModified = thumbs.Poster != chng.Poster;
|
||||
Database.Entry(thumbs).Reference(x => x.Thumbnail).IsModified = thumbs.Thumbnail != chng.Thumbnail;
|
||||
Database.Entry(thumbs).Reference(x => x.Logo).IsModified = thumbs.Logo != chng.Logo;
|
||||
}
|
||||
return Validate(resource);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ namespace Kyoo.Core.Controllers
|
||||
// Edit seasons slugs when the show's slug changes.
|
||||
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)
|
||||
{
|
||||
season.ShowSlug = show.Slug;
|
||||
@ -86,7 +86,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
|
||||
@ -112,9 +112,9 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<Season> Create(Season 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;
|
||||
await _database.SaveChangesAsync(() => Get(obj.ShowID, obj.SeasonNumber));
|
||||
await _database.SaveChangesAsync(() => Get(obj.ShowId, obj.SeasonNumber));
|
||||
OnResourceCreated(obj);
|
||||
return obj;
|
||||
}
|
||||
@ -123,14 +123,14 @@ namespace Kyoo.Core.Controllers
|
||||
protected override async Task Validate(Season resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
if (resource.ShowID <= 0)
|
||||
if (resource.ShowId <= 0)
|
||||
{
|
||||
if (resource.Show == null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
resource.Studio = await _studios.CreateIfNotExists(resource.Studio);
|
||||
resource.StudioID = resource.Studio.Id;
|
||||
resource.StudioId = resource.Studio.Id;
|
||||
}
|
||||
|
||||
if (resource.People != null)
|
||||
|
@ -16,12 +16,14 @@
|
||||
// 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.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Permissions;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@ -162,11 +164,11 @@ namespace Kyoo.Core.Api
|
||||
public async Task<ActionResult<T>> Edit([FromBody] T resource)
|
||||
{
|
||||
if (resource.Id > 0)
|
||||
return await Repository.Edit(resource, true);
|
||||
return await Repository.Edit(resource);
|
||||
|
||||
T old = await Repository.Get(resource.Slug);
|
||||
resource.Id = old.Id;
|
||||
return await Repository.Edit(resource, true);
|
||||
return await Repository.Edit(resource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -185,14 +187,15 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||
[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)
|
||||
return await Repository.Edit(resource, false);
|
||||
if (resource.Id.HasValue)
|
||||
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);
|
||||
resource.Id = old.Id;
|
||||
return await Repository.Edit(resource, false);
|
||||
return await Repository.Patch(old.Id, TryUpdateModelAsync);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -83,7 +83,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
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),
|
||||
pagination
|
||||
);
|
||||
|
@ -84,7 +84,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
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),
|
||||
pagination
|
||||
);
|
||||
|
@ -86,7 +86,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
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),
|
||||
pagination
|
||||
);
|
||||
@ -121,7 +121,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
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),
|
||||
pagination
|
||||
);
|
||||
|
@ -306,13 +306,13 @@ namespace Kyoo.Postgresql
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<Season>()
|
||||
.HasIndex(x => new { x.ShowID, x.SeasonNumber })
|
||||
.HasIndex(x => new { ShowID = x.ShowId, x.SeasonNumber })
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<Season>()
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
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();
|
||||
modelBuilder.Entity<Episode>()
|
||||
.HasIndex(x => x.Slug)
|
||||
|
@ -140,16 +140,10 @@ namespace Kyoo.Tests.Database
|
||||
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(TestSample.Get<T>()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditNullTest()
|
||||
{
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(() => _repository.Edit(null!, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditNonExistingTest()
|
||||
{
|
||||
await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Edit(new T { Id = 56 }, false));
|
||||
await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Edit(new T { Id = 56 }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -170,12 +164,6 @@ namespace Kyoo.Tests.Database
|
||||
await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Get(x => x.Slug == "non-existing"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetExpressionNullTest()
|
||||
{
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(() => _repository.Get((Expression<Func<T, bool>>)null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetOrDefaultTest()
|
||||
{
|
||||
|
@ -66,29 +66,19 @@ namespace Kyoo.Tests.Database
|
||||
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]
|
||||
public async Task CreateWithExternalIdTest()
|
||||
{
|
||||
Collection collection = TestSample.GetNew<Collection>();
|
||||
collection.ExternalId = new[]
|
||||
collection.ExternalId = new Dictionary<string, MetadataId>
|
||||
{
|
||||
new MetadataId
|
||||
["1"] = new()
|
||||
{
|
||||
Provider = TestSample.Get<Provider>(),
|
||||
Link = "link",
|
||||
DataId = "id"
|
||||
},
|
||||
new MetadataId
|
||||
["2"] = new()
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "new-provider-link",
|
||||
DataId = "new-id"
|
||||
}
|
||||
@ -96,7 +86,6 @@ namespace Kyoo.Tests.Database
|
||||
await _repository.Create(collection);
|
||||
|
||||
Collection retrieved = await _repository.Get(2);
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||
KAssert.DeepEqual(collection.ExternalId.First(), retrieved.ExternalId.First());
|
||||
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);
|
||||
value.Name = "New Title";
|
||||
value.Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "new-poster"
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
value.Poster = new Image("new-poster");
|
||||
await _repository.Edit(value);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections.FirstAsync();
|
||||
@ -123,21 +109,19 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditMetadataTest()
|
||||
{
|
||||
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",
|
||||
DataId = "id"
|
||||
},
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
@ -147,40 +131,36 @@ namespace Kyoo.Tests.Database
|
||||
public async Task AddMetadataTest()
|
||||
{
|
||||
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",
|
||||
DataId = "id"
|
||||
},
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
value.ExternalId.Add(new MetadataId
|
||||
value.ExternalId.Add("test", new MetadataId
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "link",
|
||||
DataId = "id"
|
||||
});
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
|
@ -57,10 +57,10 @@ namespace Kyoo.Tests.Database
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
|
||||
Show show = new()
|
||||
{
|
||||
Id = episode.ShowID,
|
||||
Id = episode.ShowId,
|
||||
Slug = "new-slug"
|
||||
};
|
||||
await Repositories.LibraryManager.ShowRepository.Edit(show, false);
|
||||
await Repositories.LibraryManager.ShowRepository.Edit(show);
|
||||
episode = await _repository.Get(1);
|
||||
Assert.Equal("new-slug-s1e1", episode.Slug);
|
||||
}
|
||||
@ -74,8 +74,8 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
Id = 1,
|
||||
SeasonNumber = 2,
|
||||
ShowID = 1
|
||||
}, false);
|
||||
ShowId = 1
|
||||
});
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e1", episode.Slug);
|
||||
episode = await _repository.Get(1);
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e1", episode.Slug);
|
||||
@ -90,8 +90,8 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
Id = 1,
|
||||
EpisodeNumber = 2,
|
||||
ShowID = 1
|
||||
}, false);
|
||||
ShowId = 1
|
||||
});
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
||||
episode = await _repository.Get(1);
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
||||
@ -102,7 +102,7 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
Episode episode = await _repository.Create(new Episode
|
||||
{
|
||||
ShowID = TestSample.Get<Show>().Id,
|
||||
ShowId = TestSample.Get<Show>().Id,
|
||||
SeasonNumber = 2,
|
||||
EpisodeNumber = 4
|
||||
});
|
||||
@ -129,10 +129,10 @@ namespace Kyoo.Tests.Database
|
||||
Episode episode = await _repository.Create(TestSample.GetAbsoluteEpisode());
|
||||
Show show = new()
|
||||
{
|
||||
Id = episode.ShowID,
|
||||
Id = episode.ShowId,
|
||||
Slug = "new-slug"
|
||||
};
|
||||
await Repositories.LibraryManager.ShowRepository.Edit(show, false);
|
||||
await Repositories.LibraryManager.ShowRepository.Edit(show);
|
||||
episode = await _repository.Get(2);
|
||||
Assert.Equal($"new-slug-3", episode.Slug);
|
||||
}
|
||||
@ -145,8 +145,8 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
Id = 2,
|
||||
AbsoluteNumber = 56,
|
||||
ShowID = 1
|
||||
}, false);
|
||||
ShowId = 1
|
||||
});
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-56", episode.Slug);
|
||||
episode = await _repository.Get(2);
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-56", episode.Slug);
|
||||
@ -161,8 +161,8 @@ namespace Kyoo.Tests.Database
|
||||
Id = 2,
|
||||
SeasonNumber = 1,
|
||||
EpisodeNumber = 2,
|
||||
ShowID = 1
|
||||
}, false);
|
||||
ShowId = 1
|
||||
});
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
||||
episode = await _repository.Get(2);
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
||||
@ -174,49 +174,25 @@ namespace Kyoo.Tests.Database
|
||||
Episode episode = await _repository.Get(1);
|
||||
episode.SeasonNumber = null;
|
||||
episode.AbsoluteNumber = 12;
|
||||
episode = await _repository.Edit(episode, true);
|
||||
episode = await _repository.Edit(episode);
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-12", episode.Slug);
|
||||
episode = await _repository.Get(1);
|
||||
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]
|
||||
public async Task CreateWithExternalIdTest()
|
||||
{
|
||||
Episode value = TestSample.GetNew<Episode>();
|
||||
value.ExternalId = new[]
|
||||
value.ExternalId = new Dictionary<string, MetadataId>
|
||||
{
|
||||
new MetadataId
|
||||
["2"] = new()
|
||||
{
|
||||
Provider = TestSample.Get<Provider>(),
|
||||
Link = "link",
|
||||
DataId = "id"
|
||||
},
|
||||
new MetadataId
|
||||
["3"] = new()
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "new-provider-link",
|
||||
DataId = "new-id"
|
||||
}
|
||||
@ -224,7 +200,6 @@ namespace Kyoo.Tests.Database
|
||||
await _repository.Create(value);
|
||||
|
||||
Episode retrieved = await _repository.Get(2);
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||
KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
|
||||
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);
|
||||
value.Name = "New Title";
|
||||
value.Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "new-poster"
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
value.Poster = new Image("poster");
|
||||
await _repository.Edit(value);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Episode retrieved = await database.Episodes.FirstAsync();
|
||||
@ -251,22 +223,18 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditMetadataTest()
|
||||
{
|
||||
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",
|
||||
DataId = "id"
|
||||
},
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Episode retrieved = await database.Episodes
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
Episode retrieved = await database.Episodes.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
@ -275,41 +243,33 @@ namespace Kyoo.Tests.Database
|
||||
public async Task AddMetadataTest()
|
||||
{
|
||||
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",
|
||||
DataId = "id"
|
||||
},
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Episode retrieved = await database.Episodes
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
Episode retrieved = await database.Episodes.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
value.ExternalId.Add(new MetadataId
|
||||
value.ExternalId.Add("test", new MetadataId
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "link",
|
||||
DataId = "id"
|
||||
});
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Episode retrieved = await database.Episodes
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
Episode retrieved = await database.Episodes.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
@ -326,7 +286,7 @@ namespace Kyoo.Tests.Database
|
||||
Episode value = new()
|
||||
{
|
||||
Name = "This is a test super title",
|
||||
ShowID = 1,
|
||||
ShowId = 1,
|
||||
AbsoluteNumber = 2
|
||||
};
|
||||
await _repository.Create(value);
|
||||
@ -343,8 +303,8 @@ namespace Kyoo.Tests.Database
|
||||
|
||||
Episode expected = TestSample.Get<Episode>();
|
||||
expected.Id = 0;
|
||||
expected.ShowID = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get<Show>())).Id;
|
||||
expected.SeasonID = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get<Season>())).Id;
|
||||
expected.ShowId = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get<Show>())).Id;
|
||||
expected.SeasonId = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get<Season>())).Id;
|
||||
await _repository.Create(expected);
|
||||
KAssert.DeepEqual(expected, await _repository.Get(expected.Slug));
|
||||
}
|
||||
@ -355,8 +315,8 @@ namespace Kyoo.Tests.Database
|
||||
Episode expected = TestSample.Get<Episode>();
|
||||
KAssert.DeepEqual(expected, await _repository.CreateIfNotExists(TestSample.Get<Episode>()));
|
||||
await _repository.Delete(TestSample.Get<Episode>());
|
||||
expected.ShowID = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get<Show>())).Id;
|
||||
expected.SeasonID = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get<Season>())).Id;
|
||||
expected.ShowId = (await Repositories.LibraryManager.ShowRepository.Create(TestSample.Get<Show>())).Id;
|
||||
expected.SeasonId = (await Repositories.LibraryManager.SeasonRepository.Create(TestSample.Get<Season>())).Id;
|
||||
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()
|
||||
{
|
||||
People value = TestSample.GetNew<People>();
|
||||
value.ExternalId = new[]
|
||||
value.ExternalId = new Dictionary<string, MetadataId>
|
||||
{
|
||||
new MetadataId
|
||||
["2"] = new()
|
||||
{
|
||||
Provider = TestSample.Get<Provider>(),
|
||||
Link = "link",
|
||||
DataId = "id"
|
||||
},
|
||||
new MetadataId
|
||||
["1"] = new()
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "new-provider-link",
|
||||
DataId = "new-id"
|
||||
}
|
||||
@ -70,7 +68,6 @@ namespace Kyoo.Tests.Database
|
||||
await _repository.Create(value);
|
||||
|
||||
People retrieved = await _repository.Get(2);
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||
KAssert.DeepEqual(value.ExternalId.First(), retrieved.ExternalId.First());
|
||||
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);
|
||||
value.Name = "New Name";
|
||||
value.Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "new-poster"
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
value.Poster = new Image("poster");
|
||||
await _repository.Edit(value);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
People retrieved = await database.People.FirstAsync();
|
||||
@ -97,22 +91,18 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditMetadataTest()
|
||||
{
|
||||
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",
|
||||
DataId = "id"
|
||||
},
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
People retrieved = await database.People
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
People retrieved = await database.People.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
@ -121,41 +111,32 @@ namespace Kyoo.Tests.Database
|
||||
public async Task AddMetadataTest()
|
||||
{
|
||||
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",
|
||||
DataId = "id"
|
||||
},
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
People retrieved = await database.People
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
People retrieved = await database.People.FirstAsync();
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
value.ExternalId.Add(new MetadataId
|
||||
value.ExternalId.Add("toto", new MetadataId
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "link",
|
||||
DataId = "id"
|
||||
});
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
People retrieved = await database.People
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
People retrieved = await database.People.FirstAsync();
|
||||
|
||||
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);
|
||||
Show show = new()
|
||||
{
|
||||
Id = season.ShowID,
|
||||
Id = season.ShowId,
|
||||
Slug = "new-slug"
|
||||
};
|
||||
await Repositories.LibraryManager.ShowRepository.Edit(show, false);
|
||||
await Repositories.LibraryManager.ShowRepository.Edit(show);
|
||||
season = await _repository.Get(1);
|
||||
Assert.Equal("new-slug-s1", season.Slug);
|
||||
}
|
||||
@ -72,8 +72,8 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
Id = 1,
|
||||
SeasonNumber = 2,
|
||||
ShowID = 1
|
||||
}, false);
|
||||
ShowId = 1
|
||||
});
|
||||
season = await _repository.Get(1);
|
||||
Assert.Equal("anohana-s2", season.Slug);
|
||||
}
|
||||
@ -83,7 +83,7 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
Season season = await _repository.Create(new Season
|
||||
{
|
||||
ShowID = TestSample.Get<Show>().Id,
|
||||
ShowId = TestSample.Get<Show>().Id,
|
||||
SeasonNumber = 2
|
||||
});
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2", season.Slug);
|
||||
@ -93,17 +93,15 @@ namespace Kyoo.Tests.Database
|
||||
public async Task CreateWithExternalIdTest()
|
||||
{
|
||||
Season season = TestSample.GetNew<Season>();
|
||||
season.ExternalId = new[]
|
||||
season.ExternalId = new Dictionary<string, MetadataId>
|
||||
{
|
||||
new MetadataId
|
||||
["2"] = new()
|
||||
{
|
||||
Provider = TestSample.Get<Provider>(),
|
||||
Link = "link",
|
||||
DataId = "id"
|
||||
},
|
||||
new MetadataId
|
||||
["1"] = new()
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "new-provider-link",
|
||||
DataId = "new-id"
|
||||
}
|
||||
@ -111,7 +109,6 @@ namespace Kyoo.Tests.Database
|
||||
await _repository.Create(season);
|
||||
|
||||
Season retrieved = await _repository.Get(2);
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalId);
|
||||
Assert.Equal(2, retrieved.ExternalId.Count);
|
||||
KAssert.DeepEqual(season.ExternalId.First(), retrieved.ExternalId.First());
|
||||
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);
|
||||
value.Name = "New Title";
|
||||
value.Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "new-poster"
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
value.Poster = new Image("test");
|
||||
await _repository.Edit(value);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Season retrieved = await database.Seasons.FirstAsync();
|
||||
@ -138,22 +132,18 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditMetadataTest()
|
||||
{
|
||||
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",
|
||||
DataId = "id"
|
||||
},
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Season retrieved = await database.Seasons
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
Season retrieved = await database.Seasons.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
@ -162,41 +152,33 @@ namespace Kyoo.Tests.Database
|
||||
public async Task AddMetadataTest()
|
||||
{
|
||||
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",
|
||||
DataId = "id"
|
||||
},
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Season retrieved = await database.Seasons
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
Season retrieved = await database.Seasons.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
value.ExternalId.Add(new MetadataId
|
||||
value.ExternalId.Add("toto", new MetadataId
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "link",
|
||||
DataId = "id"
|
||||
});
|
||||
await _repository.Edit(value, false);
|
||||
await _repository.Edit(value);
|
||||
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Season retrieved = await database.Seasons
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
Season retrieved = await database.Seasons.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
@ -213,7 +195,7 @@ namespace Kyoo.Tests.Database
|
||||
Season value = new()
|
||||
{
|
||||
Name = "This is a test super title",
|
||||
ShowID = 1
|
||||
ShowId = 1
|
||||
};
|
||||
await _repository.Create(value);
|
||||
ICollection<Season> ret = await _repository.Search(query);
|
||||
|
@ -16,7 +16,6 @@
|
||||
// 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;
|
||||
@ -55,9 +54,8 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditTest()
|
||||
{
|
||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||
value.Path = "/super";
|
||||
value.Name = "New Title";
|
||||
Show edited = await _repository.Edit(value, false);
|
||||
Show edited = await _repository.Edit(value);
|
||||
KAssert.DeepEqual(value, edited);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
@ -70,11 +68,11 @@ namespace Kyoo.Tests.Database
|
||||
public async Task EditGenreTest()
|
||||
{
|
||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||
value.Genres = new[] { new Genre("test") };
|
||||
Show edited = await _repository.Edit(value, false);
|
||||
value.Genres = new List<Genre> { Genre.Action };
|
||||
Show edited = await _repository.Edit(value);
|
||||
|
||||
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();
|
||||
Show show = await database.Shows
|
||||
@ -82,19 +80,18 @@ namespace Kyoo.Tests.Database
|
||||
.FirstAsync();
|
||||
|
||||
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]
|
||||
public async Task AddGenreTest()
|
||||
{
|
||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||
await Repositories.LibraryManager.Load(value, x => x.Genres);
|
||||
value.Genres.Add(new Genre("test"));
|
||||
Show edited = await _repository.Edit(value, false);
|
||||
value.Genres.Add(Genre.Drama);
|
||||
Show edited = await _repository.Edit(value);
|
||||
|
||||
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();
|
||||
Show show = await database.Shows
|
||||
@ -102,7 +99,7 @@ namespace Kyoo.Tests.Database
|
||||
.FirstAsync();
|
||||
|
||||
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]
|
||||
@ -110,10 +107,10 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||
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("studio", edited.Studio.Slug);
|
||||
Assert.Equal("studio", edited.Studio!.Slug);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Show show = await database.Shows
|
||||
@ -121,15 +118,15 @@ namespace Kyoo.Tests.Database
|
||||
.FirstAsync();
|
||||
|
||||
Assert.Equal(value.Slug, show.Slug);
|
||||
Assert.Equal("studio", show.Studio.Slug);
|
||||
Assert.Equal("studio", show.Studio!.Slug);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditAliasesTest()
|
||||
{
|
||||
Show value = await _repository.Get(TestSample.Get<Show>().Slug);
|
||||
value.Aliases = new[] { "NiceNewAlias", "SecondAlias" };
|
||||
Show edited = await _repository.Edit(value, false);
|
||||
value.Aliases = new List<string>() { "NiceNewAlias", "SecondAlias" };
|
||||
Show edited = await _repository.Edit(value);
|
||||
|
||||
Assert.Equal(value.Slug, edited.Slug);
|
||||
Assert.Equal(value.Aliases, edited.Aliases);
|
||||
@ -156,10 +153,10 @@ namespace Kyoo.Tests.Database
|
||||
Role = "NiceCharacter"
|
||||
}
|
||||
};
|
||||
Show edited = await _repository.Edit(value, false);
|
||||
Show edited = await _repository.Edit(value);
|
||||
|
||||
Assert.Equal(value.Slug, edited.Slug);
|
||||
Assert.Equal(edited.People.First().ShowID, value.Id);
|
||||
Assert.Equal(edited.People!.First().ShowID, value.Id);
|
||||
Assert.Equal(
|
||||
value.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.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]
|
||||
public async Task EditExternalIDsTest()
|
||||
{
|
||||
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"
|
||||
}
|
||||
};
|
||||
Show edited = await _repository.Edit(value, false);
|
||||
Show edited = await _repository.Edit(value);
|
||||
|
||||
Assert.Equal(value.Slug, edited.Slug);
|
||||
Assert.Equal(
|
||||
value.ExternalId.Select(x => new { x.DataID, x.Provider.Slug }),
|
||||
edited.ExternalId.Select(x => new { x.DataID, x.Provider.Slug }));
|
||||
Assert.Equal(value.ExternalId, edited.ExternalId);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Show show = await database.Shows
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
Show show = await database.Shows.FirstAsync();
|
||||
|
||||
Assert.Equal(value.Slug, show.Slug);
|
||||
Assert.Equal(
|
||||
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);
|
||||
Assert.Equal(value.ExternalId, show.ExternalId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -237,22 +202,14 @@ namespace Kyoo.Tests.Database
|
||||
Show expected = TestSample.Get<Show>();
|
||||
expected.Id = 0;
|
||||
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"
|
||||
}
|
||||
};
|
||||
expected.Genres = new[]
|
||||
{
|
||||
new Genre
|
||||
{
|
||||
Name = "Genre",
|
||||
Slug = "genre"
|
||||
}
|
||||
};
|
||||
expected.Genres = new List<Genre>() { Genre.Action };
|
||||
expected.People = new[]
|
||||
{
|
||||
new PeopleRole
|
||||
@ -270,7 +227,6 @@ namespace Kyoo.Tests.Database
|
||||
await using DatabaseContext context = Repositories.Context.New();
|
||||
Show retrieved = await context.Shows
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.Include(x => x.Genres)
|
||||
.Include(x => x.People)
|
||||
.ThenInclude(x => x.People)
|
||||
@ -281,10 +237,7 @@ namespace Kyoo.Tests.Database
|
||||
x.Show = null;
|
||||
x.People.Roles = null;
|
||||
});
|
||||
retrieved.Studio.Shows = null;
|
||||
retrieved.Genres.ForEach(x => x.Shows = null);
|
||||
|
||||
expected.Genres.ForEach(x => x.Shows = null);
|
||||
retrieved.Studio!.Shows = null;
|
||||
expected.People.ForEach(x =>
|
||||
{
|
||||
x.Show = null;
|
||||
@ -300,11 +253,10 @@ namespace Kyoo.Tests.Database
|
||||
Show expected = TestSample.Get<Show>();
|
||||
expected.Id = 0;
|
||||
expected.Slug = "created-relation-test";
|
||||
expected.ExternalId = new[]
|
||||
expected.ExternalId = new Dictionary<string, MetadataId>
|
||||
{
|
||||
new MetadataId
|
||||
["test"] = new()
|
||||
{
|
||||
Provider = TestSample.Get<Provider>(),
|
||||
DataId = "ID"
|
||||
}
|
||||
};
|
||||
@ -313,11 +265,10 @@ namespace Kyoo.Tests.Database
|
||||
await using DatabaseContext context = Repositories.Context.New();
|
||||
Show retrieved = await context.Shows
|
||||
.Include(x => x.ExternalId)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync(x => x.Id == created.Id);
|
||||
KAssert.DeepEqual(expected, retrieved);
|
||||
Assert.Single(retrieved.ExternalId);
|
||||
Assert.Equal("ID", retrieved.ExternalId.First().DataID);
|
||||
Assert.Equal("ID", retrieved.ExternalId["test"].DataId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -362,25 +313,12 @@ namespace Kyoo.Tests.Database
|
||||
await Repositories.LibraryManager.Load(show, x => x.Seasons);
|
||||
await Repositories.LibraryManager.Load(show, x => x.Episodes);
|
||||
Assert.Equal(1, await _repository.GetCount());
|
||||
Assert.Single(show.Seasons);
|
||||
Assert.Single(show.Episodes);
|
||||
Assert.Single(show.Seasons!);
|
||||
Assert.Single(show.Episodes!);
|
||||
await _repository.Delete(show);
|
||||
Assert.Equal(0, await Repositories.LibraryManager.ShowRepository.GetCount());
|
||||
Assert.Equal(0, await Repositories.LibraryManager.SeasonRepository.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",
|
||||
Name = "New Collection",
|
||||
Overview = "A collection created by new sample",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Thumbnail] = "thumbnail"
|
||||
}
|
||||
Thumbnail = new Image("thumbnail")
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -52,13 +49,9 @@ namespace Kyoo.Tests
|
||||
Status = Status.Planned,
|
||||
StartAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
||||
EndAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "Poster",
|
||||
[Images.Logo] = "Logo",
|
||||
[Images.Thumbnail] = "Thumbnail"
|
||||
},
|
||||
IsMovie = false,
|
||||
Poster = new Image("Poster"),
|
||||
Logo = new Image("Logo"),
|
||||
Thumbnail = new Image("Thumbnail"),
|
||||
Studio = null
|
||||
}
|
||||
},
|
||||
@ -67,17 +60,14 @@ namespace Kyoo.Tests
|
||||
() => new Season
|
||||
{
|
||||
Id = 2,
|
||||
ShowID = 1,
|
||||
ShowId = 1,
|
||||
ShowSlug = Get<Show>().Slug,
|
||||
Name = "New season",
|
||||
Overview = "New overview",
|
||||
EndDate = new DateTime(2000, 10, 10).ToUniversalTime(),
|
||||
SeasonNumber = 2,
|
||||
StartDate = new DateTime(2010, 10, 10).ToUniversalTime(),
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Logo] = "logo"
|
||||
}
|
||||
Logo = new Image("logo")
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -85,9 +75,9 @@ namespace Kyoo.Tests
|
||||
() => new Episode
|
||||
{
|
||||
Id = 2,
|
||||
ShowID = 1,
|
||||
ShowId = 1,
|
||||
ShowSlug = Get<Show>().Slug,
|
||||
SeasonID = 1,
|
||||
SeasonId = 1,
|
||||
SeasonNumber = Get<Season>().SeasonNumber,
|
||||
EpisodeNumber = 3,
|
||||
AbsoluteNumber = 4,
|
||||
@ -95,23 +85,7 @@ namespace Kyoo.Tests
|
||||
Name = "New Episode Title",
|
||||
ReleaseDate = new DateTime(2000, 10, 10).ToUniversalTime(),
|
||||
Overview = "new episode overview",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[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"
|
||||
}
|
||||
Logo = new Image("new episode logo")
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -121,27 +95,14 @@ namespace Kyoo.Tests
|
||||
Id = 2,
|
||||
Slug = "new-person-name",
|
||||
Name = "New person name",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Logo] = "Old Logo",
|
||||
[Images.Poster] = "Old poster"
|
||||
}
|
||||
Logo = new Image("Old Logo"),
|
||||
Poster = new Image("Old poster")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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),
|
||||
() => new Collection
|
||||
@ -150,10 +111,7 @@ namespace Kyoo.Tests
|
||||
Slug = "collection",
|
||||
Name = "Collection",
|
||||
Overview = "A nice collection for tests",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "Poster"
|
||||
}
|
||||
Poster = new Image("Poster")
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -163,7 +121,7 @@ namespace Kyoo.Tests
|
||||
Id = 1,
|
||||
Slug = "anohana",
|
||||
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.",
|
||||
"AnoHana",
|
||||
@ -173,16 +131,12 @@ namespace Kyoo.Tests
|
||||
"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.",
|
||||
Status = Status.Finished,
|
||||
StudioID = 1,
|
||||
StudioId = 1,
|
||||
StartAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
||||
EndAir = new DateTime(2011, 1, 1).ToUniversalTime(),
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "Poster",
|
||||
[Images.Logo] = "Logo",
|
||||
[Images.Thumbnail] = "Thumbnail"
|
||||
},
|
||||
IsMovie = false,
|
||||
Poster = new Image("Poster"),
|
||||
Logo = new Image("Logo"),
|
||||
Thumbnail = new Image("Thumbnail"),
|
||||
Studio = null
|
||||
}
|
||||
},
|
||||
@ -192,18 +146,15 @@ namespace Kyoo.Tests
|
||||
{
|
||||
Id = 1,
|
||||
ShowSlug = "anohana",
|
||||
ShowID = 1,
|
||||
ShowId = 1,
|
||||
SeasonNumber = 1,
|
||||
Name = "Season 1",
|
||||
Overview = "The first season",
|
||||
StartDate = new DateTime(2020, 06, 05).ToUniversalTime(),
|
||||
EndDate = new DateTime(2020, 07, 05).ToUniversalTime(),
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "Poster",
|
||||
[Images.Logo] = "Logo",
|
||||
[Images.Thumbnail] = "Thumbnail"
|
||||
},
|
||||
Poster = new Image("Poster"),
|
||||
Logo = new Image("Logo"),
|
||||
Thumbnail = new Image("Thumbnail")
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -212,18 +163,15 @@ namespace Kyoo.Tests
|
||||
{
|
||||
Id = 1,
|
||||
ShowSlug = "anohana",
|
||||
ShowID = 1,
|
||||
SeasonID = 1,
|
||||
ShowId = 1,
|
||||
SeasonId = 1,
|
||||
SeasonNumber = 1,
|
||||
EpisodeNumber = 1,
|
||||
AbsoluteNumber = 1,
|
||||
Path = "/home/kyoo/anohana-s1e1",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "Poster",
|
||||
[Images.Logo] = "Logo",
|
||||
[Images.Thumbnail] = "Thumbnail"
|
||||
},
|
||||
Poster = new Image("Poster"),
|
||||
Logo = new Image("Logo"),
|
||||
Thumbnail = new Image("Thumbnail"),
|
||||
Name = "Episode 1",
|
||||
Overview = "Summary of the first episode",
|
||||
ReleaseDate = new DateTime(2020, 06, 05).ToUniversalTime()
|
||||
@ -236,12 +184,9 @@ namespace Kyoo.Tests
|
||||
Id = 1,
|
||||
Slug = "the-actor",
|
||||
Name = "The Actor",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "Poster",
|
||||
[Images.Logo] = "Logo",
|
||||
[Images.Thumbnail] = "Thumbnail"
|
||||
},
|
||||
Poster = new Image("Poster"),
|
||||
Logo = new Image("Logo"),
|
||||
Thumbnail = new Image("Thumbnail")
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -253,30 +198,6 @@ namespace Kyoo.Tests
|
||||
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),
|
||||
() => new User
|
||||
@ -309,20 +230,20 @@ namespace Kyoo.Tests
|
||||
|
||||
Show show = Get<Show>();
|
||||
show.Id = 0;
|
||||
show.StudioID = 0;
|
||||
show.StudioId = 0;
|
||||
context.Shows.Add(show);
|
||||
|
||||
Season season = Get<Season>();
|
||||
season.Id = 0;
|
||||
season.ShowID = 0;
|
||||
season.ShowId = 0;
|
||||
season.Show = show;
|
||||
context.Seasons.Add(season);
|
||||
|
||||
Episode episode = Get<Episode>();
|
||||
episode.Id = 0;
|
||||
episode.ShowID = 0;
|
||||
episode.ShowId = 0;
|
||||
episode.Show = show;
|
||||
episode.SeasonID = 0;
|
||||
episode.SeasonId = 0;
|
||||
episode.Season = season;
|
||||
context.Episodes.Add(episode);
|
||||
|
||||
@ -331,20 +252,10 @@ namespace Kyoo.Tests
|
||||
studio.Shows = new List<Show> { show };
|
||||
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.Id = 0;
|
||||
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.Id = 0;
|
||||
context.Users.Add(user);
|
||||
@ -358,41 +269,18 @@ namespace Kyoo.Tests
|
||||
{
|
||||
Id = 2,
|
||||
ShowSlug = "anohana",
|
||||
ShowID = 1,
|
||||
ShowId = 1,
|
||||
SeasonNumber = null,
|
||||
EpisodeNumber = null,
|
||||
AbsoluteNumber = 3,
|
||||
Path = "/home/kyoo/anohana-3",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "Poster",
|
||||
[Images.Logo] = "Logo",
|
||||
[Images.Thumbnail] = "Thumbnail"
|
||||
},
|
||||
Poster = new Image("Poster"),
|
||||
Logo = new Image("Logo"),
|
||||
Thumbnail = new Image("Thumbnail"),
|
||||
Name = "Episode 3",
|
||||
Overview = "Summary of the third absolute episode",
|
||||
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/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Utils;
|
||||
using Xunit;
|
||||
|
||||
@ -27,53 +25,6 @@ namespace Kyoo.Tests.Utility
|
||||
{
|
||||
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]
|
||||
public void IfEmptyTest()
|
||||
{
|
||||
|
@ -16,13 +16,9 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
using Xunit;
|
||||
|
||||
@ -30,318 +26,21 @@ namespace Kyoo.Tests.Utility
|
||||
{
|
||||
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]
|
||||
public void CompleteTest()
|
||||
{
|
||||
Genre genre = new()
|
||||
Studio genre = new()
|
||||
{
|
||||
ID = 5,
|
||||
Id = 5,
|
||||
Name = "merged"
|
||||
};
|
||||
Genre genre2 = new()
|
||||
Studio genre2 = new()
|
||||
{
|
||||
Name = "test"
|
||||
};
|
||||
Genre ret = Merger.Complete(genre, genre2);
|
||||
Studio ret = Merger.Complete(genre, genre2);
|
||||
Assert.True(ReferenceEquals(genre, ret));
|
||||
Assert.Equal(5, ret.ID);
|
||||
Assert.Equal(5, ret.Id);
|
||||
Assert.Equal("test", genre.Name);
|
||||
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.
|
||||
}
|
||||
|
||||
[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]
|
||||
public void CompleteDictionaryNullValue()
|
||||
{
|
||||
|
@ -26,13 +26,6 @@ namespace Kyoo.Tests.Utility
|
||||
{
|
||||
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]
|
||||
public async Task ThenTest()
|
||||
{
|
||||
@ -59,37 +52,5 @@ namespace Kyoo.Tests.Utility
|
||||
await Assert.ThrowsAsync<TaskCanceledException>(() => Task.Run(Infinite, token.Token)
|
||||
.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.Reflection;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Utils;
|
||||
using Xunit;
|
||||
|
||||
using KUtility = Kyoo.Utils.Utility;
|
||||
@ -35,7 +34,6 @@ namespace Kyoo.Tests.Utility
|
||||
Expression<Func<Show, int>> member = 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(memberCast));
|
||||
|
||||
@ -51,7 +49,6 @@ namespace Kyoo.Tests.Utility
|
||||
|
||||
Assert.Equal("ID", KUtility.GetPropertyName(member));
|
||||
Assert.Equal("ID", KUtility.GetPropertyName(memberCast));
|
||||
Assert.Throws<ArgumentException>(() => KUtility.GetPropertyName(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -84,16 +81,5 @@ namespace Kyoo.Tests.Utility
|
||||
Array.Empty<Type>(),
|
||||
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