PipeDelimitedCollectionModelBinderTests.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Threading.Tasks;
  5. using Jellyfin.Api.ModelBinders;
  6. using Microsoft.AspNetCore.Http;
  7. using Microsoft.AspNetCore.Mvc.ModelBinding;
  8. using Microsoft.Extensions.Logging.Abstractions;
  9. using Microsoft.Extensions.Primitives;
  10. using Moq;
  11. using Xunit;
  12. namespace Jellyfin.Api.Tests.ModelBinders
  13. {
  14. public sealed class PipeDelimitedCollectionModelBinderTests
  15. {
  16. [Fact]
  17. public async Task BindModelAsync_CorrectlyBindsValidPipeDelimitedStringArrayQuery()
  18. {
  19. var queryParamName = "test";
  20. IReadOnlyList<string> queryParamValues = new[] { "lol", "xd" };
  21. var queryParamString = "lol|xd";
  22. var queryParamType = typeof(string[]);
  23. var modelBinder = new PipeDelimitedCollectionModelBinder(new NullLogger<PipeDelimitedCollectionModelBinder>());
  24. var valueProvider = new QueryStringValueProvider(
  25. new BindingSource(string.Empty, string.Empty, false, false),
  26. new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }),
  27. CultureInfo.InvariantCulture);
  28. var bindingContextMock = new Mock<ModelBindingContext>();
  29. bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
  30. bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName);
  31. bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType);
  32. bindingContextMock.SetupProperty(b => b.Result);
  33. await modelBinder.BindModelAsync(bindingContextMock.Object);
  34. Assert.True(bindingContextMock.Object.Result.IsModelSet);
  35. Assert.Equal((IReadOnlyList<string>?)bindingContextMock.Object?.Result.Model, queryParamValues);
  36. }
  37. [Fact]
  38. public async Task BindModelAsync_CorrectlyBindsValidDelimitedIntArrayQuery()
  39. {
  40. var queryParamName = "test";
  41. IReadOnlyList<int> queryParamValues = new[] { 42, 0 };
  42. var queryParamString = "42|0";
  43. var queryParamType = typeof(int[]);
  44. var modelBinder = new PipeDelimitedCollectionModelBinder(new NullLogger<PipeDelimitedCollectionModelBinder>());
  45. var valueProvider = new QueryStringValueProvider(
  46. new BindingSource(string.Empty, string.Empty, false, false),
  47. new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }),
  48. CultureInfo.InvariantCulture);
  49. var bindingContextMock = new Mock<ModelBindingContext>();
  50. bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
  51. bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName);
  52. bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType);
  53. bindingContextMock.SetupProperty(b => b.Result);
  54. await modelBinder.BindModelAsync(bindingContextMock.Object);
  55. Assert.True(bindingContextMock.Object.Result.IsModelSet);
  56. Assert.Equal((IReadOnlyList<int>?)bindingContextMock.Object.Result.Model, queryParamValues);
  57. }
  58. [Fact]
  59. public async Task BindModelAsync_CorrectlyBindsValidPipeDelimitedEnumArrayQuery()
  60. {
  61. var queryParamName = "test";
  62. IReadOnlyList<TestType> queryParamValues = new[] { TestType.How, TestType.Much };
  63. var queryParamString = "How|Much";
  64. var queryParamType = typeof(TestType[]);
  65. var modelBinder = new PipeDelimitedCollectionModelBinder(new NullLogger<PipeDelimitedCollectionModelBinder>());
  66. var valueProvider = new QueryStringValueProvider(
  67. new BindingSource(string.Empty, string.Empty, false, false),
  68. new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }),
  69. CultureInfo.InvariantCulture);
  70. var bindingContextMock = new Mock<ModelBindingContext>();
  71. bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
  72. bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName);
  73. bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType);
  74. bindingContextMock.SetupProperty(b => b.Result);
  75. await modelBinder.BindModelAsync(bindingContextMock.Object);
  76. Assert.True(bindingContextMock.Object.Result.IsModelSet);
  77. Assert.Equal((IReadOnlyList<TestType>?)bindingContextMock.Object.Result.Model, queryParamValues);
  78. }
  79. [Fact]
  80. public async Task BindModelAsync_CorrectlyBindsValidPipeDelimitedEnumArrayQueryWithDoublePipes()
  81. {
  82. var queryParamName = "test";
  83. IReadOnlyList<TestType> queryParamValues = new[] { TestType.How, TestType.Much };
  84. var queryParamString = "How||Much";
  85. var queryParamType = typeof(TestType[]);
  86. var modelBinder = new PipeDelimitedCollectionModelBinder(new NullLogger<PipeDelimitedCollectionModelBinder>());
  87. var valueProvider = new QueryStringValueProvider(
  88. new BindingSource(string.Empty, string.Empty, false, false),
  89. new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }),
  90. CultureInfo.InvariantCulture);
  91. var bindingContextMock = new Mock<ModelBindingContext>();
  92. bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
  93. bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName);
  94. bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType);
  95. bindingContextMock.SetupProperty(b => b.Result);
  96. await modelBinder.BindModelAsync(bindingContextMock.Object);
  97. Assert.True(bindingContextMock.Object.Result.IsModelSet);
  98. Assert.Equal((IReadOnlyList<TestType>?)bindingContextMock.Object.Result.Model, queryParamValues);
  99. }
  100. [Fact]
  101. public async Task BindModelAsync_CorrectlyBindsValidEnumArrayQuery()
  102. {
  103. var queryParamName = "test";
  104. IReadOnlyList<TestType> queryParamValues = new[] { TestType.How, TestType.Much };
  105. var queryParamString1 = "How";
  106. var queryParamString2 = "Much";
  107. var queryParamType = typeof(TestType[]);
  108. var modelBinder = new PipeDelimitedCollectionModelBinder(new NullLogger<PipeDelimitedCollectionModelBinder>());
  109. var valueProvider = new QueryStringValueProvider(
  110. new BindingSource(string.Empty, string.Empty, false, false),
  111. new QueryCollection(new Dictionary<string, StringValues>
  112. {
  113. { queryParamName, new StringValues(new[] { queryParamString1, queryParamString2 }) },
  114. }),
  115. CultureInfo.InvariantCulture);
  116. var bindingContextMock = new Mock<ModelBindingContext>();
  117. bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
  118. bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName);
  119. bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType);
  120. bindingContextMock.SetupProperty(b => b.Result);
  121. await modelBinder.BindModelAsync(bindingContextMock.Object);
  122. Assert.True(bindingContextMock.Object.Result.IsModelSet);
  123. Assert.Equal((IReadOnlyList<TestType>?)bindingContextMock.Object.Result.Model, queryParamValues);
  124. }
  125. [Fact]
  126. public async Task BindModelAsync_CorrectlyBindsEmptyEnumArrayQuery()
  127. {
  128. var queryParamName = "test";
  129. IReadOnlyList<TestType> queryParamValues = Array.Empty<TestType>();
  130. var queryParamType = typeof(TestType[]);
  131. var modelBinder = new PipeDelimitedCollectionModelBinder(new NullLogger<PipeDelimitedCollectionModelBinder>());
  132. var valueProvider = new QueryStringValueProvider(
  133. new BindingSource(string.Empty, string.Empty, false, false),
  134. new QueryCollection(new Dictionary<string, StringValues>
  135. {
  136. { queryParamName, new StringValues(value: null) },
  137. }),
  138. CultureInfo.InvariantCulture);
  139. var bindingContextMock = new Mock<ModelBindingContext>();
  140. bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
  141. bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName);
  142. bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType);
  143. bindingContextMock.SetupProperty(b => b.Result);
  144. await modelBinder.BindModelAsync(bindingContextMock.Object);
  145. Assert.True(bindingContextMock.Object.Result.IsModelSet);
  146. Assert.Equal((IReadOnlyList<TestType>?)bindingContextMock.Object.Result.Model, queryParamValues);
  147. }
  148. [Fact]
  149. public async Task BindModelAsync_EnumArrayQuery_BindValidOnly()
  150. {
  151. var queryParamName = "test";
  152. var queryParamString = "🔥|😢";
  153. var queryParamType = typeof(IReadOnlyList<TestType>);
  154. var modelBinder = new PipeDelimitedCollectionModelBinder(new NullLogger<PipeDelimitedCollectionModelBinder>());
  155. var valueProvider = new QueryStringValueProvider(
  156. new BindingSource(string.Empty, string.Empty, false, false),
  157. new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }),
  158. CultureInfo.InvariantCulture);
  159. var bindingContextMock = new Mock<ModelBindingContext>();
  160. bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
  161. bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName);
  162. bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType);
  163. bindingContextMock.SetupProperty(b => b.Result);
  164. await modelBinder.BindModelAsync(bindingContextMock.Object);
  165. Assert.True(bindingContextMock.Object.Result.IsModelSet);
  166. var listResult = (IReadOnlyList<TestType>?)bindingContextMock.Object.Result.Model;
  167. Assert.NotNull(listResult);
  168. Assert.Empty(listResult);
  169. }
  170. [Fact]
  171. public async Task BindModelAsync_EnumArrayQuery_BindValidOnly_2()
  172. {
  173. var queryParamName = "test";
  174. var queryParamString1 = "How";
  175. var queryParamString2 = "😱";
  176. var queryParamType = typeof(IReadOnlyList<TestType>);
  177. var modelBinder = new PipeDelimitedCollectionModelBinder(new NullLogger<PipeDelimitedCollectionModelBinder>());
  178. var valueProvider = new QueryStringValueProvider(
  179. new BindingSource(string.Empty, string.Empty, false, false),
  180. new QueryCollection(new Dictionary<string, StringValues>
  181. {
  182. { queryParamName, new StringValues(new[] { queryParamString1, queryParamString2 }) },
  183. }),
  184. CultureInfo.InvariantCulture);
  185. var bindingContextMock = new Mock<ModelBindingContext>();
  186. bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
  187. bindingContextMock.Setup(b => b.ModelName).Returns(queryParamName);
  188. bindingContextMock.Setup(b => b.ModelType).Returns(queryParamType);
  189. bindingContextMock.SetupProperty(b => b.Result);
  190. await modelBinder.BindModelAsync(bindingContextMock.Object);
  191. Assert.True(bindingContextMock.Object.Result.IsModelSet);
  192. var listResult = (IReadOnlyList<TestType>?)bindingContextMock.Object.Result.Model;
  193. Assert.NotNull(listResult);
  194. Assert.Single(listResult);
  195. }
  196. }
  197. }