Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b6e9dd9b5d | |||
| fc5a7c680e | |||
| 5569c0d4fd | |||
| adc27ae5d3 | |||
| cacd6f03f5 | |||
| e85e00bff2 | |||
| 739befc3fa |
@@ -34,3 +34,4 @@ jobs:
|
|||||||
speckle_token: ${{ secrets.SPECKLE_FUNCTION_TOKEN }}
|
speckle_token: ${{ secrets.SPECKLE_FUNCTION_TOKEN }}
|
||||||
speckle_function_id: ${{ secrets.SPECKLE_FUNCTION_ID }}
|
speckle_function_id: ${{ secrets.SPECKLE_FUNCTION_ID }}
|
||||||
speckle_function_input_schema_file_path: ${{ env.FUNCTION_SCHEMA_FILE_NAME }}
|
speckle_function_input_schema_file_path: ${{ env.FUNCTION_SCHEMA_FILE_NAME }}
|
||||||
|
speckle_function_recommended_memory_mi: 8000
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ VisualStudioVersion = 17.5.002.0
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpeckleAutomateDotnetExample", "SpeckleAutomateDotnetExample\SpeckleAutomateDotnetExample.csproj", "{E1DE5809-1111-4817-9B7D-7ADCA087FA1C}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpeckleAutomateDotnetExample", "SpeckleAutomateDotnetExample\SpeckleAutomateDotnetExample.csproj", "{E1DE5809-1111-4817-9B7D-7ADCA087FA1C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAutomateFunction", "TestAutomateFunction\TestAutomateFunction.csproj", "{B74B115F-D553-4FD5-8E4C-7B13134217DA}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{E1DE5809-1111-4817-9B7D-7ADCA087FA1C}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
using Objects;
|
using Objects;
|
||||||
using Speckle.Automate.Sdk;
|
using Speckle.Automate.Sdk;
|
||||||
using Speckle.Automate.Sdk.Schema;
|
|
||||||
using Speckle.Core.Api;
|
using Speckle.Core.Api;
|
||||||
using Speckle.Core.Credentials;
|
|
||||||
using Speckle.Core.Models;
|
using Speckle.Core.Models;
|
||||||
using Speckle.Core.Models.Extensions;
|
using SpeckleAutomateDotnetExample;
|
||||||
using Speckle.Core.Transports;
|
|
||||||
|
|
||||||
static class AutomateFunction
|
public static class AutomateFunction
|
||||||
{
|
{
|
||||||
public static string ADDED = "Added";
|
public static string ADDED = "ADDED";
|
||||||
public static string MODIFIED = "Modified";
|
public static string MODIFIED = "MODIFIED";
|
||||||
public static string DELETED = "Deleted";
|
public static string DELETED = "DELETED";
|
||||||
|
public static string UNCHANGED = "UNCHANGED";
|
||||||
|
|
||||||
public static async Task Run(
|
public static async Task Run(
|
||||||
AutomationContext automationContext,
|
AutomationContext automationContext,
|
||||||
@@ -21,9 +19,10 @@ static class AutomateFunction
|
|||||||
Console.WriteLine("Starting execution");
|
Console.WriteLine("Starting execution");
|
||||||
_ = typeof(ObjectsKit).Assembly; // INFO: Force objects kit to initialize
|
_ = typeof(ObjectsKit).Assembly; // INFO: Force objects kit to initialize
|
||||||
|
|
||||||
// get the test and release branch name
|
// get the testing and release branches
|
||||||
var testBranchName = automationContext.AutomationRunData.BranchName;
|
string testBranchName = automationContext.AutomationRunData.BranchName;
|
||||||
var releaseBranchName = testBranchName.Replace("/testing", "/release");
|
string releaseBranchName = functionInputs.DiffBranch;
|
||||||
|
Console.WriteLine($"Comparing {testBranchName} against {releaseBranchName}");
|
||||||
Branch? releaseBranch = await automationContext.SpeckleClient
|
Branch? releaseBranch = await automationContext.SpeckleClient
|
||||||
.BranchGet(automationContext.AutomationRunData.ProjectId, releaseBranchName, 1)
|
.BranchGet(automationContext.AutomationRunData.ProjectId, releaseBranchName, 1)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
@@ -32,251 +31,267 @@ static class AutomateFunction
|
|||||||
throw new Exception("Release branch was null");
|
throw new Exception("Release branch was null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the release branch latest commit
|
||||||
Commit releaseCommit = releaseBranch.commits.items.First();
|
Commit releaseCommit = releaseBranch.commits.items.First();
|
||||||
if (releaseCommit is null)
|
if (releaseCommit is null)
|
||||||
{
|
{
|
||||||
throw new Exception("Release branch has no commits");
|
throw new Exception("Diff branch has no commits");
|
||||||
}
|
}
|
||||||
|
|
||||||
var tolerance = functionInputs.Tolerance;
|
// get the test and release commit base
|
||||||
|
|
||||||
Console.WriteLine($"Comparing {testBranchName} against {releaseBranchName}");
|
|
||||||
|
|
||||||
// get the test and release commits
|
|
||||||
Console.WriteLine("Receiving test version");
|
Console.WriteLine("Receiving test version");
|
||||||
Base? testingCommitObject = await automationContext.ReceiveVersion();
|
Base testingCommitObject = await automationContext.ReceiveVersion();
|
||||||
Console.WriteLine("Received test version: " + testingCommitObject);
|
Console.WriteLine("Received test version: " + testingCommitObject);
|
||||||
Console.WriteLine("Receiving release version");
|
Console.WriteLine("Receiving release version");
|
||||||
ServerTransport serverTransport = new ServerTransport(
|
Base releaseCommitObject = await Utils.RecieveVersionAsync(
|
||||||
automationContext.SpeckleClient.Account,
|
releaseCommit.id,
|
||||||
automationContext.AutomationRunData.ProjectId
|
automationContext
|
||||||
);
|
);
|
||||||
Base? releaseCommitObject = await Operations
|
|
||||||
.Receive(
|
|
||||||
(
|
|
||||||
await automationContext.SpeckleClient
|
|
||||||
.CommitGet(automationContext.AutomationRunData.ProjectId, releaseCommit.id)
|
|
||||||
.ConfigureAwait(continueOnCapturedContext: false)
|
|
||||||
).referencedObject,
|
|
||||||
serverTransport,
|
|
||||||
new MemoryTransport()
|
|
||||||
)
|
|
||||||
.ConfigureAwait(continueOnCapturedContext: false);
|
|
||||||
if (releaseCommitObject == null)
|
|
||||||
{
|
|
||||||
throw new Exception("Commit root object was null");
|
|
||||||
}
|
|
||||||
Console.WriteLine("Received release version: " + releaseCommitObject);
|
Console.WriteLine("Received release version: " + releaseCommitObject);
|
||||||
|
|
||||||
// flatten both commits
|
// Create dictionaries by appId (or speckle id if no appId exists) for the release and testing commits
|
||||||
IEnumerable<Base> releaseCommitObjects = releaseCommitObject.Flatten();
|
// note that it is possible for multiple objects to have the same app id
|
||||||
IEnumerable<Base> testCommitObjects = testingCommitObject.Flatten();
|
// (eg mapped schema objects have the same id as their parent geometry)
|
||||||
var releaseCommitObjectsDict = new Dictionary<string, Base>();
|
// and it is also possible for multiple objects (with no app id) to have the same speckle id
|
||||||
foreach (var releaseObject in releaseCommitObjects)
|
// (eg the same mesh sent from gh multiple times)
|
||||||
|
Dictionary<string, List<Base>> releaseCommitAppIdDict = new();
|
||||||
|
Dictionary<string, List<Base>> releaseCommitSpeckleIdDict = new();
|
||||||
|
Dictionary<string, List<Base>> testingCommitAppIdDict = new();
|
||||||
|
Dictionary<string, List<Base>> testingCommitSpeckleIdDict = new();
|
||||||
|
Utils.CreateDictionaryFromBaseById(
|
||||||
|
releaseCommitObject,
|
||||||
|
out releaseCommitAppIdDict,
|
||||||
|
out releaseCommitSpeckleIdDict,
|
||||||
|
out int releaseObjectCount
|
||||||
|
);
|
||||||
|
Console.WriteLine(
|
||||||
|
$"Found {releaseObjectCount} objects in RELEASE with {releaseCommitAppIdDict.Count} unique applicationIds and {releaseCommitSpeckleIdDict.Count} unique speckle ids (for objects with no application id)."
|
||||||
|
);
|
||||||
|
Utils.CreateDictionaryFromBaseById(
|
||||||
|
testingCommitObject,
|
||||||
|
out testingCommitAppIdDict,
|
||||||
|
out testingCommitSpeckleIdDict,
|
||||||
|
out int testingObjectCount
|
||||||
|
);
|
||||||
|
Console.WriteLine(
|
||||||
|
$"Found {testingObjectCount} objects in TESTING with {testingCommitAppIdDict.Count} unique applicationIds and {testingCommitSpeckleIdDict.Count} unique speckle ids (for objects with no application id)."
|
||||||
|
);
|
||||||
|
|
||||||
|
// COMPARE COMMIT OBJECTS WITH APPLICATION IDS
|
||||||
|
// and store in hash lists where each object is (id, appId, type), and for modified, and additional string of property changes.
|
||||||
|
HashSet<Tuple<string, string?, string>> deletedAppIdObjects = new();
|
||||||
|
HashSet<Tuple<string, string?, string>> unchangedAppIdObjects = new();
|
||||||
|
HashSet<Tuple<string, string?, string>> addedAppIdObjects = new();
|
||||||
|
HashSet<Tuple<string, string?, string, string>> modifiedAppIdObjects = new();
|
||||||
|
|
||||||
|
// first find deleted objects in the testing commit and remove their keys from the release commit dict
|
||||||
|
foreach (string releaseAppId in releaseCommitAppIdDict.Keys)
|
||||||
{
|
{
|
||||||
if (
|
if (!testingCommitAppIdDict.ContainsKey(releaseAppId))
|
||||||
releaseObject.applicationId != null
|
|
||||||
&& !releaseCommitObjectsDict.ContainsKey(releaseObject.applicationId)
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
releaseCommitObjectsDict.Add(releaseObject.applicationId, releaseObject);
|
releaseCommitAppIdDict[releaseAppId].ForEach(
|
||||||
|
o =>
|
||||||
|
deletedAppIdObjects.Add(
|
||||||
|
new Tuple<string, string?, string>(o.id, releaseAppId, o.speckle_type)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
releaseCommitAppIdDict.Remove(releaseAppId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(
|
// then find unchanged, added, and modified objects by iterating through testing commit app ids
|
||||||
$"Found {releaseCommitObjects.Count()} objects in release version"
|
foreach (string testingAppId in testingCommitAppIdDict.Keys)
|
||||||
);
|
|
||||||
Console.WriteLine($"Found {testCommitObjects.Count()} objects in release version");
|
|
||||||
|
|
||||||
// compare objects
|
|
||||||
int unchangedCount = 0;
|
|
||||||
var addedList = new List<Tuple<string, string>>();
|
|
||||||
var modifiedList = new List<Tuple<string, string, string>>();
|
|
||||||
foreach (Base testObject in testCommitObjects)
|
|
||||||
{
|
{
|
||||||
if (
|
List<Base> testObjects = testingCommitAppIdDict[testingAppId];
|
||||||
testObject.applicationId != null
|
|
||||||
&& releaseCommitObjectsDict.ContainsKey(testObject.applicationId)
|
// test for added objects
|
||||||
)
|
if (!releaseCommitAppIdDict.ContainsKey(testingAppId))
|
||||||
{
|
{
|
||||||
Base releaseObject = releaseCommitObjectsDict[testObject.applicationId];
|
testObjects.ForEach(
|
||||||
|
o =>
|
||||||
|
addedAppIdObjects.Add(
|
||||||
|
new Tuple<string, string?, string>(o.id, testingAppId, o.speckle_type)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<Base> releaseObjects = releaseCommitAppIdDict[testingAppId];
|
||||||
|
|
||||||
// if these have the same hash, no properties have changed
|
// test for unchanged objects
|
||||||
if (testObject.id == releaseObject.id)
|
// by filtering the testing and release objects with matching speckle ids
|
||||||
|
Utils
|
||||||
|
.FilterListsBySpeckleIdMatch(testObjects, releaseObjects)
|
||||||
|
.ForEach(o => unchangedAppIdObjects.Add(o));
|
||||||
|
|
||||||
|
// for remaining objects, determine deleted objects
|
||||||
|
// and then compare them in order (assume modified) and handle leftovers (added)
|
||||||
|
// this is imperfect, as there's a chance we are not comparing the correct objects.
|
||||||
|
if (releaseObjects.Count > testObjects.Count)
|
||||||
{
|
{
|
||||||
unchangedCount++;
|
for (int i = releaseObjects.Count - 1; i >= testObjects.Count; i--)
|
||||||
|
{
|
||||||
|
deletedAppIdObjects.Add(
|
||||||
|
new Tuple<string, string?, string>(
|
||||||
|
releaseObjects[i].id,
|
||||||
|
testingAppId,
|
||||||
|
releaseObjects[i].speckle_type
|
||||||
|
)
|
||||||
|
);
|
||||||
|
releaseObjects.RemoveAt(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if ids are different, find property differences
|
|
||||||
else
|
for (int i = 0; i < testObjects.Count; i++)
|
||||||
{
|
{
|
||||||
var diffDictionary = new Dictionary<string, string>();
|
Base testObject = testObjects[i];
|
||||||
Dictionary<string, object?> releaseObjectPropDict =
|
|
||||||
releaseObject.GetMembers();
|
|
||||||
Dictionary<string, object?> testObjectPropDict = testObject.GetMembers();
|
|
||||||
foreach (var entry in testObjectPropDict)
|
|
||||||
{
|
|
||||||
if (releaseObjectPropDict.ContainsKey(entry.Key))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bool changed = !Equals(entry.Value, releaseObjectPropDict[entry.Key]);
|
|
||||||
if (changed)
|
|
||||||
{
|
|
||||||
object? releaseValue = releaseObjectPropDict[entry.Key];
|
|
||||||
object? testValue = entry.Value;
|
|
||||||
string diff = $"Property ({entry.Key}) changed";
|
|
||||||
if (
|
|
||||||
releaseValue is not null
|
|
||||||
&& testValue is not null
|
|
||||||
&& !releaseValue.GetType().Equals(testValue.GetType())
|
|
||||||
)
|
|
||||||
{
|
|
||||||
diff +=
|
|
||||||
$" from ({releaseObjectPropDict[entry.Key]}) to ({entry.Value})";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!diffDictionary.ContainsKey(entry.Key))
|
if (i < releaseObjectCount)
|
||||||
{
|
|
||||||
diffDictionary.Add(entry.Key, diff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
releaseObjectPropDict.Remove(entry.Key);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!diffDictionary.ContainsKey(entry.Key))
|
|
||||||
{
|
|
||||||
diffDictionary.Add(entry.Key, ADDED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if there are any props left on the release object - these were missing in the test object
|
|
||||||
foreach (var entry in releaseObjectPropDict)
|
|
||||||
{
|
{
|
||||||
if (!diffDictionary.ContainsKey(entry.Key))
|
Base releaseObject = releaseObjects[i];
|
||||||
{
|
|
||||||
diffDictionary.Add(entry.Key, DELETED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the diff dict info to the object
|
// compare object properties to determine changes
|
||||||
if (diffDictionary.Count > 0)
|
List<string> addedProps = new();
|
||||||
{
|
List<string> deletedProps = new();
|
||||||
|
List<Tuple<string, string>> modifiedProps = new();
|
||||||
|
Utils.CompareBaseProperties(
|
||||||
|
testObject,
|
||||||
|
releaseObject,
|
||||||
|
out addedProps,
|
||||||
|
out deletedProps,
|
||||||
|
out modifiedProps
|
||||||
|
);
|
||||||
var sb = new System.Text.StringBuilder();
|
var sb = new System.Text.StringBuilder();
|
||||||
foreach (var entry in diffDictionary)
|
addedProps.ForEach(s => sb.AppendLine($"{ADDED} prop ({s})."));
|
||||||
{
|
deletedProps.ForEach(s => sb.AppendLine($"{DELETED} prop ({s})."));
|
||||||
sb.AppendLine($"{entry.Key}: {entry.Value}. ");
|
modifiedProps.ForEach(
|
||||||
}
|
t => sb.AppendLine($"{MODIFIED} ({t.Item2}) of prop ({t.Item1})")
|
||||||
|
);
|
||||||
modifiedList.Add(
|
modifiedAppIdObjects.Add(
|
||||||
new Tuple<string, string, string>(
|
new Tuple<string, string?, string, string>(
|
||||||
testObject.id,
|
testObject.id,
|
||||||
|
testObject.applicationId,
|
||||||
testObject.speckle_type,
|
testObject.speckle_type,
|
||||||
sb.ToString()
|
sb.ToString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
//automationContext.AttachWarningToObjects( MODIFIED, new List<string>() { testObject.id }, sb.ToString());
|
}
|
||||||
|
// remaining test objects are considered added
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addedAppIdObjects.Add(
|
||||||
|
new Tuple<string, string?, string>(
|
||||||
|
testObject.id,
|
||||||
|
testingAppId,
|
||||||
|
testObject.speckle_type
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
releaseCommitObjectsDict.Remove(testObject.applicationId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// we're skipping objects without an applicationId for now, since we're doing so in the release commit
|
|
||||||
if (!string.IsNullOrEmpty(testObject.applicationId))
|
|
||||||
{
|
|
||||||
//automationContext.AttachInfoToObjects(ADDED, new List<string>() { testObject.id });
|
|
||||||
addedList.Add(
|
|
||||||
new Tuple<string, string>(testObject.id, testObject.speckle_type)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there are any remaining release commit objects, this indicates missing objects in the test run.
|
// COMPARE COMMIT OBJECTS WITHOUT APPLICATION IDS USING SPECKLE IDS
|
||||||
var deletedList = new List<Tuple<string, string>>();
|
// since we only have 1 parameter of comparison, we can rule out any matching speckle ids as unchanged.
|
||||||
foreach (var entry in releaseCommitObjectsDict)
|
// for all other objects, mark as modified, and indicate quantity of any added or deleted objects
|
||||||
|
// store modified in hash lists of (id, type)
|
||||||
|
HashSet<Tuple<string, string?, string>> unchangedSpeckleIdObjects = new();
|
||||||
|
HashSet<Tuple<string, string>> changedSpeckleIdObjects = new();
|
||||||
|
|
||||||
|
// first filter out matching speckle ids
|
||||||
|
List<Base> flattenedTestingSpeckleIdDict = testingCommitSpeckleIdDict.Values
|
||||||
|
.SelectMany(o => o)
|
||||||
|
.ToList();
|
||||||
|
List<Base> flattenedReleaseSpeckleIdDict = releaseCommitSpeckleIdDict.Values
|
||||||
|
.SelectMany(o => o)
|
||||||
|
.ToList();
|
||||||
|
Utils
|
||||||
|
.FilterListsBySpeckleIdMatch(
|
||||||
|
flattenedTestingSpeckleIdDict,
|
||||||
|
flattenedReleaseSpeckleIdDict
|
||||||
|
)
|
||||||
|
.ForEach(o => unchangedSpeckleIdObjects.Add(o));
|
||||||
|
|
||||||
|
// then store all remaining testing objects as changed
|
||||||
|
flattenedTestingSpeckleIdDict.ForEach(
|
||||||
|
o => changedSpeckleIdObjects.Add(new Tuple<string, string>(o.id, o.speckle_type))
|
||||||
|
);
|
||||||
|
|
||||||
|
// calculate count difference
|
||||||
|
int speckleIdObjectCountDifference =
|
||||||
|
flattenedTestingSpeckleIdDict.Count - flattenedReleaseSpeckleIdDict.Count;
|
||||||
|
|
||||||
|
// REPORT ALL DIFF RESULTS FOR APP IDS AND SPECKLE IDS
|
||||||
|
// mark run succeeded if there are no added, modified, or deleted app id objects, and no changed speckle id objects
|
||||||
|
// mark run failed otherwise
|
||||||
|
if (
|
||||||
|
addedAppIdObjects.Count
|
||||||
|
+ deletedAppIdObjects.Count
|
||||||
|
+ modifiedAppIdObjects.Count
|
||||||
|
+ changedSpeckleIdObjects.Count
|
||||||
|
== 0
|
||||||
|
)
|
||||||
{
|
{
|
||||||
deletedList.Add(new Tuple<string, string>(entry.Key, entry.Value.speckle_type));
|
automationContext.MarkRunSuccess($"Run passed with no changes to objects.");
|
||||||
}
|
|
||||||
|
|
||||||
// mark run failed if there are any added, modified, or deleted objects and report
|
|
||||||
if (addedList.Count + deletedList.Count + modifiedList.Count > 0)
|
|
||||||
{
|
|
||||||
automationContext.MarkRunFailed(
|
|
||||||
$"Run failed due to {addedList.Count} ADDED, {modifiedList.Count} MODIFIED, and {deletedList.Count} DELETED objects compared to the release commit ({unchangedCount} objects were unchanged)."
|
|
||||||
);
|
|
||||||
|
|
||||||
addedList.ForEach(
|
|
||||||
o => Console.WriteLine($"ADDED object: id( {o.Item1} ), type( {o.Item2} )")
|
|
||||||
);
|
|
||||||
deletedList.ForEach(
|
|
||||||
o => Console.WriteLine($"DELETED object: id( {o.Item1} ), type( {o.Item2} )")
|
|
||||||
);
|
|
||||||
modifiedList.ForEach(
|
|
||||||
o =>
|
|
||||||
Console.WriteLine(
|
|
||||||
$"MODIFIED object: id( {o.Item1} ), type( {o.Item2} ), changed props:( {o.Item3} )"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
automationContext.AttachWarningToObjects(
|
|
||||||
MODIFIED,
|
|
||||||
modifiedList.Select(o => o.Item1).ToList()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
automationContext.MarkRunSuccess(
|
automationContext.MarkRunFailed(
|
||||||
$"Run passed with {unchangedCount} unchanged objects."
|
$"Run failed due to: {addedAppIdObjects.Count} {ADDED}, {modifiedAppIdObjects.Count} {MODIFIED}, and {deletedAppIdObjects.Count} {DELETED} objects WITH APP IDS, and {(speckleIdObjectCountDifference > 0 ? $"{speckleIdObjectCountDifference} {ADDED}" : $"{Math.Abs(speckleIdObjectCountDifference)} {DELETED}")} and {changedSpeckleIdObjects.Count} CHANGED objects WITHOUT APP IDS compared to the release commit. "
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Equals<T>(T a, T b)
|
foreach (var added in addedAppIdObjects)
|
||||||
{
|
{
|
||||||
switch (a)
|
Console.WriteLine(
|
||||||
{
|
$"{ADDED} {added.Item3} object: id( {added.Item1} ), appId: {added.Item2}"
|
||||||
case Base o:
|
);
|
||||||
return b is Base bBase ? o.id == bBase.id : false;
|
}
|
||||||
case List<object> aList:
|
|
||||||
if (b is List<object> bList && aList.Count == bList.Count)
|
if (addedAppIdObjects.Count > 0)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < aList.Count; i++)
|
automationContext.AttachErrorToObjects(
|
||||||
{
|
"ADDED",
|
||||||
if (!Equals(aList[i], bList[i]))
|
addedAppIdObjects.Select(o => o.Item1),
|
||||||
{
|
"added objects with an application Id"
|
||||||
return false;
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true;
|
foreach (var deleted in deletedAppIdObjects)
|
||||||
}
|
{
|
||||||
return false;
|
Console.WriteLine(
|
||||||
case Dictionary<string, object> aDictionary:
|
$"{DELETED} {deleted.Item3} object: id( {deleted.Item1} ), appId: {deleted.Item2}"
|
||||||
if (
|
);
|
||||||
b is Dictionary<string, object> bDictionary
|
}
|
||||||
&& aDictionary.Count == bDictionary.Count
|
|
||||||
)
|
foreach (var modified in modifiedAppIdObjects)
|
||||||
{
|
{
|
||||||
foreach (var entry in aDictionary)
|
Console.WriteLine(
|
||||||
{
|
$"{MODIFIED} {modified.Item3} object: id( {modified.Item1} ), appId: {modified.Item2}, category: {modified.Item4}"
|
||||||
if (
|
);
|
||||||
!bDictionary.ContainsKey(entry.Key)
|
}
|
||||||
|| !Equals(entry.Value, bDictionary[entry.Key])
|
|
||||||
)
|
if (modifiedAppIdObjects.Count > 0)
|
||||||
{
|
{
|
||||||
return false;
|
automationContext.AttachErrorToObjects(
|
||||||
}
|
"MODIFIED",
|
||||||
}
|
modifiedAppIdObjects.Select(o => o.Item1),
|
||||||
return true;
|
"modified objects with an application Id"
|
||||||
}
|
);
|
||||||
return false;
|
}
|
||||||
default:
|
|
||||||
return EqualityComparer<T>.Default.Equals(a, b);
|
foreach (var changed in changedSpeckleIdObjects)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"CHANGED {changed.Item2} object: id( {changed.Item1} )");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedSpeckleIdObjects.Count > 0)
|
||||||
|
{
|
||||||
|
automationContext.AttachErrorToObjects(
|
||||||
|
"CHANGED",
|
||||||
|
changedSpeckleIdObjects.Select(o => o.Item1),
|
||||||
|
"changed objects with no application Id"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// This class is used to generate a JSON Schema to ensure that the user provided values
|
/// This class is used to generate a JSON Schema to ensure that the user provided values
|
||||||
/// are valid and match the required schema.
|
/// are valid and match the required schema.
|
||||||
struct FunctionInputs
|
public struct FunctionInputs
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the branch to compare against. This should be the full branch path.
|
||||||
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
public double Tolerance;
|
public string DiffBranch;
|
||||||
|
|
||||||
//public string Exclusions;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
using Speckle.Automate.Sdk;
|
||||||
|
using Speckle.Automate.Sdk.Schema;
|
||||||
|
using Speckle.Core.Api;
|
||||||
|
using Speckle.Core.Models;
|
||||||
|
using Speckle.Core.Models.Extensions;
|
||||||
|
using Speckle.Core.Transports;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SpeckleAutomateDotnetExample
|
||||||
|
{
|
||||||
|
internal class Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Receives a commit object from the same project and account as the <paramref name="context"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="commitId"> The id of the commit to receive</param>
|
||||||
|
/// <param name="context">The Automation context</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public static async Task<Base> RecieveVersionAsync(
|
||||||
|
string commitId,
|
||||||
|
AutomationContext context
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ServerTransport serverTransport = new ServerTransport(
|
||||||
|
context.SpeckleClient.Account,
|
||||||
|
context.AutomationRunData.ProjectId
|
||||||
|
);
|
||||||
|
|
||||||
|
Base? receivedCommitObject = await Operations
|
||||||
|
.Receive(
|
||||||
|
(
|
||||||
|
await context.SpeckleClient
|
||||||
|
.CommitGet(context.AutomationRunData.ProjectId, commitId)
|
||||||
|
.ConfigureAwait(continueOnCapturedContext: false)
|
||||||
|
).referencedObject,
|
||||||
|
serverTransport,
|
||||||
|
new MemoryTransport()
|
||||||
|
)
|
||||||
|
.ConfigureAwait(continueOnCapturedContext: false);
|
||||||
|
|
||||||
|
if (receivedCommitObject == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Commit root object was null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return receivedCommitObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates Dictionaries from a commit Base, using applicationId if available or speckle id if no applicationId exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="commit">The commit Base to create dictionaries from</param>
|
||||||
|
/// <param name="appIdDict">The dictionary of commit objects sorted by applicationId</param>
|
||||||
|
/// <param name="speckleIdDict">The dictionary of commit objects without applicationIds, sorted by speckle id</param>
|
||||||
|
public static void CreateDictionaryFromBaseById(
|
||||||
|
Base commit,
|
||||||
|
out Dictionary<string, List<Base>> appIdDict,
|
||||||
|
out Dictionary<string, List<Base>> speckleIdDict,
|
||||||
|
out int objectCount
|
||||||
|
)
|
||||||
|
{
|
||||||
|
IEnumerable<Base> commitObjects = commit.Flatten();
|
||||||
|
objectCount = commitObjects.Count();
|
||||||
|
|
||||||
|
appIdDict = new Dictionary<string, List<Base>>();
|
||||||
|
speckleIdDict = new Dictionary<string, List<Base>>();
|
||||||
|
foreach (var commitObject in commitObjects)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(commitObject.applicationId))
|
||||||
|
{
|
||||||
|
if (appIdDict.ContainsKey(commitObject.applicationId))
|
||||||
|
{
|
||||||
|
appIdDict[commitObject.applicationId].Add(commitObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appIdDict.Add(
|
||||||
|
commitObject.applicationId,
|
||||||
|
new List<Base>() { commitObject }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrWhiteSpace(commitObject.id))
|
||||||
|
{
|
||||||
|
if (speckleIdDict.ContainsKey(commitObject.id))
|
||||||
|
{
|
||||||
|
speckleIdDict[commitObject.id].Add(commitObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
speckleIdDict.Add(commitObject.id, new List<Base>() { commitObject });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Filters two lists by removing all objects from <paramref name="setA"/> with a matching speckle id in <paramref name="setB"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setA">The list to filter</param>
|
||||||
|
/// <param name="setB">The list to filter against</param>
|
||||||
|
/// <returns>A list of matches found in <paramref name="setA"/></returns>
|
||||||
|
public static List<Tuple<string, string?, string>> FilterListsBySpeckleIdMatch(
|
||||||
|
List<Base> setA,
|
||||||
|
List<Base> setB
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var matches = new List<Tuple<string, string?, string>>();
|
||||||
|
|
||||||
|
for (int i = setA.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var testObject = setA[i];
|
||||||
|
for (int j = setB.Count - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
var releaseObject = setB[j];
|
||||||
|
|
||||||
|
// if a match was found, remove from both lists and add to matches
|
||||||
|
if (testObject.id == releaseObject.id)
|
||||||
|
{
|
||||||
|
matches.Add(
|
||||||
|
new Tuple<string, string?, string>(
|
||||||
|
testObject.id,
|
||||||
|
testObject.applicationId,
|
||||||
|
testObject.speckle_type
|
||||||
|
)
|
||||||
|
);
|
||||||
|
setA.RemoveAt(i);
|
||||||
|
setB.RemoveAt(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compares the properties of <paramref name="a"/> against <paramref name="b"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="a"></param>
|
||||||
|
/// <param name="b"></param>
|
||||||
|
/// <param name="addedProps">Properties on <paramref name="a"/> that do not exist on <paramref name="b"/></param>
|
||||||
|
/// <param name="deletedProps">Properties on <paramref name="b"/> that do not exist on <paramref name="a"/></param>
|
||||||
|
/// <param name="modifiedProps">Properties on <paramref name="a"/> that have a different value on <paramref name="b"/>. The second string is the category of modification (eg count/primitive/base)</param>
|
||||||
|
public static void CompareBaseProperties(
|
||||||
|
Base a,
|
||||||
|
Base b,
|
||||||
|
out List<string> addedProps,
|
||||||
|
out List<string> deletedProps,
|
||||||
|
out List<Tuple<string, string>> modifiedProps
|
||||||
|
)
|
||||||
|
{
|
||||||
|
addedProps = new List<string>();
|
||||||
|
deletedProps = new List<string>();
|
||||||
|
modifiedProps = new List<Tuple<string, string>>();
|
||||||
|
Dictionary<string, object?> bObjectPropDict = b.GetMembers();
|
||||||
|
Dictionary<string, object?> aObjectPropDict = a.GetMembers();
|
||||||
|
foreach (KeyValuePair<string, object?> entry in aObjectPropDict)
|
||||||
|
{
|
||||||
|
if (bObjectPropDict.ContainsKey(entry.Key))
|
||||||
|
{
|
||||||
|
bool changed = !IsEqual(
|
||||||
|
entry.Value,
|
||||||
|
bObjectPropDict[entry.Key],
|
||||||
|
out string category
|
||||||
|
);
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
modifiedProps.Add(new Tuple<string, string>(entry.Key, category));
|
||||||
|
}
|
||||||
|
|
||||||
|
bObjectPropDict.Remove(entry.Key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addedProps.Add(entry.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if there are any props left on the b - these were missing in the a
|
||||||
|
foreach (var entry in bObjectPropDict)
|
||||||
|
{
|
||||||
|
deletedProps.Add(entry.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines equality between two objects
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="a"></param>
|
||||||
|
/// <param name="b"></param>
|
||||||
|
/// <param name="category">The category of the modification, if objects are not equal</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsEqual<T>(T a, T b, out string category)
|
||||||
|
{
|
||||||
|
category = "Value";
|
||||||
|
switch (a)
|
||||||
|
{
|
||||||
|
case Base aBase:
|
||||||
|
if (b is Base bBase && bBase.speckle_type == aBase.speckle_type)
|
||||||
|
{
|
||||||
|
return aBase.id == bBase.id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
category = "Type";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
case List<object> aList:
|
||||||
|
if (b is List<object> bList)
|
||||||
|
{
|
||||||
|
if (aList.Count != bList.Count)
|
||||||
|
{
|
||||||
|
category = "Count";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < aList.Count; i++)
|
||||||
|
{
|
||||||
|
if (!Equals(aList[i], bList[i]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
category = "Type";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Dictionary<string, object> aDictionary:
|
||||||
|
if (b is Dictionary<string, object> bDictionary)
|
||||||
|
{
|
||||||
|
if (aDictionary.Count != bDictionary.Count)
|
||||||
|
{
|
||||||
|
category = "Count";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach (var entry in aDictionary)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!bDictionary.ContainsKey(entry.Key)
|
||||||
|
|| !Equals(entry.Value, bDictionary[entry.Key])
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
category = "Type";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return EqualityComparer<T>.Default.Equals(a, b);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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> AutomationRunData(
|
||||||
|
Base testObject,
|
||||||
|
Base releaseObject
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// send the release commit to the release branch
|
||||||
|
string releaseObjId = await Operations.Send(
|
||||||
|
releaseObject,
|
||||||
|
new List<ITransport> { 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<ITransport> { 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"TestAutomateFunction": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"environmentVariables": {
|
||||||
|
"SPECKLE_TOKEN": "718d0650eea8038642850414e43bd81314d740359a",
|
||||||
|
"SPECKLE_SERVER_URL": "https://latest.speckle.dev/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
|
<UserSecretsId>0b5cf4dd-cfa3-4982-9722-ca6c6672b8dd</UserSecretsId>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||||
|
<PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
|
||||||
|
<PackageReference Include="coverlet.collector" Version="3.1.2" />
|
||||||
|
<PackageReference Include="Speckle.Automate.Sdk" Version="2.18.0-fileInput" />
|
||||||
|
<PackageReference Include="Speckle.Objects" Version="2.18.0-fileInput" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SpeckleAutomateDotnetExample\SpeckleAutomateDotnetExample.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The test object to compare against the release object
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <remarks>Contains 3 children objects to capture the Added, Deleted, Modified, and Unchanged categories</remarks>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The release object to compare the test object against
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Contains 3 children objects to capture the Added, Deleted, Modified, and Unchanged categories</returns>
|
||||||
|
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<object>(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
global using NUnit.Framework;
|
||||||
Reference in New Issue
Block a user