Fixed missing feature states exception.

- Fixed an issue with Orchard failing to initialize when the Settings_ShellFeatureRecord contains orphaned features (which happens when a module has been removed).
- Added unit tests.
This commit is contained in:
Sipke Schoorstra 2015-09-15 12:38:27 +01:00
parent 40f1e39622
commit 40071a6050
2 changed files with 61 additions and 13 deletions
src
Orchard.Tests/Environment/ShellBuilders
Orchard/Environment/ShellBuilders

View File

@ -9,6 +9,7 @@ using Orchard.Environment.Descriptor.Models;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.Environment.ShellBuilders;
using Orchard.Logging;
using Orchard.Tests.Environment.TestDependencies;
using Orchard.Utility.Extensions;
@ -17,16 +18,22 @@ namespace Orchard.Tests.Environment.ShellBuilders {
public class CompositionStrategyTests : ContainerTestBase {
private CompositionStrategy _compositionStrategy;
private Mock<IExtensionManager> _extensionManager;
private IEnumerable<ExtensionDescriptor> _availableExtensions;
private IEnumerable<Feature> _installedFeatures;
private Mock<ILogger> _loggerMock;
protected override void Register(ContainerBuilder builder) {
_extensionManager = new Mock<IExtensionManager>(MockBehavior.Loose);
_extensionManager = new Mock<IExtensionManager>();
_loggerMock = new Mock<ILogger>();
builder.RegisterType<CompositionStrategy>().AsSelf();
builder.RegisterInstance(_extensionManager.Object);
builder.RegisterInstance(_loggerMock.Object);
}
protected override void Resolve(ILifetimeScope container) {
_compositionStrategy = container.Resolve<CompositionStrategy>();
_compositionStrategy.Logger = container.Resolve<ILogger>();
var alphaExtension = new ExtensionDescriptor {
Id = "Alpha",
@ -54,7 +61,11 @@ namespace Orchard.Tests.Environment.ShellBuilders {
betaFeatureDescriptor
};
var features = new List<Feature> {
_availableExtensions = new[] {
alphaExtension
};
_installedFeatures = new List<Feature> {
new Feature {
Descriptor = alphaFeatureDescriptor,
ExportedTypes = new List<Type> {
@ -69,16 +80,16 @@ namespace Orchard.Tests.Environment.ShellBuilders {
}
};
_extensionManager.Setup(x => x.AvailableExtensions()).Returns(new List<ExtensionDescriptor> {
alphaExtension
});
_loggerMock.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
_extensionManager.Setup(x => x.AvailableFeatures()).Returns(
_extensionManager.Setup(x => x.AvailableExtensions()).Returns(() => _availableExtensions);
_extensionManager.Setup(x => x.AvailableFeatures()).Returns(() =>
_extensionManager.Object.AvailableExtensions()
.SelectMany(ext => ext.Features)
.ToReadOnlyCollection());
_extensionManager.Setup(x => x.LoadFeatures(It.IsAny<IEnumerable<FeatureDescriptor>>())).Returns(features);
_extensionManager.Setup(x => x.LoadFeatures(It.IsAny<IEnumerable<FeatureDescriptor>>())).Returns(() => _installedFeatures);
}
[Test]
@ -87,7 +98,7 @@ namespace Orchard.Tests.Environment.ShellBuilders {
var shellDescriptor = CreateShellDescriptor("Alpha", "Beta");
var shellBlueprint = _compositionStrategy.Compose(shellSettings, shellDescriptor);
Assert.That(shellBlueprint.Dependencies.Count(x => x.Type == typeof (AlphaDependency)), Is.EqualTo(1));
Assert.That(shellBlueprint.Dependencies.Count(x => x.Type == typeof(AlphaDependency)), Is.EqualTo(1));
Assert.That(shellBlueprint.Dependencies.Count(x => x.Type == typeof(BetaDependency)), Is.EqualTo(1));
}
@ -101,6 +112,34 @@ namespace Orchard.Tests.Environment.ShellBuilders {
Assert.That(shellDescriptor.Features.Count(x => x.Name == "Alpha"), Is.EqualTo(1));
}
[Test]
public void ComposeDoesNotThrowWhenFeatureStateRecordDoesNotExist() {
var shellSettings = CreateShell();
var shellDescriptor = CreateShellDescriptor("MyFeature");
Assert.DoesNotThrow(() => _compositionStrategy.Compose(shellSettings, shellDescriptor));
_loggerMock.Verify(x => x.Log(LogLevel.Warning, null, It.IsAny<string>(), It.IsAny<object[]>()));
}
[Test]
public void ComposeThrowsWhenAutoEnabledDependencyDoesNotExist() {
var myModule = _availableExtensions.First();
myModule.Features = myModule.Features.Concat(new[] {
new FeatureDescriptor {
Extension = myModule,
Name = "MyFeature",
Id = "MyFeature",
Dependencies = new[] { "NonExistingFeature" }
}
});
var shellSettings = CreateShell();
var shellDescriptor = CreateShellDescriptor("MyFeature");
Assert.Throws<OrchardException>(() => _compositionStrategy.Compose(shellSettings, shellDescriptor));
}
private ShellSettings CreateShell() {
return new ShellSettings();
}

View File

@ -9,7 +9,6 @@ using Orchard.ContentManagement.Records;
using Orchard.Environment.Configuration;
using Orchard.Environment.Descriptor.Models;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Helpers;
using Orchard.Environment.Extensions.Models;
using Orchard.Environment.ShellBuilders.Models;
using Orchard.Localization;
@ -76,19 +75,29 @@ namespace Orchard.Environment.ShellBuilders {
}
private IEnumerable<string> ExpandDependencies(IDictionary<string, FeatureDescriptor> availableFeatures, IEnumerable<string> features) {
return ExpandDependenciesInternal(availableFeatures, features).Distinct();
return ExpandDependenciesInternal(availableFeatures, features, dependentFeatureDescriptor: null).Distinct();
}
private IEnumerable<string> ExpandDependenciesInternal(IDictionary<string, FeatureDescriptor> availableFeatures, IEnumerable<string> features) {
private IEnumerable<string> ExpandDependenciesInternal(IDictionary<string, FeatureDescriptor> availableFeatures, IEnumerable<string> features, FeatureDescriptor dependentFeatureDescriptor = null) {
foreach (var shellFeature in features) {
if (!availableFeatures.ContainsKey(shellFeature)) {
throw new OrchardException(T("The feature {0} is not available", shellFeature));
// If the feature comes from a list of feature dependencies it indicates a bug, so throw an exception.
if(dependentFeatureDescriptor != null)
throw new OrchardException(
T("The feature '{0}' was listed as a dependency of '{1}' of extension '{2}', but this feature could not be found. Please update your manifest file or install the module providing the missing feature.",
shellFeature,
dependentFeatureDescriptor.Name,
dependentFeatureDescriptor.Extension.Name));
// If the feature comes from the shell descriptor it means the feature is simply orphaned, so don't throw an exception.
Logger.Warning("Identified '{0}' as an orphaned feature state record in Settings_ShellFeatureRecord.", shellFeature);
continue;
}
var feature = availableFeatures[shellFeature];
foreach (var childDependency in ExpandDependenciesInternal(availableFeatures, feature.Dependencies))
foreach (var childDependency in ExpandDependenciesInternal(availableFeatures, feature.Dependencies, dependentFeatureDescriptor: feature))
yield return childDependency;
foreach (var dependency in feature.Dependencies)