Merge branch 'master' into simplify_authz

# Conflicts:
#	Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
This commit is contained in:
cvium
2023-02-12 22:59:48 +01:00
60 changed files with 700 additions and 357 deletions

View File

@@ -0,0 +1,23 @@
<Project>
<!-- Sets defaults for all test projects -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)/jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<!-- Code Analyzers -->
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
</Project>

View File

@@ -5,12 +5,6 @@
<ProjectGuid>{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoFixture" />
<PackageReference Include="AutoFixture.AutoMoq" />
@@ -27,17 +21,6 @@
<PackageReference Include="Moq" />
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../Jellyfin.Api/Jellyfin.Api.csproj" />
<ProjectReference Include="../../Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj" />

View File

@@ -5,12 +5,6 @@
<ProjectGuid>{DF194677-DFD3-42AF-9F75-D44D5A416478}</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
@@ -22,17 +16,6 @@
<PackageReference Include="FsCheck.Xunit" />
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../MediaBrowser.Common/MediaBrowser.Common.csproj" />
<ProjectReference Include="../../MediaBrowser.Providers/MediaBrowser.Providers.csproj" />

View File

@@ -5,12 +5,6 @@
<ProjectGuid>{462584F7-5023-4019-9EAC-B98CA458C0A0}</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
@@ -22,17 +16,6 @@
<PackageReference Include="coverlet.collector" />
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../MediaBrowser.Controller/MediaBrowser.Controller.csproj" />
</ItemGroup>

View File

@@ -1,11 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
@@ -17,17 +11,6 @@
<PackageReference Include="coverlet.collector" />
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../Emby.Dlna/Emby.Dlna.csproj" />
</ItemGroup>

View File

@@ -1,11 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
@@ -20,17 +14,6 @@
<PackageReference Include="FsCheck.Xunit" />
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../MediaBrowser.Model/MediaBrowser.Model.csproj" />
<ProjectReference Include="../../src/Jellyfin.Extensions/Jellyfin.Extensions.csproj" />

View File

@@ -1,11 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
@@ -19,16 +13,6 @@
</PackageReference>
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Jellyfin.MediaEncoding.Hls\Jellyfin.MediaEncoding.Hls.csproj" />
<ProjectReference Include="..\..\src\Jellyfin.MediaEncoding.Keyframes\Jellyfin.MediaEncoding.Keyframes.csproj" />

View File

@@ -1,12 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
<RootNamespace>Jellyfin.MediaEncoding.Keyframes</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
@@ -20,17 +13,6 @@
</PackageReference>
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj" />
</ItemGroup>

View File

@@ -5,12 +5,6 @@
<ProjectGuid>{28464062-0939-4AA7-9F7B-24DDDA61A7C0}</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<None Include="Test Data\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -31,17 +25,6 @@
</PackageReference>
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj" />
</ItemGroup>

View File

@@ -1,11 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
@@ -24,17 +18,6 @@
</None>
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../MediaBrowser.Model/MediaBrowser.Model.csproj" />
</ItemGroup>

View File

@@ -5,12 +5,6 @@
<ProjectGuid>{3998657B-1CCC-49DD-A19F-275DC8495F57}</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
@@ -26,15 +20,4 @@
<ProjectReference Include="..\..\Emby.Naming\Emby.Naming.csproj" />
</ItemGroup>
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
</Project>

View File

@@ -5,12 +5,6 @@
<ProjectGuid>{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
@@ -23,17 +17,6 @@
<PackageReference Include="Moq" />
</ItemGroup>
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../Emby.Server.Implementations/Emby.Server.Implementations.csproj" />
<ProjectReference Include="../../MediaBrowser.Common/MediaBrowser.Common.csproj" />

View File

@@ -1,11 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<None Include="Test Data\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -26,17 +20,6 @@
</PackageReference>
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../MediaBrowser.Providers/MediaBrowser.Providers.csproj" />
</ItemGroup>

View File

@@ -5,13 +5,6 @@
<ProjectGuid>{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
<RootNamespace>Jellyfin.Server.Implementations.Tests</RootNamespace>
</PropertyGroup>
<ItemGroup>
<None Include="Test Data\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -32,17 +25,6 @@
<PackageReference Include="coverlet.collector" />
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" />
<ProjectReference Include="..\..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" />

View File

@@ -67,4 +67,23 @@ public class XmlTvListingsProviderTests
Assert.Equal("https://domain.tld/image.png", program.ImageUrl);
Assert.Equal("3297", program.ChannelId);
}
[Theory]
[InlineData("Test Data/LiveTv/Listings/XmlTv/emptycategory.xml")]
[InlineData("https://example.com/emptycategory.xml")]
public async Task GetProgramsAsync_EmptyCategories_Success(string path)
{
var info = new ListingsProviderInfo()
{
Path = path
};
var startDate = new DateTime(2022, 11, 4);
var programs = await _xmlTvListingsProvider.GetProgramsAsync(info, "3297", startDate, startDate.AddDays(1), CancellationToken.None);
var programsList = programs.ToList();
Assert.Single(programsList);
var program = programsList[0];
Assert.DoesNotContain(program.Genres, g => string.IsNullOrEmpty(g));
Assert.Equal("3297", program.ChannelId);
}
}

View File

@@ -0,0 +1,6 @@
<tv date="20221104">
<programme channel="3297" start="20221104130000 -0400" stop="20221105235959 -0400">
<category lang="en" />
<category lang="en">sports</category>
</programme>
</tv>

View File

@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Jellyfin.Api.Models.StartupDtos;
using Jellyfin.Api.Models.UserDtos;
using Jellyfin.Extensions.Json;
using MediaBrowser.Model.Dto;
using Xunit;
namespace Jellyfin.Server.Integration.Tests
@@ -43,6 +44,33 @@ namespace Jellyfin.Server.Integration.Tests
return auth!.AccessToken;
}
public static async Task<UserDto> GetUserDtoAsync(HttpClient client)
{
using var response = await client.GetAsync("Users/Me").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var userDto = await JsonSerializer.DeserializeAsync<UserDto>(
await response.Content.ReadAsStreamAsync().ConfigureAwait(false), JsonDefaults.Options).ConfigureAwait(false);
Assert.NotNull(userDto);
return userDto;
}
public static async Task<BaseItemDto> GetRootFolderDtoAsync(HttpClient client, Guid userId = default)
{
if (userId.Equals(default))
{
var userDto = await GetUserDtoAsync(client).ConfigureAwait(false);
userId = userDto.Id;
}
var response = await client.GetAsync($"Users/{userId}/Items/Root").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var rootDto = await JsonSerializer.DeserializeAsync<BaseItemDto>(
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
JsonDefaults.Options).ConfigureAwait(false);
Assert.NotNull(rootDto);
return rootDto;
}
public static void AddAuthHeader(this HttpHeaders headers, string accessToken)
{
headers.Add(AuthHeaderName, DummyAuthHeader + $", Token={accessToken}");

View File

@@ -0,0 +1,64 @@
using System;
using System.Globalization;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using Jellyfin.Extensions.Json;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using Xunit;
namespace Jellyfin.Server.Integration.Tests.Controllers;
public sealed class ItemsControllerTests : IClassFixture<JellyfinApplicationFactory>
{
private readonly JellyfinApplicationFactory _factory;
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
private static string? _accessToken;
public ItemsControllerTests(JellyfinApplicationFactory factory)
{
_factory = factory;
}
[Fact]
public async Task GetItems_NoApiKeyOrUserId_BadRequest()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var response = await client.GetAsync("Items").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Theory]
[InlineData("Users/{0}/Items")]
[InlineData("Users/{0}/Items/Resume")]
public async Task GetUserItems_NonExistentUserId_NotFound(string format)
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, Guid.NewGuid())).ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory]
[InlineData("Items?userId={0}")]
[InlineData("Users/{0}/Items")]
[InlineData("Users/{0}/Items/Resume")]
public async Task GetItems_UserId_Ok(string format)
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, userDto.Id)).ConfigureAwait(false);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var items = await JsonSerializer.DeserializeAsync<QueryResult<BaseItemDto>>(
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
_jsonOptions).ConfigureAwait(false);
Assert.NotNull(items);
}
}

