mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-03 21:54:49 -04:00
CollectionRepository: Adding tests
This commit is contained in:
parent
4ae28f2594
commit
0fa73b1d6a
@ -22,12 +22,13 @@ namespace Kyoo
|
||||
/// <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>
|
||||
/// <returns>The two list merged as an array</returns>
|
||||
public static T[] MergeLists<T>(IEnumerable<T> first,
|
||||
IEnumerable<T> second,
|
||||
Func<T, T, bool> isEqual = null)
|
||||
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
||||
public static T[] MergeLists<T>([CanBeNull] IEnumerable<T> first,
|
||||
[CanBeNull] IEnumerable<T> second,
|
||||
[CanBeNull] Func<T, T, bool> isEqual = null)
|
||||
{
|
||||
if (first == null)
|
||||
return second.ToArray();
|
||||
return second?.ToArray();
|
||||
if (second == null)
|
||||
return first.ToArray();
|
||||
if (isEqual == null)
|
||||
@ -36,6 +37,31 @@ namespace Kyoo
|
||||
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>A dictionary containing the result of the merge.</returns>
|
||||
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
||||
public static IDictionary<T, T2> MergeDictionaries<T, T2>([CanBeNull] IDictionary<T, T2> first,
|
||||
[CanBeNull] IDictionary<T, T2> second)
|
||||
{
|
||||
if (first == null)
|
||||
return second;
|
||||
if (second == null)
|
||||
return first;
|
||||
Dictionary<T, T2> merged = new();
|
||||
merged.EnsureCapacity(first.Count + second.Count);
|
||||
foreach ((T key, T2 value) in first)
|
||||
merged.Add(key, value);
|
||||
foreach ((T key, T2 value) in second)
|
||||
merged.TryAdd(key, value);
|
||||
return merged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set every fields of first to those of second. Ignore fields marked with the <see cref="NotMergeableAttribute"/> attribute
|
||||
/// At the end, the OnMerge method of first will be called if first is a <see cref="IOnMerge"/>
|
||||
@ -63,16 +89,32 @@ namespace Kyoo
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set every default values of first to the value of second. ex: {id: 0, slug: "test"}, {id: 4, slug: "foo"} -> {id: 4, slug: "test"}.
|
||||
/// 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}"/> for more details).
|
||||
/// At the end, the OnMerge method of first will be called if first is a <see cref="IOnMerge"/>
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <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>
|
||||
/// <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 completed</typeparam>
|
||||
/// <returns><see cref="first"/></returns>
|
||||
/// <exception cref="ArgumentNullException">If first is null</exception>
|
||||
public static T Complete<T>([NotNull] T first, [CanBeNull] T second, Func<PropertyInfo, bool> where = null)
|
||||
public static T Complete<T>([NotNull] T first,
|
||||
[CanBeNull] T second,
|
||||
[InstantHandle] Func<PropertyInfo, bool> where = null)
|
||||
{
|
||||
if (first == null)
|
||||
throw new ArgumentNullException(nameof(first));
|
||||
@ -93,7 +135,19 @@ namespace Kyoo
|
||||
object defaultValue = property.GetCustomAttribute<DefaultValueAttribute>()?.Value
|
||||
?? property.PropertyType.GetClrDefault();
|
||||
|
||||
if (value?.Equals(defaultValue) == false && value != property.GetValue(first))
|
||||
if (value?.Equals(defaultValue) != false || value == property.GetValue(first))
|
||||
continue;
|
||||
if (Utility.IsOfGenericType(property.PropertyType, typeof(IDictionary<,>)))
|
||||
{
|
||||
Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
|
||||
.GenericTypeArguments;
|
||||
property.SetValue(first, Utility.RunGenericMethod<object>(
|
||||
typeof(Merger),
|
||||
nameof(MergeDictionaries),
|
||||
dictionaryTypes,
|
||||
value, property.GetValue(first)));
|
||||
}
|
||||
else
|
||||
property.SetValue(first, value);
|
||||
}
|
||||
|
||||
@ -103,17 +157,28 @@ namespace Kyoo
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An advanced <see cref="Complete{T}"/> function.
|
||||
/// This will set missing values of <see cref="first"/> to the corresponding values of <see cref="second"/>.
|
||||
/// Enumerable will be merged (concatenated).
|
||||
/// 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>
|
||||
/// <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>
|
||||
/// <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><see cref="first"/></returns>
|
||||
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
||||
public static T Merge<T>([CanBeNull] T first, [CanBeNull] T second)
|
||||
public static T Merge<T>([CanBeNull] T first,
|
||||
[CanBeNull] T second,
|
||||
[InstantHandle] Func<PropertyInfo, bool> where = null)
|
||||
{
|
||||
if (first == null)
|
||||
return second;
|
||||
@ -125,6 +190,9 @@ namespace Kyoo
|
||||
.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);
|
||||
@ -133,6 +201,16 @@ namespace Kyoo
|
||||
|
||||
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;
|
||||
property.SetValue(first, Utility.RunGenericMethod<object>(
|
||||
typeof(Merger),
|
||||
nameof(MergeDictionaries),
|
||||
dictionaryTypes,
|
||||
oldValue, newValue));
|
||||
}
|
||||
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType)
|
||||
&& property.PropertyType != typeof(string))
|
||||
{
|
||||
|
@ -234,16 +234,23 @@ namespace Kyoo.Controllers
|
||||
finally
|
||||
{
|
||||
Database.ChangeTracker.LazyLoadingEnabled = lazyLoading;
|
||||
Database.ChangeTracker.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An overridable method to edit relation of a resource.
|
||||
/// </summary>
|
||||
/// <param name="resource">The non edited resource</param>
|
||||
/// <param name="changed">The new version of <see cref="resource"/>. This item will be saved on the databse and replace <see cref="resource"/></param>
|
||||
/// <param name="resetOld">A boolean to indicate if all values of resource should be discarded or not.</param>
|
||||
/// <returns></returns>
|
||||
/// <param name="resource">
|
||||
/// The non edited resource
|
||||
/// </param>
|
||||
/// <param name="changed">
|
||||
/// The new version of <see cref="resource"/>.
|
||||
/// This item will be saved on the database and replace <see cref="resource"/>
|
||||
/// </param>
|
||||
/// <param name="resetOld">
|
||||
/// A boolean to indicate if all values of resource should be discarded or not.
|
||||
/// </param>
|
||||
protected virtual Task EditRelations(T resource, T changed, bool resetOld)
|
||||
{
|
||||
return Validate(resource);
|
||||
@ -254,7 +261,9 @@ namespace Kyoo.Controllers
|
||||
/// It is also called on the default implementation of <see cref="EditRelations"/>
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource that will be saved</param>
|
||||
/// <exception cref="ArgumentException">You can throw this if the resource is illegal and should not be saved.</exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// You can throw this if the resource is illegal and should not be saved.
|
||||
/// </exception>
|
||||
protected virtual Task Validate(T resource)
|
||||
{
|
||||
if (typeof(T).GetProperty(nameof(resource.Slug))!.GetCustomAttribute<ComputedAttribute>() != null)
|
||||
|
@ -1,5 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
@ -33,5 +38,161 @@ namespace Kyoo.Tests.Database
|
||||
{
|
||||
_repository = Repositories.LibraryManager.CollectionRepository;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateWithEmptySlugTest()
|
||||
{
|
||||
Collection collection = TestSample.GetNew<Collection>();
|
||||
collection.Slug = "";
|
||||
await Assert.ThrowsAsync<ArgumentException>(() => _repository.Create(collection));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateWithNumberSlugTest()
|
||||
{
|
||||
Collection collection = TestSample.GetNew<Collection>();
|
||||
collection.Slug = "2";
|
||||
Collection ret = await _repository.Create(collection);
|
||||
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.ExternalIDs = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
Provider = TestSample.Get<Provider>(),
|
||||
Link = "link",
|
||||
DataID = "id"
|
||||
},
|
||||
new MetadataID
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "new-provider-link",
|
||||
DataID = "new-id"
|
||||
}
|
||||
};
|
||||
await _repository.Create(collection);
|
||||
|
||||
Collection retrieved = await _repository.Get(2);
|
||||
await Repositories.LibraryManager.Load(retrieved, x => x.ExternalIDs);
|
||||
Assert.Equal(2, retrieved.ExternalIDs.Count);
|
||||
KAssert.DeepEqual(collection.ExternalIDs.First(), retrieved.ExternalIDs.First());
|
||||
KAssert.DeepEqual(collection.ExternalIDs.Last(), retrieved.ExternalIDs.Last());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditTest()
|
||||
{
|
||||
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
||||
value.Name = "New Title";
|
||||
value.Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "poster"
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditMetadataTest()
|
||||
{
|
||||
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
||||
value.ExternalIDs = new[]
|
||||
{
|
||||
new MetadataID
|
||||
{
|
||||
Provider = TestSample.Get<Provider>(),
|
||||
Link = "link",
|
||||
DataID = "id"
|
||||
},
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections
|
||||
.Include(x => x.ExternalIDs)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddMetadataTest()
|
||||
{
|
||||
Collection value = await _repository.Get(TestSample.Get<Collection>().Slug);
|
||||
value.ExternalIDs = new List<MetadataID>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Provider = TestSample.Get<Provider>(),
|
||||
Link = "link",
|
||||
DataID = "id"
|
||||
},
|
||||
};
|
||||
await _repository.Edit(value, false);
|
||||
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections
|
||||
.Include(x => x.ExternalIDs)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
|
||||
value.ExternalIDs.Add(new MetadataID
|
||||
{
|
||||
Provider = TestSample.GetNew<Provider>(),
|
||||
Link = "link",
|
||||
DataID = "id"
|
||||
});
|
||||
await _repository.Edit(value, false);
|
||||
|
||||
{
|
||||
await using DatabaseContext database = Repositories.Context.New();
|
||||
Collection retrieved = await database.Collections
|
||||
.Include(x => x.ExternalIDs)
|
||||
.ThenInclude(x => x.Provider)
|
||||
.FirstAsync();
|
||||
|
||||
KAssert.DeepEqual(value, retrieved);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("test")]
|
||||
[InlineData("super")]
|
||||
[InlineData("title")]
|
||||
[InlineData("TiTlE")]
|
||||
[InlineData("SuPeR")]
|
||||
public async Task SearchTest(string query)
|
||||
{
|
||||
Collection value = new()
|
||||
{
|
||||
Slug = "super-test",
|
||||
Name = "This is a test title",
|
||||
};
|
||||
await _repository.Create(value);
|
||||
ICollection<Collection> ret = await _repository.Search(query);
|
||||
KAssert.DeepEqual(value, ret.First());
|
||||
}
|
||||
}
|
||||
}
|
@ -79,9 +79,10 @@ namespace Kyoo.Tests.Database
|
||||
[Fact]
|
||||
public async Task GetDuplicatedSlugTests()
|
||||
{
|
||||
await _repositories.LibraryManager.Create(new Collection()
|
||||
await _repositories.LibraryManager.Create(new Collection
|
||||
{
|
||||
Slug = TestSample.Get<Show>().Slug
|
||||
Slug = TestSample.Get<Show>().Slug,
|
||||
Name = "name"
|
||||
});
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() => _repository.Get(TestSample.Get<Show>().Slug));
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ namespace Kyoo.Tests.Database
|
||||
Library library = TestSample.GetNew<Library>();
|
||||
library.Slug = "2";
|
||||
Library ret = await _repository.Create(library);
|
||||
Assert.Equal("2!", library.Slug);
|
||||
Assert.Equal("2!", ret.Slug);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -18,6 +18,20 @@ namespace Kyoo.Tests
|
||||
Paths = new [] {"/a/random/path"}
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(Collection),
|
||||
() => new Collection
|
||||
{
|
||||
ID = 2,
|
||||
Slug = "new-collection",
|
||||
Name = "New Collection",
|
||||
Overview = "A collection created by new sample",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Thumbnail] = "thumbnail"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(Show),
|
||||
() => new Show
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Kyoo.Models;
|
||||
@ -135,6 +136,68 @@ namespace Kyoo.Tests.Utility
|
||||
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()
|
||||
{
|
||||
@ -208,5 +271,99 @@ namespace Kyoo.Tests.Utility
|
||||
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()
|
||||
{
|
||||
ID = 5,
|
||||
Name = "merged"
|
||||
};
|
||||
Genre genre2 = new()
|
||||
{
|
||||
Name = "test"
|
||||
};
|
||||
Genre ret = Merger.Complete(genre, genre2);
|
||||
Assert.True(ReferenceEquals(genre, ret));
|
||||
Assert.Equal(5, ret.ID);
|
||||
Assert.Equal("test", genre.Name);
|
||||
Assert.Null(genre.Slug);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompleteDictionaryTest()
|
||||
{
|
||||
Collection collection = new()
|
||||
{
|
||||
ID = 5,
|
||||
Name = "merged",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Logo] = "logo",
|
||||
[Images.Poster] = "poster"
|
||||
}
|
||||
|
||||
};
|
||||
Collection collection2 = new()
|
||||
{
|
||||
Name = "test",
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Images.Poster] = "new-poster",
|
||||
[Images.Thumbnail] = "thumbnails"
|
||||
}
|
||||
};
|
||||
Collection ret = Merger.Complete(collection, collection2);
|
||||
Assert.True(ReferenceEquals(collection, ret));
|
||||
Assert.Equal(5, ret.ID);
|
||||
Assert.Equal("test", ret.Name);
|
||||
Assert.Null(ret.Slug);
|
||||
Assert.Equal(3, ret.Images.Count);
|
||||
Assert.Equal("new-poster", ret.Images[Images.Poster]);
|
||||
Assert.Equal("thumbnails", ret.Images[Images.Thumbnail]);
|
||||
Assert.Equal("logo", ret.Images[Images.Logo]);
|
||||
}
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ namespace Kyoo.Controllers
|
||||
public override async Task<ICollection<Collection>> Search(string query)
|
||||
{
|
||||
return await _database.Collections
|
||||
.Where(_database.Like<Collection>(x => x.Name, $"%{query}%"))
|
||||
.Where(_database.Like<Collection>(x => x.Name + " " + x.Slug, $"%{query}%"))
|
||||
.OrderBy(DefaultSort)
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
@ -53,7 +53,6 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
obj.ExternalIDs.ForEach(x => _database.MetadataIds<Collection>().Attach(x));
|
||||
await _database.SaveChangesAsync($"Trying to insert a duplicated collection (slug {obj.Slug} already exists).");
|
||||
return obj;
|
||||
}
|
||||
@ -62,24 +61,34 @@ namespace Kyoo.Controllers
|
||||
protected override async Task Validate(Collection resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
await resource.ExternalIDs.ForEachAsync(async x =>
|
||||
{
|
||||
x.Provider = await _providers.CreateIfNotExists(x.Provider);
|
||||
x.ProviderID = x.Provider.ID;
|
||||
_database.Entry(x.Provider).State = EntityState.Detached;
|
||||
});
|
||||
|
||||
if (string.IsNullOrEmpty(resource.Slug))
|
||||
throw new ArgumentException("The collection's slug must be set and not empty");
|
||||
if (string.IsNullOrEmpty(resource.Name))
|
||||
throw new ArgumentException("The collection's name must be set and not empty");
|
||||
|
||||
if (resource.ExternalIDs != null)
|
||||
{
|
||||
foreach (MetadataID id in resource.ExternalIDs)
|
||||
{
|
||||
id.Provider = await _providers.CreateIfNotExists(id.Provider);
|
||||
id.ProviderID = id.Provider.ID;
|
||||
_database.Entry(id.Provider).State = EntityState.Detached;
|
||||
}
|
||||
_database.MetadataIds<Collection>().AttachRange(resource.ExternalIDs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task EditRelations(Collection resource, Collection changed, bool resetOld)
|
||||
{
|
||||
await Validate(resource);
|
||||
|
||||
if (changed.ExternalIDs != null || resetOld)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
|
||||
resource.ExternalIDs = changed.ExternalIDs;
|
||||
}
|
||||
|
||||
await base.EditRelations(resource, changed, resetOld);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
Loading…
x
Reference in New Issue
Block a user