Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions src/Ombi.Core.Tests/Engine/MovieRequestEngineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Rule;
using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Settings;
using Ombi.Helpers;
using Ombi.Notifications.Models;
using Ombi.Store.Entities;
Expand All @@ -17,6 +20,13 @@
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
using Ombi.Api.External.ExternalApis.Radarr;
using Ombi.Api.External.ExternalApis.Radarr.Models.V3;
using Ombi.Api.External.ExternalApis.TheMovieDb;
using Ombi.Api.External.ExternalApis.TheMovieDb.Models;
using Ombi.Core.Services;
using Ombi.Settings.Settings.Models.External;
using Ombi.Settings.Settings.Models;

namespace Ombi.Core.Tests.Engine
{
Expand Down Expand Up @@ -58,6 +68,205 @@ public void Setup()
_mocker.Setup<IUserPlayedMovieRepository, IQueryable<UserPlayedMovie>>(x => x.GetAll()).Returns(new List<UserPlayedMovie>().AsQueryable().BuildMock());
}

[Test]
public async Task RequestMovie_RejectsQualityOverrideWithoutPermission()
{
SetupMovieRequestRoles();

var result = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1, QualityPathOverride = 2 });

Assert.That(result.ErrorCode, Is.EqualTo(ErrorCode.NoPermissions));
_mocker.GetMock<IRadarrV3Api>().Verify(x => x.GetProfiles(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}

[Test]
public async Task RequestMovie_RejectsUnknownStandardProfile()
{
SetupMovieRequestRoles(OmbiRoles.SelectRadarrQualityProfile);
SetupRadarrProfile(false, 3);

var result = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1, QualityPathOverride = 2 });

Assert.That(result.Result, Is.False);
_repoMock.Verify(x => x.Add(It.IsAny<MovieRequests>()), Times.Never);
}

[Test]
public async Task RequestMovie_AllowsDedicatedRoleWithValidProfile()
{
SetupMovieRequestRoles(OmbiRoles.RequestMovie, OmbiRoles.SelectRadarrQualityProfile);
SetupRadarrProfile(false, 2);

var result = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1, QualityPathOverride = 2 });

Assert.That(result.Result, Is.True);
_repoMock.Verify(x => x.Add(It.Is<MovieRequests>(r =>
r.QualityOverride == 2 && r.QualityOverride4K == 0 && r.RootPathOverride == 0)), Times.Once);
}

[Test]
public async Task RequestMovie_RejectsSelectorRoleWithoutRequestRole()
{
SetupMovieRequestRoles(OmbiRoles.SelectRadarrQualityProfile);
_mocker.GetMock<IRuleEvaluator>().Setup(x => x.StartRequestRules(It.IsAny<MovieRequests>()))
.ReturnsAsync(new List<RuleResult> { new RuleResult { Success = false, ErrorCode = ErrorCode.NoPermissions } });

var result = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1 });

Assert.That(result.ErrorCode, Is.EqualTo(ErrorCode.NoPermissions));
_repoMock.Verify(x => x.Add(It.IsAny<MovieRequests>()), Times.Never);
}

[Test]
public async Task RequestMovie_New4KRequestKeepsStandardQualityOverrideIndependent()
{
SetupMovieRequestRoles(OmbiRoles.RequestMovie, OmbiRoles.SelectRadarrQualityProfile);
SetupRadarrProfile(true, 4);
_mocker.GetMock<IFeatureService>().Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(true);

var result = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1, Is4kRequest = true, QualityPathOverride = 4 });

Assert.That(result.Result, Is.True);
_repoMock.Verify(x => x.Add(It.Is<MovieRequests>(r =>
r.QualityOverride == 0 && r.QualityOverride4K == 4)), Times.Once);
}

[Test]
public async Task RequestMovie_RejectsProfileOutsideUsersAllowlist()
{
SetupMovieRequestRoles(OmbiRoles.SelectRadarrQualityProfile);
SetupRadarrProfile(false, 2);
_mocker.GetMock<IRepository<UserSelectableQualityProfile>>().Setup(x => x.GetAll())
.Returns(new List<UserSelectableQualityProfile>().AsQueryable().BuildMock());

var result = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1, QualityPathOverride = 2 });

Assert.That(result.Result, Is.False);
_mocker.GetMock<IRadarrV3Api>().Verify(x => x.GetProfiles(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}

[Test]
public async Task RequestMovie_UpdatesQualityOverrideOnExistingStandardRequest()
{
SetupMovieRequestRoles(OmbiRoles.RequestMovie, OmbiRoles.SelectRadarrQualityProfile);
SetupRadarrProfile(false, 2);
_mocker.GetMock<IFeatureService>().Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(true);
var existing = new MovieRequests { TheMovieDbId = 1, Is4kRequest = true, Has4KRequest = true, QualityOverride = 4, QualityOverride4K = 6 };
_repoMock.Setup(x => x.GetRequestAsync(1)).ReturnsAsync(existing);

var result = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1, QualityPathOverride = 2 });

Assert.That(result.Result, Is.True);
_repoMock.Verify(x => x.Update(It.Is<MovieRequests>(r =>
r == existing && r.QualityOverride == 2 && r.QualityOverride4K == 6 && r.Is4kRequest && r.Has4KRequest)), Times.Once);
}

[Test]
public async Task RequestMovie_UpdatesQualityOverrideOnExisting4KRequest()
{
SetupMovieRequestRoles(OmbiRoles.RequestMovie, OmbiRoles.SelectRadarrQualityProfile);
SetupRadarrProfile(true, 4);
_mocker.GetMock<IFeatureService>().Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(true);
var existing = new MovieRequests { TheMovieDbId = 1, QualityOverride = 2 };
_repoMock.Setup(x => x.GetRequestAsync(1)).ReturnsAsync(existing);

var result = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1, Is4kRequest = true, QualityPathOverride = 4 });

