aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Directory.Build.props2
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj1
-rw-r--r--tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs34
-rw-r--r--tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj2
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs12
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_web_like_mkv_with_subtitle.json137
-rw-r--r--tests/Jellyfin.Model.Tests/Dlna/LegacyStreamInfo.cs2
-rw-r--r--tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs43
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidPixel.json3
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidTVExoPlayer.json2
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Chrome-NoHLS.json11
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Chrome.json7
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Firefox.json3
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-JellyfinMediaPlayer.json3
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-LowBandwidth.json3
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-RokuSSPlus.json6
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-RokuSSPlusNext.json6
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-SafariNext.json7
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json13
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json13
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-TranscodeMedia.json4
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-WebOS-23.json4
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Yatse.json3
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Yatse2.json3
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkParseTests.cs8
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Cryptography/CryptographyProviderTests.cs102
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryTests.cs72
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs87
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs4
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs19
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj1
-rw-r--r--tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj1
-rw-r--r--tests/Jellyfin.Server.Tests/ParseNetworkTests.cs7
33 files changed, 470 insertions, 155 deletions
diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props
index 6b851021f..feec35307 100644
--- a/tests/Directory.Build.props
+++ b/tests/Directory.Build.props
@@ -4,7 +4,7 @@
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
- <TargetFramework>net9.0</TargetFramework>
+ <TargetFramework>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index 015018910..6b84c4438 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -10,7 +10,6 @@
<PackageReference Include="AutoFixture.AutoMoq" />
<PackageReference Include="AutoFixture.Xunit2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
- <PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
diff --git a/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs b/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs
deleted file mode 100644
index 105e2a52a..000000000
--- a/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-using System.Linq;
-using Xunit;
-
-namespace Jellyfin.Extensions.Tests
-{
- public class AlphanumericComparatorTests
- {
- // InlineData is pre-sorted
- [Theory]
- [InlineData(null, "", "1", "9", "10", "a", "z")]
- [InlineData("50F", "100F", "SR9", "SR100")]
- [InlineData("image-1.jpg", "image-02.jpg", "image-4.jpg", "image-9.jpg", "image-10.jpg", "image-11.jpg", "image-22.jpg")]
- [InlineData("Hard drive 2GB", "Hard drive 20GB")]
- [InlineData("b", "e", "è", "ě", "f", "g", "k")]
- [InlineData("123456789", "123456789a", "abc", "abcd")]
- [InlineData("12345678912345678912345678913234567891", "123456789123456789123456789132345678912")]
- [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567891")]
- [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567892")]
- [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891a")]
- [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891b")]
- [InlineData("a5", "a11")]
- [InlineData("a05a", "a5b")]
- [InlineData("a5a", "a05b")]
- [InlineData("6xxx", "007asdf")]
- [InlineData("00042Q", "42s")]
- public void AlphanumericComparatorTest(params string?[] strings)
- {
- var copy = strings.Reverse().ToArray();
- Array.Sort(copy, new AlphanumericComparator());
- Assert.Equal(strings, copy);
- }
- }
-}
diff --git a/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj
index fdcf7d61e..bdf6bc383 100644
--- a/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj
+++ b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net9.0</TargetFramework>
+ <TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
index 94710a095..8a2f84734 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
@@ -196,6 +196,18 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
}
[Fact]
+ public void GetMediaInfo_WebM_Like_Mkv()
+ {
+ var bytes = File.ReadAllBytes("Test Data/Probing/video_web_like_mkv_with_subtitle.json");
+ var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
+
+ MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_metadata.mkv", MediaProtocol.File);
+
+ Assert.Equal("mkv", res.Container);
+ Assert.Equal(3, res.MediaStreams.Count);
+ }
+
+ [Fact]
public void GetMediaInfo_ProgressiveVideoNoFieldOrder_Success()
{
var bytes = File.ReadAllBytes("Test Data/Probing/video_progressive_no_field_order.json");
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_web_like_mkv_with_subtitle.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_web_like_mkv_with_subtitle.json
new file mode 100644
index 000000000..4f52dd90d
--- /dev/null
+++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_web_like_mkv_with_subtitle.json
@@ -0,0 +1,137 @@
+{
+ "streams": [
+ {
+ "index": 0,
+ "codec_name": "vp8",
+ "codec_long_name": "On2 VP8",
+ "profile": "1",
+ "codec_type": "video",
+ "codec_tag_string": "[0][0][0][0]",
+ "codec_tag": "0x0000",
+ "width": 540,
+ "height": 360,
+ "coded_width": 540,
+ "coded_height": 360,
+ "closed_captions": 0,
+ "film_grain": 0,
+ "has_b_frames": 0,
+ "sample_aspect_ratio": "1:1",
+ "display_aspect_ratio": "3:2",
+ "pix_fmt": "yuv420p",
+ "level": -99,
+ "field_order": "progressive",
+ "refs": 1,
+ "r_frame_rate": "2997/125",
+ "avg_frame_rate": "2997/125",
+ "time_base": "1/1000",
+ "start_pts": 0,
+ "start_time": "0.000000",
+ "disposition": {
+ "default": 1,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 0,
+ "timed_thumbnails": 0,
+ "captions": 0,
+ "descriptions": 0,
+ "metadata": 0,
+ "dependent": 0,
+ "still_image": 0
+ },
+ "tags": {
+ "language": "eng"
+ }
+ },
+ {
+ "index": 1,
+ "codec_name": "vorbis",
+ "codec_long_name": "Vorbis",
+ "codec_type": "audio",
+ "codec_tag_string": "[0][0][0][0]",
+ "codec_tag": "0x0000",
+ "sample_fmt": "fltp",
+ "sample_rate": "44100",
+ "channels": 2,
+ "channel_layout": "stereo",
+ "bits_per_sample": 0,
+ "r_frame_rate": "0/0",
+ "avg_frame_rate": "0/0",
+ "time_base": "1/1000",
+ "start_pts": 0,
+ "start_time": "0.000000",
+ "duration": "117.707000",
+ "bit_rate": "127998",
+ "disposition": {
+ "default": 1,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 0,
+ "timed_thumbnails": 0,
+ "captions": 0,
+ "descriptions": 0,
+ "metadata": 0,
+ "dependent": 0,
+ "still_image": 0
+ },
+ "tags": {
+ "language": "eng"
+ }
+ },
+ {
+ "index": 2,
+ "codec_name": "subrip",
+ "codec_long_name": "SubRip subtitle",
+ "codec_type": "subtitle",
+ "codec_tag_string": "[0][0][0][0]",
+ "codec_tag": "0x0000",
+ "disposition": {
+ "default": 0,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 0,
+ "timed_thumbnails": 0,
+ "captions": 0,
+ "descriptions": 0,
+ "metadata": 0,
+ "dependent": 0,
+ "still_image": 0
+ },
+ "tags": {
+ "language": "eng"
+ }
+ }
+ ],
+ "format": {
+ "filename": "sample.mkv",
+ "nb_streams": 3,
+ "nb_programs": 0,
+ "format_name": "matroska,webm",
+ "format_long_name": "Matroska / WebM",
+ "start_time": "0.000000",
+ "duration": "117.700914",
+ "size": "8566268",
+ "bit_rate": "582239",
+ "probe_score": 100
+ }
+}
diff --git a/tests/Jellyfin.Model.Tests/Dlna/LegacyStreamInfo.cs b/tests/Jellyfin.Model.Tests/Dlna/LegacyStreamInfo.cs
index e32baef55..6436d7d0e 100644
--- a/tests/Jellyfin.Model.Tests/Dlna/LegacyStreamInfo.cs
+++ b/tests/Jellyfin.Model.Tests/Dlna/LegacyStreamInfo.cs
@@ -134,8 +134,6 @@ public class LegacyStreamInfo : StreamInfo
{
list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture)));
}
-
- list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString(CultureInfo.InvariantCulture)));
}
else
{
diff --git a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
index f4c0d9fe8..c1a3a4544 100644
--- a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
+++ b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs
@@ -108,6 +108,49 @@ namespace Jellyfin.Model.Tests.Entities
IsExternal = true
});
+ // Test LocalizedLanguage is used when set (fixes zh-CN display issue #15935)
+ data.Add(
+ "Chinese (Simplified) - SRT",
+ new MediaStream
+ {
+ Type = MediaStreamType.Subtitle,
+ Title = null,
+ Language = "zh-CN",
+ LocalizedLanguage = "Chinese (Simplified)",
+ IsForced = false,
+ IsDefault = false,
+ Codec = "SRT"
+ });
+
+ // Test LocalizedLanguage for audio streams
+ data.Add(
+ "Japanese - AAC - Stereo",
+ new MediaStream
+ {
+ Type = MediaStreamType.Audio,
+ Title = null,
+ Language = "jpn",
+ LocalizedLanguage = "Japanese",
+ IsForced = false,
+ IsDefault = false,
+ Codec = "AAC",
+ ChannelLayout = "stereo"
+ });
+
+ // Test fallback to Language when LocalizedLanguage is null
+ data.Add(
+ "Eng - ASS",
+ new MediaStream
+ {
+ Type = MediaStreamType.Subtitle,
+ Title = null,
+ Language = "eng",
+ LocalizedLanguage = null,
+ IsForced = false,
+ IsDefault = false,
+ Codec = "ASS"
+ });
+
return data;
}
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidPixel.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidPixel.json
index 68ce3ea4a..643ff2638 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidPixel.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidPixel.json
@@ -152,7 +152,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -169,7 +168,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -185,7 +183,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
}
],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidTVExoPlayer.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidTVExoPlayer.json
index 3d3968268..44f63f384 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidTVExoPlayer.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidTVExoPlayer.json
@@ -130,7 +130,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -146,7 +145,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
}
],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Chrome-NoHLS.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Chrome-NoHLS.json
index 5d1f5f162..f1fc9e0db 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Chrome-NoHLS.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Chrome-NoHLS.json
@@ -127,7 +127,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -144,7 +143,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -161,7 +159,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -178,7 +175,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -195,7 +191,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -212,7 +207,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -229,7 +223,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -246,7 +239,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -263,7 +255,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -281,7 +272,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -298,7 +288,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
}
],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Chrome.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Chrome.json
index e2f75b569..7e37a6236 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Chrome.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Chrome.json
@@ -107,7 +107,6 @@
"Protocol": "hls",
"MaxAudioChannels": "2",
"MinSegments": "2",
- "BreakOnNonKeyFrames": true,
"EnableAudioVbrEncoding": true
},
{
@@ -182,8 +181,7 @@
"Context": "Streaming",
"Protocol": "hls",
"MaxAudioChannels": "2",
- "MinSegments": "2",
- "BreakOnNonKeyFrames": true
+ "MinSegments": "2"
},
{
"Container": "ts",
@@ -193,8 +191,7 @@
"Context": "Streaming",
"Protocol": "hls",
"MaxAudioChannels": "2",
- "MinSegments": "2",
- "BreakOnNonKeyFrames": true
+ "MinSegments": "2"
}
],
"ContainerProfiles": [],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Firefox.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Firefox.json
index 21ae7e5cb..4380d80ef 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Firefox.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Firefox.json
@@ -95,7 +95,6 @@
"TranscodingProfiles": [
{
"AudioCodec": "aac",
- "BreakOnNonKeyFrames": true,
"Container": "mp4",
"Context": "Streaming",
"EnableAudioVbrEncoding": true,
@@ -170,7 +169,6 @@
},
{
"AudioCodec": "aac,mp2,opus,flac",
- "BreakOnNonKeyFrames": true,
"Container": "mp4",
"Context": "Streaming",
"MaxAudioChannels": "2",
@@ -181,7 +179,6 @@
},
{
"AudioCodec": "aac,mp3,mp2",
- "BreakOnNonKeyFrames": true,
"Container": "ts",
"Context": "Streaming",
"MaxAudioChannels": "2",
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-JellyfinMediaPlayer.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-JellyfinMediaPlayer.json
index da9a1a4ad..cca1c16ee 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-JellyfinMediaPlayer.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-JellyfinMediaPlayer.json
@@ -30,7 +30,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -48,7 +47,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -62,7 +60,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
}
],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-LowBandwidth.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-LowBandwidth.json
index 82b73fb0f..b7cd170b9 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-LowBandwidth.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-LowBandwidth.json
@@ -30,7 +30,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -48,7 +47,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -62,7 +60,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
}
],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-RokuSSPlus.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-RokuSSPlus.json
index 37b923558..b823ac4b8 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-RokuSSPlus.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-RokuSSPlus.json
@@ -49,7 +49,6 @@
"MaxAudioChannels": " 2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -66,7 +65,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -83,7 +81,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -100,7 +97,6 @@
"MaxAudioChannels": " 2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -118,7 +114,6 @@
"MaxAudioChannels": " 2",
"MinSegments": 1,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -135,7 +130,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
}
],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-RokuSSPlusNext.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-RokuSSPlusNext.json
index 542bf6370..708ff73c4 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-RokuSSPlusNext.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-RokuSSPlusNext.json
@@ -49,7 +49,6 @@
"MaxAudioChannels": " 2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -66,7 +65,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -83,7 +81,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -100,7 +97,6 @@
"MaxAudioChannels": " 2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -118,7 +114,6 @@
"MaxAudioChannels": " 2",
"MinSegments": 1,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -135,7 +130,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
}
],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-SafariNext.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-SafariNext.json
index f61d0e36b..10382fa82 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-SafariNext.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-SafariNext.json
@@ -114,7 +114,6 @@
"Protocol": "hls",
"MaxAudioChannels": "6",
"MinSegments": "2",
- "BreakOnNonKeyFrames": true,
"EnableAudioVbrEncoding": true
},
{
@@ -173,8 +172,7 @@
"Context": "Streaming",
"Protocol": "hls",
"MaxAudioChannels": "2",
- "MinSegments": "2",
- "BreakOnNonKeyFrames": true
+ "MinSegments": "2"
},
{
"Container": "ts",
@@ -184,8 +182,7 @@
"Context": "Streaming",
"Protocol": "hls",
"MaxAudioChannels": "2",
- "MinSegments": "2",
- "BreakOnNonKeyFrames": true
+ "MinSegments": "2"
}
],
"ContainerProfiles": [],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json
index 9d43d2166..3625b099c 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json
@@ -165,7 +165,6 @@
"MaxAudioChannels": "2",
"MinSegments": 1,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": true,
"$type": "TranscodingProfile"
},
{
@@ -182,7 +181,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -199,7 +197,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -216,7 +213,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -233,7 +229,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -250,7 +245,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -267,7 +261,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -284,7 +277,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -301,7 +293,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -319,7 +310,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"Conditions": [
{
"Condition": "LessThanEqual",
@@ -346,7 +336,6 @@
"MaxAudioChannels": "2",
"MinSegments": 1,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"Conditions": [
{
"Condition": "LessThanEqual",
@@ -373,7 +362,6 @@
"MaxAudioChannels": "2",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"Conditions": [
{
"Condition": "LessThanEqual",
@@ -399,7 +387,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"Conditions": [
{
"Condition": "LessThanEqual",
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json
index 3859ef994..deee650b2 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json
@@ -165,7 +165,6 @@
"MaxAudioChannels": "6",
"MinSegments": 1,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": true,
"$type": "TranscodingProfile"
},
{
@@ -182,7 +181,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -199,7 +197,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -216,7 +213,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -233,7 +229,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -250,7 +245,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -267,7 +261,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -284,7 +277,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -301,7 +293,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -319,7 +310,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"Conditions": [
{
"Condition": "LessThanEqual",
@@ -346,7 +336,6 @@
"MaxAudioChannels": "6",
"MinSegments": 1,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"Conditions": [
{
"Condition": "LessThanEqual",
@@ -373,7 +362,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"Conditions": [
{
"Condition": "LessThanEqual",
@@ -399,7 +387,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"Conditions": [
{
"Condition": "LessThanEqual",
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-TranscodeMedia.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-TranscodeMedia.json
index 9fc1ae6bb..38de51b04 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-TranscodeMedia.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-TranscodeMedia.json
@@ -16,7 +16,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -28,7 +27,6 @@
"Protocol": "hls",
"MaxAudioChannels": "2",
"MinSegments": "2",
- "BreakOnNonKeyFrames": true,
"$type": "TranscodingProfile"
},
{
@@ -40,7 +38,6 @@
"Protocol": "hls",
"MaxAudioChannels": "2",
"MinSegments": "2",
- "BreakOnNonKeyFrames": true,
"$type": "TranscodingProfile"
},
{
@@ -64,7 +61,6 @@
"EnableSubtitlesInManifest": false,
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
}
],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-WebOS-23.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-WebOS-23.json
index 094b0723b..3ff11a684 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-WebOS-23.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-WebOS-23.json
@@ -135,7 +135,6 @@
"Protocol": "hls",
"MaxAudioChannels": "6",
"MinSegments": "1",
- "BreakOnNonKeyFrames": false,
"EnableAudioVbrEncoding": true
},
{
@@ -210,8 +209,7 @@
"Context": "Streaming",
"Protocol": "hls",
"MaxAudioChannels": "6",
- "MinSegments": "1",
- "BreakOnNonKeyFrames": false
+ "MinSegments": "1"
}
],
"ContainerProfiles": [],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Yatse.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Yatse.json
index 256c8dc2f..838a1f920 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Yatse.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Yatse.json
@@ -52,7 +52,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -70,7 +69,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -88,7 +86,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
}
],
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Yatse2.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Yatse2.json
index 256c8dc2f..838a1f920 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Yatse2.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Yatse2.json
@@ -52,7 +52,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -70,7 +69,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
},
{
@@ -88,7 +86,6 @@
"MaxAudioChannels": "6",
"MinSegments": 0,
"SegmentLength": 0,
- "BreakOnNonKeyFrames": false,
"$type": "TranscodingProfile"
}
],
diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
index 38208476f..871604514 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
+++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
@@ -113,7 +113,7 @@ namespace Jellyfin.Networking.Tests
public void IPv4SubnetMaskMatchesValidIPAddress(string netMask, string ipAddress)
{
var ipa = IPAddress.Parse(ipAddress);
- Assert.True(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress)));
+ Assert.True(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Subnet.Contains(IPAddress.Parse(ipAddress)));
}
/// <summary>
@@ -131,7 +131,7 @@ namespace Jellyfin.Networking.Tests
public void IPv4SubnetMaskDoesNotMatchInvalidIPAddress(string netMask, string ipAddress)
{
var ipa = IPAddress.Parse(ipAddress);
- Assert.False(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress)));
+ Assert.False(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Subnet.Contains(IPAddress.Parse(ipAddress)));
}
/// <summary>
@@ -147,7 +147,7 @@ namespace Jellyfin.Networking.Tests
[InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")]
public void IPv6SubnetMaskMatchesValidIPAddress(string netMask, string ipAddress)
{
- Assert.True(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress)));
+ Assert.True(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Subnet.Contains(IPAddress.Parse(ipAddress)));
}
[Theory]
@@ -158,7 +158,7 @@ namespace Jellyfin.Networking.Tests
[InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0001")]
public void IPv6SubnetMaskDoesNotMatchInvalidIPAddress(string netMask, string ipAddress)
{
- Assert.False(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress)));
+ Assert.False(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Subnet.Contains(IPAddress.Parse(ipAddress)));
}
[Theory]
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Cryptography/CryptographyProviderTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Cryptography/CryptographyProviderTests.cs
new file mode 100644
index 000000000..052bdf740
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Cryptography/CryptographyProviderTests.cs
@@ -0,0 +1,102 @@
+using System;
+using Emby.Server.Implementations.Cryptography;
+using MediaBrowser.Model.Cryptography;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Cryptography;
+
+public class CryptographyProviderTests
+{
+ private readonly CryptographyProvider _sut = new();
+
+ [Fact]
+ public void CreatePasswordHash_WithPassword_ReturnsHashWithIterations()
+ {
+ var hash = _sut.CreatePasswordHash("testpassword");
+
+ Assert.Equal("PBKDF2-SHA512", hash.Id);
+ Assert.True(hash.Parameters.ContainsKey("iterations"));
+ Assert.NotEmpty(hash.Salt.ToArray());
+ Assert.NotEmpty(hash.Hash.ToArray());
+ }
+
+ [Fact]
+ public void Verify_WithValidPassword_ReturnsTrue()
+ {
+ const string password = "testpassword";
+ var hash = _sut.CreatePasswordHash(password);
+
+ Assert.True(_sut.Verify(hash, password));
+ }
+
+ [Fact]
+ public void Verify_WithWrongPassword_ReturnsFalse()
+ {
+ var hash = _sut.CreatePasswordHash("correctpassword");
+
+ Assert.False(_sut.Verify(hash, "wrongpassword"));
+ }
+
+ [Fact]
+ public void Verify_PBKDF2_MissingIterations_ThrowsFormatException()
+ {
+ var hash = PasswordHash.Parse("$PBKDF2$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D");
+
+ var exception = Assert.Throws<FormatException>(() => _sut.Verify(hash, "password"));
+ Assert.Contains("missing required 'iterations' parameter", exception.Message, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void Verify_PBKDF2SHA512_MissingIterations_ThrowsFormatException()
+ {
+ var hash = PasswordHash.Parse("$PBKDF2-SHA512$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D");
+
+ var exception = Assert.Throws<FormatException>(() => _sut.Verify(hash, "password"));
+ Assert.Contains("missing required 'iterations' parameter", exception.Message, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void Verify_PBKDF2_InvalidIterationsFormat_ThrowsFormatException()
+ {
+ var hash = PasswordHash.Parse("$PBKDF2$iterations=abc$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D");
+
+ var exception = Assert.Throws<FormatException>(() => _sut.Verify(hash, "password"));
+ Assert.Contains("invalid 'iterations' parameter", exception.Message, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void Verify_PBKDF2SHA512_InvalidIterationsFormat_ThrowsFormatException()
+ {
+ var hash = PasswordHash.Parse("$PBKDF2-SHA512$iterations=notanumber$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D");
+
+ var exception = Assert.Throws<FormatException>(() => _sut.Verify(hash, "password"));
+ Assert.Contains("invalid 'iterations' parameter", exception.Message, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void Verify_UnsupportedHashId_ThrowsNotSupportedException()
+ {
+ var hash = PasswordHash.Parse("$UNKNOWN$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D");
+
+ Assert.Throws<NotSupportedException>(() => _sut.Verify(hash, "password"));
+ }
+
+ [Fact]
+ public void GenerateSalt_ReturnsNonEmptyArray()
+ {
+ var salt = _sut.GenerateSalt();
+
+ Assert.NotEmpty(salt);
+ }
+
+ [Theory]
+ [InlineData(16)]
+ [InlineData(32)]
+ [InlineData(64)]
+ public void GenerateSalt_WithLength_ReturnsArrayOfSpecifiedLength(int length)
+ {
+ var salt = _sut.GenerateSalt(length);
+
+ Assert.Equal(length, salt.Length);
+ }
+}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryTests.cs
new file mode 100644
index 000000000..c450cbb0e
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryTests.cs
@@ -0,0 +1,72 @@
+using System;
+using Jellyfin.Database.Implementations.Entities;
+using Jellyfin.Server.Implementations.Item;
+using MediaBrowser.Controller;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Moq;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Item;
+
+public class BaseItemRepositoryTests
+{
+ [Fact]
+ public void DeserializeBaseItem_WithUnknownType_ReturnsNull()
+ {
+ // Arrange
+ var entity = new BaseItemEntity
+ {
+ Id = Guid.NewGuid(),
+ Type = "NonExistent.Plugin.CustomItemType"
+ };
+
+ // Act
+ var result = BaseItemRepository.DeserializeBaseItem(entity, NullLogger.Instance, null, false);
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public void DeserializeBaseItem_WithUnknownType_LogsWarning()
+ {
+ // Arrange
+ var entity = new BaseItemEntity
+ {
+ Id = Guid.NewGuid(),
+ Type = "NonExistent.Plugin.CustomItemType"
+ };
+ var loggerMock = new Mock<ILogger>();
+
+ // Act
+ BaseItemRepository.DeserializeBaseItem(entity, loggerMock.Object, null, false);
+
+ // Assert
+ loggerMock.Verify(
+ x => x.Log(
+ LogLevel.Warning,
+ It.IsAny<EventId>(),
+ It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("unknown type", StringComparison.OrdinalIgnoreCase)),
+ It.IsAny<Exception?>(),
+ It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
+ Times.Once);
+ }
+
+ [Fact]
+ public void DeserializeBaseItem_WithKnownType_ReturnsItem()
+ {
+ // Arrange
+ var entity = new BaseItemEntity
+ {
+ Id = Guid.NewGuid(),
+ Type = "MediaBrowser.Controller.Entities.Movies.Movie"
+ };
+
+ // Act
+ var result = BaseItemRepository.DeserializeBaseItem(entity, NullLogger.Instance, null, false);
+
+ // Assert
+ Assert.NotNull(result);
+ }
+}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs
index d677c9f09..a7bbef7ed 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs
@@ -1,30 +1,81 @@
+using Emby.Server.Implementations.Library;
using Xunit;
namespace Jellyfin.Server.Implementations.Tests.Library;
public class DotIgnoreIgnoreRuleTest
{
- [Fact]
- public void Test()
+ private static readonly string[] _rule1 = ["SPs"];
+ private static readonly string[] _rule2 = ["SPs", "!thebestshot.mkv"];
+ private static readonly string[] _rule3 = ["*.txt", @"{\colortbl;\red255\green255\blue255;}", "videos/", @"\invalid\escape\sequence", "*.mkv"];
+ private static readonly string[] _rule4 = [@"{\colortbl;\red255\green255\blue255;}", @"\invalid\escape\sequence"];
+
+ public static TheoryData<string[], string, bool, bool> CheckIgnoreRulesTestData =>
+ new()
+ {
+ // Basic pattern matching
+ { _rule1, "f:/cd/sps/ffffff.mkv", false, true },
+ { _rule1, "cd/sps/ffffff.mkv", false, true },
+ { _rule1, "/cd/sps/ffffff.mkv", false, true },
+
+ // Negate pattern
+ { _rule2, "f:/cd/sps/ffffff.mkv", false, true },
+ { _rule2, "cd/sps/ffffff.mkv", false, true },
+ { _rule2, "/cd/sps/ffffff.mkv", false, true },
+ { _rule2, "f:/cd/sps/thebestshot.mkv", false, false },
+ { _rule2, "cd/sps/thebestshot.mkv", false, false },
+ { _rule2, "/cd/sps/thebestshot.mkv", false, false },
+
+ // Mixed valid and invalid patterns - skips invalid, applies valid
+ { _rule3, "test.txt", false, true },
+ { _rule3, "videos/movie.mp4", false, true },
+ { _rule3, "movie.mkv", false, true },
+ { _rule3, "test.mp3", false, false },
+
+ // Only invalid patterns - falls back to ignore all
+ { _rule4, "any-file.txt", false, true },
+ { _rule4, "any/path/to/file.mkv", false, true },
+ };
+
+ public static TheoryData<string[], string, bool, bool> WindowsPathNormalizationTestData =>
+ new()
+ {
+ // Windows paths with backslashes - should match when normalizePath is true
+ { _rule1, @"C:\cd\sps\ffffff.mkv", false, true },
+ { _rule1, @"D:\media\sps\movie.mkv", false, true },
+ { _rule1, @"\\server\share\sps\file.mkv", false, true },
+
+ // Negate pattern with Windows paths
+ { _rule2, @"C:\cd\sps\ffffff.mkv", false, true },
+ { _rule2, @"C:\cd\sps\thebestshot.mkv", false, false },
+
+ // Directory matching with Windows paths
+ { _rule3, @"C:\videos\movie.mp4", false, true },
+ { _rule3, @"D:\documents\test.txt", false, true },
+ { _rule3, @"E:\music\song.mp3", false, false },
+ };
+
+ [Theory]
+ [MemberData(nameof(CheckIgnoreRulesTestData))]
+ public void CheckIgnoreRules_ReturnsExpectedResult(string[] rules, string path, bool isDirectory, bool expectedIgnored)
+ {
+ Assert.Equal(expectedIgnored, DotIgnoreIgnoreRule.CheckIgnoreRules(path, rules, isDirectory));
+ }
+
+ [Theory]
+ [MemberData(nameof(WindowsPathNormalizationTestData))]
+ public void CheckIgnoreRules_WithWindowsPaths_NormalizesBackslashes(string[] rules, string path, bool isDirectory, bool expectedIgnored)
{
- var ignore = new Ignore.Ignore();
- ignore.Add("SPs");
- Assert.True(ignore.IsIgnored("f:/cd/sps/ffffff.mkv"));
- Assert.True(ignore.IsIgnored("cd/sps/ffffff.mkv"));
- Assert.True(ignore.IsIgnored("/cd/sps/ffffff.mkv"));
+ // With normalizePath=true, backslashes should be converted to forward slashes
+ Assert.Equal(expectedIgnored, DotIgnoreIgnoreRule.CheckIgnoreRules(path, rules, isDirectory, normalizePath: true));
}
- [Fact]
- public void TestNegatePattern()
+ [Theory]
+ [InlineData(@"C:\cd\sps\ffffff.mkv")]
+ [InlineData(@"D:\media\sps\movie.mkv")]
+ public void CheckIgnoreRules_WithWindowsPaths_WithoutNormalization_DoesNotMatch(string path)
{
- var ignore = new Ignore.Ignore();
- ignore.Add("SPs");
- ignore.Add("!thebestshot.mkv");
- Assert.True(ignore.IsIgnored("f:/cd/sps/ffffff.mkv"));
- Assert.True(ignore.IsIgnored("cd/sps/ffffff.mkv"));
- Assert.True(ignore.IsIgnored("/cd/sps/ffffff.mkv"));
- Assert.True(!ignore.IsIgnored("f:/cd/sps/thebestshot.mkv"));
- Assert.True(!ignore.IsIgnored("cd/sps/thebestshot.mkv"));
- Assert.True(!ignore.IsIgnored("/cd/sps/thebestshot.mkv"));
+ // Without normalization, Windows paths with backslashes won't match patterns expecting forward slashes
+ Assert.False(DotIgnoreIgnoreRule.CheckIgnoreRules(path, _rule1, isDirectory: false, normalizePath: false));
}
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs
index 07061cfc7..4cb6cb960 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs
@@ -19,7 +19,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library
[InlineData("/media/movies/#recycle", true)]
[InlineData("thumbs.db", true)]
[InlineData(@"C:\media\movies\movie.avi", false)]
- [InlineData("/media/.hiddendir/file.mp4", false)]
+ [InlineData("/media/.hiddendir/file.mp4", true)]
[InlineData("/media/dir/.hiddenfile.mp4", true)]
[InlineData("/media/dir/._macjunk.mp4", true)]
[InlineData("/volume1/video/Series/@eaDir", true)]
@@ -32,7 +32,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library
[InlineData("/media/music/Foo B.A.R", false)]
[InlineData("/media/music/Foo B.A.R.", false)]
[InlineData("/movies/.zfs/snapshot/AutoM-2023-09", true)]
- public void PathIgnored(string path, bool expected)
+ public void PathIgnored(string path, bool expected)
{
Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path));
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
index 6d6bba4fc..e60522bf7 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
@@ -204,6 +204,25 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
}
[Theory]
+ [InlineData("TV-MA", "DE", 17, 1)] // US-only rating, DE country code
+ [InlineData("PG-13", "FR", 13, 0)] // US-only rating, FR country code
+ [InlineData("R", "JP", 17, 0)] // US-only rating, JP country code
+ public async Task GetRatingScore_FallbackPrioritizesUS_Success(string rating, string countryCode, int expectedScore, int? expectedSubScore)
+ {
+ var localizationManager = Setup(new ServerConfiguration()
+ {
+ MetadataCountryCode = countryCode
+ });
+ await localizationManager.LoadAll();
+
+ var score = localizationManager.GetRatingScore(rating);
+
+ Assert.NotNull(score);
+ Assert.Equal(expectedScore, score.Score);
+ Assert.Equal(expectedSubScore, score.SubScore);
+ }
+
+ [Theory]
[InlineData("Default", "Default")]
[InlineData("HeaderLiveTV", "Live TV")]
public void GetLocalizedString_Valid_Success(string key, string expected)
diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
index 8228c0df7..7b0e23788 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
+++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
@@ -5,7 +5,6 @@
<PackageReference Include="AutoFixture.AutoMoq" />
<PackageReference Include="AutoFixture.Xunit2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
- <PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
index 5fea805ae..21596e0ed 100644
--- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
+++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
@@ -5,7 +5,6 @@
<PackageReference Include="AutoFixture.AutoMoq" />
<PackageReference Include="AutoFixture.Xunit2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
- <PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
diff --git a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
index 123266d29..14f4c33b6 100644
--- a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
+++ b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs
@@ -11,7 +11,6 @@ using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager;
-using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
namespace Jellyfin.Server.Tests
{
@@ -87,7 +86,7 @@ namespace Jellyfin.Server.Tests
// Need this here as ::1 and 127.0.0.1 are in them by default.
options.KnownProxies.Clear();
- options.KnownNetworks.Clear();
+ options.KnownIPNetworks.Clear();
ApiServiceCollectionExtensions.AddProxyAddresses(settings, hostList, options);
@@ -97,10 +96,10 @@ namespace Jellyfin.Server.Tests
Assert.True(options.KnownProxies.Contains(item));
}
- Assert.Equal(knownNetworks.Length, options.KnownNetworks.Count);
+ Assert.Equal(knownNetworks.Length, options.KnownIPNetworks.Count);
foreach (var item in knownNetworks)
{
- Assert.NotNull(options.KnownNetworks.FirstOrDefault(x => x.Prefix.Equals(item.Prefix) && x.PrefixLength == item.PrefixLength));
+ Assert.NotEqual(default, options.KnownIPNetworks.FirstOrDefault(x => x.BaseAddress.Equals(item.BaseAddress) && x.PrefixLength == item.PrefixLength));
}
}