diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c6aed46..ebe5735a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Better error message to console when no configuration files are found. +- Allow quality group names to duplicate quality names (#200). ## [5.2.0] - 2023-08-06 diff --git a/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs b/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs index f98f88bd..7fe92f83 100644 --- a/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs +++ b/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs @@ -110,12 +110,6 @@ public class QualityProfileConfigYamlValidator : AbstractValidator x is {Qualities.Count: > 0}); RuleFor(x => x.Qualities) - .Must((o, x) => !x! - .Where(y => y.Qualities is not null) - .SelectMany(y => y.Qualities!) - .Contains(o.Upgrade!.UntilQuality, StringComparer.InvariantCultureIgnoreCase)) - .WithMessage(o => - $"For profile {o.Name}, 'until_quality' must not refer to qualities contained within groups") .Must((o, x) => !x! .Where(y => y is {Enabled: false, Name: not null}) .Select(y => y.Name!) @@ -135,20 +129,36 @@ public class QualityProfileConfigYamlValidator : AbstractValidator qualities, ValidationContext context) { - var dupes = qualities + // Check for quality duplicates between non-groups and groups + var qualityDupes = qualities + .Where(x => x.Qualities is null) .Select(x => x.Name) .Concat(qualities.Where(x => x.Qualities is not null).SelectMany(x => x.Qualities!)) - .NotNull() .GroupBy(x => x) .Select(x => x.Skip(1).FirstOrDefault()) .NotNull(); - foreach (var dupe in dupes) + foreach (var dupe in qualityDupes) { var x = context.InstanceToValidate; context.AddFailure( $"For profile {x.Name}, 'qualities' contains duplicates for quality '{dupe}'"); } + + // Check for quality duplicates between non-groups and groups + var groupDupes = qualities + .Where(x => x.Qualities is not null) + .Select(x => x.Name) + .GroupBy(x => x) + .Select(x => x.Skip(1).FirstOrDefault()) + .NotNull(); + + foreach (var dupe in groupDupes) + { + var x = context.InstanceToValidate; + context.AddFailure( + $"For profile {x.Name}, 'qualities' contains duplicates for quality group '{dupe}'"); + } } } diff --git a/src/tests/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigYamlDataObjectsValidationTest.cs b/src/tests/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigYamlDataObjectsValidationTest.cs index 068e0ddd..188e0157 100644 --- a/src/tests/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigYamlDataObjectsValidationTest.cs +++ b/src/tests/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigYamlDataObjectsValidationTest.cs @@ -83,36 +83,6 @@ public class ConfigYamlDataObjectsValidationTest $"which is '{data.Upgrade!.UntilQuality}'"); } - [Test] - public void Quality_profile_cutoff_must_not_reference_child_qualities() - { - var data = new QualityProfileConfigYaml - { - Name = "My QP", - Upgrade = new QualityProfileFormatUpgradeYaml - { - Allowed = true, - UntilQuality = "Child Quality" - }, - Qualities = new[] - { - new QualityProfileQualityConfigYaml - { - Name = "Parent Group", - Qualities = new[] {"Child Quality"} - } - } - }; - - var validator = new QualityProfileConfigYamlValidator(); - var result = validator.TestValidate(data); - - result.ShouldHaveValidationErrorFor(x => x.Qualities); - - result.Errors.Select(x => x.ErrorMessage).Should().BeEquivalentTo( - $"For profile {data.Name}, 'until_quality' must not refer to qualities contained within groups"); - } - [Test] public void Quality_profile_qualities_must_have_no_duplicates() { @@ -125,13 +95,21 @@ public class ConfigYamlDataObjectsValidationTest new QualityProfileQualityConfigYaml {Name = "Dupe Quality"}, new QualityProfileQualityConfigYaml {Name = "Dupe Quality"}, new QualityProfileQualityConfigYaml {Name = "Dupe Quality 2"}, - new QualityProfileQualityConfigYaml {Name = "Dupe Quality 2"}, new QualityProfileQualityConfigYaml {Name = "Dupe Quality 3"}, - new QualityProfileQualityConfigYaml {Name = "Dupe Quality 4"}, new QualityProfileQualityConfigYaml { - Name = "Dupe Quality 3", - Qualities = new[] {"Dupe Quality 4"} + Name = "Dupe Quality 2", + Qualities = new[] {"Dupe Quality 3"} + }, + new QualityProfileQualityConfigYaml + { + Name = "Dupe Quality 4", + Qualities = new[] {"Dupe Quality 5"} + }, + new QualityProfileQualityConfigYaml + { + Name = "Dupe Quality 4", + Qualities = new[] {"Dupe Quality 5"} } } }; @@ -143,9 +121,9 @@ public class ConfigYamlDataObjectsValidationTest result.Errors.Select(x => x.ErrorMessage).Should().BeEquivalentTo( $"For profile {data.Name}, 'qualities' contains duplicates for quality 'Dupe Quality'", - $"For profile {data.Name}, 'qualities' contains duplicates for quality 'Dupe Quality 2'", $"For profile {data.Name}, 'qualities' contains duplicates for quality 'Dupe Quality 3'", - $"For profile {data.Name}, 'qualities' contains duplicates for quality 'Dupe Quality 4'"); + $"For profile {data.Name}, 'qualities' contains duplicates for quality group 'Dupe Quality 4'", + $"For profile {data.Name}, 'qualities' contains duplicates for quality 'Dupe Quality 5'"); } [Test]