Assert.That(result.Result, Is.True);
_repoMock.Verify(x => x.Update(It.Is<MovieRequests>(r =>
r == existing && r.QualityOverride == 2 && r.QualityOverride4K == 4 && r.Is4kRequest && r.Has4KRequest)), Times.Once);
}

[Test]
public async Task RequestMovie_DedicatedRoleCannotOverrideRootOrRequestOnBehalf()
{
SetupMovieRequestRoles(OmbiRoles.SelectRadarrQualityProfile);

var rootResult = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1, RootFolderOverride = 3 });
var behalfResult = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1, RequestOnBehalf = "other" });

Assert.Multiple(() =>
{
Assert.That(rootResult.ErrorCode, Is.EqualTo(ErrorCode.NoPermissions));
Assert.That(behalfResult.Result, Is.False);
});
}

[Test]
public async Task RequestMovie_ValidatesProfileAgainstRequestedRadarrInstance()
{
SetupMovieRequestRoles(OmbiRoles.SelectRadarrQualityProfile);
SetupRadarrProfile(false, 2);
SetupRadarrProfile(true, 4);
_mocker.GetMock<IRepository<UserSelectableQualityProfile>>().Setup(x => x.GetAll()).Returns(new List<UserSelectableQualityProfile>
{
new UserSelectableQualityProfile { UserId = "a", Application = SelectableQualityProfileApplication.Radarr, QualityProfileId = 2, Is4K = true }
}.AsQueryable().BuildMock());
_mocker.GetMock<IFeatureService>().Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(true);

var result = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1, Is4kRequest = true, QualityPathOverride = 2 });

Assert.That(result.Result, Is.False);
_mocker.GetMock<IRadarrV3Api>().Verify(x => x.GetProfiles("4k-key", It.IsAny<string>()), Times.Once);
_mocker.GetMock<IRadarrV3Api>().Verify(x => x.GetProfiles("standard-key", It.IsAny<string>()), Times.Never);
}

[Test]
public async Task RequestMovie_WithoutOverrideKeepsDefaultBehavior()
{
SetupMovieRequestRoles();

var result = await _subject.RequestMovie(new MovieRequestViewModel { TheMovieDbId = 1 });

Assert.That(result.Result, Is.True);
_repoMock.Verify(x => x.Add(It.Is<MovieRequests>(r => r.QualityOverride == 0)), Times.Once);
_mocker.GetMock<IRadarrV3Api>().Verify(x => x.GetProfiles(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}

private void SetupMovieRequestRoles(params string[] roles)
{
_userManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), It.IsAny<string>()))
.ReturnsAsync((OmbiUser _, string role) => roles.Contains(role));
_mocker.GetMock<IMovieDbApi>().Setup(x => x.GetMovieInformationWithExtraInfo(1, It.IsAny<string>()))
.ReturnsAsync(new MovieResponseDto { Id = 1, Title = "Movie", ReleaseDate = "2020-01-01" });
_mocker.GetMock<IFeatureService>().Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(false);
_repoMock.Setup(x => x.GetRequestAsync(1)).ReturnsAsync((MovieRequests)null);
_repoMock.Setup(x => x.Add(It.IsAny<MovieRequests>())).ReturnsAsync((MovieRequests request) => request);
_mocker.GetMock<IRuleEvaluator>().Setup(x => x.StartRequestRules(It.IsAny<MovieRequests>()))
.ReturnsAsync(new List<RuleResult>());
_mocker.GetMock<IRuleEvaluator>().Setup(x => x.StartSpecificRules(It.IsAny<object>(), It.IsAny<SpecificRules>(), It.IsAny<string>()))
.ReturnsAsync(new RuleResult { Success = false });
_mocker.GetMock<IMediaCacheService>().Setup(x => x.Purge()).Returns(Task.CompletedTask);
_mocker.GetMock<IRepository<RequestLog>>().Setup(x => x.Add(It.IsAny<RequestLog>())).ReturnsAsync((RequestLog log) => log);
_mocker.GetMock<IRepository<UserSelectableQualityProfile>>().Setup(x => x.GetAll()).Returns(new List<UserSelectableQualityProfile>
{
new UserSelectableQualityProfile { UserId = "a", Application = SelectableQualityProfileApplication.Radarr, QualityProfileId = 2 },
new UserSelectableQualityProfile { UserId = "a", Application = SelectableQualityProfileApplication.Radarr, QualityProfileId = 4, Is4K = true }
}.AsQueryable().BuildMock());
}

private void SetupRadarrProfile(bool is4k, params int[] profileIds)
{
var settings = new RadarrSettings
{
Enabled = true,
ApiKey = is4k ? "4k-key" : "standard-key",
Ip = is4k ? "radarr-4k" : "radarr",
Port = 7878
};
if (is4k)
{
_mocker.GetMock<ISettingsService<Radarr4KSettings>>().Setup(x => x.GetSettingsAsync())
.ReturnsAsync(new Radarr4KSettings { Enabled = true, ApiKey = settings.ApiKey, Ip = settings.Ip, Port = settings.Port });
}
else
{
_mocker.GetMock<ISettingsService<RadarrSettings>>().Setup(x => x.GetSettingsAsync()).ReturnsAsync(settings);
}
_mocker.GetMock<IRadarrV3Api>().Setup(x => x.GetProfiles(settings.ApiKey, settings.FullUri))
.ReturnsAsync(profileIds.Select(id => new RadarrV3QualityProfile { id = id, name = $"Profile {id}" }).ToList());
}

[Test]
public async Task RemoveMovieRequest_TriggersRequestDeletedNotification()
{
Expand Down
Loading
Loading