mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-01-23 21:32:14 +08:00
Added support for multiple recipe steps of the same type and added unit tests.
Enables execution of multiple child recipes in a particular sequence. This is useful for the RecipesStep for example, where you want to execute a child recipe first, then execute some commands and import some content, then execute a second child recipe.
This commit is contained in:
@@ -159,6 +159,7 @@
|
||||
<Compile Include="Packaging\Services\FileBasedProjectSystemTests.cs" />
|
||||
<Compile Include="Packaging\Services\FolderUpdaterTests.cs" />
|
||||
<Compile Include="Packaging\Services\PackageInstallerTests.cs" />
|
||||
<Compile Include="Recipes\RecipeHandlers\RecipeParserTest.cs" />
|
||||
<Compile Include="Recipes\RecipeHandlers\RecipeExecutionStepHandlerTest.cs" />
|
||||
<Compile Include="Recipes\RecipeHandlers\ModuleStepTest.cs" />
|
||||
<Compile Include="Recipes\RecipeHandlers\ThemeStepTest.cs" />
|
||||
@@ -336,6 +337,12 @@
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Recipes\Services\FoldersData\Sample2\Module.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Recipes\Services\FoldersData\Sample2\Recipes\duplicate-steps.recipe.xml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -94,7 +94,7 @@ Features:
|
||||
Enumerable.Empty<ShellParameter>());
|
||||
|
||||
var moduleStep = _container.Resolve<ModuleStep>();
|
||||
var recipeExecutionContext = new RecipeExecutionContext {RecipeStep = new RecipeStep( recipeName: "Test", name: "Module", step: new XElement("SuperWiki")) };
|
||||
var recipeExecutionContext = new RecipeExecutionContext {RecipeStep = new RecipeStep(id: "1", recipeName: "Test", name: "Module", step: new XElement("SuperWiki")) };
|
||||
recipeExecutionContext.RecipeStep.Step.Add(new XAttribute("packageId", "Orchard.Module.SuperWiki"));
|
||||
recipeExecutionContext.RecipeStep.Step.Add(new XAttribute("repository", "test"));
|
||||
|
||||
@@ -120,7 +120,7 @@ Features:
|
||||
");
|
||||
|
||||
var moduleStep = _container.Resolve<ModuleStep>();
|
||||
var recipeContext = new RecipeContext { RecipeStep = new RecipeStep(recipeName: "Test", name: "Module", step: new XElement("SuperWiki")) };
|
||||
var recipeContext = new RecipeContext { RecipeStep = new RecipeStep(id: "1", recipeName: "Test", name: "Module", step: new XElement("SuperWiki")) };
|
||||
var recipeExecutionContext = new RecipeExecutionContext { RecipeStep = recipeContext.RecipeStep };
|
||||
recipeContext.RecipeStep.Step.Add(new XAttribute("repository", "test"));
|
||||
|
||||
@@ -145,7 +145,7 @@ Features:
|
||||
});
|
||||
|
||||
var moduleStep = _container.Resolve<ModuleStep>();
|
||||
var recipeExecutionContext = new RecipeExecutionContext { RecipeStep = new RecipeStep(recipeName: "Test", name: "Module", step: new XElement("SuperWiki")) };
|
||||
var recipeExecutionContext = new RecipeExecutionContext { RecipeStep = new RecipeStep(id: "1", recipeName: "Test", name: "Module", step: new XElement("SuperWiki")) };
|
||||
|
||||
recipeExecutionContext.RecipeStep.Step.Add(new XAttribute("packageId", "Orchard.Module.SuperWiki"));
|
||||
recipeExecutionContext.RecipeStep.Step.Add(new XAttribute("repository", "test"));
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers {
|
||||
var fakeRecipeStep = _container.Resolve<StubRecipeExecutionStep>();
|
||||
|
||||
var context = new RecipeContext {
|
||||
RecipeStep = new RecipeStep (recipeName: "FakeRecipe", name: "FakeRecipeStep", step: new XElement("FakeRecipeStep")),
|
||||
RecipeStep = new RecipeStep (id: "1", recipeName: "FakeRecipe", name: "FakeRecipeStep", step: new XElement("FakeRecipeStep")),
|
||||
ExecutionId = "12345"
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Tests.Modules.Recipes.RecipeHandlers {
|
||||
[TestFixture]
|
||||
public class RecipeParserTest {
|
||||
protected IContainer _container;
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterType<RecipeParser>().As<IRecipeParser>();
|
||||
|
||||
_container = builder.Build();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParsingRecipeYieldsUniqueIdsForSteps() {
|
||||
var recipeText = @"<Orchard><Foo /><Bar /><Baz /></Orchard>";
|
||||
var recipeParser = _container.Resolve<IRecipeParser>();
|
||||
var recipe = recipeParser.ParseRecipe(recipeText);
|
||||
|
||||
// Assert that each step has a unique ID.
|
||||
Assert.IsTrue(recipe.RecipeSteps.GroupBy(x => x.Id).All(y => y.Count() == 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ Features:
|
||||
Enumerable.Empty<ShellParameter>());
|
||||
|
||||
var themeStep = _container.Resolve<ThemeStep>();
|
||||
var recipeExecutionContext = new RecipeExecutionContext {RecipeStep = new RecipeStep (recipeName: "Test", name: "Theme", step: new XElement("SuperWiki")) };
|
||||
var recipeExecutionContext = new RecipeExecutionContext {RecipeStep = new RecipeStep (id: "1", recipeName: "Test", name: "Theme", step: new XElement("SuperWiki")) };
|
||||
|
||||
recipeExecutionContext.RecipeStep.Step.Add(new XAttribute("packageId", "Orchard.Theme.SuperWiki"));
|
||||
recipeExecutionContext.RecipeStep.Step.Add(new XAttribute("repository", "test"));
|
||||
@@ -135,7 +135,7 @@ Features:
|
||||
");
|
||||
|
||||
var themeStep = _container.Resolve<ThemeStep>();
|
||||
var recipeExecutionContext = new RecipeExecutionContext { RecipeStep = new RecipeStep(recipeName: "Test", name: "Theme", step: new XElement("SuperWiki")) };
|
||||
var recipeExecutionContext = new RecipeExecutionContext { RecipeStep = new RecipeStep(id: "1", recipeName: "Test", name: "Theme", step: new XElement("SuperWiki")) };
|
||||
|
||||
recipeExecutionContext.RecipeStep.Step.Add(new XAttribute("repository", "test"));
|
||||
Assert.Throws(typeof (InvalidOperationException), () => themeStep.Execute(recipeExecutionContext));
|
||||
@@ -159,7 +159,7 @@ Features:
|
||||
});
|
||||
|
||||
var themeStep = _container.Resolve<ThemeStep>();
|
||||
var recipeExecutionContext = new RecipeExecutionContext { RecipeStep = new RecipeStep(recipeName: "Test", name: "Theme", step: new XElement("SuperWiki")) };
|
||||
var recipeExecutionContext = new RecipeExecutionContext { RecipeStep = new RecipeStep(id: "1", recipeName: "Test", name: "Theme", step: new XElement("SuperWiki")) };
|
||||
|
||||
recipeExecutionContext.RecipeStep.Step.Add(new XAttribute("packageId", "Orchard.Theme.SuperWiki"));
|
||||
recipeExecutionContext.RecipeStep.Step.Add(new XAttribute("repository", "test"));
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
Name: Le plug-in français
|
||||
Author: Bertrand Le Roy
|
||||
Description:
|
||||
This plug-in replaces
|
||||
'the' with 'le'
|
||||
Version: 1.4.1
|
||||
Tags: plug-in, français, the, le
|
||||
homepage: http://weblogs.asp.net/bleroy
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<Orchard>
|
||||
<Recipe>
|
||||
<Name>Duplicate Steps</Name>
|
||||
</Recipe>
|
||||
|
||||
<Recipes />
|
||||
<Recipes />
|
||||
<Recipes />
|
||||
|
||||
</Orchard>
|
||||
|
||||
@@ -1,61 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using Autofac;
|
||||
using Moq;
|
||||
using NHibernate;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Caching;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Data;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Environment.Extensions.Folders;
|
||||
using Orchard.Environment.Extensions.Loaders;
|
||||
using Orchard.FileSystems.AppData;
|
||||
using Orchard.FileSystems.WebSite;
|
||||
using Orchard.Recipes.Events;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Services;
|
||||
using Orchard.Tests.Environment.Extensions;
|
||||
using Orchard.Tests.Stubs;
|
||||
using Orchard.Recipes.Events;
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Orchard.Tests.Modules.Recipes.Services {
|
||||
[TestFixture]
|
||||
public class RecipeManagerTests {
|
||||
private IContainer _container;
|
||||
public class RecipeManagerTests : DatabaseEnabledTestsBase {
|
||||
private IRecipeManager _recipeManager;
|
||||
private IRecipeHarvester _recipeHarvester;
|
||||
private IRecipeParser _recipeParser;
|
||||
private IExtensionFolders _folders;
|
||||
private ISessionFactory _sessionFactory;
|
||||
private ISession _session;
|
||||
|
||||
private const string DataPrefix = "Orchard.Tests.Modules.Recipes.Services.FoldersData.";
|
||||
private string _tempFolderName;
|
||||
|
||||
[TestFixtureSetUp]
|
||||
public void InitFixture() {
|
||||
var databaseFileName = System.IO.Path.GetTempFileName();
|
||||
_sessionFactory = DataUtility.CreateSessionFactory(
|
||||
databaseFileName,
|
||||
typeof(ContentTypeRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentItemVersionRecord),
|
||||
typeof(RecipeStepResultRecord));
|
||||
protected override IEnumerable<Type> DatabaseTypes {
|
||||
get { yield return typeof (RecipeStepResultRecord); }
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
public override void Register(ContainerBuilder builder) {
|
||||
_tempFolderName = Path.GetTempFileName();
|
||||
File.Delete(_tempFolderName);
|
||||
var assembly = GetType().Assembly;
|
||||
foreach (var name in assembly.GetManifestResourceNames()) {
|
||||
if (name.StartsWith(DataPrefix)) {
|
||||
foreach (var name in assembly.GetManifestResourceNames())
|
||||
{
|
||||
if (name.StartsWith(DataPrefix))
|
||||
{
|
||||
string text;
|
||||
using (var stream = assembly.GetManifestResourceStream(name)) {
|
||||
using (var stream = assembly.GetManifestResourceStream(name))
|
||||
{
|
||||
using (var reader = new StreamReader(stream))
|
||||
text = reader.ReadToEnd();
|
||||
|
||||
@@ -73,15 +64,16 @@ namespace Orchard.Tests.Modules.Recipes.Services {
|
||||
var targetPath = Path.Combine(_tempFolderName, relativePath);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
using (var stream = new FileStream(targetPath, FileMode.Create)) {
|
||||
using (var writer = new StreamWriter(stream)) {
|
||||
using (var stream = new FileStream(targetPath, FileMode.Create))
|
||||
{
|
||||
using (var writer = new StreamWriter(stream))
|
||||
{
|
||||
writer.Write(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var builder = new ContainerBuilder();
|
||||
var harvester = new ExtensionHarvester(new StubCacheManager(), new StubWebSiteFolder(), new Mock<ICriticalErrorProvider>().Object);
|
||||
_folders = new ModuleFolders(new[] { _tempFolderName }, harvester);
|
||||
builder.RegisterType<RecipeManager>().As<IRecipeManager>();
|
||||
@@ -97,21 +89,23 @@ namespace Orchard.Tests.Modules.Recipes.Services {
|
||||
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
|
||||
builder.RegisterInstance(_folders).As<IExtensionFolders>();
|
||||
builder.RegisterInstance(new Mock<IRecipeExecuteEventHandler>().Object);
|
||||
builder.RegisterType<Environment.Extensions.ExtensionManagerTests.StubLoaders>().As<IExtensionLoader>();
|
||||
builder.RegisterType<ExtensionManagerTests.StubLoaders>().As<IExtensionLoader>();
|
||||
builder.RegisterType<RecipeParser>().As<IRecipeParser>();
|
||||
builder.RegisterType<StubWebSiteFolder>().As<IWebSiteFolder>();
|
||||
builder.RegisterType<CustomRecipeHandler>().As<IRecipeHandler>();
|
||||
builder.RegisterInstance(new StubRecipeStepResultRecordRepository()).As<IRepository<RecipeStepResultRecord>>();
|
||||
}
|
||||
|
||||
public override void Init() {
|
||||
base.Init();
|
||||
|
||||
_container = builder.Build();
|
||||
_recipeManager = _container.Resolve<IRecipeManager>();
|
||||
_recipeParser = _container.Resolve<IRecipeParser>();
|
||||
_recipeHarvester = _container.Resolve<IRecipeHarvester>();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Term() {
|
||||
public override void Cleanup() {
|
||||
Directory.Delete(_tempFolderName, true);
|
||||
base.Cleanup();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -168,6 +162,38 @@ namespace Orchard.Tests.Modules.Recipes.Services {
|
||||
|
||||
Assert.That(CustomRecipeHandler.AttributeValue == "value1");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExecuteUpdatesStepResults()
|
||||
{
|
||||
var recipes = (List<Recipe>)_recipeHarvester.HarvestRecipes("Sample1");
|
||||
var sampleRecipe = recipes.First();
|
||||
var steps = sampleRecipe.RecipeSteps.ToArray();
|
||||
|
||||
_recipeManager.Execute(sampleRecipe);
|
||||
|
||||
var stepResultRepository = _container.Resolve<IRepository<RecipeStepResultRecord>>();
|
||||
var stepResults = stepResultRepository.Table.ToArray();
|
||||
|
||||
Assert.That(stepResults.Count(), Is.EqualTo(steps.Count()));
|
||||
Assert.IsTrue(stepResults.All(x => x.IsCompleted));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanExecuteSameStepMultipleTimes()
|
||||
{
|
||||
var recipes = (List<Recipe>)_recipeHarvester.HarvestRecipes("Sample2");
|
||||
var recipe = recipes.Single(x => x.Name == "Duplicate Steps");
|
||||
var steps = recipe.RecipeSteps.ToArray();
|
||||
|
||||
_recipeManager.Execute(recipe);
|
||||
|
||||
var stepResultRepository = _container.Resolve<IRepository<RecipeStepResultRecord>>();
|
||||
var stepResults = stepResultRepository.Table.ToArray();
|
||||
|
||||
Assert.That(stepResults.Count(), Is.EqualTo(steps.Count()));
|
||||
Assert.IsTrue(stepResults.All(x => x.IsCompleted));
|
||||
}
|
||||
}
|
||||
|
||||
public class StubStepQueue : IRecipeStepQueue {
|
||||
@@ -182,22 +208,6 @@ namespace Orchard.Tests.Modules.Recipes.Services {
|
||||
}
|
||||
}
|
||||
|
||||
public class StubRecipeStepResultRecordRepository : IRepository<RecipeStepResultRecord> {
|
||||
private List<RecipeStepResultRecord> _records = new List<RecipeStepResultRecord>();
|
||||
public IQueryable<RecipeStepResultRecord> Table { get { return _records.AsQueryable(); } }
|
||||
public void Copy(RecipeStepResultRecord source, RecipeStepResultRecord target) { }
|
||||
public int Count(Expression<Func<RecipeStepResultRecord, bool>> predicate) { return _records.Count; }
|
||||
public void Create(RecipeStepResultRecord entity) { _records.Add(entity); }
|
||||
public void Delete(RecipeStepResultRecord entity) { _records.Remove(entity); }
|
||||
public IEnumerable<RecipeStepResultRecord> Fetch(Expression<Func<RecipeStepResultRecord, bool>> predicate) { throw new NotImplementedException(); }
|
||||
public IEnumerable<RecipeStepResultRecord> Fetch(Expression<Func<RecipeStepResultRecord, bool>> predicate, Action<Orderable<RecipeStepResultRecord>> order) { throw new NotImplementedException(); }
|
||||
public IEnumerable<RecipeStepResultRecord> Fetch(Expression<Func<RecipeStepResultRecord, bool>> predicate, Action<Orderable<RecipeStepResultRecord>> order, int skip, int count) { throw new NotImplementedException(); }
|
||||
public void Flush() { }
|
||||
public RecipeStepResultRecord Get(Expression<Func<RecipeStepResultRecord, bool>> predicate) { throw new NotImplementedException(); }
|
||||
public RecipeStepResultRecord Get(int id) { throw new NotImplementedException(); }
|
||||
public void Update(RecipeStepResultRecord entity) { }
|
||||
}
|
||||
|
||||
public class StubRecipeScheduler : IRecipeScheduler {
|
||||
private readonly IRecipeStepExecutor _recipeStepExecutor;
|
||||
|
||||
@@ -212,7 +222,7 @@ namespace Orchard.Tests.Modules.Recipes.Services {
|
||||
|
||||
public class CustomRecipeHandler : IRecipeHandler {
|
||||
public static string AttributeValue;
|
||||
public string[] _handles = { "Module", "Theme", "Migration", "Custom1", "Custom2", "Command", "Metadata", "Feature", "Settings" };
|
||||
public string[] _handles = { "Module", "Theme", "Migration", "Custom1", "Custom2", "Command", "Metadata", "Feature", "Settings", "Recipes" };
|
||||
|
||||
public void ExecuteRecipeStep(RecipeContext recipeContext) {
|
||||
if (_handles.Contains(recipeContext.RecipeStep.Name)) {
|
||||
|
||||
Reference in New Issue
Block a user