View File

@@ -0,0 +1,40 @@
using System;
using System.Globalization;
using System.Net;
using System.Threading.Tasks;
using Xunit;
namespace Jellyfin.Server.Integration.Tests.Controllers;
public sealed class LibraryControllerTests : IClassFixture<JellyfinApplicationFactory>
{
private readonly JellyfinApplicationFactory _factory;
private static string? _accessToken;
public LibraryControllerTests(JellyfinApplicationFactory factory)
{
_factory = factory;
}
[Theory]
[InlineData("Items/{0}/File")]
[InlineData("Items/{0}/ThemeSongs")]
[InlineData("Items/{0}/ThemeVideos")]
[InlineData("Items/{0}/ThemeMedia")]
[InlineData("Items/{0}/Ancestors")]
[InlineData("Items/{0}/Download")]
[InlineData("Artists/{0}/Similar")]
[InlineData("Items/{0}/Similar")]
[InlineData("Albums/{0}/Similar")]
[InlineData("Shows/{0}/Similar")]
[InlineData("Movies/{0}/Similar")]
[InlineData("Trailers/{0}/Similar")]
public async Task Get_NonExistentItemId_NotFound(string format)
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, Guid.NewGuid())).ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}

View File

@@ -0,0 +1,26 @@
using System.Net;
using System.Threading.Tasks;
using Xunit;
namespace Jellyfin.Server.Integration.Tests.Controllers;
public sealed class MusicGenreControllerTests : IClassFixture<JellyfinApplicationFactory>
{
private readonly JellyfinApplicationFactory _factory;
private static string? _accessToken;
public MusicGenreControllerTests(JellyfinApplicationFactory factory)
{
_factory = factory;
}
[Fact]
public async Task MusicGenres_FakeMusicGenre_NotFound()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var response = await client.GetAsync("MusicGenres/Fake-MusicGenre").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}

