зеркало из https://github.com/microsoft/Forge.git
Merge pull request #9 from microsoft/feature/MinorUnitTestRefactor
Split out test schema files to ExampleSchemas folder and test ForgeAc…
This commit is contained in:
Коммит
31e13341a2
|
@ -76,11 +76,33 @@
|
|||
<Compile Include="test\ForgeSchemaHelper.cs" />
|
||||
<Compile Include="test\ForgeSchemaValidationTests.cs" />
|
||||
<Compile Include="test\TreeWalkerUnitTests.cs" />
|
||||
<Compile Include="test\ActionsAndCallbacks\BaseCommonAction.cs" />
|
||||
<Compile Include="test\ActionsAndCallbacks\CollectDiagnosticsAction.cs" />
|
||||
<Compile Include="test\ActionsAndCallbacks\ReturnSessionIdAction.cs" />
|
||||
<Compile Include="test\ActionsAndCallbacks\RevisitAction.cs" />
|
||||
<Compile Include="test\ActionsAndCallbacks\TardigradeAction.cs" />
|
||||
<Compile Include="test\ActionsAndCallbacks\TestDelayExceptionAction.cs" />
|
||||
<Compile Include="test\ActionsAndCallbacks\TestEvaluateInputType_FailOnField_Action.cs" />
|
||||
<Compile Include="test\ActionsAndCallbacks\TestEvaluateInputType_FailOnNonEmptyCtor_Action.cs" />
|
||||
<Compile Include="test\ActionsAndCallbacks\TestEvaluateInputTypeAction.cs" />
|
||||
<Compile Include="test\ActionsAndCallbacks\TreeWalkerCallbacks.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="test\ExampleSchemas\LeafNodeSummarySchema.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="test\ExampleSchemas\SubroutineSchema.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="test\ExampleSchemas\TardigradeSchema.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="test\ExampleSchemas\TestEvaluateInputTypeSchema.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Forge.TreeWalker\Forge.TreeWalker.csproj">
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
//-----------------------------------------------------------------------
|
||||
// <copyright file="BaseCommonAction.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// The BaseCommonAction abstract class implements the BaseAction abstract class.
|
||||
// </summary>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Forge.TreeWalker.UnitTests
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Forge.TreeWalker;
|
||||
|
||||
public abstract class BaseCommonAction : BaseAction
|
||||
{
|
||||
public object Input { get; private set; }
|
||||
|
||||
public CancellationToken Token { get; private set; }
|
||||
|
||||
public Guid SessionId { get; private set; }
|
||||
|
||||
public string TreeNodeKey { get; private set; }
|
||||
|
||||
private ActionContext actionContext;
|
||||
|
||||
public override Task<ActionResponse> RunAction(ActionContext actionContext)
|
||||
{
|
||||
this.Input = actionContext.ActionInput;
|
||||
this.Token = actionContext.Token;
|
||||
this.SessionId = actionContext.SessionId;
|
||||
this.TreeNodeKey = actionContext.TreeNodeKey;
|
||||
this.actionContext = actionContext;
|
||||
|
||||
return this.RunAction();
|
||||
}
|
||||
|
||||
public abstract Task<ActionResponse> RunAction();
|
||||
|
||||
public Task CommitIntermediates<T>(T intermediates)
|
||||
{
|
||||
return this.actionContext.CommitIntermediates<T>(intermediates);
|
||||
}
|
||||
|
||||
public Task<T> GetIntermediates<T>()
|
||||
{
|
||||
return this.actionContext.GetIntermediates<T>();
|
||||
}
|
||||
|
||||
public Task<ActionResponse> GetPreviousActionResponse()
|
||||
{
|
||||
return this.actionContext.GetPreviousActionResponse();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
//-----------------------------------------------------------------------
|
||||
// <copyright file="CollectDiagnosticsAction.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// The CollectDiagnosticsAction class implements the BaseCommonAction abstract class.
|
||||
// </summary>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Forge.TreeWalker.UnitTests
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Forge.Attributes;
|
||||
using Forge.TreeWalker;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
[ForgeAction(InputType: typeof(CollectDiagnosticsInput))]
|
||||
public class CollectDiagnosticsAction : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
CollectDiagnosticsInput actionInput = (CollectDiagnosticsInput)this.Input;
|
||||
|
||||
// Collect diagnostics using the input command and commit results.
|
||||
string intermediates = this.GetIntermediates<string>().GetAwaiter().GetResult();
|
||||
Assert.AreEqual(null, intermediates);
|
||||
|
||||
string result = MockCollectDiagnosticsResult(actionInput.Command);
|
||||
|
||||
intermediates = result;
|
||||
this.CommitIntermediates<string>(intermediates).GetAwaiter().GetResult();
|
||||
Assert.AreEqual(intermediates, this.GetIntermediates<string>().GetAwaiter().GetResult());
|
||||
|
||||
await Task.Run(() => Console.WriteLine(string.Format(
|
||||
"CollectDiagnosticsAction - SessionId: {0}, TreeNodeKey: {1}, ActionInput: {2}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
JsonConvert.SerializeObject(actionInput))));
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success", Output = result };
|
||||
return actionResponse;
|
||||
}
|
||||
|
||||
private static string MockCollectDiagnosticsResult(string command)
|
||||
{
|
||||
return command + "_Results";
|
||||
}
|
||||
}
|
||||
|
||||
public class CollectDiagnosticsInput
|
||||
{
|
||||
public string Command { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//-----------------------------------------------------------------------
|
||||
// <copyright file="ReturnSessionIdAction.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// The ReturnSessionIdAction class implements the BaseCommonAction abstract class.
|
||||
// </summary>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Forge.TreeWalker.UnitTests
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Forge.Attributes;
|
||||
using Forge.TreeWalker;
|
||||
|
||||
[ForgeAction]
|
||||
public class ReturnSessionIdAction : BaseCommonAction
|
||||
{
|
||||
public override Task<ActionResponse> RunAction()
|
||||
{
|
||||
return Task.FromResult(new ActionResponse() { Status = this.SessionId.ToString() });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------
|
||||
// <copyright file="RevisitAction.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// The RevisitAction class implements the BaseCommonAction abstract class.
|
||||
// </summary>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Forge.TreeWalker.UnitTests
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Forge.Attributes;
|
||||
using Forge.TreeWalker;
|
||||
|
||||
/// <summary>
|
||||
/// This action increases counters each time it is visited from the same TreeActionKey in the same SessionId.
|
||||
/// It persists these counters in the ActionResponse/PreviousActionResponse.
|
||||
/// This tests CommitCurrentTreeNode revisit/cycle behavior.
|
||||
/// </summary>
|
||||
[ForgeAction]
|
||||
public class RevisitAction : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
// Confirm that intermediates are getting cleared every time we revisit the node, despite us increasing the counter.
|
||||
int intermediates = await this.GetIntermediates<int>();
|
||||
Assert.AreEqual(0, intermediates);
|
||||
|
||||
intermediates++;
|
||||
await this.CommitIntermediates<int>(intermediates);
|
||||
|
||||
// GetPreviousActionResponse, increase the Output counter by one, and return.
|
||||
ActionResponse actionResponse = await this.GetPreviousActionResponse() ?? new ActionResponse() { Status = "Success", Output = 0 };
|
||||
actionResponse.Output = (int)actionResponse.Output + 1;
|
||||
|
||||
Console.WriteLine(string.Format(
|
||||
"RevisitAction - SessionId: {0}, TreeNodeKey: {1}, ActionResponse.Output: {2}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
actionResponse.Output));
|
||||
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
//-----------------------------------------------------------------------
|
||||
// <copyright file="TardigradeAction.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// The TardigradeAction class implements the BaseCommonAction abstract class.
|
||||
// </summary>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Forge.TreeWalker.UnitTests
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Forge.Attributes;
|
||||
using Forge.TreeWalker;
|
||||
|
||||
[ForgeAction]
|
||||
public class TardigradeAction : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
await Task.Run(() => Console.WriteLine(string.Format(
|
||||
"TardigradeAction - SessionId: {0}, TreeNodeKey: {1}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey)));
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success" };
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------
|
||||
// <copyright file="TestDelayExceptionAction.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// The TestDelayExceptionAction class implements the BaseCommonAction abstract class.
|
||||
// </summary>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Forge.TreeWalker.UnitTests
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Forge.Attributes;
|
||||
using Forge.TreeWalker;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
[ForgeAction(InputType: typeof(TestDelayExceptionInput))]
|
||||
public class TestDelayExceptionAction : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
TestDelayExceptionInput actionInput = (TestDelayExceptionInput)this.Input;
|
||||
Console.WriteLine(string.Format(
|
||||
"TestDelayExceptionAction - SessionId: {0}, TreeNodeKey: {1}, ActionInput: {2}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
JsonConvert.SerializeObject(actionInput)));
|
||||
|
||||
await Task.Delay(actionInput.DelayMilliseconds, this.Token);
|
||||
|
||||
if (actionInput.ThrowException)
|
||||
{
|
||||
throw new NullReferenceException("Throwing unexpected Exception!!");
|
||||
}
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success" };
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public class TestDelayExceptionInput
|
||||
{
|
||||
public int DelayMilliseconds { get; set; } = 0;
|
||||
|
||||
public bool ThrowException { get; set; } = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//-----------------------------------------------------------------------
|
||||
// <copyright file="TestEvaluateInputTypeAction.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// The TestEvaluateInputTypeAction class implements the BaseCommonAction abstract class.
|
||||
// </summary>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Forge.TreeWalker.UnitTests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Forge.Attributes;
|
||||
using Forge.TreeWalker;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
[ForgeAction(InputType: typeof(FooActionInput))]
|
||||
public class TestEvaluateInputTypeAction : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
FooActionInput actionInput = (FooActionInput)this.Input;
|
||||
bool boolDelegateResult = actionInput.BoolDelegate();
|
||||
bool boolDelegateAsyncResult = await actionInput.BoolDelegateAsync();
|
||||
|
||||
Console.WriteLine(string.Format(
|
||||
"TestEvaluateInputTypeAction - SessionId: {0}, TreeNodeKey: {1}, ActionInput: {2}, BoolDelegateResult: {3}, BoolDelegateAsyncResult: {4}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
JsonConvert.SerializeObject(actionInput),
|
||||
boolDelegateResult,
|
||||
boolDelegateAsyncResult));
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success", Output = boolDelegateResult && boolDelegateAsyncResult };
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public class FooActionInput
|
||||
{
|
||||
public string Command { get; set; }
|
||||
public int IntExpression { get; set; }
|
||||
public bool BoolExpression { get; set; }
|
||||
public string PropertyNotInSchema { get; set; }
|
||||
public FooActionObject NestedObject { get; set; }
|
||||
public FooActionObject[] ObjectArray { get; set; }
|
||||
public string[] StringArray { get; set; }
|
||||
public long[] LongArray { get; set; }
|
||||
public Func<bool> BoolDelegate { get; set; }
|
||||
public Func<Task<bool>> BoolDelegateAsync { get; set; }
|
||||
public Dictionary<string, string> StringDictionary { get; set; }
|
||||
public object DynamicObject { get; set; }
|
||||
}
|
||||
|
||||
public class FooActionObject
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
public int IntPropertyInObject { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//-----------------------------------------------------------------------
|
||||
// <copyright file="TestEvaluateInputType_FailOnField_Action.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// The TestEvaluateInputType_FailOnField_Action class implements the BaseCommonAction abstract class.
|
||||
// </summary>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Forge.TreeWalker.UnitTests
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Forge.Attributes;
|
||||
using Forge.TreeWalker;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
[ForgeAction(InputType: typeof(FooActionInput_UnexpectedField))]
|
||||
public class TestEvaluateInputType_FailOnField_Action : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
FooActionInput_UnexpectedField actionInput = (FooActionInput_UnexpectedField)this.Input;
|
||||
|
||||
await Task.Run(() => Console.WriteLine(string.Format(
|
||||
"TestEvaluateInputType_FailOnField_Action - SessionId: {0}, TreeNodeKey: {1}, ActionInput: {2}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
JsonConvert.SerializeObject(actionInput))));
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success" };
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public class FooActionInput_UnexpectedField
|
||||
{
|
||||
public bool UnexpectedField = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
//-----------------------------------------------------------------------
|
||||
// <copyright file="TestEvaluateInputType_FailOnNonEmptyCtor_Action.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// The TestEvaluateInputType_FailOnNonEmptyCtor_Action class implements the BaseCommonAction abstract class.
|
||||
// </summary>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Forge.TreeWalker.UnitTests
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Forge.Attributes;
|
||||
using Forge.TreeWalker;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
[ForgeAction(InputType: typeof(FooActionInput_NonEmptyConstructor))]
|
||||
public class TestEvaluateInputType_FailOnNonEmptyCtor_Action : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
FooActionInput_NonEmptyConstructor actionInput = (FooActionInput_NonEmptyConstructor)this.Input;
|
||||
|
||||
await Task.Run(() => Console.WriteLine(string.Format(
|
||||
"TestEvaluateInputType_FailOnNonEmptyCtor_Action - SessionId: {0}, TreeNodeKey: {1}, ActionInput: {2}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
JsonConvert.SerializeObject(actionInput))));
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success" };
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public class FooActionInput_NonEmptyConstructor
|
||||
{
|
||||
public bool BoolProperty { get; set; }
|
||||
|
||||
public FooActionInput_NonEmptyConstructor(int unexpectedParameter) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
//-----------------------------------------------------------------------
|
||||
// <copyright file="TreeWalkerCallbacks.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// The TreeWalkerCallbacks class implements the ITreeWalkerCallbacks interface.
|
||||
// </summary>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace Forge.TreeWalker.UnitTests
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Forge.TreeWalker;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public class TreeWalkerCallbacks : ITreeWalkerCallbacks
|
||||
{
|
||||
public async Task BeforeVisitNode(
|
||||
Guid sessionId,
|
||||
string treeNodeKey,
|
||||
dynamic properties,
|
||||
dynamic userContext,
|
||||
string treeName,
|
||||
Guid rootSessionId,
|
||||
CancellationToken token)
|
||||
{
|
||||
string serializeProperties = JsonConvert.SerializeObject(properties);
|
||||
|
||||
await Task.Run(() => Console.WriteLine(string.Format(
|
||||
"OnBeforeVisitNode: SessionId: {0}, TreeNodeKey: {1}, Properties: {2}.",
|
||||
sessionId,
|
||||
treeNodeKey,
|
||||
serializeProperties)));
|
||||
}
|
||||
|
||||
public Task AfterVisitNode(
|
||||
Guid sessionId,
|
||||
string treeNodeKey,
|
||||
dynamic properties,
|
||||
dynamic userContext,
|
||||
string treeName,
|
||||
Guid rootSessionId,
|
||||
CancellationToken token)
|
||||
{
|
||||
Console.WriteLine(string.Format(
|
||||
"OnAfterVisitNode: SessionId: {0}, TreeNodeKey: {1}, Properties: {2}.",
|
||||
sessionId,
|
||||
treeNodeKey,
|
||||
JsonConvert.SerializeObject(properties)));
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"Tree": {
|
||||
"Root": {
|
||||
"Type": "Action",
|
||||
"Actions": {
|
||||
"Root_CollectDiagnosticsAction": {
|
||||
"Action": "CollectDiagnosticsAction",
|
||||
"Input": {
|
||||
"Command": "TheCommand"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ChildSelector": [
|
||||
{
|
||||
"Label": "Label",
|
||||
"Child": "LeafNodeSummaryTest"
|
||||
}
|
||||
]
|
||||
},
|
||||
"LeafNodeSummaryTest": {
|
||||
"Type": "Leaf",
|
||||
"Actions": {
|
||||
"LeafNodeSummaryTest_LeafNodeSummaryAction": {
|
||||
"Action": "LeafNodeSummaryAction",
|
||||
"Input": "C#|Session.GetLastActionResponse()"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"ParentTree": {
|
||||
"Tree": {
|
||||
"Root": {
|
||||
"Type": "Subroutine",
|
||||
"Actions": {
|
||||
"Root_Subroutine": {
|
||||
"Action": "SubroutineAction",
|
||||
"Input": {
|
||||
"TreeName": "SubroutineTree",
|
||||
"TreeInput": {
|
||||
"TestStatusCode": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"SubroutineTree": {
|
||||
"RootTreeNodeKey": "StartingNode",
|
||||
"Tree": {
|
||||
"StartingNode": {
|
||||
"Type": "Leaf",
|
||||
"Actions": {
|
||||
"Root_LeafNodeSummaryAction": {
|
||||
"Action": "LeafNodeSummaryAction",
|
||||
"Input": {
|
||||
"Status": "Success",
|
||||
"StatusCode": "C#|(int)TreeInput.TestStatusCode"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
{
|
||||
"Tree": {
|
||||
"Root": {
|
||||
"Type": "Selection",
|
||||
"ChildSelector": [
|
||||
{
|
||||
"Label": "Container",
|
||||
"ShouldSelect": "C#|UserContext.ResourceType == \"Container\"",
|
||||
"Child": "Container"
|
||||
},
|
||||
{
|
||||
"Label": "Node",
|
||||
"ShouldSelect": "C#|UserContext.ResourceType == \"Node\"",
|
||||
"Child": "Node"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Container": {
|
||||
"Type": "Action",
|
||||
"Actions": {
|
||||
"Container_CollectDiagnosticsAction": {
|
||||
"Action": "CollectDiagnosticsAction",
|
||||
"Input": {
|
||||
"Command": "RunCollectDiagnostics.exe"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ChildSelector": [
|
||||
{
|
||||
"Label": "Tardigrade",
|
||||
"ShouldSelect": "C#|Session.GetLastActionResponse().Status == \"Success\"",
|
||||
"Child": "Tardigrade"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Tardigrade": {
|
||||
"Type": "Action",
|
||||
"Actions": {
|
||||
"Tardigrade_TardigradeAction": {
|
||||
"Action": "TardigradeAction"
|
||||
}
|
||||
},
|
||||
"ChildSelector": [
|
||||
{
|
||||
"Label": "Tardigrade_Success",
|
||||
"ShouldSelect": "C#|Session.GetLastActionResponse().Status == \"Success\"",
|
||||
"Child": "Tardigrade_Success"
|
||||
},
|
||||
{
|
||||
"Label": "Tardigrade_Failure",
|
||||
"Child": "Tardigrade_Failure"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Tardigrade_Success": {
|
||||
"Type": "Leaf"
|
||||
},
|
||||
"Tardigrade_Failure": {
|
||||
"Type": "Leaf"
|
||||
},
|
||||
"Node": {
|
||||
"Type": "Selection",
|
||||
"ChildSelector": [
|
||||
{
|
||||
"Label": "Reboot",
|
||||
"ShouldSelect": "C#|UserContext.GetCount() == 1",
|
||||
"Child": "Reboot"
|
||||
},
|
||||
{
|
||||
"Label": "Evacuate",
|
||||
"Child": "Evacuate"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Reboot": {
|
||||
"Type": "Action",
|
||||
"Actions": {
|
||||
"Reboot_RebootAction": {
|
||||
"Action": "RebootAction"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Evacuate": {
|
||||
"Type": "Action",
|
||||
"Actions": {
|
||||
"Evacuate_EvacuateAction": {
|
||||
"Action": "EvacuateAction"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"Tree": {
|
||||
"Root": {
|
||||
"Type": "Action",
|
||||
"Actions": {
|
||||
"Root_TestEvaluateInputTypeAction": {
|
||||
"Action": "TestEvaluateInputTypeAction",
|
||||
"Input": {
|
||||
"Command": "tasklist",
|
||||
"IntExpression": "C#<Int64>|UserContext.GetCount()",
|
||||
"BoolExpression": "C#|true",
|
||||
"NestedObject": {
|
||||
"Name": "C#|string.Format(\"{0}_{1}\", \"MyName\", UserContext.Name)",
|
||||
"Value": "MyValue",
|
||||
"IntPropertyInObject": "C#<Int64>|UserContext.GetCount()"
|
||||
},
|
||||
"ObjectArray": [
|
||||
{
|
||||
"Name": "C#|UserContext.Name",
|
||||
"Value": "FirstValue"
|
||||
},
|
||||
{
|
||||
"Name": "SecondName",
|
||||
"Value": "SecondValue"
|
||||
}
|
||||
],
|
||||
"StringArray": [
|
||||
"C#|UserContext.Name",
|
||||
"value2"
|
||||
],
|
||||
"LongArray": [
|
||||
"C#<Int64>|(long)UserContext.GetCount()",
|
||||
3,
|
||||
2
|
||||
],
|
||||
"BoolDelegate": "C#|(Func<bool>)(() => {return UserContext.GetCount() == 1;})",
|
||||
"BoolDelegateAsync": "C#|(Func<Task<bool>>)(async() => { return await UserContext.GetCountAsync() == 2; })",
|
||||
"StringDictionary": {
|
||||
"TestKey1": "C#|UserContext.Name",
|
||||
"TestKey2": "TestValue2"
|
||||
},
|
||||
"DynamicObject": {
|
||||
"DynamicPropertyString": "TestValue1",
|
||||
"DynamicPropertyInt": 10,
|
||||
"DynamicPropertyExpression": "C#|UserContext.Name",
|
||||
"DynamicPropertyNestedObject": {
|
||||
"NestedPropertyOne": "NestedValue1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,64 +11,6 @@ namespace Forge.TreeWalker.UnitTests
|
|||
{
|
||||
public static class ForgeSchemaHelper
|
||||
{
|
||||
public const string TardigradeScenario = @"
|
||||
{
|
||||
""Tree"": {
|
||||
""Root"": {
|
||||
""Type"": ""Selection"",
|
||||
""ChildSelector"": [
|
||||
{
|
||||
""Label"": ""Node"",
|
||||
""ShouldSelect"": ""C#|UserContext.ResourceType == \""Node\"""",
|
||||
""Child"": ""Node""
|
||||
},
|
||||
{
|
||||
""Label"": ""Label"",
|
||||
""ShouldSelect"": ""C#|UserContext.ResourceType == \""Container\"""",
|
||||
""Child"": ""Container""
|
||||
}
|
||||
]
|
||||
},
|
||||
""Container"": {
|
||||
""Type"": ""Action"",
|
||||
""Actions"": {
|
||||
""Container_CollectDiagnosticsAction"": {
|
||||
""Action"": ""CollectDiagnosticsAction"",
|
||||
""Input"":
|
||||
{
|
||||
""Command"": ""RunCollectDiagnostics.exe""
|
||||
}
|
||||
}
|
||||
},
|
||||
""ChildSelector"": [
|
||||
{
|
||||
""Label"": ""Label"",
|
||||
""ShouldSelect"": ""C#|Session.GetLastActionResponse().Status == \""Success\"""",
|
||||
""Child"": ""Tardigrade""
|
||||
}
|
||||
]
|
||||
},
|
||||
""Tardigrade"": {
|
||||
""Type"": ""Action"",
|
||||
""Actions"": {
|
||||
""Tardigrade_TardigradeAction"": {
|
||||
""Action"": ""TardigradeAction""
|
||||
}
|
||||
},
|
||||
""ChildSelector"": [
|
||||
{
|
||||
""Label"": ""Label"",
|
||||
""ShouldSelect"": ""C#|Session.GetLastActionResponse().Status == \""Success\"""",
|
||||
""Child"": ""Tardigrade_Success""
|
||||
}
|
||||
]
|
||||
},
|
||||
""Tardigrade_Success"": {
|
||||
""Type"": ""Leaf""
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
public const string ActionException_Fail = @"
|
||||
{
|
||||
""Tree"": {
|
||||
|
@ -209,64 +151,6 @@ namespace Forge.TreeWalker.UnitTests
|
|||
}
|
||||
";
|
||||
|
||||
public const string TestEvaluateInputTypeAction = @"
|
||||
{
|
||||
""Tree"": {
|
||||
""Root"": {
|
||||
""Type"": ""Action"",
|
||||
""Actions"": {
|
||||
""Root_TestEvaluateInputTypeAction"": {
|
||||
""Action"": ""TestEvaluateInputTypeAction"",
|
||||
""Input"": {
|
||||
""Command"": ""tasklist"",
|
||||
""IntExpression"": ""C#<Int64>|UserContext.GetCount()"",
|
||||
""BoolExpression"": ""C#|true"",
|
||||
""NestedObject"": {
|
||||
""Name"": ""C#|string.Format(\""{0}_{1}\"", \""MyName\"", UserContext.Name)"",
|
||||
""Value"": ""MyValue"",
|
||||
""IntPropertyInObject"": ""C#<Int64>|UserContext.GetCount()""
|
||||
},
|
||||
""ObjectArray"": [
|
||||
{
|
||||
""Name"": ""C#|UserContext.Name"",
|
||||
""Value"": ""FirstValue""
|
||||
},
|
||||
{
|
||||
""Name"": ""SecondName"",
|
||||
""Value"": ""SecondValue""
|
||||
}
|
||||
],
|
||||
""StringArray"": [
|
||||
""C#|UserContext.Name"",
|
||||
""value2""
|
||||
],
|
||||
""LongArray"": [
|
||||
""C#<Int64>|(long)UserContext.GetCount()"",
|
||||
3,
|
||||
2
|
||||
],
|
||||
""BoolDelegate"": ""C#|(Func<bool>)(() => {return UserContext.GetCount() == 1;})"",
|
||||
""BoolDelegateAsync"": ""C#|(Func<Task<bool>>)(async() => { return await UserContext.GetCountAsync() == 2; })"",
|
||||
""StringDictionary"": {
|
||||
""TestKey1"": ""C#|UserContext.Name"",
|
||||
""TestKey2"": ""TestValue2""
|
||||
},
|
||||
""DynamicObject"": {
|
||||
""DynamicPropertyString"": ""TestValue1"",
|
||||
""DynamicPropertyInt"": 10,
|
||||
""DynamicPropertyExpression"": ""C#|UserContext.Name"",
|
||||
""DynamicPropertyNestedObject"": {
|
||||
""NestedPropertyOne"": ""NestedValue1""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
";
|
||||
|
||||
public const string TestEvaluateInputType_FailOnField_Action = @"
|
||||
{
|
||||
""Tree"": {
|
||||
|
@ -344,39 +228,6 @@ namespace Forge.TreeWalker.UnitTests
|
|||
}
|
||||
";
|
||||
|
||||
public const string LeafNodeSummaryAction_InputIsActionResponse = @"
|
||||
{
|
||||
""Tree"": {
|
||||
""Root"": {
|
||||
""Type"": ""Action"",
|
||||
""Actions"": {
|
||||
""Root_CollectDiagnosticsAction"": {
|
||||
""Action"": ""CollectDiagnosticsAction"",
|
||||
""Input"": {
|
||||
""Command"": ""TheCommand""
|
||||
}
|
||||
}
|
||||
},
|
||||
""ChildSelector"": [
|
||||
{
|
||||
""Label"": ""Label"",
|
||||
""Child"": ""LeafNodeSummaryTest""
|
||||
}
|
||||
]
|
||||
},
|
||||
""LeafNodeSummaryTest"": {
|
||||
""Type"": ""Leaf"",
|
||||
""Actions"": {
|
||||
""LeafNodeSummaryTest_LeafNodeSummaryAction"": {
|
||||
""Action"": ""LeafNodeSummaryAction"",
|
||||
""Input"": ""C#|Session.GetLastActionResponse()""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
";
|
||||
|
||||
public const string ExternalExecutors = @"
|
||||
{
|
||||
""Tree"": {
|
||||
|
@ -395,46 +246,6 @@ namespace Forge.TreeWalker.UnitTests
|
|||
}
|
||||
";
|
||||
|
||||
public const string SubroutineAction_GetLastActionResponse = @"
|
||||
{
|
||||
""ParentTree"": {
|
||||
""Tree"": {
|
||||
""Root"": {
|
||||
""Type"": ""Subroutine"",
|
||||
""Actions"": {
|
||||
""Root_Subroutine"": {
|
||||
""Action"": ""SubroutineAction"",
|
||||
""Input"": {
|
||||
""TreeName"": ""SubroutineTree"",
|
||||
""TreeInput"": {
|
||||
""TestStatusCode"": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
""SubroutineTree"": {
|
||||
""RootTreeNodeKey"": ""StartingNode"",
|
||||
""Tree"": {
|
||||
""StartingNode"": {
|
||||
""Type"": ""Leaf"",
|
||||
""Actions"": {
|
||||
""Root_LeafNodeSummaryAction"": {
|
||||
""Action"": ""LeafNodeSummaryAction"",
|
||||
""Input"": {
|
||||
""Status"": ""Success"",
|
||||
""StatusCode"": ""C#|(int)TreeInput.TestStatusCode""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
";
|
||||
|
||||
public const string SubroutineAction_NoActions = @"
|
||||
{
|
||||
""RootTree"": {
|
||||
|
|
|
@ -11,11 +11,11 @@ namespace Forge.TreeWalker.UnitTests
|
|||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Forge.Attributes;
|
||||
using Forge.DataContracts;
|
||||
using Forge.TreeWalker;
|
||||
using Forge.TreeWalker.ForgeExceptions;
|
||||
|
@ -24,6 +24,11 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestClass]
|
||||
public class TreeWalkerUnitTests
|
||||
{
|
||||
private const string TardigradeSchemaPath = "test\\ExampleSchemas\\TardigradeSchema.json";
|
||||
private const string TestEvaluateInputTypeSchemaPath = "test\\ExampleSchemas\\TestEvaluateInputTypeSchema.json";
|
||||
private const string LeafNodeSummarySchemaPath = "test\\ExampleSchemas\\LeafNodeSummarySchema.json";
|
||||
private const string SubroutineSchemaPath = "test\\ExampleSchemas\\SubroutineSchema.json";
|
||||
|
||||
private Guid sessionId;
|
||||
private IForgeDictionary forgeState = new ForgeDictionary(new Dictionary<string, object>(), Guid.Empty, Guid.Empty);
|
||||
private dynamic UserContext = new System.Dynamic.ExpandoObject();
|
||||
|
@ -80,10 +85,23 @@ namespace Forge.TreeWalker.UnitTests
|
|||
this.TestInitialize(rootSchema, treeName);
|
||||
}
|
||||
|
||||
public void TestFromFileInitialize(string filePath, string treeName = null)
|
||||
{
|
||||
string jsonSchema = File.ReadAllText(Path.Combine(Environment.CurrentDirectory, filePath));
|
||||
if (treeName == null)
|
||||
{
|
||||
this.TestInitialize(jsonSchema, treeName);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.TestSubroutineInitialize(jsonSchema, treeName);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestTreeWalkerSession_Constructor()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
this.TestFromFileInitialize(filePath: TardigradeSchemaPath);
|
||||
|
||||
// Test 1 - Verify jsonSchema was successfully deserialized in constructor.
|
||||
Assert.AreEqual("Action", this.session.Schema.Tree["Tardigrade"].Type.ToString());
|
||||
|
@ -95,7 +113,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void TestTreeWalkerSession_VisitNode_Success()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
this.TestFromFileInitialize(filePath: TardigradeSchemaPath);
|
||||
|
||||
// Test - VisitNode and expect the first child to be returned.
|
||||
string expected = "Tardigrade";
|
||||
|
@ -107,7 +125,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void TestTreeWalkerSession_VisitNode_LeafNode_Success()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
this.TestFromFileInitialize(filePath: TardigradeSchemaPath);
|
||||
|
||||
// Test - VisitNode on node of Leaf type and confirm it does not throw.
|
||||
string expected = null;
|
||||
|
@ -119,7 +137,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void TestTreeWalkerSession_VisitNode_NoTimeout_Success()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
this.TestFromFileInitialize(filePath: TardigradeSchemaPath);
|
||||
|
||||
// Test - VisitNode with no Timeout and execute an Action with no Timeout set. Confirm we do not throw exceptions.
|
||||
string expected = "Tardigrade_Success";
|
||||
|
@ -130,7 +148,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void TestTreeWalkerSession_WalkTree_Success()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
this.TestFromFileInitialize(filePath: TardigradeSchemaPath);
|
||||
|
||||
// Test - WalkTree and expect the Status to be RanToCompletion.
|
||||
string actualStatus = this.session.WalkTree("Root").GetAwaiter().GetResult();
|
||||
|
@ -228,7 +246,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void TestTreeWalkerSession_WalkTree_CancelledBeforeExecution()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
this.TestFromFileInitialize(filePath: TardigradeSchemaPath);
|
||||
|
||||
// Test - CancelWalkTree before WalkTree and expect the Status to be CancelledBeforeExecution.
|
||||
this.session.CancelWalkTree();
|
||||
|
@ -265,7 +283,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void TestTreeWalkerSession_WalkTree_Failed_MissingKey()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
this.TestFromFileInitialize(filePath: TardigradeSchemaPath);
|
||||
|
||||
// Test - WalkTree and expect the Status to be Failed because the key does not exist which threw an exception.
|
||||
Assert.ThrowsException<KeyNotFoundException>(() =>
|
||||
|
@ -290,7 +308,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void TestGetCurrentTreeNode()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
this.TestFromFileInitialize(filePath: TardigradeSchemaPath);
|
||||
|
||||
// Test 1 - Confirm GetCurrentTreeNode returns null before walking tree.
|
||||
Assert.AreEqual(null, this.session.GetCurrentTreeNode().GetAwaiter().GetResult(), "Expected CurrentTreeNode to be null before starting walk tree.");
|
||||
|
@ -303,7 +321,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void TestGetLastTreeAction()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
this.TestFromFileInitialize(filePath: TardigradeSchemaPath);
|
||||
|
||||
// Test 1 - Confirm GetLastTreeAction returns null before walking tree.
|
||||
Assert.AreEqual(null, this.session.GetLastTreeAction().GetAwaiter().GetResult(), "Expected LastTreeAction to be null before starting walk tree.");
|
||||
|
@ -316,7 +334,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void TestGetOutput()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
this.TestFromFileInitialize(filePath: TardigradeSchemaPath);
|
||||
|
||||
string actualStatus = this.session.WalkTree("Root").GetAwaiter().GetResult();
|
||||
Assert.AreEqual("RanToCompletion", actualStatus, "Expected WalkTree to run to completion.");
|
||||
|
@ -360,25 +378,10 @@ namespace Forge.TreeWalker.UnitTests
|
|||
"Expected to successfully read ActionResponse.Status.");
|
||||
}
|
||||
|
||||
// TODO: Add back test once we decide how we want to handle this case: Should people be able to change function definitions?
|
||||
// TestScript in local jsonSchema is related to this test.
|
||||
// [TestMethod]
|
||||
// public void TestTreeWalkerSession_WalkTree_Success_ChangingFunctionDefinition()
|
||||
// {
|
||||
// this.TestInitialize(jsonSchema: ForgeSchemaHelper.TardigradeScenario);
|
||||
|
||||
// Assert.AreEqual(this.UserContext.GetCount(), 1);
|
||||
// string actual = this.session.WalkTree("TestScript").GetAwaiter().GetResult();
|
||||
// Assert.AreEqual(2, this.UserContext.GetCount());
|
||||
// }
|
||||
|
||||
// TODO: Add back test once we decide how we want to handle this case: Should people be able to change function definitions?
|
||||
// TestScript in local jsonSchema is related to this test.
|
||||
|
||||
[TestMethod]
|
||||
public void Test_EvaluateInputType_Success()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.TestEvaluateInputTypeAction);
|
||||
this.TestFromFileInitialize(filePath: TestEvaluateInputTypeSchemaPath);
|
||||
|
||||
// Test - WalkTree to execute an Action with its ActionInput type defined in the ActionDefinition.InputType.
|
||||
string actualStatus = this.session.WalkTree("Root").GetAwaiter().GetResult();
|
||||
|
@ -479,7 +482,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void Test_LeafNodeSummaryAction_InputAsObject_Success()
|
||||
{
|
||||
this.TestInitialize(jsonSchema: ForgeSchemaHelper.LeafNodeSummaryAction_InputIsActionResponse);
|
||||
this.TestFromFileInitialize(filePath: LeafNodeSummarySchemaPath);
|
||||
|
||||
// Test - WalkTree to execute a LeafNodeSummaryAction node with its ActionInput set to ActionResponse object of the previously ran Action in the parent node.
|
||||
string actualStatus = this.session.WalkTree("Root").GetAwaiter().GetResult();
|
||||
|
@ -522,7 +525,7 @@ namespace Forge.TreeWalker.UnitTests
|
|||
[TestMethod]
|
||||
public void Test_SubroutineAction_ConfirmLastActionResponseGetsPersisted_Success()
|
||||
{
|
||||
this.TestSubroutineInitialize(jsonSchema: ForgeSchemaHelper.SubroutineAction_GetLastActionResponse, treeName: "ParentTree");
|
||||
this.TestFromFileInitialize(filePath: SubroutineSchemaPath, treeName: "ParentTree");
|
||||
|
||||
// Test - WalkTree to execute a SubroutineAction. Subroutine tree contains an action, defines a RootTreeNodeKey, and queries TreeInput from the schema.
|
||||
// Confirm the output of the SubroutineAction is the last ActionResponse in the Subroutine tree.
|
||||
|
@ -734,302 +737,5 @@ namespace Forge.TreeWalker.UnitTests
|
|||
|
||||
return new TreeWalkerSession(subroutineParameters);
|
||||
}
|
||||
|
||||
private sealed class TreeWalkerCallbacks : ITreeWalkerCallbacks
|
||||
{
|
||||
public async Task BeforeVisitNode(
|
||||
Guid sessionId,
|
||||
string treeNodeKey,
|
||||
dynamic properties,
|
||||
dynamic userContext,
|
||||
string treeName,
|
||||
Guid rootSessionId,
|
||||
CancellationToken token)
|
||||
{
|
||||
string serializeProperties = JsonConvert.SerializeObject(properties);
|
||||
|
||||
await Task.Run(() => Console.WriteLine(string.Format(
|
||||
"OnBeforeVisitNode: SessionId: {0}, TreeNodeKey: {1}, Properties: {2}.",
|
||||
sessionId,
|
||||
treeNodeKey,
|
||||
serializeProperties)));
|
||||
}
|
||||
|
||||
public Task AfterVisitNode(
|
||||
Guid sessionId,
|
||||
string treeNodeKey,
|
||||
dynamic properties,
|
||||
dynamic userContext,
|
||||
string treeName,
|
||||
Guid rootSessionId,
|
||||
CancellationToken token)
|
||||
{
|
||||
Console.WriteLine(string.Format(
|
||||
"OnAfterVisitNode: SessionId: {0}, TreeNodeKey: {1}, Properties: {2}.",
|
||||
sessionId,
|
||||
treeNodeKey,
|
||||
JsonConvert.SerializeObject(properties)));
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BaseCommonAction : BaseAction
|
||||
{
|
||||
public object Input { get; private set; }
|
||||
|
||||
public CancellationToken Token { get; private set; }
|
||||
|
||||
public Guid SessionId { get; private set; }
|
||||
|
||||
public string TreeNodeKey { get; private set; }
|
||||
|
||||
private ActionContext actionContext;
|
||||
|
||||
public override Task<ActionResponse> RunAction(ActionContext actionContext)
|
||||
{
|
||||
this.Input = actionContext.ActionInput;
|
||||
this.Token = actionContext.Token;
|
||||
this.SessionId = actionContext.SessionId;
|
||||
this.TreeNodeKey = actionContext.TreeNodeKey;
|
||||
this.actionContext = actionContext;
|
||||
|
||||
return this.RunAction();
|
||||
}
|
||||
|
||||
public abstract Task<ActionResponse> RunAction();
|
||||
|
||||
public Task CommitIntermediates<T>(T intermediates)
|
||||
{
|
||||
return this.actionContext.CommitIntermediates<T>(intermediates);
|
||||
}
|
||||
|
||||
public Task<T> GetIntermediates<T>()
|
||||
{
|
||||
return this.actionContext.GetIntermediates<T>();
|
||||
}
|
||||
|
||||
public Task<ActionResponse> GetPreviousActionResponse()
|
||||
{
|
||||
return this.actionContext.GetPreviousActionResponse();
|
||||
}
|
||||
}
|
||||
|
||||
[ForgeAction(InputType: typeof(CollectDiagnosticsInput))]
|
||||
public class CollectDiagnosticsAction : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
CollectDiagnosticsInput actionInput = (CollectDiagnosticsInput)this.Input;
|
||||
|
||||
// Collect diagnostics using the input command and commit results.
|
||||
string intermediates = this.GetIntermediates<string>().GetAwaiter().GetResult();
|
||||
Assert.AreEqual(null, intermediates);
|
||||
|
||||
string result = MockCollectDiagnosticsResult(actionInput.Command);
|
||||
|
||||
intermediates = result;
|
||||
this.CommitIntermediates<string>(intermediates).GetAwaiter().GetResult();
|
||||
Assert.AreEqual(intermediates, this.GetIntermediates<string>().GetAwaiter().GetResult());
|
||||
|
||||
await Task.Run(() => Console.WriteLine(string.Format(
|
||||
"CollectDiagnosticsAction - SessionId: {0}, TreeNodeKey: {1}, ActionInput: {2}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
JsonConvert.SerializeObject(actionInput))));
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success", Output = result };
|
||||
return actionResponse;
|
||||
}
|
||||
|
||||
private static string MockCollectDiagnosticsResult(string command)
|
||||
{
|
||||
return command + "_Results";
|
||||
}
|
||||
}
|
||||
|
||||
public class CollectDiagnosticsInput
|
||||
{
|
||||
public string Command { get; set; }
|
||||
}
|
||||
|
||||
[ForgeAction]
|
||||
public class TardigradeAction : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
await Task.Run(() => Console.WriteLine(string.Format(
|
||||
"TardigradeAction - SessionId: {0}, TreeNodeKey: {1}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey)));
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success" };
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
[ForgeAction(InputType: typeof(TestDelayExceptionInput))]
|
||||
public class TestDelayExceptionAction : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
TestDelayExceptionInput actionInput = (TestDelayExceptionInput)this.Input;
|
||||
Console.WriteLine(string.Format(
|
||||
"TestDelayExceptionAction - SessionId: {0}, TreeNodeKey: {1}, ActionInput: {2}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
JsonConvert.SerializeObject(actionInput)));
|
||||
|
||||
await Task.Delay(actionInput.DelayMilliseconds, this.Token);
|
||||
|
||||
if (actionInput.ThrowException)
|
||||
{
|
||||
throw new NullReferenceException("Throwing unexpected Exception!!");
|
||||
}
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success" };
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public class TestDelayExceptionInput
|
||||
{
|
||||
public int DelayMilliseconds { get; set; } = 0;
|
||||
|
||||
public bool ThrowException { get; set; } = false;
|
||||
}
|
||||
|
||||
[ForgeAction(InputType: typeof(FooActionInput))]
|
||||
public class TestEvaluateInputTypeAction : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
FooActionInput actionInput = (FooActionInput)this.Input;
|
||||
bool boolDelegateResult = actionInput.BoolDelegate();
|
||||
bool boolDelegateAsyncResult = await actionInput.BoolDelegateAsync();
|
||||
|
||||
Console.WriteLine(string.Format(
|
||||
"TestEvaluateInputTypeAction - SessionId: {0}, TreeNodeKey: {1}, ActionInput: {2}, BoolDelegateResult: {3}, BoolDelegateAsyncResult: {4}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
JsonConvert.SerializeObject(actionInput),
|
||||
boolDelegateResult,
|
||||
boolDelegateAsyncResult));
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success", Output = boolDelegateResult && boolDelegateAsyncResult };
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public class FooActionInput
|
||||
{
|
||||
public string Command { get; set; }
|
||||
public int IntExpression { get; set; }
|
||||
public bool BoolExpression { get; set; }
|
||||
public string PropertyNotInSchema { get; set; }
|
||||
public FooActionObject NestedObject { get; set; }
|
||||
public FooActionObject[] ObjectArray { get; set; }
|
||||
public string[] StringArray { get; set; }
|
||||
public long[] LongArray { get; set; }
|
||||
public Func<bool> BoolDelegate { get; set; }
|
||||
public Func<Task<bool>> BoolDelegateAsync { get; set; }
|
||||
public Dictionary<string, string> StringDictionary { get; set; }
|
||||
public object DynamicObject { get; set; }
|
||||
}
|
||||
|
||||
public class FooActionObject
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
public int IntPropertyInObject { get; set; }
|
||||
}
|
||||
|
||||
[ForgeAction(InputType: typeof(FooActionInput_UnexpectedField))]
|
||||
public class TestEvaluateInputType_FailOnField_Action : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
FooActionInput_UnexpectedField actionInput = (FooActionInput_UnexpectedField)this.Input;
|
||||
|
||||
await Task.Run(() => Console.WriteLine(string.Format(
|
||||
"TestEvaluateInputType_FailOnField_Action - SessionId: {0}, TreeNodeKey: {1}, ActionInput: {2}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
JsonConvert.SerializeObject(actionInput))));
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success" };
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public class FooActionInput_UnexpectedField
|
||||
{
|
||||
public bool UnexpectedField = false;
|
||||
}
|
||||
|
||||
[ForgeAction(InputType: typeof(FooActionInput_NonEmptyConstructor))]
|
||||
public class TestEvaluateInputType_FailOnNonEmptyCtor_Action : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
FooActionInput_NonEmptyConstructor actionInput = (FooActionInput_NonEmptyConstructor)this.Input;
|
||||
|
||||
await Task.Run(() => Console.WriteLine(string.Format(
|
||||
"TestEvaluateInputType_FailOnNonEmptyCtor_Action - SessionId: {0}, TreeNodeKey: {1}, ActionInput: {2}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
JsonConvert.SerializeObject(actionInput))));
|
||||
|
||||
ActionResponse actionResponse = new ActionResponse() { Status = "Success" };
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public class FooActionInput_NonEmptyConstructor
|
||||
{
|
||||
public bool BoolProperty { get; set; }
|
||||
|
||||
public FooActionInput_NonEmptyConstructor(int unexpectedParameter) {}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This action increases counters each time it is visited from the same TreeActionKey in the same SessionId.
|
||||
/// It persists these counters in the ActionResponse/PreviousActionResponse.
|
||||
/// This tests CommitCurrentTreeNode revisit/cycle behavior.
|
||||
/// </summary>
|
||||
[ForgeAction]
|
||||
public class RevisitAction : BaseCommonAction
|
||||
{
|
||||
public override async Task<ActionResponse> RunAction()
|
||||
{
|
||||
// Confirm that intermediates are getting cleared every time we revisit the node, despite us increasing the counter.
|
||||
int intermediates = await this.GetIntermediates<int>();
|
||||
Assert.AreEqual(0, intermediates);
|
||||
|
||||
intermediates++;
|
||||
await this.CommitIntermediates<int>(intermediates);
|
||||
|
||||
// GetPreviousActionResponse, increase the Output counter by one, and return.
|
||||
ActionResponse actionResponse = await this.GetPreviousActionResponse() ?? new ActionResponse() { Status = "Success", Output = 0 };
|
||||
actionResponse.Output = (int)actionResponse.Output + 1;
|
||||
|
||||
Console.WriteLine(string.Format(
|
||||
"RevisitAction - SessionId: {0}, TreeNodeKey: {1}, ActionResponse.Output: {2}.",
|
||||
this.SessionId,
|
||||
this.TreeNodeKey,
|
||||
actionResponse.Output));
|
||||
|
||||
return actionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
[ForgeAction]
|
||||
public class ReturnSessionIdAction : BaseCommonAction
|
||||
{
|
||||
public override Task<ActionResponse> RunAction()
|
||||
{
|
||||
return Task.FromResult(new ActionResponse() { Status = this.SessionId.ToString() });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,6 +28,8 @@ Forge has 3 major components: ForgeTree, TreeWalker, and ForgeEditor.
|
|||
|
||||
* ForgeEditor is coming to GitHub soon (currently only available internally to Microsoft). ForgeEditor is an Electron application that allows you to visualize and edit the ForgeTree in a clean UI experience. It contains features such as: tree visualization, buttons to create/delete TreeNodes, auto-complete when editing JSON file, text highlighting when hovering over TreeNode, evaluates ForgeSchemaValidationRules while editing, Diagnose mode, etc..
|
||||
|
||||
![](images/ForgeEditorImage.PNG)
|
||||
|
||||
## Values of using Forge:
|
||||
* Clarity: Allows users to intuitively walk a visualized tree and understand the workflow logic.
|
||||
* Versatility: Once you understand the tree, you can easily add/update nodes, actions, child selectors, paths, etc.. The dynamic capabilities allows for high extensibility on top of the base features.
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 161 KiB |
Загрузка…
Ссылка в новой задаче