mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-26 20:16:15 +08:00
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:
@@ -9,6 +9,7 @@ using Orchard.Environment.Descriptor.Models;
|
|||||||
using Orchard.Environment.Extensions;
|
using Orchard.Environment.Extensions;
|
||||||
using Orchard.Environment.Extensions.Models;
|
using Orchard.Environment.Extensions.Models;
|
||||||
using Orchard.Environment.ShellBuilders;
|
using Orchard.Environment.ShellBuilders;
|
||||||
|
using Orchard.Logging;
|
||||||
using Orchard.Tests.Environment.TestDependencies;
|
using Orchard.Tests.Environment.TestDependencies;
|
||||||
using Orchard.Utility.Extensions;
|
using Orchard.Utility.Extensions;
|
||||||
|
|
||||||
@@ -17,16 +18,22 @@ namespace Orchard.Tests.Environment.ShellBuilders {
|
|||||||
public class CompositionStrategyTests : ContainerTestBase {
|
public class CompositionStrategyTests : ContainerTestBase {
|
||||||
private CompositionStrategy _compositionStrategy;
|
private CompositionStrategy _compositionStrategy;
|
||||||
private Mock<IExtensionManager> _extensionManager;
|
private Mock<IExtensionManager> _extensionManager;
|
||||||
|
private IEnumerable<ExtensionDescriptor> _availableExtensions;
|
||||||
|
private IEnumerable<Feature> _installedFeatures;
|
||||||
|
private Mock<ILogger> _loggerMock;
|
||||||
|
|
||||||
protected override void Register(ContainerBuilder builder) {
|
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.RegisterType<CompositionStrategy>().AsSelf();
|
||||||
builder.RegisterInstance(_extensionManager.Object);
|
builder.RegisterInstance(_extensionManager.Object);
|
||||||
|
builder.RegisterInstance(_loggerMock.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Resolve(ILifetimeScope container) {
|
protected override void Resolve(ILifetimeScope container) {
|
||||||
_compositionStrategy = container.Resolve<CompositionStrategy>();
|
_compositionStrategy = container.Resolve<CompositionStrategy>();
|
||||||
|
_compositionStrategy.Logger = container.Resolve<ILogger>();
|
||||||
|
|
||||||
var alphaExtension = new ExtensionDescriptor {
|
var alphaExtension = new ExtensionDescriptor {
|
||||||
Id = "Alpha",
|
Id = "Alpha",
|
||||||
@@ -54,7 +61,11 @@ namespace Orchard.Tests.Environment.ShellBuilders {
|
|||||||
betaFeatureDescriptor
|
betaFeatureDescriptor
|
||||||
};
|
};
|
||||||
|
|
||||||
var features = new List<Feature> {
|
_availableExtensions = new[] {
|
||||||
|
alphaExtension
|
||||||
|
};
|
||||||
|
|
||||||
|
_installedFeatures = new List<Feature> {
|
||||||
new Feature {
|
new Feature {
|
||||||
Descriptor = alphaFeatureDescriptor,
|
Descriptor = alphaFeatureDescriptor,
|
||||||
ExportedTypes = new List<Type> {
|
ExportedTypes = new List<Type> {
|
||||||
@@ -69,16 +80,16 @@ namespace Orchard.Tests.Environment.ShellBuilders {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_extensionManager.Setup(x => x.AvailableExtensions()).Returns(new List<ExtensionDescriptor> {
|
_loggerMock.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
|
||||||
alphaExtension
|
|
||||||
});
|
|
||||||
|
|
||||||
_extensionManager.Setup(x => x.AvailableFeatures()).Returns(
|
_extensionManager.Setup(x => x.AvailableExtensions()).Returns(() => _availableExtensions);
|
||||||
|
|
||||||
|
_extensionManager.Setup(x => x.AvailableFeatures()).Returns(() =>
|
||||||
_extensionManager.Object.AvailableExtensions()
|
_extensionManager.Object.AvailableExtensions()
|
||||||
.SelectMany(ext => ext.Features)
|
.SelectMany(ext => ext.Features)
|
||||||
.ToReadOnlyCollection());
|
.ToReadOnlyCollection());
|
||||||
|
|
||||||
_extensionManager.Setup(x => x.LoadFeatures(It.IsAny<IEnumerable<FeatureDescriptor>>())).Returns(features);
|
_extensionManager.Setup(x => x.LoadFeatures(It.IsAny<IEnumerable<FeatureDescriptor>>())).Returns(() => _installedFeatures);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@@ -87,7 +98,7 @@ namespace Orchard.Tests.Environment.ShellBuilders {
|
|||||||
var shellDescriptor = CreateShellDescriptor("Alpha", "Beta");
|
var shellDescriptor = CreateShellDescriptor("Alpha", "Beta");
|
||||||
var shellBlueprint = _compositionStrategy.Compose(shellSettings, shellDescriptor);
|
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));
|
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));
|
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() {
|
private ShellSettings CreateShell() {
|
||||||
return new ShellSettings();
|
return new ShellSettings();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using Orchard.ContentManagement.Records;
|
|||||||
using Orchard.Environment.Configuration;
|
using Orchard.Environment.Configuration;
|
||||||
using Orchard.Environment.Descriptor.Models;
|
using Orchard.Environment.Descriptor.Models;
|
||||||
using Orchard.Environment.Extensions;
|
using Orchard.Environment.Extensions;
|
||||||
using Orchard.Environment.Extensions.Helpers;
|
|
||||||
using Orchard.Environment.Extensions.Models;
|
using Orchard.Environment.Extensions.Models;
|
||||||
using Orchard.Environment.ShellBuilders.Models;
|
using Orchard.Environment.ShellBuilders.Models;
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
@@ -76,19 +75,29 @@ namespace Orchard.Environment.ShellBuilders {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<string> ExpandDependencies(IDictionary<string, FeatureDescriptor> availableFeatures, IEnumerable<string> features) {
|
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) {
|
foreach (var shellFeature in features) {
|
||||||
|
|
||||||
if (!availableFeatures.ContainsKey(shellFeature)) {
|
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];
|
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;
|
yield return childDependency;
|
||||||
|
|
||||||
foreach (var dependency in feature.Dependencies)
|
foreach (var dependency in feature.Dependencies)
|
||||||
|
|||||||
Reference in New Issue
Block a user