diff --git a/SpeckleAutomateDotnetExample.sln b/SpeckleAutomateDotnetExample.sln
index 48a9afe..e9b84bf 100644
--- a/SpeckleAutomateDotnetExample.sln
+++ b/SpeckleAutomateDotnetExample.sln
@@ -5,6 +5,8 @@ VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpeckleAutomateDotnetExample", "SpeckleAutomateDotnetExample\SpeckleAutomateDotnetExample.csproj", "{E1DE5809-1111-4817-9B7D-7ADCA087FA1C}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAutomateFunction", "TestAutomateFunction\TestAutomateFunction.csproj", "{B74B115F-D553-4FD5-8E4C-7B13134217DA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -15,6 +17,10 @@ Global
{E1DE5809-1111-4817-9B7D-7ADCA087FA1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E1DE5809-1111-4817-9B7D-7ADCA087FA1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E1DE5809-1111-4817-9B7D-7ADCA087FA1C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B74B115F-D553-4FD5-8E4C-7B13134217DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B74B115F-D553-4FD5-8E4C-7B13134217DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B74B115F-D553-4FD5-8E4C-7B13134217DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B74B115F-D553-4FD5-8E4C-7B13134217DA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SpeckleAutomateDotnetExample/AutomateFunction.cs b/SpeckleAutomateDotnetExample/AutomateFunction.cs
index 667f3f4..257da4e 100644
--- a/SpeckleAutomateDotnetExample/AutomateFunction.cs
+++ b/SpeckleAutomateDotnetExample/AutomateFunction.cs
@@ -4,7 +4,7 @@ using Speckle.Core.Api;
using Speckle.Core.Models;
using SpeckleAutomateDotnetExample;
-static class AutomateFunction
+public static class AutomateFunction
{
public static string ADDED = "ADDED";
public static string MODIFIED = "MODIFIED";
@@ -19,11 +19,9 @@ static class AutomateFunction
Console.WriteLine("Starting execution");
_ = typeof(ObjectsKit).Assembly; // INFO: Force objects kit to initialize
- double tolerance = functionInputs.Tolerance;
-
// get the testing and release branches
string testBranchName = automationContext.AutomationRunData.BranchName;
- string releaseBranchName = testBranchName.Replace("/testing", "/release");
+ string releaseBranchName = functionInputs.DiffBranch;
Console.WriteLine($"Comparing {testBranchName} against {releaseBranchName}");
Branch? releaseBranch = await automationContext.SpeckleClient
.BranchGet(automationContext.AutomationRunData.ProjectId, releaseBranchName, 1)
@@ -37,7 +35,7 @@ static class AutomateFunction
Commit releaseCommit = releaseBranch.commits.items.First();
if (releaseCommit is null)
{
- throw new Exception("Release branch has no commits");
+ throw new Exception("Diff branch has no commits");
}
// get the test and release commit base
@@ -249,6 +247,12 @@ static class AutomateFunction
);
}
+ automationContext.AttachErrorToObjects(
+ "ADDED",
+ addedAppIdObjects.Select(o => o.Item1),
+ "added objects with an application Id"
+ );
+
foreach (var deleted in deletedAppIdObjects)
{
Console.WriteLine(
@@ -256,17 +260,22 @@ static class AutomateFunction
);
}
- foreach (var modified in modifiedAppIdObjects)
- {
- Console.WriteLine(
- $"{MODIFIED} {modified.Item3} object: id( {modified.Item1} ), appId: {modified.Item2}. Changed props: {modified.Item4}"
- );
- }
+ automationContext.AttachErrorToObjects(
+ "MODIFIED",
+ modifiedAppIdObjects.Select(o => o.Item1),
+ "modified objects with an application Id"
+ );
foreach (var changed in changedSpeckleIdObjects)
{
Console.WriteLine($"CHANGED {changed.Item2} object: id( {changed.Item1} )");
}
+
+ automationContext.AttachErrorToObjects(
+ "CHANGED",
+ changedSpeckleIdObjects.Select(o => o.Item1),
+ "changed objects with no application Id"
+ );
}
}
}
diff --git a/SpeckleAutomateDotnetExample/FunctionInputs.cs b/SpeckleAutomateDotnetExample/FunctionInputs.cs
index 864a9e9..08ebd8d 100644
--- a/SpeckleAutomateDotnetExample/FunctionInputs.cs
+++ b/SpeckleAutomateDotnetExample/FunctionInputs.cs
@@ -5,10 +5,11 @@ using System.ComponentModel.DataAnnotations;
///
/// This class is used to generate a JSON Schema to ensure that the user provided values
/// are valid and match the required schema.
-struct FunctionInputs
+public struct FunctionInputs
{
- //[Required]
- public double Tolerance;
-
- //public string Exclusions;
+ ///
+ /// The name of the branch to compare against. This should be the full branch path.
+ ///
+ [Required]
+ public string DiffBranch;
}
diff --git a/TestAutomateFunction/AutomationContextTest.cs b/TestAutomateFunction/AutomationContextTest.cs
new file mode 100644
index 0000000..b587ae3
--- /dev/null
+++ b/TestAutomateFunction/AutomationContextTest.cs
@@ -0,0 +1,150 @@
+# nullable enable
+namespace TestAutomateFunction;
+
+using Speckle.Automate.Sdk.Schema;
+using Speckle.Automate.Sdk;
+using Speckle.Core.Api;
+using Speckle.Core.Credentials;
+using Speckle.Core.Models;
+using Speckle.Core.Transports;
+using Utils = TestAutomateUtils;
+
+[TestFixture]
+public sealed class AutomationContextTest : IDisposable
+{
+ // Regression testing function project id
+ public string projectId = "6ada180f0c";
+
+ // e2e release branch and model id
+ public string releaseBranchName = "e2e/release";
+ public string releaseModelId = "e4c8cc3802";
+
+ // e2e testing branch and model id
+ public string testingBranchName = "e2e/testing";
+ public string testingModelId = "c374f0aade";
+
+ private async Task AutomationRunData(
+ Base testObject,
+ Base releaseObject
+ )
+ {
+ // send the release commit to the release branch
+ string releaseObjId = await Operations.Send(
+ releaseObject,
+ new List { new ServerTransport(client.Account, projectId) }
+ );
+
+ string releaseVersionId = await client.CommitCreate(
+ new()
+ {
+ streamId = projectId,
+ objectId = releaseObjId,
+ branchName = releaseBranchName
+ }
+ );
+
+ // send the test object to the test branch
+ string testObjId = await Operations.Send(
+ testObject,
+ new List { new ServerTransport(client.Account, projectId) }
+ );
+
+ string testingVersionId = await client.CommitCreate(
+ new()
+ {
+ streamId = projectId,
+ objectId = testObjId,
+ branchName = testingBranchName
+ }
+ );
+
+ // create a new automation on the testing branch
+ var automationName = TestAutomateUtils.RandomString(10);
+ var automationId = TestAutomateUtils.RandomString(10);
+ var automationRevisionId = TestAutomateUtils.RandomString(10);
+
+ await TestAutomateUtils.RegisterNewAutomation(
+ projectId,
+ testingModelId,
+ client,
+ automationId,
+ automationName,
+ automationRevisionId
+ );
+
+ var automationRunId = TestAutomateUtils.RandomString(10);
+ var functionId = TestAutomateUtils.RandomString(10);
+ var functionName = "Automation name " + TestAutomateUtils.RandomString(10);
+ var functionRelease = TestAutomateUtils.RandomString(10);
+
+ return new AutomationRunData
+ {
+ ProjectId = projectId,
+ ModelId = testingModelId,
+ BranchName = testingBranchName,
+ VersionId = testingVersionId,
+ SpeckleServerUrl = client.ServerUrl,
+ AutomationId = automationId,
+ AutomationRevisionId = automationRevisionId,
+ AutomationRunId = automationRunId,
+ FunctionId = functionId,
+ FunctionName = functionName,
+ FunctionRelease = functionRelease,
+ };
+ }
+
+ private Client client;
+ private Account account;
+
+ private string GetSpeckleToken()
+ {
+ var envVarName = "SPECKLE_TOKEN";
+ Environment.SetEnvironmentVariable(envVarName, "");
+ var token = Environment.GetEnvironmentVariable(envVarName);
+ if (token is null)
+ {
+ throw new Exception(
+ $"Cannot run tests without a {envVarName} environment variable"
+ );
+ }
+
+ return token;
+ }
+
+ private string GetSpeckleServerUrl() =>
+ Environment.GetEnvironmentVariable("SPECKLE_SERVER_URL")
+ ?? "https://latest.speckle.systems";
+
+ [OneTimeSetUp]
+ public void Setup()
+ {
+ account = new Account
+ {
+ token = GetSpeckleToken(),
+ serverInfo = new ServerInfo { url = GetSpeckleServerUrl() }
+ };
+ client = new Client(account);
+ }
+
+ [Test]
+ public async Task TestFunctionRun()
+ {
+ var automationRunData = await AutomationRunData(
+ TestAutomateUtils.TestObject(),
+ TestAutomateUtils.ReleaseObject()
+ );
+ var automationContext = await AutomationRunner.RunFunction(
+ AutomateFunction.Run,
+ automationRunData,
+ account.token,
+ new FunctionInputs { DiffBranch = releaseBranchName }
+ );
+
+ Assert.That(automationContext.RunStatus, Is.EqualTo("FAILED"));
+ }
+
+ public void Dispose()
+ {
+ client.Dispose();
+ }
+}
diff --git a/TestAutomateFunction/Properties/launchSettings.json b/TestAutomateFunction/Properties/launchSettings.json
new file mode 100644
index 0000000..bd9d2d3
--- /dev/null
+++ b/TestAutomateFunction/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "TestAutomateFunction": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "SPECKLE_TOKEN": "718d0650eea8038642850414e43bd81314d740359a",
+ "SPECKLE_SERVER_URL": "https://latest.speckle.dev/"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TestAutomateFunction/TestAutomateFunction.csproj b/TestAutomateFunction/TestAutomateFunction.csproj
new file mode 100644
index 0000000..ec49856
--- /dev/null
+++ b/TestAutomateFunction/TestAutomateFunction.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+ false
+
+ 0b5cf4dd-cfa3-4982-9722-ca6c6672b8dd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TestAutomateFunction/TestAutomateUtils.cs b/TestAutomateFunction/TestAutomateUtils.cs
new file mode 100644
index 0000000..7609a1f
--- /dev/null
+++ b/TestAutomateFunction/TestAutomateUtils.cs
@@ -0,0 +1,98 @@
+using System.Diagnostics.CodeAnalysis;
+using GraphQL;
+using Speckle.Core.Api;
+using Speckle.Core.Models;
+
+namespace TestAutomateFunction;
+
+public static class TestAutomateUtils
+{
+ [SuppressMessage("Security", "CA5394:Do not use insecure randomness")]
+ public static string RandomString(int length)
+ {
+ Random rand = new();
+ const string pool = "abcdefghijklmnopqrstuvwxyz0123456789";
+ var chars = Enumerable
+ .Range(0, length)
+ .Select(_ => pool[rand.Next(0, pool.Length)]);
+ return new string(chars.ToArray());
+ }
+
+ ///
+ /// The test object to compare against the release object
+ ///
+ ///
+ /// Contains 3 children objects to capture the Added, Deleted, Modified, and Unchanged categories
+ public static Base TestObject()
+ {
+ Base unchanged = new() { ["unchangedProp"] = true };
+ Base added = new() { ["addedProp"] = "added" };
+ Base modified = new() { ["modifiedProp"] = 1 };
+ Base testObject = new();
+ testObject["unchanged"] = unchanged;
+ testObject["added"] = added;
+ testObject["modified"] = modified;
+ return testObject;
+ }
+
+ ///
+ /// The release object to compare the test object against
+ ///
+ /// Contains 3 children objects to capture the Added, Deleted, Modified, and Unchanged categories
+ public static Base ReleaseObject()
+ {
+ Base unchanged = new() { ["unchangedProp"] = true };
+ Base deleted = new() { ["deletedProp"] = "deleted" };
+ Base modified = new() { ["modifiedProp"] = 0.5 };
+ Base releaseObject = new();
+ releaseObject["unchanged"] = unchanged;
+ releaseObject["deleted"] = deleted;
+ releaseObject["modified"] = modified;
+ return releaseObject;
+ }
+
+ public static async Task RegisterNewAutomation(
+ string projectId,
+ string modelId,
+ Client speckleClient,
+ string automationId,
+ string automationName,
+ string automationRevisionId
+ )
+ {
+ GraphQLRequest query =
+ new(
+ query: """
+ mutation CreateAutomation(
+ $projectId: String!
+ $modelId: String!
+ $automationName: String!
+ $automationId: String!
+ $automationRevisionId: String!
+ ) {
+ automationMutations {
+ create(
+ input: {
+ projectId: $projectId
+ modelId: $modelId
+ automationName: $automationName
+ automationId: $automationId
+ automationRevisionId: $automationRevisionId
+ }
+ )
+ }
+ }
+ """,
+ variables: new
+ {
+ projectId,
+ modelId,
+ automationName,
+ automationId,
+ automationRevisionId,
+ }
+ );
+
+ await speckleClient.ExecuteGraphQLRequest