View File

@@ -1,18 +1,13 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
using Xunit.Priority;
namespace Jellyfin.Server.Integration.Tests.Controllers;
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
public class PlaystateControllerTests : IClassFixture<JellyfinApplicationFactory>
{
private readonly JellyfinApplicationFactory _factory;
private static readonly Guid _testUserId = Guid.NewGuid();
private static readonly Guid _testItemId = Guid.NewGuid();
private static string? _accessToken;
public PlaystateControllerTests(JellyfinApplicationFactory factory)
@@ -20,31 +15,47 @@ public class PlaystateControllerTests : IClassFixture<JellyfinApplicationFactory
_factory = factory;
}
private Task<HttpResponseMessage> DeleteUserPlayedItems(HttpClient httpClient, Guid userId, Guid itemId)
=> httpClient.DeleteAsync($"Users/{userId}/PlayedItems/{itemId}");
private Task<HttpResponseMessage> PostUserPlayedItems(HttpClient httpClient, Guid userId, Guid itemId)
=> httpClient.PostAsync($"Users/{userId}/PlayedItems/{itemId}", null);
[Fact]
[Priority(0)]
public async Task DeleteMarkUnplayedItem_DoesNotExist_NotFound()
public async Task DeleteMarkUnplayedItem_NonExistentUserId_NotFound()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
using var response = await DeleteUserPlayedItems(client, _testUserId, _testItemId).ConfigureAwait(false);
using var response = await client.DeleteAsync($"Users/{Guid.NewGuid()}/PlayedItems/{Guid.NewGuid()}").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
[Priority(0)]
public async Task PostMarkPlayedItem_DoesNotExist_NotFound()
public async Task PostMarkPlayedItem_NonExistentUserId_NotFound()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
using var response = await PostUserPlayedItems(client, _testUserId, _testItemId).ConfigureAwait(false);
using var response = await client.PostAsync($"Users/{Guid.NewGuid()}/PlayedItems/{Guid.NewGuid()}", null).ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task DeleteMarkUnplayedItem_NonExistentItemId_NotFound()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
using var response = await client.DeleteAsync($"Users/{userDto.Id}/PlayedItems/{Guid.NewGuid()}").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task PostMarkPlayedItem_NonExistentItemId_NotFound()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
using var response = await client.PostAsync($"Users/{userDto.Id}/PlayedItems/{Guid.NewGuid()}", null).ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Xunit;
namespace Jellyfin.Server.Integration.Tests.Controllers;
public class SessionControllerTests : IClassFixture<JellyfinApplicationFactory>
{
private readonly JellyfinApplicationFactory _factory;
private static string? _accessToken;
public SessionControllerTests(JellyfinApplicationFactory factory)
{
_factory = factory;
}
[Fact]
public async Task GetSessions_NonExistentUserId_NotFound()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
using var response = await client.GetAsync($"Session/Sessions?userId={Guid.NewGuid()}").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}

View File

