Merge branch 'dev' into bjorn/cnx-2722-grasshopper-root-collection-props
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup Condition="'$(IsTestProject)' == 'true'">
|
||||
<NoWarn>
|
||||
<!-- Things we need to test -->
|
||||
CS0618;CA1034;CA2201;CA1051;CA1040;CA1724;
|
||||
CS0618;CA1034;CA2201;CA1051;CA1040;CA1724;CA1065;
|
||||
IDE0044;IDE0130;CA1508;
|
||||
<!-- Analysers that provide no tangeable value to a test project -->
|
||||
CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618;CA1054;CA1810;CA2208;CA1019;CA1831;
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Proxies;
|
||||
|
||||
namespace Speckle.Objects.Other;
|
||||
|
||||
/// <summary>
|
||||
/// Proxy for 3D views.
|
||||
/// </summary>
|
||||
/// <remarks>The <see cref="objects"/> list points to the applicationIds of any atomic objects that are visible in this view. An empty objects list indicates that all objects by default are visible.</remarks>
|
||||
[SpeckleType("Objects.Other.ViewProxy")]
|
||||
public class ViewProxy : Base, IProxyCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of application ids of objects that belong to this view
|
||||
/// </summary>
|
||||
public required List<string> objects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The camera used for this view
|
||||
/// </summary>
|
||||
public required Camera value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of this view
|
||||
/// </summary>
|
||||
public required string name { get; set; }
|
||||
}
|
||||
@@ -23,9 +23,10 @@ public class SpeckleGraphQLException : SpeckleException
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a "FORBIDDEN" or "UNAUTHORIZED" GraphQL error as an exception.
|
||||
/// Represents a "FORBIDDEN" or "UNAUTHORIZED" or "UNAUTHORIZED_ACCESS_ERROR" GraphQL error as an exception.
|
||||
/// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#unauthenticated
|
||||
/// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#forbidden
|
||||
/// https://github.com/specklesystems/speckle-server/blob/v2.23.18/packages/server/modules/shared/errors/index.ts#L34
|
||||
/// </summary>
|
||||
public sealed class SpeckleGraphQLForbiddenException : SpeckleGraphQLException
|
||||
{
|
||||
|
||||
@@ -28,7 +28,9 @@ internal static class GraphQLErrorHandler
|
||||
var ex = code switch
|
||||
{
|
||||
"GRAPHQL_PARSE_FAILED" or "GRAPHQL_VALIDATION_FAILED" => new SpeckleGraphQLInvalidQueryException(message),
|
||||
"FORBIDDEN" or "UNAUTHENTICATED" => new SpeckleGraphQLForbiddenException(message),
|
||||
"FORBIDDEN" or "UNAUTHENTICATED" or "UNAUTHORIZED_ACCESS_ERROR" => new SpeckleGraphQLForbiddenException(
|
||||
message
|
||||
),
|
||||
"STREAM_NOT_FOUND" => new SpeckleGraphQLStreamNotFoundException(message),
|
||||
"BAD_USER_INPUT" => new SpeckleGraphQLBadInputException(message),
|
||||
"INTERNAL_SERVER_ERROR" => new SpeckleGraphQLInternalErrorException(message),
|
||||
|
||||
@@ -14,7 +14,7 @@ public sealed class Comment
|
||||
public string rawText { get; init; }
|
||||
public ResourceCollection<Comment> replies { get; init; }
|
||||
public CommentReplyAuthorCollection replyAuthors { get; init; }
|
||||
public List<ResourceIdentifier> resources { get; init; }
|
||||
public List<ResourceIdentifier> resources { get; init; } //todo: add resourceIds/baseResourceIds
|
||||
public string? screenshot { get; init; }
|
||||
public DateTime updatedAt { get; init; }
|
||||
public DateTime? viewedAt { get; init; }
|
||||
|
||||
@@ -16,6 +16,7 @@ public partial class Operations
|
||||
/// <exception cref="ArgumentException">No transports were specified</exception>
|
||||
/// <exception cref="ArgumentNullException">The <paramref name="objectId"/> was <see langword="null"/></exception>
|
||||
/// <exception cref="SpeckleException">Serialization or Send operation was unsuccessful</exception>
|
||||
/// <exception cref="HttpRequestException">HTTP layer errors</exception>
|
||||
/// <exception cref="OperationCanceledException">The <paramref name="cancellationToken"/> requested cancellation</exception>
|
||||
public async Task<Base> Receive2(
|
||||
Uri url,
|
||||
|
||||
@@ -10,12 +10,11 @@ using Speckle.Sdk.Transports;
|
||||
namespace Speckle.Sdk.Serialisation.V2.Receive;
|
||||
|
||||
public record DeserializeProcessOptions(
|
||||
bool SkipCache = false,
|
||||
bool SkipCache = false, //TODO: This appears to be bugged when set to `true`, `LoadId` depends on sqlite
|
||||
bool ThrowOnMissingReferences = true,
|
||||
bool SkipInvalidConverts = false,
|
||||
int? MaxParallelism = null,
|
||||
bool SkipServer = false,
|
||||
string? AttributeMask = null
|
||||
bool SkipServer = false
|
||||
);
|
||||
|
||||
public partial interface IDeserializeProcess : IAsyncDisposable;
|
||||
@@ -45,7 +44,6 @@ public sealed class DeserializeProcess(
|
||||
new ObjectLoader(
|
||||
sqLiteJsonCacheManager,
|
||||
serverObjectManager,
|
||||
options?.AttributeMask,
|
||||
progress,
|
||||
loggerFactory.CreateLogger<ObjectLoader>(),
|
||||
cancellationToken
|
||||
|
||||
@@ -16,13 +16,10 @@ public partial interface IObjectLoader : IDisposable;
|
||||
public sealed class ObjectLoader(
|
||||
ISqLiteJsonCacheManager sqLiteJsonCacheManager,
|
||||
IServerObjectManager serverObjectManager,
|
||||
string? attributeMask,
|
||||
IProgress<ProgressArgs>? progress,
|
||||
ILogger<ObjectLoader> logger,
|
||||
CancellationToken cancellationToken
|
||||
#pragma warning disable CS9107 // Parameter is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.
|
||||
) : ChannelLoader<BaseItem>(cancellationToken), IObjectLoader
|
||||
#pragma warning restore CS9107 // Parameter is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.
|
||||
{
|
||||
private int? _allChildrenCount;
|
||||
private long _checkCache;
|
||||
@@ -30,6 +27,7 @@ public sealed class ObjectLoader(
|
||||
private long _downloaded;
|
||||
private long _totalToDownload;
|
||||
private DeserializeProcessOptions _options = new();
|
||||
private readonly CancellationToken _cancellationToken = cancellationToken;
|
||||
|
||||
[AutoInterfaceIgnore]
|
||||
public void Dispose() => sqLiteJsonCacheManager.Dispose();
|
||||
@@ -47,7 +45,7 @@ public sealed class ObjectLoader(
|
||||
{
|
||||
//assume everything exists as the root is there.
|
||||
var allChildren = ClosureParser
|
||||
.GetClosuresSorted(rootJson, cancellationToken)
|
||||
.GetClosuresSorted(rootJson, _cancellationToken)
|
||||
.Select(x => new Id(x.Item1))
|
||||
.ToList();
|
||||
//this probably yields away from the Main thread to let host apps update progress
|
||||
@@ -60,11 +58,11 @@ public sealed class ObjectLoader(
|
||||
if (!options.SkipServer)
|
||||
{
|
||||
rootJson = await serverObjectManager
|
||||
.DownloadSingleObject(rootId, progress, cancellationToken)
|
||||
.DownloadSingleObject(rootId, progress, _cancellationToken)
|
||||
.NotNull()
|
||||
.ConfigureAwait(false);
|
||||
IReadOnlyCollection<Id> allChildrenIds = ClosureParser
|
||||
.GetClosures(rootJson, cancellationToken)
|
||||
.GetClosures(rootJson, _cancellationToken)
|
||||
.OrderByDescending(x => x.Item2)
|
||||
.Select(x => new Id(x.Item1))
|
||||
.Where(x => !x.Value.StartsWith("blob", StringComparison.Ordinal))
|
||||
@@ -112,13 +110,13 @@ public sealed class ObjectLoader(
|
||||
await foreach (
|
||||
var (id, json) in serverObjectManager.DownloadObjects(
|
||||
ids.Select(x => x.NotNull()).ToList(),
|
||||
attributeMask,
|
||||
null, //TODO: Implement attribute masking in a safe way that will not poison SQLite DB.
|
||||
progress,
|
||||
cancellationToken
|
||||
_cancellationToken
|
||||
)
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
_cancellationToken.ThrowIfCancellationRequested();
|
||||
Interlocked.Increment(ref _downloaded);
|
||||
progress?.Report(new(ProgressEvent.DownloadObjects, _downloaded, _totalToDownload));
|
||||
toCache.Add(new(new(id), new(json), true, null));
|
||||
@@ -140,7 +138,7 @@ public sealed class ObjectLoader(
|
||||
{
|
||||
if (!_options.SkipCache)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
_cancellationToken.ThrowIfCancellationRequested();
|
||||
sqLiteJsonCacheManager.SaveObjects(batch.Select(x => (x.Id.Value, x.Json.Value)));
|
||||
Interlocked.Exchange(ref _cached, _cached + batch.Count);
|
||||
progress?.Report(new(ProgressEvent.CachedToLocal, _cached, _allChildrenCount));
|
||||
@@ -170,7 +168,7 @@ public sealed class ObjectLoader(
|
||||
private void ThrowIfFailed()
|
||||
{
|
||||
//always check for cancellation first
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
_cancellationToken.ThrowIfCancellationRequested();
|
||||
if (Exception is not null)
|
||||
{
|
||||
throw new SpeckleException($"Error while loading: {Exception.Message}", Exception);
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="altcover" />
|
||||
<PackageReference Include="AwesomeAssertions" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit.assert" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Speckle.Sdk.Testing\Speckle.Sdk.Testing.csproj" />
|
||||
<ProjectReference Include="..\..\src\Speckle.Automate.Sdk\Speckle.Automate.Sdk.csproj" />
|
||||
|
||||
@@ -25,7 +25,9 @@ public sealed class AutomationContextTest : IAsyncLifetime
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
var serviceProvider = TestServiceSetup.GetServiceProvider();
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddAutomateSdk();
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
_account = await Fixtures.SeedUser().ConfigureAwait(false);
|
||||
_client = serviceProvider.GetRequiredService<IClientFactory>().Create(_account);
|
||||
_runner = serviceProvider.GetRequiredService<IAutomationRunner>();
|
||||
@@ -42,7 +44,7 @@ public sealed class AutomationContextTest : IAsyncLifetime
|
||||
private async Task<AutomationRunData> AutomationRunData(Base testObject)
|
||||
{
|
||||
Project project = await _client.Project.Create(new("Automate function e2e test", null, ProjectVisibility.Public));
|
||||
const string BRANCH_NAME = "main";
|
||||
const string BRANCH_NAME = "Trigger";
|
||||
|
||||
var model = await _client.Model.Create(new(BRANCH_NAME, null, project.id));
|
||||
string modelId = model.id;
|
||||
|
||||
@@ -2,6 +2,28 @@
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
"net8.0": {
|
||||
"altcover": {
|
||||
"type": "Direct",
|
||||
"requested": "[9.0.1, )",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA=="
|
||||
},
|
||||
"AwesomeAssertions": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.1.0, )",
|
||||
"resolved": "8.1.0",
|
||||
"contentHash": "IfNC4cpXPi9tclWvuNO9lfkuIxJsUTLTS1NXto55jDrAUQJYl0zLI9ByISrfkbBE2Xtg+IWaAXQ6jnUx3anDuw=="
|
||||
},
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"type": "Direct",
|
||||
"requested": "[17.13.0, )",
|
||||
"resolved": "17.13.0",
|
||||
"contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeCoverage": "17.13.0",
|
||||
"Microsoft.TestPlatform.TestHost": "17.13.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
@@ -24,6 +46,18 @@
|
||||
"resolved": "0.9.6",
|
||||
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
|
||||
},
|
||||
"xunit.assert": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.9.3, )",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
|
||||
},
|
||||
"xunit.runner.visualstudio": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.2, )",
|
||||
"resolved": "3.0.2",
|
||||
"contentHash": "oXbusR6iPq0xlqoikjdLvzh+wQDkMv9If58myz9MEzldS4nIcp442Btgs2sWbYWV+caEluMe2pQCZ0hUZgPiow=="
|
||||
},
|
||||
"Argon": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.28.0",
|
||||
@@ -364,18 +398,6 @@
|
||||
"xunit.runner.visualstudio": "[3.0.2, )"
|
||||
}
|
||||
},
|
||||
"altcover": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[9.0.1, )",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA=="
|
||||
},
|
||||
"AwesomeAssertions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.1.0, )",
|
||||
"resolved": "8.1.0",
|
||||
"contentHash": "IfNC4cpXPi9tclWvuNO9lfkuIxJsUTLTS1NXto55jDrAUQJYl0zLI9ByISrfkbBE2Xtg+IWaAXQ6jnUx3anDuw=="
|
||||
},
|
||||
"GraphQL.Client": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[6.0.0, )",
|
||||
@@ -424,16 +446,6 @@
|
||||
"Microsoft.Extensions.Options": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[17.13.0, )",
|
||||
"resolved": "17.13.0",
|
||||
"contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeCoverage": "17.13.0",
|
||||
"Microsoft.TestPlatform.TestHost": "17.13.0"
|
||||
}
|
||||
},
|
||||
"Moq": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[4.20.72, )",
|
||||
@@ -515,18 +527,6 @@
|
||||
"xunit.assert": "2.9.3",
|
||||
"xunit.core": "[2.9.3]"
|
||||
}
|
||||
},
|
||||
"xunit.assert": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.9.3, )",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
|
||||
},
|
||||
"xunit.runner.visualstudio": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.0.2, )",
|
||||
"resolved": "3.0.2",
|
||||
"contentHash": "oXbusR6iPq0xlqoikjdLvzh+wQDkMv9If58myz9MEzldS4nIcp442Btgs2sWbYWV+caEluMe2pQCZ0hUZgPiow=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,16 +112,15 @@ public class ExceptionTests
|
||||
new DummySqLiteReceiveManager(new Dictionary<string, string>()),
|
||||
new ExceptionServerObjectManager(),
|
||||
null,
|
||||
null,
|
||||
new NullLogger<ObjectLoader>(),
|
||||
default
|
||||
CancellationToken.None
|
||||
);
|
||||
await using var process = new DeserializeProcess(
|
||||
o,
|
||||
null,
|
||||
new BaseDeserializer(new ObjectDeserializerFactory()),
|
||||
new NullLoggerFactory(),
|
||||
default,
|
||||
CancellationToken.None,
|
||||
new(SkipCache: true, MaxParallelism: 1, SkipServer: true)
|
||||
);
|
||||
|
||||
@@ -145,7 +144,7 @@ public class ExceptionTests
|
||||
null,
|
||||
new BaseDeserializer(new ObjectDeserializerFactory()),
|
||||
new NullLoggerFactory(),
|
||||
default,
|
||||
CancellationToken.None,
|
||||
new(true, MaxParallelism: 1)
|
||||
);
|
||||
|
||||
@@ -170,7 +169,7 @@ public class ExceptionTests
|
||||
null,
|
||||
new BaseDeserializer(new ObjectDeserializerFactory()),
|
||||
new NullLoggerFactory(),
|
||||
default,
|
||||
CancellationToken.None,
|
||||
new(MaxParallelism: 1)
|
||||
);
|
||||
|
||||
@@ -195,9 +194,7 @@ public class ExceptionTests
|
||||
[SpeckleType("Objects.Geometry.BadBase")]
|
||||
public class BadBase : Base
|
||||
{
|
||||
#pragma warning disable CA1065
|
||||
public string BadProp => throw new NotImplementedException();
|
||||
#pragma warning restore CA1065
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -202,9 +202,8 @@ public class SerializationTests
|
||||
new DummySqLiteReceiveManager(closures),
|
||||
new DummyReceiveServerObjectManager(closures),
|
||||
null,
|
||||
null,
|
||||
new NullLogger<ObjectLoader>(),
|
||||
default
|
||||
CancellationToken.None
|
||||
)
|
||||
)
|
||||
{
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ public class ProjectResourceExceptionalTests : IAsyncLifetime
|
||||
new(_testProject.id, "My new name", ProjectVisibility.Public, "NonExistentWorkspace")
|
||||
)
|
||||
);
|
||||
ex.InnerExceptions.Single().Should().BeOfType<SpeckleGraphQLException>();
|
||||
ex.InnerExceptions.Single().Should().BeOfType<SpeckleGraphQLForbiddenException>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Data": {},
|
||||
"Message": "Response status code does not indicate success: 404 (Not Found).",
|
||||
"StatusCode": "NotFound",
|
||||
"Type": "HttpRequestException"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Data": {},
|
||||
"Message": "Response status code does not indicate success: 404 (Not Found).",
|
||||
"StatusCode": "NotFound",
|
||||
"Type": "HttpRequestException"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"ConvertedReferences": {},
|
||||
"RootId": "5313a8f61e1fa7abe9bf716ddfc767bd"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"Data": {},
|
||||
"InnerException": {
|
||||
"$type": "SpeckleSerializeException",
|
||||
"Data": {},
|
||||
"InnerException": {
|
||||
"$type": "ArgumentException",
|
||||
"Data": {},
|
||||
"Message": "Unsupported value in serialization: System.Text.StringBuilder",
|
||||
"ParamName": "obj",
|
||||
"Type": "ArgumentException"
|
||||
},
|
||||
"Message": "Failed to extract (pre-serialize) properties from the Speckle.Sdk.Models.Base",
|
||||
"Type": "SpeckleSerializeException"
|
||||
},
|
||||
"Message": "Error while sending: Failed to extract (pre-serialize) properties from the Speckle.Sdk.Models.Base",
|
||||
"Type": "SpeckleException"
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Enums;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration;
|
||||
|
||||
public sealed class SendReceiveTests : IAsyncLifetime
|
||||
{
|
||||
private Project _project;
|
||||
private IClient _client;
|
||||
private IOperations _operations;
|
||||
private const string NON_EXISTENT_OBJECT_ID = "0a480dfb7aa774f19a82bee9d6320abd";
|
||||
private const string NON_EXISTENT_PROJECT_ID = "8cdc651d13";
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, Assembly.GetExecutingAssembly());
|
||||
var serviceProvider = TestServiceSetup.GetServiceProvider();
|
||||
_operations = serviceProvider.GetRequiredService<IOperations>();
|
||||
ClearCache();
|
||||
|
||||
_client = await Fixtures.SeedUserWithClient();
|
||||
_project = await _client.Project.Create(new("Blobber", "Flobber", ProjectVisibility.Private));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendAndReceive()
|
||||
{
|
||||
var myObject = Fixtures.GenerateNestedObject();
|
||||
string expectedId = myObject.GetId(true);
|
||||
|
||||
//SEND
|
||||
var fistSend = await _operations.Send2(
|
||||
_client.ServerUrl,
|
||||
_project.id,
|
||||
_client.Account.token,
|
||||
myObject,
|
||||
null,
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
Assert.Equal(expectedId, fistSend.RootId);
|
||||
await Verify(fistSend);
|
||||
|
||||
//RECEIVE
|
||||
var received = await _operations.Receive2(
|
||||
_client.ServerUrl,
|
||||
_project.id,
|
||||
fistSend.RootId,
|
||||
_client.Account.token,
|
||||
null,
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
Assert.Equal(expectedId, received.id);
|
||||
|
||||
//SEND AGAIN!
|
||||
var secondSend = await _operations.Send2(
|
||||
_client.ServerUrl,
|
||||
_project.id,
|
||||
_client.Account.token,
|
||||
received,
|
||||
null,
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
Assert.Equal(expectedId, secondSend.RootId);
|
||||
|
||||
//RECEIVE AGAIN, but using cache
|
||||
ClearCache();
|
||||
var secondReceive = await _operations.Receive2(
|
||||
_client.ServerUrl,
|
||||
_project.id,
|
||||
fistSend.RootId,
|
||||
_client.Account.token,
|
||||
null,
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
Assert.Equal(expectedId, secondReceive.id);
|
||||
}
|
||||
|
||||
private void ClearCache() { }
|
||||
|
||||
[Fact]
|
||||
public async Task ReceiveNonExistentObjectThrows()
|
||||
{
|
||||
var ex = await Assert.ThrowsAsync<HttpRequestException>(async () =>
|
||||
{
|
||||
_ = await _operations.Receive2(
|
||||
_client.ServerUrl,
|
||||
_project.id,
|
||||
NON_EXISTENT_OBJECT_ID,
|
||||
_client.Account.token,
|
||||
null,
|
||||
CancellationToken.None,
|
||||
new(true)
|
||||
);
|
||||
});
|
||||
await Verify(ex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReceiveNonExistentProjectThrows()
|
||||
{
|
||||
var ex = await Assert.ThrowsAsync<HttpRequestException>(async () =>
|
||||
{
|
||||
_ = await _operations.Receive2(
|
||||
_client.ServerUrl,
|
||||
NON_EXISTENT_PROJECT_ID,
|
||||
NON_EXISTENT_OBJECT_ID,
|
||||
_client.Account.token,
|
||||
null,
|
||||
CancellationToken.None,
|
||||
new(true)
|
||||
);
|
||||
});
|
||||
await Verify(ex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendInvalidData()
|
||||
{
|
||||
var myObject = Fixtures.GenerateNestedObject();
|
||||
myObject["invalidProp"] = new StringBuilder(); //Serializer does not support serializing this type
|
||||
|
||||
var ex = await Assert.ThrowsAsync<SpeckleException>(async () =>
|
||||
{
|
||||
_ = await _operations.Send2(
|
||||
_client.ServerUrl,
|
||||
_project.id,
|
||||
_client.Account.token,
|
||||
myObject,
|
||||
null,
|
||||
CancellationToken.None,
|
||||
new(SkipCacheRead: true, SkipCacheWrite: true)
|
||||
);
|
||||
});
|
||||
await Verify(ex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReceiveNonAuthThrows()
|
||||
{
|
||||
using IClient unauthed = Fixtures.Unauthed;
|
||||
await Assert.ThrowsAsync<HttpRequestException>(async () =>
|
||||
{
|
||||
_ = await _operations.Receive2(
|
||||
unauthed.ServerUrl,
|
||||
_project.id,
|
||||
NON_EXISTENT_OBJECT_ID,
|
||||
unauthed.Account.token,
|
||||
null,
|
||||
CancellationToken.None,
|
||||
new(true)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReceiveCancellation()
|
||||
{
|
||||
using CancellationTokenSource ct = new();
|
||||
await ct.CancelAsync();
|
||||
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () =>
|
||||
{
|
||||
_ = await _operations.Receive2(
|
||||
_client.ServerUrl,
|
||||
_project.id,
|
||||
NON_EXISTENT_OBJECT_ID,
|
||||
_client.Account.token,
|
||||
null,
|
||||
ct.Token,
|
||||
new(true)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
_client?.Dispose();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ public class GraphQLErrorHandlerTests
|
||||
{
|
||||
yield return [typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "FORBIDDEN" } }];
|
||||
yield return [typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "UNAUTHENTICATED" } }];
|
||||
yield return [typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "UNAUTHORIZED_ACCESS_ERROR" } }];
|
||||
yield return [typeof(SpeckleGraphQLInternalErrorException), new Map { { "code", "INTERNAL_SERVER_ERROR" } }];
|
||||
yield return [typeof(SpeckleGraphQLStreamNotFoundException), new Map { { "code", "STREAM_NOT_FOUND" } }];
|
||||
yield return [typeof(SpeckleGraphQLBadInputException), new Map { { "code", "BAD_USER_INPUT" } }];
|
||||
|
||||
Reference in New Issue
Block a user