@@ -66,6 +66,16 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
Assert.False(users![0].HasConfiguredPassword);
}
[Fact]
[Priority(-1)]
public async Task Me_Valid_Success()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
_ = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
}
[Fact]
[Priority(0)]
public async Task New_Valid_Success()
@@ -108,13 +118,26 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
var createRequest = new CreateUserByName()
{
Name = username
Name = username!
};
using var response = await CreateUserByName(client, createRequest).ConfigureAwait(false);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
[Priority(0)]
public async Task Delete_DoesntExist_NotFound()
{
var client = _factory.CreateClient();
// access token can't be null here as the previous test populated it
client.DefaultRequestHeaders.AddAuthHeader(_accessToken!);
using var response = await client.DeleteAsync($"User/{Guid.NewGuid()}").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
[Priority(1)]
public async Task UpdateUserPassword_Valid_Success()

View File

@@ -0,0 +1,129 @@
using System;
using System.Globalization;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using Jellyfin.Extensions.Json;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using Xunit;
namespace Jellyfin.Server.Integration.Tests.Controllers;
public sealed class UserLibraryControllerTests : IClassFixture<JellyfinApplicationFactory>
{
private readonly JellyfinApplicationFactory _factory;
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
private static string? _accessToken;
public UserLibraryControllerTests(JellyfinApplicationFactory factory)
{
_factory = factory;
}
[Fact]
public async Task GetRootFolder_NonExistenUserId_NotFound()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var response = await client.GetAsync($"Users/{Guid.NewGuid()}/Items/Root").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task GetRootFolder_UserId_Valid()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
_ = await AuthHelper.GetRootFolderDtoAsync(client).ConfigureAwait(false);
}
[Theory]
[InlineData("Users/{0}/Items/{1}")]
[InlineData("Users/{0}/Items/{1}/Intros")]
[InlineData("Users/{0}/Items/{1}/LocalTrailers")]
[InlineData("Users/{0}/Items/{1}/SpecialFeatures")]
[InlineData("Users/{0}/Items/{1}/Lyrics")]
public async Task GetItem_NonExistenUserId_NotFound(string format)
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client).ConfigureAwait(false);
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, Guid.NewGuid(), rootFolderDto.Id)).ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory]
[InlineData("Users/{0}/Items/{1}")]
[InlineData("Users/{0}/Items/{1}/Intros")]
[InlineData("Users/{0}/Items/{1}/LocalTrailers")]
[InlineData("Users/{0}/Items/{1}/SpecialFeatures")]
[InlineData("Users/{0}/Items/{1}/Lyrics")]
public async Task GetItem_NonExistentItemId_NotFound(string format)
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, userDto.Id, Guid.NewGuid())).ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task GetItem_UserIdAndItemId_Valid()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client, userDto.Id).ConfigureAwait(false);
var response = await client.GetAsync($"Users/{userDto.Id}/Items/{rootFolderDto.Id}").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var rootDto = await JsonSerializer.DeserializeAsync<BaseItemDto>(
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
_jsonOptions).ConfigureAwait(false);
Assert.NotNull(rootDto);
}
[Fact]
public async Task GetIntros_UserIdAndItemId_Valid()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client, userDto.Id).ConfigureAwait(false);
var response = await client.GetAsync($"Users/{userDto.Id}/Items/{rootFolderDto.Id}/Intros").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var rootDto = await JsonSerializer.DeserializeAsync<QueryResult<BaseItemDto>>(
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
_jsonOptions).ConfigureAwait(false);
Assert.NotNull(rootDto);
}
[Theory]
[InlineData("Users/{0}/Items/{1}/LocalTrailers")]
[InlineData("Users/{0}/Items/{1}/SpecialFeatures")]
public async Task LocalTrailersAndSpecialFeatures_UserIdAndItemId_Valid(string format)
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client, userDto.Id).ConfigureAwait(false);
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, userDto.Id, rootFolderDto.Id)).ConfigureAwait(false);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var rootDto = await JsonSerializer.DeserializeAsync<BaseItemDto[]>(
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
_jsonOptions).ConfigureAwait(false);
Assert.NotNull(rootDto);
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Xunit;
namespace Jellyfin.Server.Integration.Tests.Controllers;
public sealed class VideosControllerTests : IClassFixture<JellyfinApplicationFactory>
{
private readonly JellyfinApplicationFactory _factory;
private static string? _accessToken;
public VideosControllerTests(JellyfinApplicationFactory factory)
{
_factory = factory;
}
[Fact]
public async Task DeleteAlternateSources_NonExistentItemId_NotFound()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
var response = await client.DeleteAsync($"Videos/{Guid.NewGuid()}").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}

View File

@@ -1,9 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoFixture" />
@@ -29,17 +24,6 @@
</None>
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../Jellyfin.Server/Jellyfin.Server.csproj" />
</ItemGroup>

View File

@@ -1,11 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoFixture" />
<PackageReference Include="AutoFixture.AutoMoq" />
@@ -22,17 +16,6 @@
<PackageReference Include="Moq" />
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../Jellyfin.Server/Jellyfin.Server.csproj" />
</ItemGroup>

View File

@@ -1,11 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<None Include="Test Data\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -23,17 +17,6 @@
<PackageReference Include="coverlet.collector" />
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj" />
<ProjectReference Include="../../MediaBrowser.Providers/MediaBrowser.Providers.csproj" />