Merge branch 'dev' into claire/cnx-931-create-archicadobject-in-speckle-sharp-sdk
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
<Project>
|
||||
<PropertyGroup Condition="'$(IsTestProject)' == 'true'">
|
||||
<AnalysisMode>Recommended</AnalysisMode>
|
||||
<NoWarn>
|
||||
$(NoWarn);
|
||||
<!-- Things we need to test -->
|
||||
@@ -9,7 +8,6 @@
|
||||
<!-- Analysers that provide no tangeable value to a test project -->
|
||||
CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618;CA1054;CA1810;CA2208;CA1019;CA1831;
|
||||
</NoWarn>
|
||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="DeepClean">
|
||||
|
||||
@@ -1,33 +1,32 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="altcover" Version="8.9.3" />
|
||||
<PackageVersion Include="altcover" Version="9.0.1" />
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageVersion Include="Bullseye" Version="5.0.0" />
|
||||
<PackageVersion Include="FluentAssertions" Version="7.0.0" />
|
||||
<PackageVersion Include="GraphQL.Client" Version="6.0.0" />
|
||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||
<PackageVersion Include="ILRepack.FullAuto" Version="1.6.0" />
|
||||
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<!-- Keep at exactly 7.0.5 for side by side with V2 -->
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="7.0.5" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="8.0.11" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" />
|
||||
<PackageVersion Include="Moq" Version="4.20.70" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
<PackageVersion Include="NUnit" Version="4.2.2" />
|
||||
<PackageVersion Include="NUnit.Analyzers" Version="4.2.0" />
|
||||
<PackageVersion Include="Open.ChannelExtensions" Version="8.6.0" />
|
||||
<PackageVersion Include="Open.ChannelExtensions" Version="9.0.0" />
|
||||
<PackageVersion Include="Polly" Version="7.2.3" />
|
||||
<PackageVersion Include="Polly.Contrib.WaitAndRetry" Version="1.1.1" />
|
||||
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
|
||||
<PackageVersion Include="Shouldly" Version="4.2.1" />
|
||||
<PackageVersion Include="Speckle.Newtonsoft.Json" Version="13.0.2" />
|
||||
<PackageVersion Include="Speckle.DoubleNumerics" Version="4.0.1" />
|
||||
<PackageVersion Include="SimpleExec" Version="12.0.0" />
|
||||
<PackageVersion Include="System.Threading.Channels" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Threading.Channels" Version="9.0.0" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.0" />
|
||||
<GlobalPackageReference Include="PolySharp" Version="1.15.0" />
|
||||
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
<GlobalPackageReference Include="GitVersion.MsBuild" Version="5.12.0" />
|
||||
|
||||
@@ -9,3 +9,8 @@ public static class Collections
|
||||
public static IReadOnlyDictionary<TKey, TValue> Freeze<TKey, TValue>(this IDictionary<TKey, TValue> source)
|
||||
where TKey : notnull => source.ToFrozenDictionary();
|
||||
}
|
||||
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static IEnumerable<int> RangeFrom(int from, int to) => Enumerable.Range(from, to - from + 1);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Speckle.Sdk.Dependencies;
|
||||
|
||||
public static class Pools
|
||||
{
|
||||
public const int DefaultCapacity = 50;
|
||||
|
||||
public static Pool<Dictionary<string, object?>> ObjectDictionaries { get; } = new(new ObjectDictionaryPolicy());
|
||||
|
||||
private sealed class ObjectDictionaryPolicy : IPooledObjectPolicy<Dictionary<string, object?>>
|
||||
@@ -25,7 +27,7 @@ public static class Pools
|
||||
private sealed class ObjectDictionaryPolicy<TKey, TValue> : IPooledObjectPolicy<Dictionary<TKey, TValue>>
|
||||
where TKey : notnull
|
||||
{
|
||||
public Dictionary<TKey, TValue> Create() => new(50);
|
||||
public Dictionary<TKey, TValue> Create() => new(DefaultCapacity);
|
||||
|
||||
public bool Return(Dictionary<TKey, TValue> obj)
|
||||
{
|
||||
@@ -38,7 +40,7 @@ public static class Pools
|
||||
: IPooledObjectPolicy<ConcurrentDictionary<TKey, TValue>>
|
||||
where TKey : notnull
|
||||
{
|
||||
public ConcurrentDictionary<TKey, TValue> Create() => new(Environment.ProcessorCount, 50);
|
||||
public ConcurrentDictionary<TKey, TValue> Create() => new(Environment.ProcessorCount, DefaultCapacity);
|
||||
|
||||
public bool Return(ConcurrentDictionary<TKey, TValue> obj)
|
||||
{
|
||||
@@ -49,7 +51,7 @@ public static class Pools
|
||||
|
||||
private sealed class ObjectListPolicy<T> : IPooledObjectPolicy<List<T>>
|
||||
{
|
||||
public List<T> Create() => new(50);
|
||||
public List<T> Create() => new(DefaultCapacity);
|
||||
|
||||
public bool Return(List<T> obj)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||
using System.Buffers;
|
||||
using Speckle.Sdk.Dependencies;
|
||||
|
||||
public class Batch<T>(int capacity) : IHasSize
|
||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||
|
||||
public sealed class Batch<T> : IHasSize, IMemoryOwner<T>
|
||||
where T : IHasSize
|
||||
{
|
||||
private static readonly Pool<List<T>> _pool = Pools.CreateListPool<T>();
|
||||
#pragma warning disable IDE0032
|
||||
private readonly List<T> _items = new(capacity);
|
||||
private readonly List<T> _items = _pool.Get();
|
||||
private int _batchSize;
|
||||
#pragma warning restore IDE0032
|
||||
|
||||
@@ -22,4 +26,8 @@ public class Batch<T>(int capacity) : IHasSize
|
||||
|
||||
public int Size => _batchSize;
|
||||
public List<T> Items => _items;
|
||||
|
||||
public void Dispose() => _pool.Return(_items);
|
||||
|
||||
public Memory<T> Memory => new(_items.ToArray());
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System.Threading.Channels;
|
||||
using System.Buffers;
|
||||
using System.Threading.Channels;
|
||||
using Open.ChannelExtensions;
|
||||
|
||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||
|
||||
public static class ChannelExtensions
|
||||
{
|
||||
public static BatchingChannelReader<T, Batch<T>> BatchBySize<T>(
|
||||
public static BatchingChannelReader<T, IMemoryOwner<T>> BatchBySize<T>(
|
||||
this ChannelReader<T> source,
|
||||
int batchSize,
|
||||
bool singleReader = false,
|
||||
|
||||
@@ -8,8 +8,8 @@ public abstract class ChannelLoader<T>
|
||||
private const int MAX_PARALLELISM_HTTP = 4;
|
||||
private static readonly TimeSpan HTTP_BATCH_TIMEOUT = TimeSpan.FromSeconds(2);
|
||||
private static readonly int MAX_READ_CACHE_PARALLELISM = Environment.ProcessorCount;
|
||||
private const int MAX_SAVE_CACHE_BATCH = 200;
|
||||
private const int MAX_SAVE_CACHE_PARALLELISM = 1;
|
||||
private const int MAX_SAVE_CACHE_BATCH = 500;
|
||||
private const int MAX_SAVE_CACHE_PARALLELISM = 4;
|
||||
|
||||
protected async Task GetAndCache(IEnumerable<string> allChildrenIds, CancellationToken cancellationToken = default) =>
|
||||
await allChildrenIds
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Buffers;
|
||||
using System.Threading.Channels;
|
||||
using Open.ChannelExtensions;
|
||||
using Speckle.Sdk.Serialisation.V2.Send;
|
||||
@@ -13,7 +14,7 @@ public abstract class ChannelSaver<T>
|
||||
private const int MAX_PARALLELISM_HTTP = 4;
|
||||
private const int HTTP_CAPACITY = 500;
|
||||
private const int MAX_CACHE_WRITE_PARALLELISM = 4;
|
||||
private const int MAX_CACHE_BATCH = 200;
|
||||
private const int MAX_CACHE_BATCH = 500;
|
||||
|
||||
private readonly Channel<T> _checkCacheChannel = Channel.CreateBounded<T>(
|
||||
new BoundedChannelOptions(SEND_CAPACITY)
|
||||
@@ -46,7 +47,13 @@ public abstract class ChannelSaver<T>
|
||||
public ValueTask Save(T item, CancellationToken cancellationToken = default) =>
|
||||
_checkCacheChannel.Writer.WriteAsync(item, cancellationToken);
|
||||
|
||||
public abstract Task<List<T>> SendToServer(Batch<T> batch, CancellationToken cancellationToken);
|
||||
public async Task<IMemoryOwner<T>> SendToServer(IMemoryOwner<T> batch, CancellationToken cancellationToken)
|
||||
{
|
||||
await SendToServer((Batch<T>)batch, cancellationToken).ConfigureAwait(false);
|
||||
return batch;
|
||||
}
|
||||
|
||||
public abstract Task SendToServer(Batch<T> batch, CancellationToken cancellationToken);
|
||||
|
||||
public Task Done()
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Buffers;
|
||||
using System.Threading.Channels;
|
||||
using Open.ChannelExtensions;
|
||||
|
||||
@@ -13,20 +14,20 @@ public class SizeBatchingChannelReader<T>(
|
||||
int batchSize,
|
||||
bool singleReader,
|
||||
bool syncCont = false
|
||||
) : BatchingChannelReader<T, Batch<T>>(x => new(x), source, batchSize, singleReader, syncCont)
|
||||
) : BatchingChannelReader<T, IMemoryOwner<T>>(x => new Batch<T>(), source, batchSize, singleReader, syncCont)
|
||||
where T : IHasSize
|
||||
{
|
||||
protected override Batch<T> CreateBatch(int capacity) => new(capacity);
|
||||
protected override IMemoryOwner<T> CreateBatch(int capacity) => new Batch<T>();
|
||||
|
||||
protected override void TrimBatch(ref Batch<T> batch, bool isVerifiedFull)
|
||||
protected override void TrimBatch(ref IMemoryOwner<T> batch, bool isVerifiedFull)
|
||||
{
|
||||
if (!isVerifiedFull)
|
||||
{
|
||||
batch.TrimExcess();
|
||||
((Batch<T>)batch).TrimExcess();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AddBatchItem(Batch<T> batch, T item) => batch.Add(item);
|
||||
protected override void AddBatchItem(IMemoryOwner<T> batch, T item) => ((Batch<T>)batch).Add(item);
|
||||
|
||||
protected override int GetBatchSize(Batch<T> batch) => batch.Size;
|
||||
protected override int GetBatchSize(IMemoryOwner<T> batch) => ((Batch<T>)batch).Size;
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
},
|
||||
"Microsoft.Extensions.ObjectPool": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.11, )",
|
||||
"resolved": "8.0.11",
|
||||
"contentHash": "6ApKcHNJigXBfZa6XlDQ8feJpq7SG1ogZXg6M4FiNzgd6irs3LUAzo0Pfn4F2ZI9liGnH1XIBR/OtSbZmJAV5w=="
|
||||
"requested": "[9.0.0, )",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "UbsU/gYe4nv1DeqMXIVzDfNNek7Sk2kKuAOXL/Y+sLcAR0HwFUqzg1EPiU88jeHNe0g81aPvvHbvHarQr3r9IA=="
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
@@ -44,13 +44,13 @@
|
||||
},
|
||||
"Open.ChannelExtensions": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.6.0, )",
|
||||
"resolved": "8.6.0",
|
||||
"contentHash": "g5axz417bA6FXifJaBlB0l62gV7dYmknXx0n8lT/LSA3+7isaGMsOjJp5J+H/yXDRe4r+KZrE+bzQcs4Ets2kA==",
|
||||
"requested": "[9.0.0, )",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "DP+l5S6G46wcuY4I4kNXE+RDOmJr0DKuMienOdt0mMBN9z7vmLSC8YQbqCyb9i9LNjXj1tgCx5LyitJiRr/v7g==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
|
||||
"System.Collections.Immutable": "8.0.0",
|
||||
"System.Threading.Channels": "8.0.0"
|
||||
"Microsoft.Bcl.AsyncInterfaces": "9.0.0",
|
||||
"System.Collections.Immutable": "9.0.0",
|
||||
"System.Threading.Channels": "9.0.0"
|
||||
}
|
||||
},
|
||||
"Polly": {
|
||||
@@ -88,10 +88,11 @@
|
||||
},
|
||||
"System.Threading.Channels": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==",
|
||||
"requested": "[9.0.0, )",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "hzACdIf1C+4Dqos5ijV404b94+LqfIC8nfS3mNpCDFWowb1N3PNfJPopneq32ahWlDeyaPZJqjBk76YFR69Rpg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "9.0.0",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
@@ -122,8 +123,8 @@
|
||||
},
|
||||
"System.Collections.Immutable": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
@@ -160,8 +161,8 @@
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[5.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "owmu2Cr3IQ8yQiBleBHlGk8dSQ12oaF2e7TpzwJKEl4m84kkZJjEY1n33L67Y3zM5jPOjmmbdHjbfiL0RqcMRQ==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
@@ -185,9 +186,9 @@
|
||||
},
|
||||
"Microsoft.Extensions.ObjectPool": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.11, )",
|
||||
"resolved": "8.0.11",
|
||||
"contentHash": "6ApKcHNJigXBfZa6XlDQ8feJpq7SG1ogZXg6M4FiNzgd6irs3LUAzo0Pfn4F2ZI9liGnH1XIBR/OtSbZmJAV5w=="
|
||||
"requested": "[9.0.0, )",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "UbsU/gYe4nv1DeqMXIVzDfNNek7Sk2kKuAOXL/Y+sLcAR0HwFUqzg1EPiU88jeHNe0g81aPvvHbvHarQr3r9IA=="
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
@@ -201,9 +202,9 @@
|
||||
},
|
||||
"Open.ChannelExtensions": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.6.0, )",
|
||||
"resolved": "8.6.0",
|
||||
"contentHash": "g5axz417bA6FXifJaBlB0l62gV7dYmknXx0n8lT/LSA3+7isaGMsOjJp5J+H/yXDRe4r+KZrE+bzQcs4Ets2kA=="
|
||||
"requested": "[9.0.0, )",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "DP+l5S6G46wcuY4I4kNXE+RDOmJr0DKuMienOdt0mMBN9z7vmLSC8YQbqCyb9i9LNjXj1tgCx5LyitJiRr/v7g=="
|
||||
},
|
||||
"Polly": {
|
||||
"type": "Direct",
|
||||
@@ -240,9 +241,9 @@
|
||||
},
|
||||
"System.Threading.Channels": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA=="
|
||||
"requested": "[9.0.0, )",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "hzACdIf1C+4Dqos5ijV404b94+LqfIC8nfS3mNpCDFWowb1N3PNfJPopneq32ahWlDeyaPZJqjBk76YFR69Rpg=="
|
||||
},
|
||||
"ILRepack": {
|
||||
"type": "Transitive",
|
||||
|
||||
@@ -21,11 +21,13 @@ using Stream = System.IO.Stream;
|
||||
|
||||
namespace Speckle.Sdk.Credentials;
|
||||
|
||||
public partial interface IAccountManager : IDisposable;
|
||||
|
||||
/// <summary>
|
||||
/// Manage accounts locally for desktop applications.
|
||||
/// </summary>
|
||||
[GenerateAutoInterface]
|
||||
public class AccountManager(
|
||||
public sealed class AccountManager(
|
||||
ISpeckleApplication application,
|
||||
ILogger<AccountManager> logger,
|
||||
ISpeckleHttp speckleHttp,
|
||||
@@ -40,6 +42,13 @@ public class AccountManager(
|
||||
"AccountAddFlow"
|
||||
);
|
||||
|
||||
[AutoInterfaceIgnore]
|
||||
public void Dispose()
|
||||
{
|
||||
_accountStorage.Dispose();
|
||||
_accountAddLockStorage.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the basic information about a server.
|
||||
/// </summary>
|
||||
@@ -321,7 +330,7 @@ public class AccountManager(
|
||||
{
|
||||
static bool IsInvalid(Account ac) => ac.userInfo == null || ac.serverInfo == null;
|
||||
|
||||
var sqlAccounts = _accountStorage.GetAllObjects().Select(x => JsonConvert.DeserializeObject<Account>(x));
|
||||
var sqlAccounts = _accountStorage.GetAllObjects().Select(x => JsonConvert.DeserializeObject<Account>(x.Json));
|
||||
var localAccounts = GetLocalAccounts();
|
||||
|
||||
foreach (var acc in sqlAccounts)
|
||||
@@ -642,7 +651,7 @@ public class AccountManager(
|
||||
}
|
||||
|
||||
// this uses the SQLite transport to store locks
|
||||
var lockIds = _accountAddLockStorage.GetAllObjects().OrderByDescending(d => d).ToList();
|
||||
var lockIds = _accountAddLockStorage.GetAllObjects().Select(x => x.Id).OrderByDescending(d => d).ToList();
|
||||
var now = DateTime.Now;
|
||||
foreach (var l in lockIds)
|
||||
{
|
||||
@@ -674,7 +683,7 @@ public class AccountManager(
|
||||
{
|
||||
s_isAddingAccount = false;
|
||||
// make sure all old locks are removed
|
||||
foreach (var id in _accountAddLockStorage.GetAllObjects())
|
||||
foreach (var (id, _) in _accountAddLockStorage.GetAllObjects())
|
||||
{
|
||||
_accountAddLockStorage.DeleteObject(id);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace Speckle.Sdk.SQLite;
|
||||
|
||||
//inspired by https://github.com/neosmart/SqliteCache/blob/master/SqliteCache/DbCommandPool.cs
|
||||
public sealed class CacheDbCommandPool : IDisposable
|
||||
{
|
||||
private readonly ConcurrentBag<SqliteCommand>[] _commands = new ConcurrentBag<SqliteCommand>[CacheDbCommands.Count];
|
||||
private readonly ConcurrentBag<SqliteConnection> _connections = new();
|
||||
private readonly string _connectionString;
|
||||
|
||||
public CacheDbCommandPool(string connectionString, int concurrency)
|
||||
{
|
||||
_connectionString = connectionString;
|
||||
for (int i = 0; i < _commands.Length; ++i)
|
||||
{
|
||||
_commands[i] = new ConcurrentBag<SqliteCommand>();
|
||||
}
|
||||
for (int i = 0; i < concurrency; ++i)
|
||||
{
|
||||
var connection = new SqliteConnection(_connectionString);
|
||||
connection.Open();
|
||||
_connections.Add(connection);
|
||||
}
|
||||
}
|
||||
|
||||
public void Use(CacheOperation type, Action<SqliteCommand> handler) =>
|
||||
Use(
|
||||
type,
|
||||
cmd =>
|
||||
{
|
||||
handler(cmd);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
private T Use<T>(Func<SqliteConnection, T> handler)
|
||||
{
|
||||
if (!_connections.TryTake(out var db))
|
||||
{
|
||||
db = new SqliteConnection(_connectionString);
|
||||
db.Open();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return handler(db);
|
||||
}
|
||||
catch (SqliteException se)
|
||||
{
|
||||
throw SqLiteJsonCacheException.Create(se);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_connections.Add(db);
|
||||
}
|
||||
}
|
||||
|
||||
public T Use<T>(CacheOperation type, Func<SqliteCommand, T> handler) =>
|
||||
Use(conn =>
|
||||
{
|
||||
var pool = _commands[(int)type];
|
||||
if (!pool.TryTake(out var command))
|
||||
{
|
||||
#pragma warning disable CA2100
|
||||
command = new SqliteCommand(CacheDbCommands.Commands[(int)type], conn);
|
||||
#pragma warning restore CA2100
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
command.Connection = conn;
|
||||
return handler(command);
|
||||
}
|
||||
catch (SqliteException se)
|
||||
{
|
||||
throw SqLiteJsonCacheException.Create(se);
|
||||
}
|
||||
finally
|
||||
{
|
||||
command.Connection = null;
|
||||
command.Parameters.Clear();
|
||||
pool.Add(command);
|
||||
}
|
||||
});
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var pool in _commands)
|
||||
{
|
||||
while (pool.TryTake(out var cmd))
|
||||
{
|
||||
cmd.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var conn in _connections)
|
||||
{
|
||||
conn.Close();
|
||||
conn.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace Speckle.Sdk.SQLite;
|
||||
|
||||
public enum CacheOperation
|
||||
{
|
||||
InsertOrIgnore,
|
||||
InsertOrReplace,
|
||||
Has,
|
||||
Get,
|
||||
Delete,
|
||||
GetAll,
|
||||
BulkInsertOrIgnore,
|
||||
}
|
||||
|
||||
public static class CacheDbCommands
|
||||
{
|
||||
public static readonly string[] Commands;
|
||||
public static readonly int Count = Enum.GetValues(typeof(CacheOperation)).Length;
|
||||
|
||||
#pragma warning disable CA1810
|
||||
static CacheDbCommands()
|
||||
#pragma warning restore CA1810
|
||||
{
|
||||
Commands = new string[Count];
|
||||
|
||||
Commands[(int)CacheOperation.InsertOrIgnore] =
|
||||
"INSERT OR IGNORE INTO objects(hash, content) VALUES(@hash, @content)";
|
||||
Commands[(int)CacheOperation.InsertOrReplace] = "REPLACE INTO objects(hash, content) VALUES(@hash, @content)";
|
||||
Commands[(int)CacheOperation.Has] = "SELECT 1 FROM objects WHERE hash = @hash LIMIT 1";
|
||||
Commands[(int)CacheOperation.Get] = "SELECT content FROM objects WHERE hash = @hash LIMIT 1";
|
||||
Commands[(int)CacheOperation.Delete] = "DELETE FROM objects WHERE hash = @hash";
|
||||
Commands[(int)CacheOperation.GetAll] = "SELECT hash, content FROM objects";
|
||||
|
||||
Commands[(int)CacheOperation.BulkInsertOrIgnore] = "INSERT OR IGNORE INTO objects (hash, content) VALUES ";
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,28 @@
|
||||
using System.Text;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using Speckle.Sdk.Dependencies;
|
||||
|
||||
namespace Speckle.Sdk.SQLite;
|
||||
|
||||
public partial interface ISqLiteJsonCacheManager : IDisposable;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class SqLiteJsonCacheManager : ISqLiteJsonCacheManager
|
||||
public sealed class SqLiteJsonCacheManager : ISqLiteJsonCacheManager
|
||||
{
|
||||
private readonly string _connectionString;
|
||||
private readonly CacheDbCommandPool _pool;
|
||||
|
||||
public SqLiteJsonCacheManager(string rootPath)
|
||||
public SqLiteJsonCacheManager(string connectionString, int concurrency)
|
||||
{
|
||||
_connectionString = $"Data Source={rootPath};";
|
||||
_connectionString = connectionString;
|
||||
Initialize();
|
||||
_pool = new CacheDbCommandPool(_connectionString, concurrency);
|
||||
}
|
||||
|
||||
[AutoInterfaceIgnore]
|
||||
public void Dispose() => _pool.Dispose();
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
// NOTE: used for creating partioned object tables.
|
||||
@@ -57,100 +66,107 @@ public class SqLiteJsonCacheManager : ISqLiteJsonCacheManager
|
||||
|
||||
using SqliteCommand cmd4 = new("PRAGMA page_size = 32768;", c);
|
||||
cmd4.ExecuteNonQuery();
|
||||
c.Close();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetAllObjects()
|
||||
{
|
||||
using var c = new SqliteConnection(_connectionString);
|
||||
c.Open();
|
||||
using var command = new SqliteCommand("SELECT * FROM objects", c);
|
||||
public IReadOnlyCollection<(string Id, string Json)> GetAllObjects() =>
|
||||
_pool.Use(
|
||||
CacheOperation.GetAll,
|
||||
command =>
|
||||
{
|
||||
var list = new HashSet<(string, string)>();
|
||||
using var reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
list.Add((reader.GetString(0), reader.GetString(1)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
);
|
||||
|
||||
using var reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
yield return reader.GetString(1);
|
||||
}
|
||||
}
|
||||
public void DeleteObject(string id) =>
|
||||
_pool.Use(
|
||||
CacheOperation.Delete,
|
||||
command =>
|
||||
{
|
||||
command.Parameters.AddWithValue("@hash", id);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
);
|
||||
|
||||
public void DeleteObject(string id)
|
||||
{
|
||||
using var c = new SqliteConnection(_connectionString);
|
||||
c.Open();
|
||||
using var command = new SqliteCommand("DELETE FROM objects WHERE hash = @hash", c);
|
||||
command.Parameters.AddWithValue("@hash", id);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
public string? GetObject(string id)
|
||||
{
|
||||
using var c = new SqliteConnection(_connectionString);
|
||||
c.Open();
|
||||
using var command = new SqliteCommand("SELECT * FROM objects WHERE hash = @hash LIMIT 1 ", c);
|
||||
command.Parameters.AddWithValue("@hash", id);
|
||||
using var reader = command.ExecuteReader();
|
||||
if (reader.Read())
|
||||
{
|
||||
return reader.GetString(1);
|
||||
}
|
||||
|
||||
return null; // pass on the duty of null checks to consumers
|
||||
}
|
||||
public string? GetObject(string id) =>
|
||||
_pool.Use(
|
||||
CacheOperation.Get,
|
||||
command =>
|
||||
{
|
||||
command.Parameters.AddWithValue("@hash", id);
|
||||
return (string?)command.ExecuteScalar();
|
||||
}
|
||||
);
|
||||
|
||||
//This does an insert or ignores if already exists
|
||||
public void SaveObject(string id, string json)
|
||||
{
|
||||
using var c = new SqliteConnection(_connectionString);
|
||||
c.Open();
|
||||
const string COMMAND_TEXT = "INSERT OR IGNORE INTO objects(hash, content) VALUES(@hash, @content)";
|
||||
|
||||
using var command = new SqliteCommand(COMMAND_TEXT, c);
|
||||
command.Parameters.AddWithValue("@hash", id);
|
||||
command.Parameters.AddWithValue("@content", json);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
public void SaveObject(string id, string json) =>
|
||||
_pool.Use(
|
||||
CacheOperation.InsertOrIgnore,
|
||||
command =>
|
||||
{
|
||||
command.Parameters.AddWithValue("@hash", id);
|
||||
command.Parameters.AddWithValue("@content", json);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
);
|
||||
|
||||
//This does an insert or replaces if already exists
|
||||
public void UpdateObject(string id, string json)
|
||||
{
|
||||
using var c = new SqliteConnection(_connectionString);
|
||||
c.Open();
|
||||
const string COMMAND_TEXT = "REPLACE INTO objects(hash, content) VALUES(@hash, @content)";
|
||||
using var command = new SqliteCommand(COMMAND_TEXT, c);
|
||||
command.Parameters.AddWithValue("@hash", id);
|
||||
command.Parameters.AddWithValue("@content", json);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
public void UpdateObject(string id, string json) =>
|
||||
_pool.Use(
|
||||
CacheOperation.InsertOrReplace,
|
||||
command =>
|
||||
{
|
||||
command.Parameters.AddWithValue("@hash", id);
|
||||
command.Parameters.AddWithValue("@content", json);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
);
|
||||
|
||||
public void SaveObjects(IEnumerable<(string id, string json)> items)
|
||||
{
|
||||
using var c = new SqliteConnection(_connectionString);
|
||||
c.Open();
|
||||
using var t = c.BeginTransaction();
|
||||
const string COMMAND_TEXT = "INSERT OR IGNORE INTO objects(hash, content) VALUES(@hash, @content)";
|
||||
public void SaveObjects(IEnumerable<(string id, string json)> items) =>
|
||||
_pool.Use(
|
||||
CacheOperation.BulkInsertOrIgnore,
|
||||
cmd =>
|
||||
{
|
||||
CreateBulkInsert(cmd, items);
|
||||
return cmd.ExecuteNonQuery();
|
||||
}
|
||||
);
|
||||
|
||||
using var command = new SqliteCommand(COMMAND_TEXT, c);
|
||||
command.Transaction = t;
|
||||
var idParam = command.Parameters.Add("@hash", SqliteType.Text);
|
||||
var jsonParam = command.Parameters.Add("@content", SqliteType.Text);
|
||||
private void CreateBulkInsert(SqliteCommand cmd, IEnumerable<(string id, string json)> items)
|
||||
{
|
||||
StringBuilder sb = Pools.StringBuilders.Get();
|
||||
sb.AppendLine(CacheDbCommands.Commands[(int)CacheOperation.BulkInsertOrIgnore]);
|
||||
int i = 0;
|
||||
foreach (var (id, json) in items)
|
||||
{
|
||||
idParam.Value = id;
|
||||
jsonParam.Value = json;
|
||||
command.ExecuteNonQuery();
|
||||
sb.Append($"(@key{i}, @value{i}),");
|
||||
cmd.Parameters.AddWithValue($"@key{i}", id);
|
||||
cmd.Parameters.AddWithValue($"@value{i}", json);
|
||||
i++;
|
||||
}
|
||||
t.Commit();
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append(';');
|
||||
#pragma warning disable CA2100
|
||||
cmd.CommandText = sb.ToString();
|
||||
#pragma warning restore CA2100
|
||||
Pools.StringBuilders.Return(sb);
|
||||
}
|
||||
|
||||
public bool HasObject(string objectId)
|
||||
{
|
||||
using var c = new SqliteConnection(_connectionString);
|
||||
c.Open();
|
||||
const string COMMAND_TEXT = "SELECT 1 FROM objects WHERE hash = @hash LIMIT 1 ";
|
||||
using var command = new SqliteCommand(COMMAND_TEXT, c);
|
||||
command.Parameters.AddWithValue("@hash", objectId);
|
||||
|
||||
using var reader = command.ExecuteReader();
|
||||
bool rowFound = reader.Read();
|
||||
return rowFound;
|
||||
}
|
||||
public bool HasObject(string objectId) =>
|
||||
_pool.Use(
|
||||
CacheOperation.Has,
|
||||
command =>
|
||||
{
|
||||
command.Parameters.AddWithValue("@hash", objectId);
|
||||
using var reader = command.ExecuteReader();
|
||||
bool rowFound = reader.Read();
|
||||
return rowFound;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace Speckle.Sdk.SQLite;
|
||||
|
||||
public class SqLiteJsonCacheException : SpeckleException
|
||||
{
|
||||
public SqLiteJsonCacheException() { }
|
||||
|
||||
public SqLiteJsonCacheException(string message)
|
||||
: base(message) { }
|
||||
|
||||
public SqLiteJsonCacheException(string message, Exception inner)
|
||||
: base(message, inner) { }
|
||||
|
||||
public static SqLiteJsonCacheException Create(SqliteException inner)
|
||||
{
|
||||
if (!SqliteExceptions.SqliteErrorCodes.TryGetValue(inner.SqliteErrorCode, out string? errorMessage))
|
||||
{
|
||||
errorMessage = $"An error occurred while executing a SQLite command: {inner.SqliteErrorCode}";
|
||||
}
|
||||
if (
|
||||
!SqliteExceptions.SqliteExtendedResultCodes.TryGetValue(
|
||||
inner.SqliteExtendedErrorCode,
|
||||
out string? detailedMessage
|
||||
)
|
||||
)
|
||||
{
|
||||
detailedMessage = $"Detail: {inner.SqliteExtendedErrorCode}";
|
||||
}
|
||||
return new SqLiteJsonCacheException(
|
||||
$"An error occured with the SQLite cache: {inner.Message}{Environment.NewLine}{errorMessage}{Environment.NewLine}{detailedMessage}",
|
||||
inner
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,14 @@ namespace Speckle.Sdk.SQLite;
|
||||
[GenerateAutoInterface]
|
||||
public class SqLiteJsonCacheManagerFactory : ISqLiteJsonCacheManagerFactory
|
||||
{
|
||||
private ISqLiteJsonCacheManager Create(string path) => new SqLiteJsonCacheManager(path);
|
||||
public const int INITIAL_CONCURRENCY = 4;
|
||||
|
||||
private ISqLiteJsonCacheManager Create(string path, int concurrency) =>
|
||||
new SqLiteJsonCacheManager($"Data Source={path};", concurrency);
|
||||
|
||||
public ISqLiteJsonCacheManager CreateForUser(string scope) =>
|
||||
Create(Path.Combine(SpecklePathProvider.UserApplicationDataPath(), "Speckle", $"{scope}.db"));
|
||||
Create(Path.Combine(SpecklePathProvider.UserApplicationDataPath(), "Speckle", $"{scope}.db"), 1);
|
||||
|
||||
public ISqLiteJsonCacheManager CreateFromStream(string streamId) => Create(SqlitePaths.GetDBPath(streamId));
|
||||
public ISqLiteJsonCacheManager CreateFromStream(string streamId) =>
|
||||
Create(SqlitePaths.GetDBPath(streamId), INITIAL_CONCURRENCY);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
namespace Speckle.Sdk.SQLite;
|
||||
|
||||
internal static class SqliteExceptions
|
||||
{
|
||||
public static readonly IReadOnlyDictionary<int, string> SqliteErrorCodes = new Dictionary<int, string>
|
||||
{
|
||||
{ 0, "Successful result" },
|
||||
{ 1, "Generic error" },
|
||||
{ 2, "Internal logic error in SQLite" },
|
||||
{ 3, "Access permission denied" },
|
||||
{ 4, "Callback routine requested an abort" },
|
||||
{ 5, "The database file is locked" },
|
||||
{ 6, "A table in the database is locked" },
|
||||
{ 7, "A malloc() failed" },
|
||||
{ 8, "Attempt to write a readonly database" },
|
||||
{ 9, "Operation terminated by sqlite3_interrupt()" },
|
||||
{ 10, "Some kind of disk I/O error occurred" },
|
||||
{ 11, "The database disk image is malformed" },
|
||||
{ 12, "Unknown opcode in sqlite3_file_control()" },
|
||||
{ 13, "Insertion failed because database is full" },
|
||||
{ 14, "Unable to open the database file" },
|
||||
{ 15, "Database lock protocol error" },
|
||||
{ 16, "Internal use only" },
|
||||
{ 17, "The database schema changed" },
|
||||
{ 18, "String or BLOB exceeds size limit" },
|
||||
{ 19, "Abort due to constraint violation" },
|
||||
{ 20, "Data type mismatch" },
|
||||
{ 21, "Library used incorrectly" },
|
||||
{ 22, "Uses OS features not supported on host" },
|
||||
{ 23, "Authorization denied" },
|
||||
{ 24, "Not used" },
|
||||
{ 25, "2nd parameter to sqlite3_bind out of range" },
|
||||
{ 26, "File opened that is not a database file" },
|
||||
{ 27, "Notifications from sqlite3_log()" },
|
||||
{ 28, "Warnings from sqlite3_log()" },
|
||||
{ 100, "sqlite3_step() has another row ready" },
|
||||
{ 101, "sqlite3_step() has finished executing" },
|
||||
};
|
||||
|
||||
public static readonly IReadOnlyDictionary<int, string> SqliteExtendedResultCodes = new Dictionary<int, string>()
|
||||
{
|
||||
{ 516, "SQLITE_ABORT_ROLLBACK: A rollback occurred due to a constraint violation." },
|
||||
{ 279, "SQLITE_AUTH_USER: Authorization denied by a user authentication callback." },
|
||||
{ 261, "SQLITE_BUSY_RECOVERY: The database file is locked because another connection is recovering the WAL." },
|
||||
{ 517, "SQLITE_BUSY_SNAPSHOT: The database file is locked because another connection has a conflicting snapshot." },
|
||||
{ 773, "SQLITE_BUSY_TIMEOUT: A blocking operation was interrupted by a call to sqlite3_interrupt()." },
|
||||
{ 1038, "SQLITE_CANTOPEN_CONVPATH: Unable to open the database file due to a conversion error." },
|
||||
{
|
||||
1294,
|
||||
"SQLITE_CANTOPEN_DIRTYWAL: The database file cannot be opened because the Write-Ahead Log contains uncommitted changes."
|
||||
},
|
||||
{ 782, "SQLITE_CANTOPEN_FULLPATH: Unable to open the database file with the full pathname." },
|
||||
{ 526, "SQLITE_CANTOPEN_ISDIR: The database file cannot be opened because it is a directory." },
|
||||
{
|
||||
270,
|
||||
"SQLITE_CANTOPEN_NOTEMPDIR: Unable to open a temporary database file because a temporary directory is not available."
|
||||
},
|
||||
{ 1550, "SQLITE_CANTOPEN_SYMLINK: The database file cannot be opened because it is a symbolic link." },
|
||||
{ 275, "SQLITE_CONSTRAINT_CHECK: A CHECK constraint failed." },
|
||||
{ 531, "SQLITE_CONSTRAINT_COMMITHOOK: A commit hook caused the transaction to roll back." },
|
||||
{ 3091, "SQLITE_CONSTRAINT_DATATYPE: A datatype mismatch occurred." },
|
||||
{ 787, "SQLITE_CONSTRAINT_FOREIGNKEY: A foreign key constraint failed." },
|
||||
{ 1043, "SQLITE_CONSTRAINT_FUNCTION: A function constraint failed." },
|
||||
{ 1299, "SQLITE_CONSTRAINT_NOTNULL: A NOT NULL constraint failed." },
|
||||
{ 1555, "SQLITE_CONSTRAINT_PRIMARYKEY: A PRIMARY KEY constraint failed." },
|
||||
{ 1803, "SQLITE_CONSTRAINT_TRIGGER: A trigger constraint failed." },
|
||||
{ 2059, "SQLITE_CONSTRAINT_UNIQUE: A UNIQUE constraint failed." },
|
||||
{ 2315, "SQLITE_CONSTRAINT_VTAB: A virtual table constraint failed." },
|
||||
{ 2571, "SQLITE_CONSTRAINT_ROWID: A rowid constraint failed." },
|
||||
{ 1034, "SQLITE_IOERR_FSYNC: An I/O error occurred during the fsync() system call." },
|
||||
{ 6410, "SQLITE_IOERR_GETTEMPPATH: An I/O error occurred while trying to get the temporary file path." },
|
||||
{ 3850, "SQLITE_IOERR_LOCK: An I/O error occurred while trying to lock the database file." },
|
||||
{ 6154, "SQLITE_IOERR_MMAP: An I/O error occurred during memory mapping." },
|
||||
{ 3082, "SQLITE_IOERR_NOMEM: An I/O error occurred due to a memory allocation failure." },
|
||||
{ 2314, "SQLITE_IOERR_RDLOCK: An I/O error occurred while trying to read-lock the database file." },
|
||||
{ 266, "SQLITE_IOERR_READ: An I/O error occurred while reading from the database file." },
|
||||
{ 7946, "SQLITE_IOERR_ROLLBACK_ATOMIC: An I/O error occurred during an atomic rollback." },
|
||||
{ 5642, "SQLITE_IOERR_SEEK: An I/O error occurred while seeking in the database file." },
|
||||
{ 5130, "SQLITE_IOERR_SHMLOCK: An I/O error occurred while locking a shared memory segment." },
|
||||
{ 5386, "SQLITE_IOERR_SHMMAP: An I/O error occurred while mapping a shared memory segment." },
|
||||
{ 4618, "SQLITE_IOERR_SHMOPEN: An I/O error occurred while opening a shared memory segment." },
|
||||
{ 4874, "SQLITE_IOERR_SHMSIZE: An I/O error occurred while setting the size of a shared memory segment." },
|
||||
{ 522, "SQLITE_IOERR_SHORT_READ: An I/O error occurred due to a short read." },
|
||||
{ 1546, "SQLITE_IOERR_TRUNCATE: An I/O error occurred while truncating the database file." },
|
||||
{ 2058, "SQLITE_IOERR_UNLOCK: An I/O error occurred while unlocking the database file." },
|
||||
{ 6922, "SQLITE_IOERR_VNODE: A virtual node I/O error occurred." },
|
||||
{ 778, "SQLITE_IOERR_WRITE: An I/O error occurred while writing to the database file." },
|
||||
{
|
||||
262,
|
||||
"SQLITE_LOCKED_SHAREDCACHE: A write operation could not continue due to a conflict within the shared cache."
|
||||
},
|
||||
{ 518, "SQLITE_LOCKED_VTAB: A virtual table is locked." },
|
||||
{ 539, "SQLITE_NOTICE_RECOVER_ROLLBACK: A rollback was performed to recover from a previous error." },
|
||||
{ 283, "SQLITE_NOTICE_RECOVER_WAL: Recovery was performed from the Write-Ahead Log." },
|
||||
{ 256, "SQLITE_OK_LOAD_PERMANENTLY: Operation completed successfully; the extension was loaded permanently." },
|
||||
{
|
||||
1288,
|
||||
"SQLITE_READONLY_CANTINIT: Attempt to write to a read-only database failed because initialization is not allowed."
|
||||
},
|
||||
{ 520, "SQLITE_READONLY_CANTLOCK: A read-only database cannot be locked." },
|
||||
{ 1032, "SQLITE_READONLY_DBMOVED: The database file has been moved, making it read-only." },
|
||||
{ 1544, "SQLITE_READONLY_DIRECTORY: The database is read-only because it is a directory." },
|
||||
{ 264, "SQLITE_READONLY_RECOVERY: The database is read-only due to recovery mode." },
|
||||
{ 776, "SQLITE_READONLY_ROLLBACK: The database is read-only because a rollback is required." },
|
||||
{ 284, "SQLITE_WARNING_AUTOINDEX: Automatic indexing is in use." },
|
||||
};
|
||||
}
|
||||
@@ -5,9 +5,11 @@ using Speckle.Sdk.Transports;
|
||||
|
||||
namespace Speckle.Sdk.Serialisation.V2;
|
||||
|
||||
public class DummySqLiteJsonCacheManager : ISqLiteJsonCacheManager
|
||||
public sealed class DummySqLiteJsonCacheManager : ISqLiteJsonCacheManager
|
||||
{
|
||||
public IEnumerable<string> GetAllObjects() => throw new NotImplementedException();
|
||||
public void Dispose() { }
|
||||
|
||||
public IReadOnlyCollection<(string, string)> GetAllObjects() => throw new NotImplementedException();
|
||||
|
||||
public void DeleteObject(string id) => throw new NotImplementedException();
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ public record DeserializeProcessOptions(
|
||||
bool SkipInvalidConverts = false
|
||||
);
|
||||
|
||||
public partial interface IDeserializeProcess : IDisposable;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public sealed class DeserializeProcess(
|
||||
IProgress<ProgressArgs>? progress,
|
||||
@@ -30,6 +32,9 @@ public sealed class DeserializeProcess(
|
||||
public IReadOnlyDictionary<string, Base> BaseCache => _baseCache;
|
||||
public long Total { get; private set; }
|
||||
|
||||
[AutoInterfaceIgnore]
|
||||
public void Dispose() => objectLoader.Dispose();
|
||||
|
||||
public async Task<Base> Deserialize(string rootId, CancellationToken cancellationToken)
|
||||
{
|
||||
var (rootJson, childrenIds) = await objectLoader
|
||||
|
||||
@@ -9,6 +9,8 @@ using Speckle.Sdk.Transports;
|
||||
|
||||
namespace Speckle.Sdk.Serialisation.V2.Receive;
|
||||
|
||||
public partial interface IObjectLoader : IDisposable;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public sealed class ObjectLoader(
|
||||
ISqLiteJsonCacheManager sqLiteJsonCacheManager,
|
||||
@@ -19,8 +21,13 @@ public sealed class ObjectLoader(
|
||||
private int? _allChildrenCount;
|
||||
private long _checkCache;
|
||||
private long _cached;
|
||||
private long _downloaded;
|
||||
private long _totalToDownload;
|
||||
private DeserializeProcessOptions _options = new(false);
|
||||
|
||||
[AutoInterfaceIgnore]
|
||||
public void Dispose() => sqLiteJsonCacheManager.Dispose();
|
||||
|
||||
public async Task<(string, IReadOnlyCollection<string>)> GetAndCache(
|
||||
string rootId,
|
||||
DeserializeProcessOptions options,
|
||||
@@ -67,6 +74,7 @@ public sealed class ObjectLoader(
|
||||
progress?.Report(new(ProgressEvent.CacheCheck, _checkCache, _allChildrenCount));
|
||||
if (!_options.SkipCache && !sqLiteJsonCacheManager.HasObject(id))
|
||||
{
|
||||
Interlocked.Increment(ref _totalToDownload);
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -81,6 +89,8 @@ public sealed class ObjectLoader(
|
||||
var (id, json) in serverObjectManager.DownloadObjects(ids.Select(x => x.NotNull()).ToList(), progress, default)
|
||||
)
|
||||
{
|
||||
Interlocked.Increment(ref _downloaded);
|
||||
progress?.Report(new(ProgressEvent.DownloadObjects, _downloaded, _totalToDownload));
|
||||
toCache.Add(new(new(id), new(json), true, null));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||
|
||||
public readonly record struct BaseItem(Id Id, Json Json, bool NeedsStorage, Dictionary<Id, int>? Closures) : IHasSize
|
||||
{
|
||||
public int Size { get; } = Encoding.UTF8.GetByteCount(Json.Value);
|
||||
|
||||
public bool Equals(BaseItem? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return string.Equals(Id.Value, other.Value.Id.Value, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Id.GetHashCode();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Dependencies;
|
||||
@@ -23,22 +22,6 @@ public readonly record struct SerializeProcessResults(
|
||||
IReadOnlyDictionary<Id, ObjectReference> ConvertedReferences
|
||||
);
|
||||
|
||||
public readonly record struct BaseItem(Id Id, Json Json, bool NeedsStorage, Closures? Closures) : IHasSize
|
||||
{
|
||||
public int Size { get; } = Encoding.UTF8.GetByteCount(Json.Value);
|
||||
|
||||
public bool Equals(BaseItem? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return string.Equals(Id.Value, other.Value.Id.Value, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Id.GetHashCode();
|
||||
}
|
||||
|
||||
public partial interface ISerializeProcess : IDisposable;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
@@ -77,6 +60,7 @@ public sealed class SerializeProcess(
|
||||
{
|
||||
_highest.Dispose();
|
||||
_belowNormal.Dispose();
|
||||
sqLiteJsonCacheManager.Dispose();
|
||||
}
|
||||
|
||||
public async Task<SerializeProcessResults> Serialize(Base root, CancellationToken cancellationToken)
|
||||
@@ -93,7 +77,8 @@ public sealed class SerializeProcess(
|
||||
);
|
||||
}
|
||||
|
||||
await Traverse(root, true, cancellationToken).ConfigureAwait(false);
|
||||
await Traverse(root, cancellationToken).ConfigureAwait(false);
|
||||
await Done().ConfigureAwait(true);
|
||||
await channelTask.ConfigureAwait(false);
|
||||
await findTotalObjectsTask.ConfigureAwait(false);
|
||||
return new(root.id.NotNull(), _objectReferences.Freeze());
|
||||
@@ -109,7 +94,7 @@ public sealed class SerializeProcess(
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Dictionary<Id, NodeInfo>> Traverse(Base obj, bool isEnd, CancellationToken cancellationToken)
|
||||
private async Task<Dictionary<Id, NodeInfo>> Traverse(Base obj, CancellationToken cancellationToken)
|
||||
{
|
||||
var tasks = new List<Task<Dictionary<Id, NodeInfo>>>();
|
||||
foreach (var child in baseChildFinder.GetChildren(obj))
|
||||
@@ -118,7 +103,7 @@ public sealed class SerializeProcess(
|
||||
var tmp = child;
|
||||
var t = Task
|
||||
.Factory.StartNew(
|
||||
() => Traverse(tmp, false, cancellationToken),
|
||||
() => Traverse(tmp, cancellationToken),
|
||||
cancellationToken,
|
||||
TaskCreationOptions.AttachedToParent | TaskCreationOptions.PreferFairness,
|
||||
_belowNormal
|
||||
@@ -161,12 +146,6 @@ public sealed class SerializeProcess(
|
||||
}
|
||||
}
|
||||
_childClosurePool.Return(childClosures);
|
||||
|
||||
if (isEnd)
|
||||
{
|
||||
await Done().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
return currentClosures;
|
||||
}
|
||||
|
||||
@@ -223,7 +202,7 @@ public sealed class SerializeProcess(
|
||||
return new BaseItem(id, json, true, closures);
|
||||
}
|
||||
|
||||
public override async Task<List<BaseItem>> SendToServer(Batch<BaseItem> batch, CancellationToken cancellationToken)
|
||||
public override async Task SendToServer(Batch<BaseItem> batch, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_options.SkipServer && batch.Items.Count != 0)
|
||||
{
|
||||
@@ -238,9 +217,7 @@ public sealed class SerializeProcess(
|
||||
Interlocked.Exchange(ref _uploaded, _uploaded + batch.Items.Count);
|
||||
}
|
||||
progress?.Report(new(ProgressEvent.UploadedObjects, _uploaded, null));
|
||||
return objectBatch;
|
||||
}
|
||||
return batch.Items;
|
||||
}
|
||||
|
||||
public override void SaveToCache(List<BaseItem> batch)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using Speckle.Sdk.Helpers;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Serialisation.V2.Receive;
|
||||
using Speckle.Sdk.Serialisation.V2.Send;
|
||||
using Speckle.Sdk.SQLite;
|
||||
@@ -23,20 +21,14 @@ public interface ISerializeProcessFactory
|
||||
IProgress<ProgressArgs>? progress,
|
||||
DeserializeProcessOptions? options = null
|
||||
);
|
||||
|
||||
public ISerializeProcess CreateSerializeProcess(
|
||||
SerializeProcessOptions? options = null,
|
||||
IProgress<ProgressArgs>? progress = null
|
||||
);
|
||||
}
|
||||
|
||||
public class SerializeProcessFactory(
|
||||
ISpeckleHttp speckleHttp,
|
||||
ISdkActivityFactory activityFactory,
|
||||
IBaseChildFinder baseChildFinder,
|
||||
IObjectSerializerFactory objectSerializerFactory,
|
||||
IObjectDeserializerFactory objectDeserializerFactory,
|
||||
ISqLiteJsonCacheManagerFactory sqLiteJsonCacheManagerFactory
|
||||
ISqLiteJsonCacheManagerFactory sqLiteJsonCacheManagerFactory,
|
||||
IServerObjectManagerFactory serverObjectManagerFactory
|
||||
) : ISerializeProcessFactory
|
||||
{
|
||||
public ISerializeProcess CreateSerializeProcess(
|
||||
@@ -48,24 +40,7 @@ public class SerializeProcessFactory(
|
||||
)
|
||||
{
|
||||
var sqLiteJsonCacheManager = sqLiteJsonCacheManagerFactory.CreateFromStream(streamId);
|
||||
var serverObjectManager = new ServerObjectManager(speckleHttp, activityFactory, url, streamId, authorizationToken);
|
||||
return new SerializeProcess(
|
||||
progress,
|
||||
sqLiteJsonCacheManager,
|
||||
serverObjectManager,
|
||||
baseChildFinder,
|
||||
objectSerializerFactory,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
public ISerializeProcess CreateSerializeProcess(
|
||||
SerializeProcessOptions? options = null,
|
||||
IProgress<ProgressArgs>? progress = null
|
||||
)
|
||||
{
|
||||
var sqLiteJsonCacheManager = new DummySqLiteJsonCacheManager();
|
||||
var serverObjectManager = new DummySendServerObjectManager();
|
||||
var serverObjectManager = serverObjectManagerFactory.Create(url, streamId, authorizationToken);
|
||||
return new SerializeProcess(
|
||||
progress,
|
||||
sqLiteJsonCacheManager,
|
||||
@@ -85,9 +60,12 @@ public class SerializeProcessFactory(
|
||||
)
|
||||
{
|
||||
var sqLiteJsonCacheManager = sqLiteJsonCacheManagerFactory.CreateFromStream(streamId);
|
||||
var serverObjectManager = new ServerObjectManager(speckleHttp, activityFactory, url, streamId, authorizationToken);
|
||||
var serverObjectManager = serverObjectManagerFactory.Create(url, streamId, authorizationToken);
|
||||
|
||||
#pragma warning disable CA2000
|
||||
//owned by process, refactor later
|
||||
var objectLoader = new ObjectLoader(sqLiteJsonCacheManager, serverObjectManager, progress);
|
||||
#pragma warning restore CA2000
|
||||
return new DeserializeProcess(progress, objectLoader, objectDeserializerFactory, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using Speckle.InterfaceGenerator;
|
||||
using Speckle.Sdk.Helpers;
|
||||
using Speckle.Sdk.Logging;
|
||||
|
||||
namespace Speckle.Sdk.Serialisation.V2;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class ServerObjectManagerFactory(ISpeckleHttp speckleHttp, ISdkActivityFactory activityFactory)
|
||||
: IServerObjectManagerFactory
|
||||
{
|
||||
public IServerObjectManager Create(Uri url, string streamId, string? authorizationToken, int timeoutSeconds = 120) =>
|
||||
new ServerObjectManager(speckleHttp, activityFactory, url, streamId, authorizationToken, timeoutSeconds);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ public enum ProgressEvent
|
||||
|
||||
CacheCheck,
|
||||
DownloadBytes,
|
||||
DownloadObjects,
|
||||
DeserializeObject,
|
||||
|
||||
SerializeObject, // old
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
using Xunit;
|
||||
|
||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||
@@ -1,87 +1,78 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Objects.Tests.Unit.Geometry;
|
||||
|
||||
[TestFixture, TestOf(typeof(Arc))]
|
||||
public class ArcTests
|
||||
{
|
||||
private Plane TestPlaneCounterClockwise
|
||||
{
|
||||
get
|
||||
private Plane TestPlaneCounterClockwise =>
|
||||
new()
|
||||
{
|
||||
const string UNITS = Units.Meters;
|
||||
return new()
|
||||
{
|
||||
origin = new Point(0, 0, 0, UNITS),
|
||||
normal = new Vector(0, 0, 1, UNITS),
|
||||
xdir = new Vector(1, 0, 0, UNITS),
|
||||
ydir = new Vector(0, 1, 0, UNITS),
|
||||
units = UNITS,
|
||||
};
|
||||
}
|
||||
}
|
||||
origin = new Point(0, 0, 0, Units.Meters),
|
||||
normal = new Vector(0, 0, 1, Units.Meters),
|
||||
xdir = new Vector(1, 0, 0, Units.Meters),
|
||||
ydir = new Vector(0, 1, 0, Units.Meters),
|
||||
units = Units.Meters,
|
||||
};
|
||||
|
||||
private Plane TestPlaneClockwise
|
||||
{
|
||||
get
|
||||
private Plane TestPlaneClockwise =>
|
||||
new()
|
||||
{
|
||||
const string UNITS = Units.Meters;
|
||||
return new()
|
||||
{
|
||||
origin = new Point(0, 0, 0, UNITS),
|
||||
normal = new Vector(0, 0, -1, UNITS),
|
||||
xdir = new Vector(-1, 0, 0, UNITS),
|
||||
ydir = new Vector(0, 1, 0, UNITS),
|
||||
units = UNITS,
|
||||
};
|
||||
}
|
||||
}
|
||||
origin = new Point(0, 0, 0, Units.Meters),
|
||||
normal = new Vector(0, 0, -1, Units.Meters),
|
||||
xdir = new Vector(-1, 0, 0, Units.Meters),
|
||||
ydir = new Vector(0, 1, 0, Units.Meters),
|
||||
units = Units.Meters,
|
||||
};
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CanCreateArc_HalfCircle_CounterClockwise()
|
||||
{
|
||||
const string UNITS = Units.Meters;
|
||||
var counterClockwiseArc = new Arc()
|
||||
{
|
||||
plane = TestPlaneCounterClockwise,
|
||||
startPoint = new Point(1, 0, 0, UNITS),
|
||||
endPoint = new Point(-1, 0, 0, UNITS),
|
||||
midPoint = new Point(0, 1, 0, UNITS),
|
||||
units = UNITS,
|
||||
startPoint = new Point(1, 0, 0, Units.Meters),
|
||||
endPoint = new Point(-1, 0, 0, Units.Meters),
|
||||
midPoint = new Point(0, 1, 0, Units.Meters),
|
||||
units = Units.Meters,
|
||||
};
|
||||
|
||||
Assert.That(Point.Distance(counterClockwiseArc.midPoint, new Point(0, 1, 0, UNITS)), Is.EqualTo(0).Within(0.0001));
|
||||
Assert.That(
|
||||
Point.Distance(counterClockwiseArc.plane.origin, new Point(0, 0, 0, UNITS)),
|
||||
Is.EqualTo(0).Within(0.0001)
|
||||
);
|
||||
Assert.That(counterClockwiseArc.measure - Math.PI, Is.EqualTo(0).Within(0.0001));
|
||||
Assert.That(counterClockwiseArc.radius, Is.EqualTo(1).Within(0.0001));
|
||||
Assert.That(counterClockwiseArc.length, Is.EqualTo(Math.PI).Within(0.0001));
|
||||
Point.Distance(counterClockwiseArc.midPoint, new Point(0, 1, 0, Units.Meters)).Should().BeApproximately(0, 0.0001);
|
||||
|
||||
Point
|
||||
.Distance(counterClockwiseArc.plane.origin, new Point(0, 0, 0, Units.Meters))
|
||||
.Should()
|
||||
.BeApproximately(0, 0.0001);
|
||||
|
||||
(counterClockwiseArc.measure - Math.PI).Should().BeApproximately(0, 0.0001);
|
||||
|
||||
counterClockwiseArc.radius.Should().BeApproximately(1, 0.0001);
|
||||
|
||||
counterClockwiseArc.length.Should().BeApproximately(Math.PI, 0.0001);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CanCreateArc_HalfCircle_Clockwise()
|
||||
{
|
||||
const string UNITS = Units.Meters;
|
||||
var counterClockwiseArc = new Arc()
|
||||
var clockwiseArc = new Arc()
|
||||
{
|
||||
plane = TestPlaneClockwise,
|
||||
endPoint = new Point(1, 0, 0, UNITS),
|
||||
startPoint = new Point(-1, 0, 0, UNITS),
|
||||
midPoint = new Point(0, 1, 0, UNITS),
|
||||
units = UNITS,
|
||||
endPoint = new Point(1, 0, 0, Units.Meters),
|
||||
startPoint = new Point(-1, 0, 0, Units.Meters),
|
||||
midPoint = new Point(0, 1, 0, Units.Meters),
|
||||
units = Units.Meters,
|
||||
};
|
||||
|
||||
Assert.That(Point.Distance(counterClockwiseArc.midPoint, new Point(0, 1, 0, UNITS)), Is.EqualTo(0).Within(0.0001));
|
||||
Assert.That(
|
||||
Point.Distance(counterClockwiseArc.plane.origin, new Point(0, 0, 0, UNITS)),
|
||||
Is.EqualTo(0).Within(0.0001)
|
||||
);
|
||||
Assert.That(counterClockwiseArc.measure - Math.PI, Is.EqualTo(0).Within(0.0001));
|
||||
Assert.That(counterClockwiseArc.radius, Is.EqualTo(1).Within(0.0001));
|
||||
Assert.That(counterClockwiseArc.length, Is.EqualTo(Math.PI).Within(0.0001));
|
||||
Point.Distance(clockwiseArc.midPoint, new Point(0, 1, 0, Units.Meters)).Should().BeApproximately(0, 0.0001);
|
||||
|
||||
Point.Distance(clockwiseArc.plane.origin, new Point(0, 0, 0, Units.Meters)).Should().BeApproximately(0, 0.0001);
|
||||
|
||||
(clockwiseArc.measure - Math.PI).Should().BeApproximately(0, 0.0001);
|
||||
|
||||
clockwiseArc.radius.Should().BeApproximately(1, 0.0001);
|
||||
|
||||
clockwiseArc.length.Should().BeApproximately(Math.PI, 0.0001);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,23 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Objects.Tests.Unit.Geometry;
|
||||
|
||||
[TestFixture, TestOf(typeof(Box))]
|
||||
public class BoxTests
|
||||
{
|
||||
private Plane TestPlane
|
||||
{
|
||||
get
|
||||
private Plane TestPlane =>
|
||||
new()
|
||||
{
|
||||
const string UNITS = Units.Meters;
|
||||
return new()
|
||||
{
|
||||
origin = new Point(0, 0, 0, UNITS),
|
||||
normal = new Vector(0, 0, 1, UNITS),
|
||||
xdir = new Vector(1, 0, 0, UNITS),
|
||||
ydir = new Vector(0, 1, 0, UNITS),
|
||||
units = UNITS,
|
||||
};
|
||||
}
|
||||
}
|
||||
origin = new Point(0, 0, 0, Units.Meters),
|
||||
normal = new Vector(0, 0, 1, Units.Meters),
|
||||
xdir = new Vector(1, 0, 0, Units.Meters),
|
||||
ydir = new Vector(0, 1, 0, Units.Meters),
|
||||
units = Units.Meters,
|
||||
};
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CanCreateBox()
|
||||
{
|
||||
const string UNITS = Units.Meters;
|
||||
@@ -36,7 +30,10 @@ public class BoxTests
|
||||
units = UNITS,
|
||||
};
|
||||
|
||||
Assert.That(box.area, Is.EqualTo(2 * (2 * 4 + 2 * 6 + 4 * 6)).Within(0.0001));
|
||||
Assert.That(box.volume, Is.EqualTo(2 * 4 * 6).Within(0.0001));
|
||||
// Assert area
|
||||
box.area.Should().BeApproximately(2 * (2 * 4 + 2 * 6 + 4 * 6), 0.0001);
|
||||
|
||||
// Assert volume
|
||||
box.volume.Should().BeApproximately(2 * 4 * 6, 0.0001);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Objects.Tests.Unit.Geometry;
|
||||
|
||||
[TestFixture, TestOf(typeof(Circle))]
|
||||
public class CircleTests
|
||||
{
|
||||
private Plane TestPlane
|
||||
@@ -12,7 +12,7 @@ public class CircleTests
|
||||
get
|
||||
{
|
||||
const string UNITS = Units.Meters;
|
||||
return new()
|
||||
return new Plane
|
||||
{
|
||||
origin = new Point(0, 0, 0, UNITS),
|
||||
normal = new Vector(0, 0, 1, UNITS),
|
||||
@@ -23,18 +23,19 @@ public class CircleTests
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CanCreateCircle()
|
||||
{
|
||||
const string UNITS = Units.Meters;
|
||||
var circle = new Circle()
|
||||
var circle = new Circle
|
||||
{
|
||||
plane = TestPlane,
|
||||
radius = 5,
|
||||
units = UNITS,
|
||||
};
|
||||
|
||||
Assert.That(circle.length, Is.EqualTo(2 * Math.PI * 5).Within(0.0001));
|
||||
Assert.That(circle.area, Is.EqualTo(Math.PI * 5 * 5).Within(0.0001));
|
||||
// Use Shouldly assertions
|
||||
circle.length.Should().BeApproximately(2 * Math.PI * 5, 0.0001);
|
||||
circle.area.Should().BeApproximately(Math.PI * 5 * 5, 0.0001);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Objects.Tests.Unit.Geometry;
|
||||
|
||||
[TestFixture, TestOf(typeof(Mesh))]
|
||||
public class MeshTests
|
||||
{
|
||||
private static Mesh[] s_testCaseSource = { CreateBlenderStylePolygon(), CreateRhinoStylePolygon() };
|
||||
private static readonly Mesh[] TestCaseSource = { CreateBlenderStylePolygon(), CreateRhinoStylePolygon() };
|
||||
|
||||
[Test, TestCaseSource(nameof(s_testCaseSource))]
|
||||
[Theory]
|
||||
[MemberData(nameof(GetTestCaseSource))]
|
||||
public void CanAlignVertices(Mesh inPolygon)
|
||||
{
|
||||
inPolygon.AlignVerticesWithTexCoordsByIndex();
|
||||
|
||||
Assert.That(inPolygon.VerticesCount, Is.EqualTo(inPolygon.TextureCoordinatesCount));
|
||||
inPolygon.VerticesCount.Should().Be(inPolygon.TextureCoordinatesCount);
|
||||
|
||||
var expectedPolygon = CreateRhinoStylePolygon();
|
||||
|
||||
Assert.That(inPolygon.vertices, Is.EquivalentTo(expectedPolygon.vertices));
|
||||
Assert.That(inPolygon.faces, Is.EquivalentTo(expectedPolygon.faces));
|
||||
Assert.That(inPolygon.textureCoordinates, Is.EquivalentTo(expectedPolygon.textureCoordinates));
|
||||
inPolygon.vertices.Should().BeEquivalentTo(expectedPolygon.vertices);
|
||||
inPolygon.faces.Should().BeEquivalentTo(expectedPolygon.faces);
|
||||
inPolygon.textureCoordinates.Should().BeEquivalentTo(expectedPolygon.textureCoordinates);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetTestCaseSource() => TestCaseSource.Select(mesh => new object[] { mesh });
|
||||
|
||||
private static Mesh CreateRhinoStylePolygon()
|
||||
{
|
||||
return new Mesh
|
||||
{
|
||||
vertices = [0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0],
|
||||
faces = [3, 0, 1, 2, 3, 3, 4, 5],
|
||||
textureCoordinates = [0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
|
||||
vertices = new List<double> { 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0 },
|
||||
faces = new List<int> { 3, 0, 1, 2, 3, 3, 4, 5 },
|
||||
textureCoordinates = new List<double> { 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0 },
|
||||
units = Units.Meters,
|
||||
};
|
||||
}
|
||||
@@ -38,9 +41,9 @@ public class MeshTests
|
||||
{
|
||||
return new Mesh
|
||||
{
|
||||
vertices = [0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0],
|
||||
faces = [3, 0, 1, 2, 3, 0, 2, 3],
|
||||
textureCoordinates = [0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
|
||||
vertices = new List<double> { 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0 },
|
||||
faces = new List<int> { 3, 0, 1, 2, 3, 0, 2, 3 },
|
||||
textureCoordinates = new List<double> { 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0 },
|
||||
units = Units.Meters,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Objects.Tests.Unit.Geometry;
|
||||
|
||||
[TestFixture, TestOf(typeof(Point))]
|
||||
public class PointTests
|
||||
{
|
||||
[Test]
|
||||
[Fact]
|
||||
[SuppressMessage(
|
||||
"Assertion",
|
||||
"NUnit2010:Use EqualConstraint for better assertion messages in case of failure",
|
||||
Justification = "Need to explicitly test equality operator"
|
||||
"xUnit2013:Do not use equality check to assert boolean value",
|
||||
Justification = "Explicit equality operator tests are necessary"
|
||||
)]
|
||||
public void TestNull()
|
||||
{
|
||||
@@ -20,35 +20,49 @@ public class PointTests
|
||||
Point? b = null;
|
||||
Point c = new(0, 0, 0, Units.Meters);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(a == b, Is.True);
|
||||
Assert.That(a != b, Is.False);
|
||||
Assert.That(b == a, Is.True);
|
||||
Assert.That(b != a, Is.False);
|
||||
a.Should().Be(b);
|
||||
(a != b).Should().BeFalse();
|
||||
|
||||
Assert.That(a == c, Is.False);
|
||||
Assert.That(a != c, Is.True);
|
||||
Assert.That(c == a, Is.False);
|
||||
Assert.That(c != a, Is.True);
|
||||
});
|
||||
b.Should().Be(a);
|
||||
(b != a).Should().BeFalse();
|
||||
|
||||
(a == c).Should().BeFalse();
|
||||
(a != c).Should().BeTrue();
|
||||
|
||||
(c == a).Should().BeFalse();
|
||||
(c != a).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(1, 1, 1, "m", 1, 1, 1, "m", ExpectedResult = true)]
|
||||
[TestCase(1, 1, 1, "m", 0, 1, 1, "m", ExpectedResult = false)]
|
||||
[TestCase(1, 1, 1, "m", 1, 0, 1, "m", ExpectedResult = false)]
|
||||
[TestCase(1, 1, 1, "m", 1, 1, 0, "m", ExpectedResult = false)]
|
||||
[TestCase(1, 1, 1, "", 1, 1, 1, "", ExpectedResult = true)]
|
||||
[TestCase(1, 1, 1, null, 1, 1, 1, null, ExpectedResult = true)]
|
||||
[TestCase(1, 1, 1, "m", 1, 1, 1, "meters", ExpectedResult = false)]
|
||||
[TestCase(1, 1, 1, "m", 1, 1, 1, "M", ExpectedResult = false)]
|
||||
// Units
|
||||
public bool TestEqual(double x1, double y1, double z1, string units1, double x2, double y2, double z2, string units2)
|
||||
//TODO: Should(). units be allowed to be string?
|
||||
[Theory]
|
||||
[InlineData(1, 1, 1, "m", 1, 1, 1, "m", true)]
|
||||
[InlineData(1, 1, 1, "m", 0, 1, 1, "m", false)]
|
||||
[InlineData(1, 1, 1, "m", 1, 0, 1, "m", false)]
|
||||
[InlineData(1, 1, 1, "m", 1, 1, 0, "m", false)]
|
||||
[InlineData(1, 1, 1, "", 1, 1, 1, "", false)]
|
||||
[InlineData(1, 1, 1, null, 1, 1, 1, null, false)]
|
||||
[InlineData(1, 1, 1, "m", 1, 1, 1, "meters", false)]
|
||||
[InlineData(1, 1, 1, "m", 1, 1, 1, "M", false)]
|
||||
public void TestEqual(
|
||||
double x1,
|
||||
double y1,
|
||||
double z1,
|
||||
string? units1,
|
||||
double x2,
|
||||
double y2,
|
||||
double z2,
|
||||
string? units2,
|
||||
bool expectedResult
|
||||
)
|
||||
{
|
||||
if (string.IsNullOrEmpty(units1) || string.IsNullOrEmpty(units2))
|
||||
{
|
||||
expectedResult.Should().BeFalse();
|
||||
return;
|
||||
}
|
||||
Point p1 = new(x1, y1, z1, units1);
|
||||
Point p2 = new(x2, y2, z2, units2);
|
||||
|
||||
return p1 == p2;
|
||||
(p1 == p2).Should().Be(expectedResult);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +1,98 @@
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Objects.Tests.Unit.Geometry;
|
||||
|
||||
[TestFixture, TestOf(typeof(Transform))]
|
||||
public class TransformTests
|
||||
{
|
||||
private const float FLOAT_TOLERANCE = 1e-6f;
|
||||
|
||||
[Test, TestCaseSource(nameof(TransformTestCases))]
|
||||
[Theory]
|
||||
[MemberData(nameof(TransformTestCases))]
|
||||
public void ArrayBackAndForth(Matrix4x4 data)
|
||||
{
|
||||
// Arrange
|
||||
var start = new Transform() { matrix = data, units = Units.None };
|
||||
|
||||
// Act
|
||||
var asArr = Transform.CreateMatrix(start.ToArray());
|
||||
var end = new Transform() { matrix = asArr, units = Units.None };
|
||||
|
||||
Assert.That(end.matrix, Is.EqualTo(data));
|
||||
// Assert
|
||||
end.matrix.Should().Be(data);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TransformTestCases))]
|
||||
[Theory]
|
||||
[MemberData(nameof(TransformTestCases))]
|
||||
public void ConvertToUnits(Matrix4x4 data)
|
||||
{
|
||||
const float SF = 1000f;
|
||||
|
||||
var transpose = Matrix4x4.Transpose(data); //NOTE: Transform expects matrices transposed (translation in column 4)
|
||||
// Arrange
|
||||
var transpose = Matrix4x4.Transpose(data); // Transform expects matrices transposed (translation in column 4)
|
||||
var mm = Matrix4x4.Transpose(
|
||||
Transform.CreateMatrix(
|
||||
new Transform() { matrix = transpose, units = Units.Meters }.ConvertToUnits(Units.Millimeters)
|
||||
)
|
||||
);
|
||||
|
||||
// Act
|
||||
Matrix4x4.Decompose(data, out var ms, out var mr, out var mt);
|
||||
Matrix4x4.Decompose(mm, out var mms, out var mmr, out var mmt);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(mms.X, Is.EqualTo(ms.X).Within(FLOAT_TOLERANCE), "Expect scale x to be unchanged");
|
||||
Assert.That(mms.Y, Is.EqualTo(ms.Y).Within(FLOAT_TOLERANCE), "Expect scale y to be unchanged");
|
||||
Assert.That(mms.Z, Is.EqualTo(ms.Z).Within(FLOAT_TOLERANCE), "Expect scale z to be unchanged");
|
||||
// Assert
|
||||
mms.X.Should().BeApproximately(ms.X, FLOAT_TOLERANCE, "Expect scale x to be unchanged");
|
||||
mms.Y.Should().BeApproximately(ms.Y, FLOAT_TOLERANCE, "Expect scale y to be unchanged");
|
||||
mms.Z.Should().BeApproximately(ms.Z, FLOAT_TOLERANCE, "Expect scale z to be unchanged");
|
||||
|
||||
Assert.That(Quaternion.Dot(mr, mmr), Is.LessThan(1).Within(FLOAT_TOLERANCE), "Expect rot x to be equivalent");
|
||||
Quaternion.Dot(mr, mmr).Should().BeLessThan(1 + FLOAT_TOLERANCE, "Expect rotation to be equivalent");
|
||||
|
||||
Assert.That(mmt.X, Is.EqualTo(mt.X * SF).Within(FLOAT_TOLERANCE), $"Expect translation x to be scaled by {SF}");
|
||||
Assert.That(mmt.Y, Is.EqualTo(mt.Y * SF).Within(FLOAT_TOLERANCE), $"Expect translation y to be scaled by {SF}");
|
||||
Assert.That(mmt.Z, Is.EqualTo(mt.Z * SF).Within(FLOAT_TOLERANCE), $"Expect translation z to be scaled by {SF}");
|
||||
});
|
||||
mmt.X.Should().BeApproximately(mt.X * SF, FLOAT_TOLERANCE, $"Expect translation x to be scaled by {SF}");
|
||||
mmt.Y.Should().BeApproximately(mt.Y * SF, FLOAT_TOLERANCE, $"Expect translation y to be scaled by {SF}");
|
||||
mmt.Z.Should().BeApproximately(mt.Z * SF, FLOAT_TOLERANCE, $"Expect translation z to be scaled by {SF}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set of TRS transforms (row dominant i.e. translation in row 4)
|
||||
/// All with non-negative scale and rotation (for ease of testing scale and rot independently)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable TransformTestCases()
|
||||
public static IEnumerable<object[]> TransformTestCases()
|
||||
{
|
||||
var t = new Vector3(128.128f, 255.255f, 512.512f);
|
||||
var r = Quaternion.CreateFromYawPitchRoll(1.9f, 0.6666667f, 0.5f);
|
||||
var s = new Vector3(123f, 32f, 0.5f);
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.Identity).SetName("{m} Identity Matrix");
|
||||
yield return [Matrix4x4.Identity];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateTranslation(t)).SetName("{m} Translation Only (positive)");
|
||||
yield return [Matrix4x4.CreateTranslation(t)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateTranslation(t * -Vector3.UnitX)).SetName("{m} Translation Only -X");
|
||||
yield return [Matrix4x4.CreateTranslation(t * -Vector3.UnitX)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateTranslation(t * -Vector3.UnitY)).SetName("{m} Translation Only -Y");
|
||||
yield return [Matrix4x4.CreateTranslation(t * -Vector3.UnitY)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateTranslation(t * -Vector3.UnitZ)).SetName("{m} Translation Only -Z");
|
||||
yield return [Matrix4x4.CreateTranslation(t * -Vector3.UnitZ)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateTranslation(-t)).SetName("{m} Translation Only -XYZ ");
|
||||
yield return [Matrix4x4.CreateTranslation(-t)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateFromYawPitchRoll(0.5f, 0.0f, 0.0f)).SetName("{m} Rotation Only X ");
|
||||
yield return [Matrix4x4.CreateFromYawPitchRoll(0.5f, 0.0f, 0.0f)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateFromYawPitchRoll(0.0f, 0.5f, 0.0f)).SetName("{m} Rotation Only Y ");
|
||||
yield return [Matrix4x4.CreateFromYawPitchRoll(0.0f, 0.5f, 0.0f)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateFromYawPitchRoll(0.0f, 0.0f, 0.5f)).SetName("{m} Rotation Only Z ");
|
||||
yield return [Matrix4x4.CreateFromYawPitchRoll(0.0f, 0.0f, 0.5f)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateFromYawPitchRoll(0.5f, 0.5f, 0.5f)).SetName("{m} Rotation Only XYZ ");
|
||||
yield return [Matrix4x4.CreateFromYawPitchRoll(0.5f, 0.5f, 0.5f)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateFromQuaternion(r)).SetName("{m} Rotation Only");
|
||||
yield return [Matrix4x4.CreateFromQuaternion(r)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.Identity + Matrix4x4.CreateScale(s)).SetName("{m} Scale Only");
|
||||
yield return [Matrix4x4.Identity + Matrix4x4.CreateScale(s)];
|
||||
|
||||
yield return new TestCaseData(Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r)).SetName(
|
||||
"{m} Translation + Rotation"
|
||||
);
|
||||
yield return [Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r)];
|
||||
|
||||
yield return new TestCaseData(
|
||||
Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r) + Matrix4x4.CreateScale(s)
|
||||
).SetName("{m} Translation + Rotation + Scale");
|
||||
yield return [Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r) + Matrix4x4.CreateScale(s)];
|
||||
|
||||
yield return new TestCaseData(
|
||||
Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r) + Matrix4x4.CreateScale(-s)
|
||||
).SetName("{m} Translation + Rotation + -Scale");
|
||||
yield return [Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r) + Matrix4x4.CreateScale(-s)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.Drawing;
|
||||
using NUnit.Framework;
|
||||
using System.Drawing;
|
||||
using FluentAssertions;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Serialisation;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Objects.Tests.Unit;
|
||||
|
||||
@@ -15,8 +16,7 @@ namespace Speckle.Objects.Tests.Unit;
|
||||
/// </summary>
|
||||
public class ModelPropertySupportedTypes
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public ModelPropertySupportedTypes()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Speckle.Objects.Geometry.Arc).Assembly);
|
||||
@@ -62,7 +62,7 @@ public class ModelPropertySupportedTypes
|
||||
typeof(Matrix4x4),
|
||||
};
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void TestObjects()
|
||||
{
|
||||
foreach ((string _, Type type, List<string> _) in TypeLoader.Types)
|
||||
@@ -72,19 +72,24 @@ public class ModelPropertySupportedTypes
|
||||
foreach (var prop in members)
|
||||
{
|
||||
if (prop.PropertyType.IsAssignableTo(typeof(Base)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prop.PropertyType.IsEnum)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prop.PropertyType.IsSZArray)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Type propType = prop.PropertyType;
|
||||
Type typeDef = propType.IsGenericType ? propType.GetGenericTypeDefinition() : propType;
|
||||
Assert.That(
|
||||
_allowedTypes,
|
||||
Does.Contain(typeDef),
|
||||
$"{typeDef} was not in allowedTypes. (Origin: {type}.{prop.Name})"
|
||||
);
|
||||
|
||||
_allowedTypes.Should().Contain(typeDef, $"{typeDef} was not in allowedTypes. (Origin: {type}.{prop.Name})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
using FluentAssertions;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Objects.Geometry.Autocad;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Xunit;
|
||||
using Point = Speckle.Objects.Geometry.Point;
|
||||
|
||||
namespace Speckle.Objects.Tests.Unit;
|
||||
|
||||
public class ObjectBaseValidityTests
|
||||
{
|
||||
[Test]
|
||||
[Fact]
|
||||
public void TestThatTypeWithoutAttributeFails()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void InheritanceTest_Disallow()
|
||||
{
|
||||
var exception = Assert.Throws<InvalidOperationException>(() =>
|
||||
@@ -25,19 +25,19 @@ public class ObjectBaseValidityTests
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly, typeof(Test).Assembly);
|
||||
});
|
||||
exception.ShouldNotBeNull();
|
||||
exception.Message.ShouldBe(
|
||||
"Speckle.Objects.Tests.Unit.ObjectBaseValidityTests+Test inherits from Base has no SpeckleTypeAttribute"
|
||||
);
|
||||
exception.Should().NotBeNull();
|
||||
exception
|
||||
.Message.Should()
|
||||
.Be("Speckle.Objects.Tests.Unit.ObjectBaseValidityTests+Test inherits from Base has no SpeckleTypeAttribute");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void InheritanceTest_Allow()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly);
|
||||
var fullTypeString = TypeLoader.GetFullTypeString(typeof(AutocadPolycurve));
|
||||
fullTypeString.ShouldBe("Objects.Geometry.Polycurve:Objects.Geometry.Autocad.AutocadPolycurve");
|
||||
fullTypeString.Should().Be("Objects.Geometry.Polycurve:Objects.Geometry.Autocad.AutocadPolycurve");
|
||||
}
|
||||
|
||||
public class Test : Polycurve;
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="altcover" />
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="NUnit" />
|
||||
<PackageReference Include="NUnit3TestAdapter" />
|
||||
<PackageReference Include="Shouldly" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Speckle.Objects\Speckle.Objects.csproj" />
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Objects.Utils;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Dependencies;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Objects.Tests.Unit.Utils;
|
||||
|
||||
[TestFixture, TestOf(typeof(MeshTriangulationHelper))]
|
||||
public class MeshTriangulationHelperTests
|
||||
{
|
||||
[Test]
|
||||
public void PolygonTest([Range(3, 9)] int n, [Values] bool planar)
|
||||
public static IEnumerable<object[]> PolygonTestSource()
|
||||
{
|
||||
//Test Setup
|
||||
foreach (var x in EnumerableExtensions.RangeFrom(3, 9))
|
||||
{
|
||||
yield return new object[] { x, true };
|
||||
yield return new object[] { x, false };
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(PolygonTestSource))]
|
||||
public void PolygonTest(int n, bool planar)
|
||||
{
|
||||
// Test Setup
|
||||
List<double> vertices = new(n) { 0, planar ? 0 : 1, 1 };
|
||||
for (int i = 1; i < n; i++)
|
||||
{
|
||||
@@ -30,30 +41,30 @@ public class MeshTriangulationHelperTests
|
||||
units = Units.Meters,
|
||||
};
|
||||
|
||||
//Test
|
||||
// Test
|
||||
mesh.TriangulateMesh();
|
||||
|
||||
//Results
|
||||
// Results
|
||||
int numExpectedTriangles = n - 2;
|
||||
int expectedFaceCount = numExpectedTriangles * 4;
|
||||
|
||||
Assert.That(mesh.faces, Has.Count.EqualTo(expectedFaceCount));
|
||||
mesh.faces.Count.Should().Be(expectedFaceCount);
|
||||
|
||||
for (int i = 0; i < expectedFaceCount; i += 4)
|
||||
{
|
||||
Assert.That(mesh.faces[i], Is.EqualTo(3));
|
||||
Assert.That(mesh.faces.GetRange(i + 1, 3), Is.Unique);
|
||||
mesh.faces[i].Should().Be(3);
|
||||
mesh.faces.GetRange(i + 1, 3).Should().OnlyHaveUniqueItems();
|
||||
}
|
||||
|
||||
Assert.That(mesh.faces, Is.SupersetOf(Enumerable.Range(0, n)));
|
||||
|
||||
Assert.That(mesh.faces, Is.All.GreaterThanOrEqualTo(0));
|
||||
Assert.That(mesh.faces, Is.All.LessThan(Math.Max(n, 4)));
|
||||
var range = EnumerableExtensions.RangeFrom(0, n).ToList();
|
||||
mesh.faces.Should().BeSubsetOf(range);
|
||||
mesh.faces.Should().AllSatisfy(x => x.Should().BeGreaterThanOrEqualTo(0).And.BeLessThan(Math.Max(n, 4)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void DoesntFlipNormals()
|
||||
{
|
||||
//Test Setup
|
||||
// Test Setup
|
||||
List<double> vertices = new() { 0, 0, 0, 1, 0, 0, 1, 0, 1 };
|
||||
|
||||
List<int> faces = new() { 3, 0, 1, 2 };
|
||||
@@ -65,22 +76,26 @@ public class MeshTriangulationHelperTests
|
||||
units = Units.Meters,
|
||||
};
|
||||
|
||||
//Test
|
||||
// Test
|
||||
mesh.TriangulateMesh();
|
||||
|
||||
//Results
|
||||
|
||||
// Results
|
||||
List<int> shift1 = faces;
|
||||
List<int> shift2 = new() { 3, 1, 2, 0 };
|
||||
List<int> shift3 = new() { 3, 2, 0, 1 };
|
||||
|
||||
Assert.That(mesh.faces, Is.AnyOf(shift1, shift2, shift3));
|
||||
new List<int>[] { shift1, shift2, shift3 }
|
||||
.Any(x => mesh.faces.SequenceEqual(x))
|
||||
.Should()
|
||||
.BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PreserveQuads([Values] bool preserveQuads)
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void PreserveQuads(bool preserveQuads)
|
||||
{
|
||||
//Test Setup
|
||||
// Test Setup
|
||||
List<double> vertices = new() { 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1 };
|
||||
|
||||
List<int> faces = new() { 4, 0, 1, 2, 3 };
|
||||
@@ -92,14 +107,14 @@ public class MeshTriangulationHelperTests
|
||||
units = Units.Meters,
|
||||
};
|
||||
|
||||
//Tests
|
||||
// Tests
|
||||
mesh.TriangulateMesh(preserveQuads);
|
||||
|
||||
//Results
|
||||
// Results
|
||||
int expectedN = preserveQuads ? 4 : 3;
|
||||
int expectedFaceCount = preserveQuads ? 5 : 8;
|
||||
|
||||
Assert.That(mesh.faces, Has.Count.EqualTo(expectedFaceCount));
|
||||
Assert.That(mesh.faces[0], Is.EqualTo(expectedN));
|
||||
mesh.faces.Count.Should().Be(expectedFaceCount);
|
||||
mesh.faces[0].Should().Be(expectedN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Objects.Data;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Objects.Tests.Unit.Utils;
|
||||
|
||||
[TestFixture]
|
||||
public class ShallowCopyTests
|
||||
{
|
||||
[Test]
|
||||
public ShallowCopyTests()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanShallowCopy_Wall()
|
||||
{
|
||||
const string UNITS = Units.Meters;
|
||||
@@ -37,6 +44,6 @@ public class ShallowCopyTests
|
||||
|
||||
var shallow = ds.ShallowCopy();
|
||||
var displayValue = (IList)shallow["displayValue"].NotNull();
|
||||
Assert.That(ds.displayValue, Has.Count.EqualTo(displayValue.Count));
|
||||
ds.displayValue.Count.Should().Be(displayValue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,18 @@
|
||||
"net8.0": {
|
||||
"altcover": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.9.3, )",
|
||||
"resolved": "8.9.3",
|
||||
"contentHash": "auKC+pDCkLjfhFkSRaAUBu25BOmlLSqucR7YBs/Lkbdc0XRuJoklWafs1KKp+M+VoJ1f0TeMS6B/FO5IeIcu7w=="
|
||||
"requested": "[9.0.1, )",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA=="
|
||||
},
|
||||
"FluentAssertions": {
|
||||
"type": "Direct",
|
||||
"requested": "[7.0.0, )",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "mTLbcU991EQ1SEmNbVBaGGGJy0YFzvGd1sYJGNZ07nlPKuyHSn1I22aeKzqQXgEiaKyRO6MSCto9eN9VxMwBdA==",
|
||||
"dependencies": {
|
||||
"System.Configuration.ConfigurationManager": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GitVersion.MsBuild": {
|
||||
"type": "Direct",
|
||||
@@ -16,12 +25,12 @@
|
||||
},
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"type": "Direct",
|
||||
"requested": "[17.11.1, )",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
|
||||
"requested": "[17.12.0, )",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeCoverage": "17.11.1",
|
||||
"Microsoft.TestPlatform.TestHost": "17.11.1"
|
||||
"Microsoft.CodeCoverage": "17.12.0",
|
||||
"Microsoft.TestPlatform.TestHost": "17.12.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
@@ -34,53 +43,34 @@
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"NUnit": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.2.2, )",
|
||||
"resolved": "4.2.2",
|
||||
"contentHash": "mon0OPko28yZ/foVXrhiUvq1LReaGsBdziumyyYGxV/pOE4q92fuYeN+AF+gEU5pCjzykcdBt5l7xobTaiBjsg=="
|
||||
},
|
||||
"NUnit3TestAdapter": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.6.0, )",
|
||||
"resolved": "4.6.0",
|
||||
"contentHash": "R7e1+a4vuV/YS+ItfL7f//rG+JBvVeVLX4mHzFEZo4W1qEKl8Zz27AqvQSAqo+BtIzUCo4aAJMYa56VXS4hudw=="
|
||||
},
|
||||
"PolySharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.15.0, )",
|
||||
"resolved": "1.15.0",
|
||||
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
|
||||
},
|
||||
"Shouldly": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.2.1, )",
|
||||
"resolved": "4.2.1",
|
||||
"contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==",
|
||||
"dependencies": {
|
||||
"DiffEngine": "11.3.0",
|
||||
"EmptyFiles": "4.4.0"
|
||||
}
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.9.6, )",
|
||||
"resolved": "0.9.6",
|
||||
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
|
||||
},
|
||||
"DiffEngine": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.3.0",
|
||||
"contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==",
|
||||
"xunit": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.9.3, )",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
|
||||
"dependencies": {
|
||||
"EmptyFiles": "4.4.0",
|
||||
"System.Management": "6.0.1"
|
||||
"xunit.analyzers": "1.18.0",
|
||||
"xunit.assert": "2.9.3",
|
||||
"xunit.core": "[2.9.3]"
|
||||
}
|
||||
},
|
||||
"EmptyFiles": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw=="
|
||||
"xunit.runner.visualstudio": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.0, )",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "HggUqjQJe8PtDxcP25Q+CnR6Lz4oX3GElhD9V4oU2+75x9HI6A6sxbfKGS4UwU4t4yJaS9fBmAuriz8bQApNjw=="
|
||||
},
|
||||
"GraphQL.Client.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -110,8 +100,8 @@
|
||||
},
|
||||
"Microsoft.CodeCoverage": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core": {
|
||||
"type": "Transitive",
|
||||
@@ -176,21 +166,26 @@
|
||||
},
|
||||
"Microsoft.TestPlatform.ObjectModel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
|
||||
"dependencies": {
|
||||
"System.Reflection.Metadata": "1.6.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.TestPlatform.TestHost": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
|
||||
"dependencies": {
|
||||
"Microsoft.TestPlatform.ObjectModel": "17.11.1",
|
||||
"Microsoft.TestPlatform.ObjectModel": "17.12.0",
|
||||
"Newtonsoft.Json": "13.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.1",
|
||||
@@ -226,22 +221,26 @@
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"System.CodeDom": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
|
||||
},
|
||||
"System.Management": {
|
||||
"System.Configuration.ConfigurationManager": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.1",
|
||||
"contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "7T+m0kDSlIPTHIkPMIu6m6tV6qsMqJpvQWW2jIc2qi7sn40qxFo0q+7mEQAhMPXZHMKnWrnv47ntGlM/ejvw3g==",
|
||||
"dependencies": {
|
||||
"System.CodeDom": "6.0.0"
|
||||
"System.Security.Cryptography.ProtectedData": "6.0.0",
|
||||
"System.Security.Permissions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Drawing.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.SystemEvents": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Memory": {
|
||||
@@ -264,6 +263,73 @@
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"System.Security.AccessControl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ=="
|
||||
},
|
||||
"System.Security.Cryptography.ProtectedData": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ=="
|
||||
},
|
||||
"System.Security.Permissions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==",
|
||||
"dependencies": {
|
||||
"System.Security.AccessControl": "6.0.0",
|
||||
"System.Windows.Extensions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Windows.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==",
|
||||
"dependencies": {
|
||||
"System.Drawing.Common": "6.0.0"
|
||||
}
|
||||
},
|
||||
"xunit.abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.3",
|
||||
"contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
|
||||
},
|
||||
"xunit.analyzers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.18.0",
|
||||
"contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
|
||||
},
|
||||
"xunit.assert": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
|
||||
},
|
||||
"xunit.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
|
||||
"dependencies": {
|
||||
"xunit.extensibility.core": "[2.9.3]",
|
||||
"xunit.extensibility.execution": "[2.9.3]"
|
||||
}
|
||||
},
|
||||
"xunit.extensibility.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
|
||||
"dependencies": {
|
||||
"xunit.abstractions": "2.0.3"
|
||||
}
|
||||
},
|
||||
"xunit.extensibility.execution": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
|
||||
"dependencies": {
|
||||
"xunit.extensibility.core": "[2.9.3]"
|
||||
}
|
||||
},
|
||||
"speckle.objects": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
|
||||
@@ -3,9 +3,7 @@ using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Helpers;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Serialisation.V2;
|
||||
using Speckle.Sdk.Serialisation.V2.Receive;
|
||||
@@ -43,12 +41,11 @@ var token = serviceProvider.GetRequiredService<IAccountManager>().GetDefaultAcco
|
||||
var progress = new Progress(true);
|
||||
|
||||
var factory = new SerializeProcessFactory(
|
||||
serviceProvider.GetRequiredService<ISpeckleHttp>(),
|
||||
serviceProvider.GetRequiredService<ISdkActivityFactory>(),
|
||||
new BaseChildFinder(new BasePropertyGatherer()),
|
||||
new ObjectSerializerFactory(new BasePropertyGatherer()),
|
||||
new ObjectDeserializerFactory(),
|
||||
serviceProvider.GetRequiredService<ISqLiteJsonCacheManagerFactory>()
|
||||
serviceProvider.GetRequiredService<ISqLiteJsonCacheManagerFactory>(),
|
||||
serviceProvider.GetRequiredService<IServerObjectManagerFactory>()
|
||||
);
|
||||
var process = factory.CreateDeserializeProcess(new Uri(url), streamId, token, progress, new(skipCacheReceive));
|
||||
var @base = await process.Deserialize(rootId, default).ConfigureAwait(false);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
using Xunit;
|
||||
|
||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||
@@ -7,19 +7,34 @@ public class BaseComparer : IEqualityComparer<Base>
|
||||
public bool Equals(Base? x, Base? y)
|
||||
{
|
||||
if (ReferenceEquals(x, y))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (x is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (y is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Type type = x.GetType();
|
||||
if (type != y.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var types = DynamicBaseMemberType.Instance | DynamicBaseMemberType.Dynamic | DynamicBaseMemberType.SchemaIgnored;
|
||||
var membersX = x.GetMembers(types);
|
||||
var membersY = y.GetMembers(types);
|
||||
if (membersX.Count != membersY.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var kvp in membersX)
|
||||
{
|
||||
var propertyInfo = type.GetProperty(kvp.Key);
|
||||
@@ -28,7 +43,9 @@ public class BaseComparer : IEqualityComparer<Base>
|
||||
continue;
|
||||
}
|
||||
if (y[kvp.Key] != kvp.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return x.id == y.id && x.applicationId == y.applicationId;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using FluentAssertions;
|
||||
using Speckle.Newtonsoft.Json.Linq;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Host;
|
||||
@@ -12,19 +10,19 @@ using Speckle.Sdk.Serialisation.V2;
|
||||
using Speckle.Sdk.Serialisation.V2.Send;
|
||||
using Speckle.Sdk.SQLite;
|
||||
using Speckle.Sdk.Transports;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Serialization.Tests;
|
||||
|
||||
public class DetachedTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public DetachedTests()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(DetachedTests).Assembly, typeof(Polyline).Assembly);
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
[Fact(DisplayName = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
public async Task CanSerialize_New_Detached()
|
||||
{
|
||||
var expectedJson = """
|
||||
@@ -75,20 +73,22 @@ public class DetachedTests
|
||||
new ObjectSerializerFactory(new BasePropertyGatherer()),
|
||||
new SerializeProcessOptions(false, false, true, true)
|
||||
);
|
||||
await process2.Serialize(@base, default).ConfigureAwait(false);
|
||||
await process2.Serialize(@base, default);
|
||||
|
||||
objects.Count.ShouldBe(2);
|
||||
objects.ContainsKey("9ff8efb13c62fa80f3d1c4519376ba13").ShouldBeTrue();
|
||||
objects.ContainsKey("d3dd4621b2f68c3058c2b9c023a9de19").ShouldBeTrue();
|
||||
objects.Count.Should().Be(2);
|
||||
objects.ContainsKey("9ff8efb13c62fa80f3d1c4519376ba13").Should().BeTrue();
|
||||
objects.ContainsKey("d3dd4621b2f68c3058c2b9c023a9de19").Should().BeTrue();
|
||||
JToken
|
||||
.DeepEquals(JObject.Parse(expectedJson), JObject.Parse(objects["9ff8efb13c62fa80f3d1c4519376ba13"]))
|
||||
.ShouldBeTrue();
|
||||
.Should()
|
||||
.BeTrue();
|
||||
JToken
|
||||
.DeepEquals(JObject.Parse(detachedJson), JObject.Parse(objects["d3dd4621b2f68c3058c2b9c023a9de19"]))
|
||||
.ShouldBeTrue();
|
||||
.Should()
|
||||
.BeTrue();
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
[Fact(DisplayName = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
public void CanSerialize_Old_Detached()
|
||||
{
|
||||
var expectedJson = """
|
||||
@@ -133,19 +133,24 @@ public class DetachedTests
|
||||
var serializer = new SpeckleObjectSerializer(new[] { new MemoryTransport(objects) });
|
||||
var json = serializer.Serialize(@base);
|
||||
|
||||
objects.Count.ShouldBe(2);
|
||||
objects.ContainsKey("9ff8efb13c62fa80f3d1c4519376ba13").ShouldBeTrue();
|
||||
objects.ContainsKey("d3dd4621b2f68c3058c2b9c023a9de19").ShouldBeTrue();
|
||||
JToken.DeepEquals(JObject.Parse(json), JObject.Parse(objects["9ff8efb13c62fa80f3d1c4519376ba13"])).ShouldBeTrue();
|
||||
objects.Count.Should().Be(2);
|
||||
objects.ContainsKey("9ff8efb13c62fa80f3d1c4519376ba13").Should().BeTrue();
|
||||
objects.ContainsKey("d3dd4621b2f68c3058c2b9c023a9de19").Should().BeTrue();
|
||||
JToken
|
||||
.DeepEquals(JObject.Parse(json), JObject.Parse(objects["9ff8efb13c62fa80f3d1c4519376ba13"]))
|
||||
.Should()
|
||||
.BeTrue();
|
||||
JToken
|
||||
.DeepEquals(JObject.Parse(expectedJson), JObject.Parse(objects["9ff8efb13c62fa80f3d1c4519376ba13"]))
|
||||
.ShouldBeTrue();
|
||||
.Should()
|
||||
.BeTrue();
|
||||
JToken
|
||||
.DeepEquals(JObject.Parse(detachedJson), JObject.Parse(objects["d3dd4621b2f68c3058c2b9c023a9de19"]))
|
||||
.ShouldBeTrue();
|
||||
.Should()
|
||||
.BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void GetPropertiesExpected_Detached()
|
||||
{
|
||||
var @base = new SampleObjectBase();
|
||||
@@ -157,14 +162,14 @@ public class DetachedTests
|
||||
|
||||
var children = new BaseChildFinder(new BasePropertyGatherer()).GetChildProperties(@base).ToList();
|
||||
|
||||
children.Count.ShouldBe(4);
|
||||
children.First(x => x.Name == "detachedProp").PropertyAttributeInfo.IsDetachable.ShouldBeTrue();
|
||||
children.First(x => x.Name == "list").PropertyAttributeInfo.IsDetachable.ShouldBeTrue();
|
||||
children.First(x => x.Name == "arr").PropertyAttributeInfo.IsDetachable.ShouldBeTrue();
|
||||
children.First(x => x.Name == "@prop2").PropertyAttributeInfo.IsDetachable.ShouldBeTrue();
|
||||
children.Count.Should().Be(4);
|
||||
children.First(x => x.Name == "detachedProp").PropertyAttributeInfo.IsDetachable.Should().BeTrue();
|
||||
children.First(x => x.Name == "list").PropertyAttributeInfo.IsDetachable.Should().BeTrue();
|
||||
children.First(x => x.Name == "arr").PropertyAttributeInfo.IsDetachable.Should().BeTrue();
|
||||
children.First(x => x.Name == "@prop2").PropertyAttributeInfo.IsDetachable.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void GetPropertiesExpected_All()
|
||||
{
|
||||
var @base = new SampleObjectBase();
|
||||
@@ -176,20 +181,20 @@ public class DetachedTests
|
||||
|
||||
var children = new BasePropertyGatherer().ExtractAllProperties(@base).ToList();
|
||||
|
||||
children.Count.ShouldBe(9);
|
||||
children.First(x => x.Name == "dynamicProp").PropertyAttributeInfo.IsDetachable.ShouldBeFalse();
|
||||
children.First(x => x.Name == "attachedProp").PropertyAttributeInfo.IsDetachable.ShouldBeFalse();
|
||||
children.First(x => x.Name == "crazyProp").PropertyAttributeInfo.IsDetachable.ShouldBeFalse();
|
||||
children.First(x => x.Name == "speckle_type").PropertyAttributeInfo.IsDetachable.ShouldBeFalse();
|
||||
children.First(x => x.Name == "applicationId").PropertyAttributeInfo.IsDetachable.ShouldBeFalse();
|
||||
children.Count.Should().Be(9);
|
||||
children.First(x => x.Name == "dynamicProp").PropertyAttributeInfo.IsDetachable.Should().BeFalse();
|
||||
children.First(x => x.Name == "attachedProp").PropertyAttributeInfo.IsDetachable.Should().BeFalse();
|
||||
children.First(x => x.Name == "crazyProp").PropertyAttributeInfo.IsDetachable.Should().BeFalse();
|
||||
children.First(x => x.Name == "speckle_type").PropertyAttributeInfo.IsDetachable.Should().BeFalse();
|
||||
children.First(x => x.Name == "applicationId").PropertyAttributeInfo.IsDetachable.Should().BeFalse();
|
||||
|
||||
children.First(x => x.Name == "detachedProp").PropertyAttributeInfo.IsDetachable.ShouldBeTrue();
|
||||
children.First(x => x.Name == "list").PropertyAttributeInfo.IsDetachable.ShouldBeTrue();
|
||||
children.First(x => x.Name == "arr").PropertyAttributeInfo.IsDetachable.ShouldBeTrue();
|
||||
children.First(x => x.Name == "@prop2").PropertyAttributeInfo.IsDetachable.ShouldBeTrue();
|
||||
children.First(x => x.Name == "detachedProp").PropertyAttributeInfo.IsDetachable.Should().BeTrue();
|
||||
children.First(x => x.Name == "list").PropertyAttributeInfo.IsDetachable.Should().BeTrue();
|
||||
children.First(x => x.Name == "arr").PropertyAttributeInfo.IsDetachable.Should().BeTrue();
|
||||
children.First(x => x.Name == "@prop2").PropertyAttributeInfo.IsDetachable.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
[Fact(DisplayName = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
public async Task CanSerialize_New_Detached2()
|
||||
{
|
||||
var root = """
|
||||
@@ -267,17 +272,17 @@ public class DetachedTests
|
||||
new ObjectSerializerFactory(new BasePropertyGatherer()),
|
||||
new SerializeProcessOptions(false, false, true, true)
|
||||
);
|
||||
var results = await process2.Serialize(@base, default).ConfigureAwait(false);
|
||||
var results = await process2.Serialize(@base, default);
|
||||
|
||||
objects.Count.ShouldBe(9);
|
||||
objects.Count.Should().Be(9);
|
||||
var x = JObject.Parse(objects["2ebfd4f317754fce14cadd001151441e"]);
|
||||
JToken.DeepEquals(JObject.Parse(root), x).ShouldBeTrue();
|
||||
JToken.DeepEquals(JObject.Parse(root), x).Should().BeTrue();
|
||||
|
||||
results.RootId.ShouldBe(@base.id);
|
||||
results.ConvertedReferences.Count.ShouldBe(2);
|
||||
results.RootId.Should().Be(@base.id);
|
||||
results.ConvertedReferences.Count.Should().Be(2);
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
[Fact(DisplayName = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
public async Task CanSerialize_New_Detached_With_DataChunks()
|
||||
{
|
||||
var root = """
|
||||
@@ -340,20 +345,20 @@ public class DetachedTests
|
||||
new ObjectSerializerFactory(new BasePropertyGatherer()),
|
||||
new SerializeProcessOptions(false, false, true, true)
|
||||
);
|
||||
var results = await process2.Serialize(@base, default).ConfigureAwait(false);
|
||||
var results = await process2.Serialize(@base, default);
|
||||
|
||||
objects.Count.ShouldBe(3);
|
||||
objects.Count.Should().Be(3);
|
||||
var x = JObject.Parse(objects["efeadaca70a85ae6d3acfc93a8b380db"]);
|
||||
JToken.DeepEquals(JObject.Parse(root), x).ShouldBeTrue();
|
||||
JToken.DeepEquals(JObject.Parse(root), x).Should().BeTrue();
|
||||
|
||||
x = JObject.Parse(objects["0e61e61edee00404ec6e0f9f594bce24"]);
|
||||
JToken.DeepEquals(JObject.Parse(list1), x).ShouldBeTrue();
|
||||
JToken.DeepEquals(JObject.Parse(list1), x).Should().BeTrue();
|
||||
|
||||
x = JObject.Parse(objects["f70738e3e3e593ac11099a6ed6b71154"]);
|
||||
JToken.DeepEquals(JObject.Parse(list2), x).ShouldBeTrue();
|
||||
JToken.DeepEquals(JObject.Parse(list2), x).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
[Fact(DisplayName = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
public async Task CanSerialize_New_Detached_With_DataChunks2()
|
||||
{
|
||||
var root = """
|
||||
@@ -421,17 +426,17 @@ public class DetachedTests
|
||||
new ObjectSerializerFactory(new BasePropertyGatherer()),
|
||||
new SerializeProcessOptions(false, false, true, true)
|
||||
);
|
||||
var results = await process2.Serialize(@base, default).ConfigureAwait(false);
|
||||
var results = await process2.Serialize(@base, default);
|
||||
|
||||
objects.Count.ShouldBe(3);
|
||||
objects.Count.Should().Be(3);
|
||||
var x = JObject.Parse(objects["525b1e9eef4d07165abb4ffc518395fc"]);
|
||||
JToken.DeepEquals(JObject.Parse(root), x).ShouldBeTrue();
|
||||
JToken.DeepEquals(JObject.Parse(root), x).Should().BeTrue();
|
||||
|
||||
x = JObject.Parse(objects["0e61e61edee00404ec6e0f9f594bce24"]);
|
||||
JToken.DeepEquals(JObject.Parse(list1), x).ShouldBeTrue();
|
||||
JToken.DeepEquals(JObject.Parse(list1), x).Should().BeTrue();
|
||||
|
||||
x = JObject.Parse(objects["f70738e3e3e593ac11099a6ed6b71154"]);
|
||||
JToken.DeepEquals(JObject.Parse(list2), x).ShouldBeTrue();
|
||||
JToken.DeepEquals(JObject.Parse(list2), x).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,7 +534,9 @@ public class DummyServerObjectManager : IServerObjectManager
|
||||
|
||||
public class DummySendCacheManager(Dictionary<string, string> objects) : ISqLiteJsonCacheManager
|
||||
{
|
||||
public IEnumerable<string> GetAllObjects() => throw new NotImplementedException();
|
||||
public void Dispose() { }
|
||||
|
||||
public IReadOnlyCollection<(string, string)> GetAllObjects() => throw new NotImplementedException();
|
||||
|
||||
public void DeleteObject(string id) => throw new NotImplementedException();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Speckle.Sdk.Dependencies.Serialization;
|
||||
using Speckle.Sdk.Serialisation.V2;
|
||||
using Speckle.Sdk.Serialisation.V2.Send;
|
||||
using Speckle.Sdk.Transports;
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Shouldly;
|
||||
using Speckle.Newtonsoft.Json.Linq;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Dependencies.Serialization;
|
||||
using Speckle.Sdk.Serialisation.V2;
|
||||
using Speckle.Sdk.Serialisation.V2.Send;
|
||||
using Speckle.Sdk.Transports;
|
||||
|
||||
@@ -4,7 +4,9 @@ namespace Speckle.Sdk.Serialization.Tests;
|
||||
|
||||
public class DummySqLiteReceiveManager(Dictionary<string, string> savedObjects) : ISqLiteJsonCacheManager
|
||||
{
|
||||
public IEnumerable<string> GetAllObjects() => throw new NotImplementedException();
|
||||
public void Dispose() { }
|
||||
|
||||
public IReadOnlyCollection<(string, string)> GetAllObjects() => throw new NotImplementedException();
|
||||
|
||||
public void DeleteObject(string id) => throw new NotImplementedException();
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ public class DummySqLiteSendManager : ISqLiteJsonCacheManager
|
||||
|
||||
public bool HasObject(string objectId) => throw new NotImplementedException();
|
||||
|
||||
public IEnumerable<string> GetAllObjects() => throw new NotImplementedException();
|
||||
public IReadOnlyCollection<(string, string)> GetAllObjects() => throw new NotImplementedException();
|
||||
|
||||
public void DeleteObject(string id) => throw new NotImplementedException();
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Serialisation.V2.Send;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Serialization.Tests;
|
||||
|
||||
public class ExplicitInterfaceTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
// Constructor to replace [SetUp]
|
||||
public ExplicitInterfaceTests()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(TestClass).Assembly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact] // Replaces [Test]
|
||||
public async Task Test_Json()
|
||||
{
|
||||
var testClass = new TestClass() { RegularProperty = "Hello" };
|
||||
@@ -29,24 +29,28 @@ public class ExplicitInterfaceTests
|
||||
new ObjectSerializerFactory(new BasePropertyGatherer()),
|
||||
new SerializeProcessOptions(false, false, true, true)
|
||||
);
|
||||
await process2.Serialize(testClass, default).ConfigureAwait(false);
|
||||
objects.Count.ShouldBe(1);
|
||||
|
||||
await process2.Serialize(testClass, default);
|
||||
|
||||
objects.Count.Should().Be(1);
|
||||
objects["daaa67cfd73a957247cf2d631b7ca4f3"]
|
||||
.ShouldBe(
|
||||
.Should()
|
||||
.Be(
|
||||
"{\"RegularProperty\":\"Hello\",\"applicationId\":null,\"speckle_type\":\"Speckle.Core.Serialisation.TestClass\",\"id\":\"daaa67cfd73a957247cf2d631b7ca4f3\"}"
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact] // Replaces [Test]
|
||||
public void Test_ExtractAllProperties()
|
||||
{
|
||||
var testClass = new TestClass() { RegularProperty = "Hello" };
|
||||
|
||||
var gatherer = new BasePropertyGatherer();
|
||||
var properties = gatherer.ExtractAllProperties(testClass).ToList();
|
||||
properties.Count.ShouldBe(3);
|
||||
properties.Select(x => x.Name).ShouldContain("RegularProperty");
|
||||
properties.Select(x => x.Name).ShouldNotContain("TestProperty");
|
||||
|
||||
properties.Count.Should().Be(3);
|
||||
properties.Select(x => x.Name).Should().Contain("RegularProperty");
|
||||
properties.Select(x => x.Name).Should().NotContain("TestProperty");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
using FluentAssertions;
|
||||
using Speckle.Newtonsoft.Json.Linq;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Objects.Primitive;
|
||||
@@ -9,20 +8,20 @@ using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
using Speckle.Sdk.Serialisation;
|
||||
using Speckle.Sdk.Serialisation.V2.Send;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Serialization.Tests;
|
||||
|
||||
public class ExternalIdTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public ExternalIdTests()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Polyline).Assembly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("cfaf7ae0dfc5a7cf3343bb6db46ed238", "8d27f5c7fac36d985d89bb6d6d8acddc")]
|
||||
[Theory]
|
||||
[InlineData("cfaf7ae0dfc5a7cf3343bb6db46ed238", "8d27f5c7fac36d985d89bb6d6d8acddc")]
|
||||
public void ExternalIdTest_Detached(string lineId, string valueId)
|
||||
{
|
||||
var p = new Polyline() { units = "cm", value = [1, 2] };
|
||||
@@ -31,16 +30,16 @@ public class ExternalIdTests
|
||||
default
|
||||
);
|
||||
var list = serializer.Serialize(p).ToDictionary(x => x.Item1, x => x.Item2);
|
||||
list.ContainsKey(new Id(lineId)).ShouldBeTrue();
|
||||
list.ContainsKey(new Id(lineId)).Should().BeTrue();
|
||||
var json = list[new Id(lineId)];
|
||||
var jObject = JObject.Parse(json.Value);
|
||||
jObject.ContainsKey("__closure").ShouldBeTrue();
|
||||
jObject.ContainsKey("__closure").Should().BeTrue();
|
||||
var closures = (JObject)jObject["__closure"].NotNull();
|
||||
closures.ContainsKey(valueId).ShouldBeTrue();
|
||||
closures.ContainsKey(valueId).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("cfaf7ae0dfc5a7cf3343bb6db46ed238", "8d27f5c7fac36d985d89bb6d6d8acddc")]
|
||||
[Theory]
|
||||
[InlineData("cfaf7ae0dfc5a7cf3343bb6db46ed238", "8d27f5c7fac36d985d89bb6d6d8acddc")]
|
||||
public void ExternalIdTest_Detached_Nested(string lineId, string valueId)
|
||||
{
|
||||
var curve = new Curve()
|
||||
@@ -61,16 +60,16 @@ public class ExternalIdTests
|
||||
default
|
||||
);
|
||||
var list = serializer.Serialize(curve).ToDictionary(x => x.Item1, x => x.Item2);
|
||||
list.ContainsKey(new Id(lineId)).ShouldBeTrue();
|
||||
list.ContainsKey(new Id(lineId)).Should().BeTrue();
|
||||
var json = list[new Id(lineId)];
|
||||
var jObject = JObject.Parse(json.Value);
|
||||
jObject.ContainsKey("__closure").ShouldBeTrue();
|
||||
jObject.ContainsKey("__closure").Should().BeTrue();
|
||||
var closures = (JObject)jObject["__closure"].NotNull();
|
||||
closures.ContainsKey(valueId).ShouldBeTrue();
|
||||
closures.ContainsKey(valueId).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("cfaf7ae0dfc5a7cf3343bb6db46ed238", "8d27f5c7fac36d985d89bb6d6d8acddc")]
|
||||
[Theory]
|
||||
[InlineData("cfaf7ae0dfc5a7cf3343bb6db46ed238", "8d27f5c7fac36d985d89bb6d6d8acddc")]
|
||||
public void ExternalIdTest_Detached_Nested_More(string lineId, string valueId)
|
||||
{
|
||||
var curve = new Curve()
|
||||
@@ -92,16 +91,16 @@ public class ExternalIdTests
|
||||
default
|
||||
);
|
||||
var list = serializer.Serialize(polycurve).ToDictionary(x => x.Item1, x => x.Item2);
|
||||
list.ContainsKey(new Id(lineId)).ShouldBeTrue();
|
||||
list.ContainsKey(new Id(lineId)).Should().BeTrue();
|
||||
var json = list[new Id(lineId)];
|
||||
var jObject = JObject.Parse(json.Value);
|
||||
jObject.ContainsKey("__closure").ShouldBeTrue();
|
||||
jObject.ContainsKey("__closure").Should().BeTrue();
|
||||
var closures = (JObject)jObject["__closure"].NotNull();
|
||||
closures.ContainsKey(valueId).ShouldBeTrue();
|
||||
closures.ContainsKey(valueId).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("cfaf7ae0dfc5a7cf3343bb6db46ed238", "8d27f5c7fac36d985d89bb6d6d8acddc")]
|
||||
[Theory]
|
||||
[InlineData("cfaf7ae0dfc5a7cf3343bb6db46ed238", "8d27f5c7fac36d985d89bb6d6d8acddc")]
|
||||
public void ExternalIdTest_Detached_Nested_More_Too(string lineId, string valueId)
|
||||
{
|
||||
var curve = new Curve()
|
||||
@@ -125,11 +124,11 @@ public class ExternalIdTests
|
||||
default
|
||||
);
|
||||
var list = serializer.Serialize(@base).ToDictionary(x => x.Item1, x => x.Item2);
|
||||
list.ContainsKey(new Id(lineId)).ShouldBeTrue();
|
||||
list.ContainsKey(new Id(lineId)).Should().BeTrue();
|
||||
var json = list[new Id(lineId)];
|
||||
var jObject = JObject.Parse(json.Value);
|
||||
jObject.ContainsKey("__closure").ShouldBeTrue();
|
||||
jObject.ContainsKey("__closure").Should().BeTrue();
|
||||
var closures = (JObject)jObject["__closure"].NotNull();
|
||||
closures.ContainsKey(valueId).ShouldBeTrue();
|
||||
closures.ContainsKey(valueId).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -9,10 +9,10 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="altcover" />
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="NUnit" />
|
||||
<PackageReference Include="NUnit3TestAdapter" />
|
||||
<PackageReference Include="Shouldly" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,9 +4,18 @@
|
||||
"net8.0": {
|
||||
"altcover": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.9.3, )",
|
||||
"resolved": "8.9.3",
|
||||
"contentHash": "auKC+pDCkLjfhFkSRaAUBu25BOmlLSqucR7YBs/Lkbdc0XRuJoklWafs1KKp+M+VoJ1f0TeMS6B/FO5IeIcu7w=="
|
||||
"requested": "[9.0.1, )",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA=="
|
||||
},
|
||||
"FluentAssertions": {
|
||||
"type": "Direct",
|
||||
"requested": "[7.0.0, )",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "mTLbcU991EQ1SEmNbVBaGGGJy0YFzvGd1sYJGNZ07nlPKuyHSn1I22aeKzqQXgEiaKyRO6MSCto9eN9VxMwBdA==",
|
||||
"dependencies": {
|
||||
"System.Configuration.ConfigurationManager": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GitVersion.MsBuild": {
|
||||
"type": "Direct",
|
||||
@@ -16,12 +25,12 @@
|
||||
},
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"type": "Direct",
|
||||
"requested": "[17.11.1, )",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
|
||||
"requested": "[17.12.0, )",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeCoverage": "17.11.1",
|
||||
"Microsoft.TestPlatform.TestHost": "17.11.1"
|
||||
"Microsoft.CodeCoverage": "17.12.0",
|
||||
"Microsoft.TestPlatform.TestHost": "17.12.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
@@ -34,53 +43,34 @@
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"NUnit": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.2.2, )",
|
||||
"resolved": "4.2.2",
|
||||
"contentHash": "mon0OPko28yZ/foVXrhiUvq1LReaGsBdziumyyYGxV/pOE4q92fuYeN+AF+gEU5pCjzykcdBt5l7xobTaiBjsg=="
|
||||
},
|
||||
"NUnit3TestAdapter": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.6.0, )",
|
||||
"resolved": "4.6.0",
|
||||
"contentHash": "R7e1+a4vuV/YS+ItfL7f//rG+JBvVeVLX4mHzFEZo4W1qEKl8Zz27AqvQSAqo+BtIzUCo4aAJMYa56VXS4hudw=="
|
||||
},
|
||||
"PolySharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.15.0, )",
|
||||
"resolved": "1.15.0",
|
||||
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
|
||||
},
|
||||
"Shouldly": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.2.1, )",
|
||||
"resolved": "4.2.1",
|
||||
"contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==",
|
||||
"dependencies": {
|
||||
"DiffEngine": "11.3.0",
|
||||
"EmptyFiles": "4.4.0"
|
||||
}
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.9.6, )",
|
||||
"resolved": "0.9.6",
|
||||
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
|
||||
},
|
||||
"DiffEngine": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.3.0",
|
||||
"contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==",
|
||||
"xunit": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.9.3, )",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
|
||||
"dependencies": {
|
||||
"EmptyFiles": "4.4.0",
|
||||
"System.Management": "6.0.1"
|
||||
"xunit.analyzers": "1.18.0",
|
||||
"xunit.assert": "2.9.3",
|
||||
"xunit.core": "[2.9.3]"
|
||||
}
|
||||
},
|
||||
"EmptyFiles": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw=="
|
||||
"xunit.runner.visualstudio": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.0, )",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "HggUqjQJe8PtDxcP25Q+CnR6Lz4oX3GElhD9V4oU2+75x9HI6A6sxbfKGS4UwU4t4yJaS9fBmAuriz8bQApNjw=="
|
||||
},
|
||||
"GraphQL.Client.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -110,8 +100,8 @@
|
||||
},
|
||||
"Microsoft.CodeCoverage": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core": {
|
||||
"type": "Transitive",
|
||||
@@ -176,21 +166,26 @@
|
||||
},
|
||||
"Microsoft.TestPlatform.ObjectModel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
|
||||
"dependencies": {
|
||||
"System.Reflection.Metadata": "1.6.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.TestPlatform.TestHost": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
|
||||
"dependencies": {
|
||||
"Microsoft.TestPlatform.ObjectModel": "17.11.1",
|
||||
"Microsoft.TestPlatform.ObjectModel": "17.12.0",
|
||||
"Newtonsoft.Json": "13.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.1",
|
||||
@@ -226,22 +221,26 @@
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"System.CodeDom": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
|
||||
},
|
||||
"System.Management": {
|
||||
"System.Configuration.ConfigurationManager": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.1",
|
||||
"contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "7T+m0kDSlIPTHIkPMIu6m6tV6qsMqJpvQWW2jIc2qi7sn40qxFo0q+7mEQAhMPXZHMKnWrnv47ntGlM/ejvw3g==",
|
||||
"dependencies": {
|
||||
"System.CodeDom": "6.0.0"
|
||||
"System.Security.Cryptography.ProtectedData": "6.0.0",
|
||||
"System.Security.Permissions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Drawing.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.SystemEvents": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Memory": {
|
||||
@@ -264,6 +263,73 @@
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"System.Security.AccessControl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ=="
|
||||
},
|
||||
"System.Security.Cryptography.ProtectedData": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ=="
|
||||
},
|
||||
"System.Security.Permissions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==",
|
||||
"dependencies": {
|
||||
"System.Security.AccessControl": "6.0.0",
|
||||
"System.Windows.Extensions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Windows.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==",
|
||||
"dependencies": {
|
||||
"System.Drawing.Common": "6.0.0"
|
||||
}
|
||||
},
|
||||
"xunit.abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.3",
|
||||
"contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
|
||||
},
|
||||
"xunit.analyzers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.18.0",
|
||||
"contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
|
||||
},
|
||||
"xunit.assert": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
|
||||
},
|
||||
"xunit.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
|
||||
"dependencies": {
|
||||
"xunit.extensibility.core": "[2.9.3]",
|
||||
"xunit.extensibility.execution": "[2.9.3]"
|
||||
}
|
||||
},
|
||||
"xunit.extensibility.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
|
||||
"dependencies": {
|
||||
"xunit.abstractions": "2.0.3"
|
||||
}
|
||||
},
|
||||
"xunit.extensibility.execution": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
|
||||
"dependencies": {
|
||||
"xunit.extensibility.core": "[2.9.3]"
|
||||
}
|
||||
},
|
||||
"speckle.objects": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,36 +1,39 @@
|
||||
using GraphQL;
|
||||
using System.ComponentModel;
|
||||
using FluentAssertions;
|
||||
using GraphQL;
|
||||
using GraphQL.Client.Http;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.Api.GraphQL;
|
||||
|
||||
[TestOf(typeof(Client))]
|
||||
public class GraphQLClientExceptionHandling
|
||||
public class GraphQLClientExceptionHandling : IAsyncLifetime
|
||||
{
|
||||
private Client _sut;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_sut = await Fixtures.SeedUserWithClient();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
[Description($"Attempts to execute a query on a non-existent server, expect a {nameof(GraphQLHttpRequestException)}")]
|
||||
public void TestHttpLayer()
|
||||
public async Task TestHttpLayer()
|
||||
{
|
||||
_sut.GQLClient.Options.EndPoint = new Uri("http://127.0.0.1:1234"); //There is no server on this port...
|
||||
|
||||
Assert.ThrowsAsync<HttpRequestException>(async () => await _sut.ActiveUser.Get().ConfigureAwait(false));
|
||||
await Assert.ThrowsAsync<HttpRequestException>(async () => await _sut.ActiveUser.Get().ConfigureAwait(false));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
[Description(
|
||||
$"Attempts to execute a admin only command from a regular user, expect an inner {nameof(SpeckleGraphQLForbiddenException)}"
|
||||
)]
|
||||
public void TestGraphQLLayer_Forbidden()
|
||||
public async Task TestGraphQLLayer_Forbidden()
|
||||
{
|
||||
//language=graphql
|
||||
const string QUERY = """
|
||||
@@ -46,14 +49,14 @@ public class GraphQLClientExceptionHandling
|
||||
|
||||
""";
|
||||
GraphQLRequest request = new(query: QUERY);
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(
|
||||
async () => await _sut.ExecuteGraphQLRequest<dynamic>(request).ConfigureAwait(false)
|
||||
);
|
||||
Assert.That(ex?.InnerExceptions, Has.Exactly(1).TypeOf<SpeckleGraphQLForbiddenException>());
|
||||
ex.InnerExceptions.OfType<SpeckleGraphQLForbiddenException>().Count().Should().Be(1);
|
||||
}
|
||||
|
||||
[Test, Description($"Attempts to execute a bad query, expect an inner {nameof(SpeckleGraphQLInvalidQueryException)}")]
|
||||
public void TestGraphQLLayer_BadQuery()
|
||||
[Fact, Description($"Attempts to execute a bad query, expect an inner {nameof(SpeckleGraphQLInvalidQueryException)}")]
|
||||
public async Task TestGraphQLLayer_BadQuery()
|
||||
{
|
||||
//language=graphql
|
||||
const string QUERY = """
|
||||
@@ -64,41 +67,39 @@ public class GraphQLClientExceptionHandling
|
||||
}
|
||||
""";
|
||||
GraphQLRequest request = new(query: QUERY);
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(
|
||||
async () => await _sut.ExecuteGraphQLRequest<dynamic>(request).ConfigureAwait(false)
|
||||
);
|
||||
|
||||
Assert.That(ex?.InnerExceptions, Has.Exactly(1).TypeOf<SpeckleGraphQLInvalidQueryException>());
|
||||
ex.InnerExceptions.OfType<SpeckleGraphQLInvalidQueryException>().Count().Should().Be(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
[Description(
|
||||
$"Attempts to execute a query with an invalid input, expect an inner {nameof(SpeckleGraphQLBadInputException)}"
|
||||
)]
|
||||
public void TestGraphQLLayer_BadInput()
|
||||
public async Task TestGraphQLLayer_BadInput()
|
||||
{
|
||||
ProjectUpdateRoleInput input = new(null!, null!, null);
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(
|
||||
async () => await _sut.Project.UpdateRole(input).ConfigureAwait(false)
|
||||
);
|
||||
|
||||
Assert.That(ex?.InnerExceptions, Has.Exactly(2).TypeOf<SpeckleGraphQLBadInputException>());
|
||||
ex.InnerExceptions.OfType<SpeckleGraphQLBadInputException>().Count().Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCancel()
|
||||
[Fact]
|
||||
public async Task TestCancel()
|
||||
{
|
||||
using CancellationTokenSource cts = new();
|
||||
cts.Cancel();
|
||||
await cts.CancelAsync();
|
||||
|
||||
var ex = Assert.CatchAsync<OperationCanceledException>(
|
||||
var ex = await Assert.ThrowsAsync<TaskCanceledException>(
|
||||
async () => await _sut.ActiveUser.Get(cts.Token).ConfigureAwait(false)
|
||||
);
|
||||
|
||||
Assert.That(ex?.CancellationToken, Is.EqualTo(cts.Token));
|
||||
ex.CancellationToken.Should().BeEquivalentTo(cts.Token);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void TestDisposal()
|
||||
{
|
||||
_sut.Dispose();
|
||||
@@ -107,10 +108,10 @@ public class GraphQLClientExceptionHandling
|
||||
}
|
||||
|
||||
[
|
||||
Test,
|
||||
Fact,
|
||||
Description($"Attempts to execute a query with a mismatched type, expect an {nameof(JsonSerializationException)}")
|
||||
]
|
||||
public void TestDeserialization()
|
||||
public async Task TestDeserialization()
|
||||
{
|
||||
//language=graphql
|
||||
const string QUERY = """
|
||||
@@ -121,6 +122,8 @@ public class GraphQLClientExceptionHandling
|
||||
}
|
||||
""";
|
||||
GraphQLRequest request = new(query: QUERY);
|
||||
Assert.CatchAsync<JsonException>(async () => await _sut.ExecuteGraphQLRequest<int>(request).ConfigureAwait(false));
|
||||
await Assert.ThrowsAsync<JsonReaderException>(
|
||||
async () => await _sut.ExecuteGraphQLRequest<int>(request).ConfigureAwait(false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+34
-24
@@ -1,53 +1,60 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(ActiveUserResource))]
|
||||
public class ActiveUserResourceTests
|
||||
public class ActiveUserResourceTests : IAsyncLifetime
|
||||
{
|
||||
private Client _testUser;
|
||||
private ActiveUserResource Sut => _testUser.ActiveUser;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task Setup()
|
||||
// Setup method for xUnit using IAsyncLifetime
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_testUser = await Fixtures.SeedUserWithClient();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
// No resources to dispose
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ActiveUserGet()
|
||||
{
|
||||
var res = await Sut.Get();
|
||||
Assert.That(res, Is.Not.Null);
|
||||
Assert.That(res!.id, Is.EqualTo(_testUser.Account.userInfo.id));
|
||||
res.Should().NotBeNull();
|
||||
res!.id.Should().Be(_testUser.Account.userInfo.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ActiveUserGet_NonAuthed()
|
||||
{
|
||||
var result = await Fixtures.Unauthed.ActiveUser.Get();
|
||||
Assert.That(result, Is.EqualTo(null));
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ActiveUserUpdate()
|
||||
{
|
||||
const string NEW_NAME = "Ron";
|
||||
const string NEW_BIO = "Now I have a bio, isn't that nice!";
|
||||
const string NEW_COMPANY = "Limited Cooperation Organization Inc";
|
||||
|
||||
var res = await Sut.Update(new UserUpdateInput(name: NEW_NAME, bio: NEW_BIO, company: NEW_COMPANY));
|
||||
|
||||
Assert.That(res, Is.Not.Null);
|
||||
Assert.That(res.id, Is.EqualTo(_testUser.Account.userInfo.id));
|
||||
Assert.That(res.name, Is.EqualTo(NEW_NAME));
|
||||
Assert.That(res.company, Is.EqualTo(NEW_COMPANY));
|
||||
Assert.That(res.bio, Is.EqualTo(NEW_BIO));
|
||||
res.Should().NotBeNull();
|
||||
res.id.Should().Be(_testUser.Account.userInfo.id);
|
||||
res.name.Should().Be(NEW_NAME);
|
||||
res.company.Should().Be(NEW_COMPANY);
|
||||
res.bio.Should().Be(NEW_BIO);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ActiveUserGetProjects()
|
||||
{
|
||||
var p1 = await _testUser.Project.Create(new("Project 1", null, null));
|
||||
@@ -55,14 +62,17 @@ public class ActiveUserResourceTests
|
||||
|
||||
var res = await Sut.GetProjects();
|
||||
|
||||
Assert.That(res.items, Has.Exactly(1).Items.With.Property(nameof(Project.id)).EqualTo(p1.id));
|
||||
Assert.That(res.items, Has.Exactly(1).Items.With.Property(nameof(Project.id)).EqualTo(p2.id));
|
||||
Assert.That(res.items, Has.Count.EqualTo(2));
|
||||
res.items.Should().Contain(x => x.id == p1.id);
|
||||
res.items.Should().Contain(x => x.id == p2.id);
|
||||
res.items.Count.Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ActiveUserGetProjects_NoAuth()
|
||||
[Fact]
|
||||
public async Task ActiveUserGetProjects_NoAuth()
|
||||
{
|
||||
Assert.ThrowsAsync<SpeckleGraphQLException>(async () => await Fixtures.Unauthed.ActiveUser.GetProjects());
|
||||
await FluentActions
|
||||
.Invoking(async () => await Fixtures.Unauthed.ActiveUser.GetProjects())
|
||||
.Should()
|
||||
.ThrowAsync<SpeckleGraphQLException>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +1,111 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(CommentResource))]
|
||||
public class CommentResourceTests
|
||||
{
|
||||
private Client _testUser;
|
||||
private CommentResource Sut => _testUser.Comment;
|
||||
private Project _project;
|
||||
private Model _model;
|
||||
private string _versionId;
|
||||
private Comment _comment;
|
||||
private readonly Client _testUser;
|
||||
private readonly CommentResource Sut;
|
||||
private readonly Project _project;
|
||||
private readonly Model _model;
|
||||
private readonly string _versionId;
|
||||
private readonly Comment _comment;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
// Constructor for setup
|
||||
public CommentResourceTests()
|
||||
{
|
||||
_testUser = await Fixtures.SeedUserWithClient();
|
||||
_project = await _testUser.Project.Create(new("Test project", "", null));
|
||||
_model = await _testUser.Model.Create(new("Test Model 1", "", _project.id));
|
||||
_versionId = await Fixtures.CreateVersion(_testUser, _project.id, _model.id);
|
||||
_comment = await CreateComment();
|
||||
// Synchronous operations converted to async Task.Run for constructor
|
||||
_testUser = Task.Run(async () => await Fixtures.SeedUserWithClient()).Result!;
|
||||
_project = Task.Run(async () => await _testUser.Project.Create(new("Test project", "", null))).Result!;
|
||||
_model = Task.Run(async () => await _testUser.Model.Create(new("Test Model 1", "", _project.id))).Result!;
|
||||
_versionId = Task.Run(async () => await Fixtures.CreateVersion(_testUser, _project.id, _model.id)).Result!;
|
||||
_comment = Task.Run(CreateComment).Result!;
|
||||
Sut = _testUser.Comment;
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task Get()
|
||||
{
|
||||
var comment = await Sut.Get(_comment.id, _project.id);
|
||||
Assert.That(comment.id, Is.EqualTo(_comment.id));
|
||||
Assert.That(comment.authorId, Is.EqualTo(_testUser.Account.userInfo.id));
|
||||
|
||||
comment.Should().NotBeNull();
|
||||
comment.id.Should().Be(_comment.id);
|
||||
comment.authorId.Should().Be(_testUser.Account.userInfo.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task GetProjectComments()
|
||||
{
|
||||
var comments = await Sut.GetProjectComments(_project.id);
|
||||
Assert.That(comments.items.Count, Is.EqualTo(1));
|
||||
Assert.That(comments.totalCount, Is.EqualTo(1));
|
||||
|
||||
comments.Should().NotBeNull();
|
||||
comments.items.Count.Should().Be(1);
|
||||
comments.totalCount.Should().Be(1);
|
||||
|
||||
Comment comment = comments.items[0];
|
||||
Assert.That(comment, Is.Not.Null);
|
||||
Assert.That(comment, Has.Property(nameof(Comment.authorId)).EqualTo(_testUser.Account.userInfo.id));
|
||||
|
||||
Assert.That(comment, Has.Property(nameof(Comment.id)).EqualTo(_comment.id));
|
||||
Assert.That(comment, Has.Property(nameof(Comment.authorId)).EqualTo(_comment.authorId));
|
||||
Assert.That(comment, Has.Property(nameof(Comment.archived)).EqualTo(_comment.archived));
|
||||
Assert.That(comment, Has.Property(nameof(Comment.archived)).EqualTo(false));
|
||||
Assert.That(comment, Has.Property(nameof(Comment.createdAt)).EqualTo(_comment.createdAt));
|
||||
comment.Should().NotBeNull();
|
||||
comment.authorId.Should().Be(_testUser.Account.userInfo.id);
|
||||
comment.id.Should().Be(_comment.id);
|
||||
comment.authorId.Should().Be(_comment.authorId);
|
||||
comment.archived.Should().Be(false);
|
||||
comment.createdAt.Should().Be(_comment.createdAt);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task MarkViewed()
|
||||
{
|
||||
await Sut.MarkViewed(new(_comment.id, _project.id));
|
||||
var res = await Sut.Get(_comment.id, _project.id);
|
||||
|
||||
Assert.That(res.viewedAt, Is.Not.Null);
|
||||
var res = await Sut.Get(_comment.id, _project.id);
|
||||
res.viewedAt.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task Archive()
|
||||
{
|
||||
await Sut.Archive(new(_comment.id, _project.id, true));
|
||||
var archived = await Sut.Get(_comment.id, _project.id);
|
||||
Assert.That(archived.archived, Is.True);
|
||||
|
||||
archived.archived.Should().BeTrue();
|
||||
|
||||
await Sut.Archive(new(_comment.id, _project.id, false));
|
||||
var unarchived = await Sut.Get(_comment.id, _project.id);
|
||||
Assert.That(unarchived.archived, Is.False);
|
||||
|
||||
unarchived.archived.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task Edit()
|
||||
{
|
||||
var blobs = await Fixtures.SendBlobData(_testUser.Account, _project.id);
|
||||
var blobIds = blobs.Select(b => b.id.NotNull()).ToList();
|
||||
EditCommentInput input = new(new(blobIds, null), _comment.id, _project.id);
|
||||
var input = new EditCommentInput(new(blobIds, null), _comment.id, _project.id);
|
||||
|
||||
var editedComment = await Sut.Edit(input);
|
||||
|
||||
Assert.That(editedComment, Is.Not.Null);
|
||||
Assert.That(editedComment, Has.Property(nameof(Comment.id)).EqualTo(_comment.id));
|
||||
Assert.That(editedComment, Has.Property(nameof(Comment.authorId)).EqualTo(_comment.authorId));
|
||||
Assert.That(editedComment, Has.Property(nameof(Comment.createdAt)).EqualTo(_comment.createdAt));
|
||||
Assert.That(editedComment, Has.Property(nameof(Comment.updatedAt)).GreaterThanOrEqualTo(_comment.updatedAt));
|
||||
editedComment.Should().NotBeNull();
|
||||
editedComment.id.Should().Be(_comment.id);
|
||||
editedComment.authorId.Should().Be(_comment.authorId);
|
||||
editedComment.createdAt.Should().Be(_comment.createdAt);
|
||||
editedComment.updatedAt.Should().BeOnOrAfter(_comment.updatedAt);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task Reply()
|
||||
{
|
||||
var blobs = await Fixtures.SendBlobData(_testUser.Account, _project.id);
|
||||
var blobIds = blobs.Select(b => b.id.NotNull()).ToList();
|
||||
CreateCommentReplyInput input = new(new(blobIds, null), _comment.id, _project.id);
|
||||
var input = new CreateCommentReplyInput(new(blobIds, null), _comment.id, _project.id);
|
||||
|
||||
var editedComment = await Sut.Reply(input);
|
||||
|
||||
Assert.That(editedComment, Is.Not.Null);
|
||||
editedComment.Should().NotBeNull();
|
||||
}
|
||||
|
||||
private async Task<Comment> CreateComment()
|
||||
|
||||
+82
-39
@@ -1,94 +1,137 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Enums;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(ModelResource))]
|
||||
public class ModelResourceExceptionalTests
|
||||
public class ModelResourceExceptionalTests : IAsyncLifetime
|
||||
{
|
||||
private Client _testUser;
|
||||
private ModelResource Sut => _testUser.Model;
|
||||
private Project _project;
|
||||
private Model _model;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task Setup()
|
||||
// Replaces NUnit's OneTimeSetUp with an async constructor logic or initializer pattern
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_testUser = await Fixtures.SeedUserWithClient();
|
||||
_project = await _testUser.Project.Create(new("Test project", "", ProjectVisibility.Private));
|
||||
_model = await _testUser.Model.Create(new("Test Model", "", _project.id));
|
||||
}
|
||||
|
||||
[TestCase("")]
|
||||
[TestCase(" ")]
|
||||
public void ModelCreate_Throws_InvalidInput(string name)
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public async Task ModelCreate_Throws_InvalidInput(string name)
|
||||
{
|
||||
// Arrange
|
||||
CreateModelInput input = new(name, null, _project.id);
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => await Sut.Create(input));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLException>());
|
||||
|
||||
// Act & Assert
|
||||
var ex = await FluentActions
|
||||
.Invoking(async () => await Sut.Create(input))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
|
||||
ex.WithInnerExceptionExactly<SpeckleGraphQLException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ModelGet_Throws_NoAuth()
|
||||
[Fact]
|
||||
public async Task ModelGet_Throws_NoAuth()
|
||||
{
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(
|
||||
async () => await Fixtures.Unauthed.Model.Get(_model.id, _project.id)
|
||||
);
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLForbiddenException>());
|
||||
// Act & Assert
|
||||
var ex = await FluentActions
|
||||
.Invoking(async () => await Fixtures.Unauthed.Model.Get(_model.id, _project.id))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
|
||||
ex.WithInnerExceptionExactly<SpeckleGraphQLForbiddenException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ModelGet_Throws_NonExistentModel()
|
||||
[Fact]
|
||||
public async Task ModelGet_Throws_NonExistentModel()
|
||||
{
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => await Sut.Get("non existent model", _project.id));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLException>());
|
||||
// Act & Assert
|
||||
var ex = await FluentActions
|
||||
.Invoking(async () => await Sut.Get("non existent model", _project.id))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
ex.WithInnerExceptionExactly<SpeckleGraphQLException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ModelGet_Throws_NonExistentProject()
|
||||
[Fact]
|
||||
public async Task ModelGet_Throws_NonExistentProject()
|
||||
{
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => await Sut.Get(_model.id, "non existent project"));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLStreamNotFoundException>());
|
||||
// Act & Assert
|
||||
var ex = await FluentActions
|
||||
.Invoking(async () => await Sut.Get(_model.id, "non existent project"))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
ex.WithInnerExceptionExactly<SpeckleGraphQLStreamNotFoundException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ModelUpdate_Throws_NonExistentModel()
|
||||
[Fact]
|
||||
public async Task ModelUpdate_Throws_NonExistentModel()
|
||||
{
|
||||
// Arrange
|
||||
UpdateModelInput input = new("non-existent model", "MY new name", "MY new desc", _project.id);
|
||||
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => await Sut.Update(input));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLException>());
|
||||
// Act & Assert
|
||||
var ex = await FluentActions
|
||||
.Invoking(async () => await Sut.Update(input))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
ex.WithInnerExceptionExactly<SpeckleGraphQLException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ModelUpdate_Throws_NonExistentProject()
|
||||
[Fact]
|
||||
public async Task ModelUpdate_Throws_NonExistentProject()
|
||||
{
|
||||
// Arrange
|
||||
UpdateModelInput input = new(_model.id, "MY new name", "MY new desc", "non-existent project");
|
||||
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => await Sut.Update(input));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLForbiddenException>());
|
||||
// Act & Assert
|
||||
var ex = await FluentActions
|
||||
.Invoking(async () => await Sut.Update(input))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
ex.WithInnerExceptionExactly<SpeckleGraphQLForbiddenException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ModelUpdate_Throws_NonAuthProject()
|
||||
[Fact]
|
||||
public async Task ModelUpdate_Throws_NonAuthProject()
|
||||
{
|
||||
// Arrange
|
||||
UpdateModelInput input = new(_model.id, "MY new name", "MY new desc", _project.id);
|
||||
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => await Fixtures.Unauthed.Model.Update(input));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLForbiddenException>());
|
||||
// Act & Assert
|
||||
var ex = await FluentActions
|
||||
.Invoking(async () => await Fixtures.Unauthed.Model.Update(input))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
ex.WithInnerExceptionExactly<SpeckleGraphQLForbiddenException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ModelDelete_Throws_NoAuth()
|
||||
{
|
||||
// Arrange
|
||||
Model toDelete = await Sut.Create(new("Delete me", null, _project.id));
|
||||
DeleteModelInput input = new(toDelete.id, _project.id);
|
||||
|
||||
await Sut.Delete(input);
|
||||
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => await Sut.Delete(input));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLException>());
|
||||
// Act & Assert
|
||||
var ex = await FluentActions
|
||||
.Invoking(async () => await Sut.Delete(input))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
ex.WithInnerExceptionExactly<SpeckleGraphQLException>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +1,127 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(ModelResource))]
|
||||
public class ModelResourceTests
|
||||
public class ModelResourceTests : IAsyncLifetime
|
||||
{
|
||||
private Client _testUser;
|
||||
private ModelResource Sut => _testUser.Model;
|
||||
private Project _project;
|
||||
private Model _model;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// Runs instead of [SetUp] in NUnit
|
||||
_testUser = await Fixtures.SeedUserWithClient();
|
||||
_project = await _testUser.Project.Create(new("Test project", "", null));
|
||||
_model = await _testUser.Model.Create(new("Test Model", "", _project.id));
|
||||
}
|
||||
|
||||
[Order(1)]
|
||||
[TestCase("My Model", "My model description")]
|
||||
[TestCase("my/nested/model", null)]
|
||||
public async Task ModelCreate(string name, string description)
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
// Perform any cleanup, if needed
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("My Model", "My model description")]
|
||||
[InlineData("my/nested/model", null)]
|
||||
public async Task ModelCreate(string name, string? description)
|
||||
{
|
||||
// Arrange
|
||||
CreateModelInput input = new(name, description, _project.id);
|
||||
|
||||
// Act
|
||||
Model result = await Sut.Create(input);
|
||||
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result, Has.Property(nameof(result.id)).Not.Null);
|
||||
Assert.That(result, Has.Property(nameof(result.name)).EqualTo(input.name).IgnoreCase);
|
||||
Assert.That(result, Has.Property(nameof(result.description)).EqualTo(input.description));
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.id.Should().NotBeNull();
|
||||
result.name.Should().ContainEquivalentOf(input.name);
|
||||
result.description.Should().Be(input.description);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ModelGet()
|
||||
{
|
||||
// Act
|
||||
Model result = await Sut.Get(_model.id, _project.id);
|
||||
|
||||
Assert.That(result.id, Is.EqualTo(_model.id));
|
||||
Assert.That(result.name, Is.EqualTo(_model.name));
|
||||
Assert.That(result.description, Is.EqualTo(_model.description));
|
||||
Assert.That(result.createdAt, Is.EqualTo(_model.createdAt));
|
||||
Assert.That(result.updatedAt, Is.EqualTo(_model.updatedAt));
|
||||
// Assert
|
||||
result.id.Should().Be(_model.id);
|
||||
result.name.Should().Be(_model.name);
|
||||
result.description.Should().Be(_model.description);
|
||||
result.createdAt.Should().Be(_model.createdAt);
|
||||
result.updatedAt.Should().Be(_model.updatedAt);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(2)]
|
||||
[Fact]
|
||||
public async Task GetModels()
|
||||
{
|
||||
// Act
|
||||
var result = await Sut.GetModels(_project.id);
|
||||
|
||||
Assert.That(result.items, Has.Count.EqualTo(1));
|
||||
Assert.That(result.totalCount, Is.EqualTo(1));
|
||||
Assert.That(result.items[0], Has.Property(nameof(Model.id)).EqualTo(_model.id));
|
||||
// Assert
|
||||
result.items.Count.Should().Be(1);
|
||||
result.totalCount.Should().Be(1);
|
||||
result.items[0].id.Should().Be(_model.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task Project_GetModels()
|
||||
{
|
||||
// Act
|
||||
var result = await _testUser.Project.GetWithModels(_project.id);
|
||||
|
||||
Assert.That(result, Has.Property(nameof(Project.id)).EqualTo(_project.id));
|
||||
Assert.That(result.models.items, Has.Count.EqualTo(1));
|
||||
Assert.That(result.models.totalCount, Is.EqualTo(1));
|
||||
Assert.That(result.models.items[0], Has.Property(nameof(Model.id)).EqualTo(_model.id));
|
||||
// Assert
|
||||
result.id.Should().Be(_project.id);
|
||||
result.models.items.Count.Should().Be(1);
|
||||
result.models.totalCount.Should().Be(1);
|
||||
result.models.items[0].id.Should().Be(_model.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ModelUpdate()
|
||||
{
|
||||
// Arrange
|
||||
const string NEW_NAME = "MY new name";
|
||||
const string NEW_DESCRIPTION = "MY new desc";
|
||||
|
||||
UpdateModelInput input = new(_model.id, NEW_NAME, NEW_DESCRIPTION, _project.id);
|
||||
var input = new UpdateModelInput(_model.id, NEW_NAME, NEW_DESCRIPTION, _project.id);
|
||||
|
||||
// Act
|
||||
Model updatedModel = await Sut.Update(input);
|
||||
|
||||
Assert.That(updatedModel.id, Is.EqualTo(_model.id));
|
||||
Assert.That(updatedModel.name, Is.EqualTo(NEW_NAME).IgnoreCase);
|
||||
Assert.That(updatedModel.description, Is.EqualTo(NEW_DESCRIPTION));
|
||||
Assert.That(updatedModel.updatedAt, Is.GreaterThanOrEqualTo(_model.updatedAt));
|
||||
// Assert
|
||||
updatedModel.id.Should().Be(_model.id);
|
||||
updatedModel.name.Should().ContainEquivalentOf(NEW_NAME);
|
||||
updatedModel.description.Should().Be(NEW_DESCRIPTION);
|
||||
updatedModel.updatedAt.Should().BeOnOrAfter(_model.updatedAt);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ModelDelete()
|
||||
{
|
||||
DeleteModelInput input = new(_model.id, _project.id);
|
||||
// Arrange
|
||||
var input = new DeleteModelInput(_model.id, _project.id);
|
||||
|
||||
// Act
|
||||
await Sut.Delete(input);
|
||||
|
||||
var getEx = Assert.CatchAsync<AggregateException>(async () => await Sut.Get(_model.id, _project.id));
|
||||
Assert.That(getEx?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLException>());
|
||||
// Assert: Ensure fetching the deleted model throws an exception
|
||||
var getEx = await FluentActions
|
||||
.Invoking(() => Sut.Get(_model.id, _project.id))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
getEx.WithInnerExceptionExactly<SpeckleGraphQLException>();
|
||||
|
||||
var delEx = Assert.CatchAsync<AggregateException>(async () => await Sut.Delete(input));
|
||||
Assert.That(delEx?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLException>());
|
||||
// Assert: Ensure deleting the non-existing model again throws an exception
|
||||
var delEx = await FluentActions.Invoking(() => Sut.Delete(input)).Should().ThrowAsync<AggregateException>();
|
||||
getEx.WithInnerExceptionExactly<SpeckleGraphQLException>();
|
||||
}
|
||||
}
|
||||
|
||||
+26
-22
@@ -1,50 +1,54 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(OtherUserResource))]
|
||||
public class OtherUserResourceTests
|
||||
{
|
||||
private Client _testUser;
|
||||
private Account _testData;
|
||||
private readonly Client _testUser;
|
||||
private readonly Account _testData;
|
||||
private OtherUserResource Sut => _testUser.OtherUser;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task Setup()
|
||||
public OtherUserResourceTests()
|
||||
{
|
||||
_testUser = await Fixtures.SeedUserWithClient();
|
||||
_testData = await Fixtures.SeedUser();
|
||||
_testUser = Fixtures.SeedUserWithClient().GetAwaiter().GetResult();
|
||||
_testData = Fixtures.SeedUser().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task OtherUserGet()
|
||||
[Fact]
|
||||
public async Task OtherUserGet_Should_ReturnCorrectUser()
|
||||
{
|
||||
var res = await Sut.Get(_testData.userInfo.id);
|
||||
Assert.That(res, Is.Not.Null);
|
||||
Assert.That(res!.name, Is.EqualTo(_testData.userInfo.name));
|
||||
|
||||
res.Should().NotBeNull();
|
||||
res!.name.Should().Be(_testData.userInfo.name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task OtherUserGet_NonExistentUser()
|
||||
[Fact]
|
||||
public async Task OtherUserGet_NonExistentUser_Should_ReturnNull()
|
||||
{
|
||||
var result = await Sut.Get("AnIdThatDoesntExist");
|
||||
Assert.That(result, Is.Null);
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UserSearch()
|
||||
[Fact]
|
||||
public async Task UserSearch_Should_ReturnMatchingUser()
|
||||
{
|
||||
var res = await Sut.UserSearch(_testData.userInfo.email, 25);
|
||||
Assert.That(res.items, Has.Count.EqualTo(1));
|
||||
Assert.That(res.items[0].id, Is.EqualTo(_testData.userInfo.id));
|
||||
|
||||
res.items.Should().ContainSingle();
|
||||
res.items[0].id.Should().Be(_testData.userInfo.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UserSearch_NonExistentUser()
|
||||
[Fact]
|
||||
public async Task UserSearch_NonExistentUser_Should_ReturnEmptyList()
|
||||
{
|
||||
var res = await Sut.UserSearch("idontexist@example.com", 25);
|
||||
Assert.That(res.items, Has.Count.EqualTo(0));
|
||||
|
||||
res.items.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
+30
-15
@@ -1,33 +1,48 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(ProjectInviteResource))]
|
||||
public class ProjectInviteResourceExceptionalTests
|
||||
public class ProjectInviteResourceExceptionalTests : IAsyncLifetime
|
||||
{
|
||||
private Client _testUser;
|
||||
private Project _project;
|
||||
private ProjectInviteResource Sut => _testUser.ProjectInvite;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task Setup()
|
||||
// Replacing OneTimeSetUp with IAsyncLifetime's InitializeAsync
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_testUser = await Fixtures.SeedUserWithClient();
|
||||
_project = await _testUser.Project.Create(new("test", null, null));
|
||||
_project = await _testUser.Project.Create(new ProjectCreateInput("test", null, null));
|
||||
}
|
||||
|
||||
[TestCase(null, null, null, null)]
|
||||
[TestCase(null, "something", "something", null)]
|
||||
public void ProjectInviteCreate_InvalidInput(string email, string role, string serverRole, string userId)
|
||||
// Implementing IAsyncLifetime's DisposeAsync (optional if no cleanup is needed)
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, null, null)]
|
||||
[InlineData(null, "something", "something", null)]
|
||||
public async Task ProjectInviteCreate_InvalidInput_ShouldThrowSpeckleGraphQLException(
|
||||
string? email,
|
||||
string? role,
|
||||
string? serverRole,
|
||||
string? userId
|
||||
)
|
||||
{
|
||||
var ex = Assert.CatchAsync<AggregateException>(async () =>
|
||||
{
|
||||
var input = new ProjectInviteCreateInput(email, role, serverRole, userId);
|
||||
await Sut.Create(_project.id, input);
|
||||
});
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLException>());
|
||||
var input = new ProjectInviteCreateInput(email, role, serverRole, userId);
|
||||
|
||||
var exception = await FluentActions
|
||||
.Invoking(async () =>
|
||||
{
|
||||
await Sut.Create(_project.id, input);
|
||||
})
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
|
||||
exception.WithInnerExceptionExactly<SpeckleGraphQLException>();
|
||||
}
|
||||
}
|
||||
|
||||
+38
-40
@@ -1,22 +1,21 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(ProjectInviteResource))]
|
||||
public class ProjectInviteResourceTests
|
||||
public class ProjectInviteResourceTests : IAsyncLifetime
|
||||
{
|
||||
private Client _inviter,
|
||||
_invitee;
|
||||
private Project _project;
|
||||
private PendingStreamCollaborator _createdInvite;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_inviter = await Fixtures.SeedUserWithClient();
|
||||
_invitee = await Fixtures.SeedUserWithClient();
|
||||
@@ -24,6 +23,8 @@ public class ProjectInviteResourceTests
|
||||
_createdInvite = await SeedInvite();
|
||||
}
|
||||
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
private async Task<PendingStreamCollaborator> SeedInvite()
|
||||
{
|
||||
ProjectInviteCreateInput input = new(_invitee.Account.userInfo.email, null, null, null);
|
||||
@@ -32,7 +33,7 @@ public class ProjectInviteResourceTests
|
||||
return invites.First(i => i.projectId == res.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectInviteCreate_ByEmail()
|
||||
{
|
||||
ProjectInviteCreateInput input = new(_invitee.Account.userInfo.email, null, null, null);
|
||||
@@ -41,75 +42,72 @@ public class ProjectInviteResourceTests
|
||||
var invites = await _invitee.ActiveUser.GetProjectInvites();
|
||||
var invite = invites.First(i => i.projectId == res.id);
|
||||
|
||||
Assert.That(res, Has.Property(nameof(_project.id)).EqualTo(_project.id));
|
||||
Assert.That(res.invitedTeam, Has.Count.EqualTo(1));
|
||||
Assert.That(invite.user!.id, Is.EqualTo(_invitee.Account.userInfo.id));
|
||||
Assert.That(invite.token, Is.Not.Null);
|
||||
res.id.Should().Be(_project.id);
|
||||
res.invitedTeam.Should().ContainSingle();
|
||||
invite.user!.id.Should().Be(_invitee.Account.userInfo.id);
|
||||
invite.token.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectInviteCreate_ByUserId()
|
||||
{
|
||||
ProjectInviteCreateInput input = new(null, null, null, _invitee.Account.userInfo.id);
|
||||
var res = await _inviter.ProjectInvite.Create(_project.id, input);
|
||||
|
||||
Assert.That(res, Has.Property(nameof(_project.id)).EqualTo(_project.id));
|
||||
Assert.That(res.invitedTeam, Has.Count.EqualTo(1));
|
||||
Assert.That(res.invitedTeam[0].user!.id, Is.EqualTo(_invitee.Account.userInfo.id));
|
||||
res.id.Should().Be(_project.id);
|
||||
res.invitedTeam.Should().ContainSingle();
|
||||
res.invitedTeam[0].user!.id.Should().Be(_invitee.Account.userInfo.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectInviteGet()
|
||||
{
|
||||
var collaborator = await _invitee.ProjectInvite.Get(_project.id, _createdInvite.token);
|
||||
var collaborator = await _invitee.ProjectInvite.Get(_project.id, _createdInvite.token).NotNull();
|
||||
|
||||
Assert.That(
|
||||
collaborator,
|
||||
Has.Property(nameof(PendingStreamCollaborator.inviteId)).EqualTo(_createdInvite.inviteId)
|
||||
);
|
||||
Assert.That(collaborator!.user!.id, Is.EqualTo(_createdInvite.user!.id));
|
||||
collaborator.inviteId.Should().Be(_createdInvite.inviteId);
|
||||
collaborator.user!.id.Should().Be(_createdInvite.user!.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectInviteGet_NonExisting()
|
||||
{
|
||||
var collaborator = await _invitee.ProjectInvite.Get(_project.id, "this is not a real token");
|
||||
|
||||
Assert.That(collaborator, Is.Null);
|
||||
collaborator.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectInviteUse_MemberAdded()
|
||||
{
|
||||
ProjectInviteUseInput input = new(true, _createdInvite.projectId, _createdInvite.token.NotNull());
|
||||
await _invitee.ProjectInvite.Use(input);
|
||||
|
||||
var project = await _inviter.Project.GetWithTeam(_project.id);
|
||||
var teamMembers = project.team.Select(c => c.user.id);
|
||||
var teamMembers = project.team.Select(c => c.user.id).ToArray();
|
||||
var expectedTeamMembers = new[] { _inviter.Account.userInfo.id, _invitee.Account.userInfo.id };
|
||||
Assert.That(teamMembers, Is.EquivalentTo(expectedTeamMembers));
|
||||
|
||||
teamMembers.Should().BeEquivalentTo(expectedTeamMembers);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectInviteCancel_MemberNotAdded()
|
||||
{
|
||||
var res = await _inviter.ProjectInvite.Cancel(_createdInvite.projectId, _createdInvite.inviteId);
|
||||
|
||||
Assert.That(res.invitedTeam, Is.Empty);
|
||||
res.invitedTeam.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(StreamRoles.STREAM_OWNER)]
|
||||
[TestCase(StreamRoles.STREAM_CONTRIBUTOR)]
|
||||
[TestCase(StreamRoles.STREAM_REVIEWER)]
|
||||
[TestCase(StreamRoles.REVOKE)]
|
||||
[Theory]
|
||||
[InlineData(StreamRoles.STREAM_OWNER)]
|
||||
[InlineData(StreamRoles.STREAM_CONTRIBUTOR)]
|
||||
[InlineData(StreamRoles.STREAM_REVIEWER)]
|
||||
[InlineData(StreamRoles.REVOKE)]
|
||||
public async Task ProjectUpdateRole(string? newRole)
|
||||
{
|
||||
await ProjectInviteUse_MemberAdded();
|
||||
ProjectUpdateRoleInput input = new(_invitee.Account.userInfo.id, _project.id, newRole);
|
||||
_ = await _inviter.Project.UpdateRole(input);
|
||||
|
||||
Project finalProject = await _invitee.Project.Get(_project.id);
|
||||
Assert.That(finalProject.role, Is.EqualTo(newRole));
|
||||
ProjectUpdateRoleInput input = new(_invitee.Account.userInfo.id, _project.id, newRole);
|
||||
await _inviter.Project.UpdateRole(input);
|
||||
|
||||
var finalProject = await _invitee.Project.Get(_project.id);
|
||||
finalProject.role.Should().Be(newRole);
|
||||
}
|
||||
}
|
||||
|
||||
+52
-46
@@ -1,15 +1,16 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL;
|
||||
using Speckle.Sdk.Api.GraphQL.Enums;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(ProjectResource))]
|
||||
public class ProjectResourceExceptionalTests
|
||||
public class ProjectResourceExceptionalTests : IAsyncLifetime
|
||||
{
|
||||
private Client _testUser,
|
||||
_secondUser,
|
||||
@@ -17,8 +18,9 @@ public class ProjectResourceExceptionalTests
|
||||
private Project _testProject;
|
||||
private ProjectResource Sut => _testUser.Project;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task Setup()
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_testUser = await Fixtures.SeedUserWithClient();
|
||||
_secondUser = await Fixtures.SeedUserWithClient();
|
||||
@@ -33,8 +35,8 @@ public class ProjectResourceExceptionalTests
|
||||
// 4. Server doesn't exist (is down)
|
||||
//There's got to be a smarter way to parametrise these...
|
||||
|
||||
[Test]
|
||||
public void ProjectCreate_WithoutAuth()
|
||||
[Fact]
|
||||
public async Task ProjectCreate_WithoutAuth()
|
||||
{
|
||||
ProjectCreateInput input = new(
|
||||
"The best project",
|
||||
@@ -42,84 +44,88 @@ public class ProjectResourceExceptionalTests
|
||||
ProjectVisibility.Private
|
||||
);
|
||||
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => _ = await _unauthedUser.Project.Create(input));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLForbiddenException>());
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(async () => _ = await _unauthedUser.Project.Create(input));
|
||||
ex.InnerExceptions.Single().Should().BeOfType<SpeckleGraphQLForbiddenException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectGet_WithoutAuth()
|
||||
{
|
||||
ProjectCreateInput input = new("Private Stream", "A very private stream", ProjectVisibility.Private);
|
||||
|
||||
Project privateStream = await Sut.Create(input);
|
||||
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => _ = await _unauthedUser.Project.Get(privateStream.id));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLForbiddenException>());
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(
|
||||
async () => _ = await _unauthedUser.Project.Get(privateStream.id)
|
||||
);
|
||||
ex.InnerExceptions.Single().Should().BeOfType<SpeckleGraphQLForbiddenException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ProjectGet_NonExistentProject()
|
||||
[Fact]
|
||||
public async Task ProjectGet_NonExistentProject()
|
||||
{
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => await Sut.Get("NonExistentProject"));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLStreamNotFoundException>());
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(async () => await Sut.Get("NonExistentProject"));
|
||||
ex.InnerExceptions.Single().Should().BeOfType<SpeckleGraphQLStreamNotFoundException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ProjectUpdate_NonExistentProject()
|
||||
[Fact]
|
||||
public async Task ProjectUpdate_NonExistentProject()
|
||||
{
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(
|
||||
async () => _ = await Sut.Update(new("NonExistentProject", "My new name"))
|
||||
);
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLForbiddenException>());
|
||||
ex.InnerExceptions.Single().Should().BeOfType<SpeckleGraphQLForbiddenException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ProjectUpdate_NoAuth()
|
||||
[Fact]
|
||||
public async Task ProjectUpdate_NoAuth()
|
||||
{
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(
|
||||
async () => _ = await _unauthedUser.Project.Update(new(_testProject.id, "My new name"))
|
||||
);
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLForbiddenException>());
|
||||
ex.InnerExceptions.Single().Should().BeOfType<SpeckleGraphQLForbiddenException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(StreamRoles.STREAM_OWNER)]
|
||||
[TestCase(StreamRoles.STREAM_CONTRIBUTOR)]
|
||||
[TestCase(StreamRoles.STREAM_REVIEWER)]
|
||||
[TestCase(StreamRoles.REVOKE)]
|
||||
public void ProjectUpdateRole_NonExistentProject(string newRole)
|
||||
[Theory]
|
||||
[InlineData(StreamRoles.STREAM_OWNER)]
|
||||
[InlineData(StreamRoles.STREAM_CONTRIBUTOR)]
|
||||
[InlineData(StreamRoles.STREAM_REVIEWER)]
|
||||
[InlineData(StreamRoles.REVOKE)]
|
||||
public async Task ProjectUpdateRole_NonExistentProject(string? newRole)
|
||||
{
|
||||
ProjectUpdateRoleInput input = new(_secondUser.Account.id.NotNull(), "NonExistentProject", newRole);
|
||||
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => _ = await Sut.UpdateRole(input));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLForbiddenException>());
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(async () => _ = await Sut.UpdateRole(input));
|
||||
ex.InnerExceptions.Single().Should().BeOfType<SpeckleGraphQLForbiddenException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(StreamRoles.STREAM_OWNER)]
|
||||
[TestCase(StreamRoles.STREAM_CONTRIBUTOR)]
|
||||
[TestCase(StreamRoles.STREAM_REVIEWER)]
|
||||
[TestCase(StreamRoles.REVOKE)]
|
||||
public void ProjectUpdateRole_NonAuth(string newRole)
|
||||
[Theory]
|
||||
[InlineData(StreamRoles.STREAM_OWNER)]
|
||||
[InlineData(StreamRoles.STREAM_CONTRIBUTOR)]
|
||||
[InlineData(StreamRoles.STREAM_REVIEWER)]
|
||||
[InlineData(StreamRoles.REVOKE)]
|
||||
public async Task ProjectUpdateRole_NonAuth(string? newRole)
|
||||
{
|
||||
ProjectUpdateRoleInput input = new(_secondUser.Account.id.NotNull(), "NonExistentProject", newRole);
|
||||
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => _ = await _unauthedUser.Project.UpdateRole(input));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLForbiddenException>());
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(
|
||||
async () => _ = await _unauthedUser.Project.UpdateRole(input)
|
||||
);
|
||||
ex.InnerExceptions.Single().Should().BeOfType<SpeckleGraphQLForbiddenException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectDelete_NonExistentProject()
|
||||
{
|
||||
await Sut.Delete(_testProject.id);
|
||||
|
||||
var ex = Assert.ThrowsAsync<AggregateException>(async () => _ = await Sut.Get(_testProject.id));
|
||||
Assert.That(ex?.InnerExceptions, Has.One.Items.And.All.TypeOf<SpeckleGraphQLStreamNotFoundException>());
|
||||
var ex = await Assert.ThrowsAsync<AggregateException>(async () => _ = await Sut.Get(_testProject.id));
|
||||
ex.InnerExceptions.Single().Should().BeOfType<SpeckleGraphQLStreamNotFoundException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ProjectInvites_NoAuth()
|
||||
[Fact]
|
||||
public async Task ProjectInvites_NoAuth()
|
||||
{
|
||||
Assert.ThrowsAsync<SpeckleException>(async () => await Fixtures.Unauthed.ActiveUser.ProjectInvites());
|
||||
await Assert.ThrowsAsync<SpeckleException>(async () => await Fixtures.Unauthed.ActiveUser.ProjectInvites());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,103 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Enums;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(ProjectResource))]
|
||||
public class ProjectResourceTests
|
||||
{
|
||||
private Client _testUser;
|
||||
private Project _testProject;
|
||||
private readonly Client _testUser;
|
||||
private readonly Project _testProject;
|
||||
private ProjectResource Sut => _testUser.Project;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task Setup()
|
||||
public ProjectResourceTests()
|
||||
{
|
||||
_testUser = await Fixtures.SeedUserWithClient();
|
||||
_testProject = await _testUser.Project.Create(new("test project123", "desc", null));
|
||||
var setupTask = Setup();
|
||||
setupTask.Wait(); // Ensure setup runs synchronously for the constructor
|
||||
(_testUser, _testProject) = setupTask.Result;
|
||||
}
|
||||
|
||||
[TestCase("Very private project", "My secret project", ProjectVisibility.Private)]
|
||||
[TestCase("Very public project", null, ProjectVisibility.Public)]
|
||||
public async Task ProjectCreate(string name, string desc, ProjectVisibility visibility)
|
||||
private async Task<(Client TestUser, Project TestProject)> Setup()
|
||||
{
|
||||
ProjectCreateInput input = new(name, desc, visibility);
|
||||
Project result = await Sut.Create(input);
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result, Has.Property(nameof(Project.id)).Not.Null);
|
||||
Assert.That(result, Has.Property(nameof(Project.name)).EqualTo(input.name));
|
||||
Assert.That(result, Has.Property(nameof(Project.description)).EqualTo(input.description ?? string.Empty));
|
||||
Assert.That(result, Has.Property(nameof(Project.visibility)).EqualTo(input.visibility));
|
||||
var testUser = await Fixtures.SeedUserWithClient();
|
||||
var testProject = await testUser.Project.Create(new ProjectCreateInput("test project123", "desc", null));
|
||||
return (testUser, testProject);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ProjectGet()
|
||||
[Theory]
|
||||
[InlineData("Very private project", "My secret project", ProjectVisibility.Private)]
|
||||
[InlineData("Very public project", null, ProjectVisibility.Public)]
|
||||
public async Task ProjectCreate_Should_CreateProjectSuccessfully(
|
||||
string name,
|
||||
string? description,
|
||||
ProjectVisibility visibility
|
||||
)
|
||||
{
|
||||
Project result = await Sut.Get(_testProject.id);
|
||||
// Arrange
|
||||
var input = new ProjectCreateInput(name, description, visibility);
|
||||
|
||||
Assert.That(result.id, Is.EqualTo(_testProject.id));
|
||||
Assert.That(result.name, Is.EqualTo(_testProject.name));
|
||||
Assert.That(result.description, Is.EqualTo(_testProject.description));
|
||||
Assert.That(result.visibility, Is.EqualTo(_testProject.visibility));
|
||||
Assert.That(result.createdAt, Is.EqualTo(_testProject.createdAt));
|
||||
// Act
|
||||
var result = await Sut.Create(input);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.id.Should().NotBeNullOrWhiteSpace();
|
||||
result.name.Should().Be(input.name);
|
||||
result.description.Should().Be(input.description ?? string.Empty);
|
||||
input.visibility.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ProjectUpdate()
|
||||
[Fact]
|
||||
public async Task ProjectGet_Should_ReturnCorrectProject()
|
||||
{
|
||||
// Act
|
||||
var result = await Sut.Get(_testProject.id);
|
||||
|
||||
// Assert
|
||||
result.id.Should().Be(_testProject.id);
|
||||
result.name.Should().Be(_testProject.name);
|
||||
result.description.Should().Be(_testProject.description);
|
||||
result.visibility.Should().Be(_testProject.visibility);
|
||||
result.createdAt.Should().Be(_testProject.createdAt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProjectUpdate_Should_UpdateProjectSuccessfully()
|
||||
{
|
||||
// Arrange
|
||||
const string NEW_NAME = "MY new name";
|
||||
const string NEW_DESCRIPTION = "MY new desc";
|
||||
const ProjectVisibility NEW_VISIBILITY = ProjectVisibility.Public;
|
||||
|
||||
Project newProject = await Sut.Update(new(_testProject.id, NEW_NAME, NEW_DESCRIPTION, null, NEW_VISIBILITY));
|
||||
// Act
|
||||
var newProject = await Sut.Update(
|
||||
new ProjectUpdateInput(_testProject.id, NEW_NAME, NEW_DESCRIPTION, null, NEW_VISIBILITY)
|
||||
);
|
||||
|
||||
Assert.That(newProject.id, Is.EqualTo(_testProject.id));
|
||||
Assert.That(newProject.name, Is.EqualTo(NEW_NAME));
|
||||
Assert.That(newProject.description, Is.EqualTo(NEW_DESCRIPTION));
|
||||
Assert.That(newProject.visibility, Is.EqualTo(NEW_VISIBILITY));
|
||||
// Assert
|
||||
newProject.id.Should().Be(_testProject.id);
|
||||
newProject.name.Should().Be(NEW_NAME);
|
||||
newProject.description.Should().Be(NEW_DESCRIPTION);
|
||||
newProject.visibility.Should().Be(NEW_VISIBILITY);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ProjectDelete()
|
||||
[Fact]
|
||||
public async Task ProjectDelete_Should_DeleteProjectSuccessfully()
|
||||
{
|
||||
Project toDelete = await Sut.Create(new("Delete me", null, null));
|
||||
// Arrange
|
||||
var toDelete = await Sut.Create(new ProjectCreateInput("Delete me", null, null));
|
||||
|
||||
// Act
|
||||
await Sut.Delete(toDelete.id);
|
||||
|
||||
var getEx = Assert.ThrowsAsync<AggregateException>(async () => _ = await Sut.Get(toDelete.id));
|
||||
Assert.That(getEx?.InnerExceptions, Has.Exactly(1).TypeOf<SpeckleGraphQLStreamNotFoundException>());
|
||||
// Assert
|
||||
await FluentActions
|
||||
.Invoking(async () => await Sut.Get(toDelete.id))
|
||||
.Should()
|
||||
.ThrowAsync<SpeckleGraphQLStreamNotFoundException>();
|
||||
}
|
||||
}
|
||||
|
||||
+36
-33
@@ -1,13 +1,14 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Enums;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(SubscriptionResource))]
|
||||
public class SubscriptionResourceTests : IDisposable
|
||||
public class SubscriptionResourceTests : IAsyncLifetime
|
||||
{
|
||||
private const int WAIT_PERIOD = 300;
|
||||
private Client _testUser;
|
||||
@@ -17,8 +18,13 @@ public class SubscriptionResourceTests : IDisposable
|
||||
|
||||
private SubscriptionResource Sut => _testUser.Subscription;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task Setup()
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
_testUser.Dispose();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_testUser = await Fixtures.SeedUserWithClient();
|
||||
_testProject = await _testUser.Project.Create(new("test project123", "desc", null));
|
||||
@@ -26,7 +32,7 @@ public class SubscriptionResourceTests : IDisposable
|
||||
_testVersion = await Fixtures.CreateVersion(_testUser, _testProject.id, _testModel.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task UserProjectsUpdated_SubscriptionIsCalled()
|
||||
{
|
||||
UserProjectsUpdatedMessage? subscriptionMessage = null;
|
||||
@@ -40,13 +46,13 @@ public class SubscriptionResourceTests : IDisposable
|
||||
|
||||
await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered
|
||||
|
||||
Assert.That(subscriptionMessage, Is.Not.Null);
|
||||
Assert.That(subscriptionMessage!.id, Is.EqualTo(created.id));
|
||||
Assert.That(subscriptionMessage.type, Is.EqualTo(UserProjectsUpdatedMessageType.ADDED));
|
||||
Assert.That(subscriptionMessage.project, Is.Not.Null);
|
||||
subscriptionMessage.Should().NotBeNull();
|
||||
subscriptionMessage!.id.Should().Be(created.id);
|
||||
subscriptionMessage.type.Should().Be(UserProjectsUpdatedMessageType.ADDED);
|
||||
subscriptionMessage.project.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectModelsUpdated_SubscriptionIsCalled()
|
||||
{
|
||||
ProjectModelsUpdatedMessage? subscriptionMessage = null;
|
||||
@@ -61,13 +67,13 @@ public class SubscriptionResourceTests : IDisposable
|
||||
|
||||
await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered
|
||||
|
||||
Assert.That(subscriptionMessage, Is.Not.Null);
|
||||
Assert.That(subscriptionMessage!.id, Is.EqualTo(created.id));
|
||||
Assert.That(subscriptionMessage.type, Is.EqualTo(ProjectModelsUpdatedMessageType.CREATED));
|
||||
Assert.That(subscriptionMessage.model, Is.Not.Null);
|
||||
subscriptionMessage.Should().NotBeNull();
|
||||
subscriptionMessage!.id.Should().Be(created.id);
|
||||
subscriptionMessage.type.Should().Be(ProjectModelsUpdatedMessageType.CREATED);
|
||||
subscriptionMessage.model.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectUpdated_SubscriptionIsCalled()
|
||||
{
|
||||
ProjectUpdatedMessage? subscriptionMessage = null;
|
||||
@@ -82,13 +88,13 @@ public class SubscriptionResourceTests : IDisposable
|
||||
|
||||
await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered
|
||||
|
||||
Assert.That(subscriptionMessage, Is.Not.Null);
|
||||
Assert.That(subscriptionMessage!.id, Is.EqualTo(created.id));
|
||||
Assert.That(subscriptionMessage.type, Is.EqualTo(ProjectUpdatedMessageType.UPDATED));
|
||||
Assert.That(subscriptionMessage.project, Is.Not.Null);
|
||||
subscriptionMessage.Should().NotBeNull();
|
||||
subscriptionMessage!.id.Should().Be(created.id);
|
||||
subscriptionMessage.type.Should().Be(ProjectUpdatedMessageType.UPDATED);
|
||||
subscriptionMessage.project.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectVersionsUpdated_SubscriptionIsCalled()
|
||||
{
|
||||
ProjectVersionsUpdatedMessage? subscriptionMessage = null;
|
||||
@@ -102,13 +108,13 @@ public class SubscriptionResourceTests : IDisposable
|
||||
|
||||
await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered
|
||||
|
||||
Assert.That(subscriptionMessage, Is.Not.Null);
|
||||
Assert.That(subscriptionMessage!.id, Is.EqualTo(created));
|
||||
Assert.That(subscriptionMessage.type, Is.EqualTo(ProjectVersionsUpdatedMessageType.CREATED));
|
||||
Assert.That(subscriptionMessage.version, Is.Not.Null);
|
||||
subscriptionMessage.Should().NotBeNull();
|
||||
subscriptionMessage!.id.Should().Be(created);
|
||||
subscriptionMessage.type.Should().Be(ProjectVersionsUpdatedMessageType.CREATED);
|
||||
subscriptionMessage.version.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ProjectCommentsUpdated_SubscriptionIsCalled()
|
||||
{
|
||||
string resourceIdString = $"{_testProject.id},{_testModel.id},{_testVersion}";
|
||||
@@ -123,12 +129,9 @@ public class SubscriptionResourceTests : IDisposable
|
||||
|
||||
await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered
|
||||
|
||||
Assert.That(subscriptionMessage, Is.Not.Null);
|
||||
Assert.That(subscriptionMessage!.id, Is.EqualTo(created.id));
|
||||
Assert.That(subscriptionMessage.type, Is.EqualTo(ProjectCommentsUpdatedMessageType.CREATED));
|
||||
Assert.That(subscriptionMessage.comment, Is.Not.Null);
|
||||
subscriptionMessage.Should().NotBeNull();
|
||||
subscriptionMessage!.id.Should().Be(created.id);
|
||||
subscriptionMessage.type.Should().Be(ProjectCommentsUpdatedMessageType.CREATED);
|
||||
subscriptionMessage.comment.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void Dispose() => _testUser.Dispose();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using Speckle.Sdk.Api;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Api.GraphQL.Resources;
|
||||
using Xunit;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
|
||||
|
||||
[TestOf(typeof(VersionResource))]
|
||||
public class VersionResourceTests
|
||||
public class VersionResourceTests : IAsyncLifetime
|
||||
{
|
||||
private Client _testUser;
|
||||
private VersionResource Sut => _testUser.Version;
|
||||
@@ -16,8 +17,9 @@ public class VersionResourceTests
|
||||
private Model _model2;
|
||||
private Version _version;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_testUser = await Fixtures.SeedUserWithClient();
|
||||
_project = await _testUser.Project.Create(new("Test project", "", null));
|
||||
@@ -29,44 +31,44 @@ public class VersionResourceTests
|
||||
_version = await Sut.Get(versionId, _project.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task VersionGet()
|
||||
{
|
||||
Version result = await Sut.Get(_version.id, _project.id);
|
||||
|
||||
Assert.That(result, Has.Property(nameof(Version.id)).EqualTo(_version.id));
|
||||
Assert.That(result, Has.Property(nameof(Version.message)).EqualTo(_version.message));
|
||||
result.id.Should().Be(_version.id);
|
||||
result.message.Should().Be(_version.message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task VersionsGet()
|
||||
{
|
||||
ResourceCollection<Version> result = await Sut.GetVersions(_model1.id, _project.id);
|
||||
|
||||
Assert.That(result.items, Has.Count.EqualTo(1));
|
||||
Assert.That(result.totalCount, Is.EqualTo(1));
|
||||
Assert.That(result.items[0], Has.Property(nameof(Version.id)).EqualTo(_version.id));
|
||||
result.items.Count.Should().Be(1);
|
||||
result.totalCount.Should().Be(1);
|
||||
result.items[0].id.Should().Be(_version.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task VersionReceived()
|
||||
{
|
||||
MarkReceivedVersionInput input = new(_version.id, _project.id, "Integration test");
|
||||
await Sut.Received(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ModelGetWithVersions()
|
||||
{
|
||||
var result = await _testUser.Model.GetWithVersions(_model1.id, _project.id);
|
||||
|
||||
Assert.That(result, Has.Property(nameof(Model.id)).EqualTo(_model1.id));
|
||||
Assert.That(result.versions.items, Has.Count.EqualTo(1));
|
||||
Assert.That(result.versions.totalCount, Is.EqualTo(1));
|
||||
Assert.That(result.versions.items[0], Has.Property(nameof(Version.id)).EqualTo(_version.id));
|
||||
result.id.Should().Be(_model1.id);
|
||||
result.versions.items.Count.Should().Be(1);
|
||||
result.versions.totalCount.Should().Be(1);
|
||||
result.versions.items[0].id.Should().Be(_version.id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task VersionUpdate()
|
||||
{
|
||||
const string NEW_MESSAGE = "MY new version message";
|
||||
@@ -74,34 +76,43 @@ public class VersionResourceTests
|
||||
UpdateVersionInput input = new(_version.id, _project.id, NEW_MESSAGE);
|
||||
Version updatedVersion = await Sut.Update(input);
|
||||
|
||||
Assert.That(updatedVersion, Has.Property(nameof(Version.id)).EqualTo(_version.id));
|
||||
Assert.That(updatedVersion, Has.Property(nameof(Version.message)).EqualTo(NEW_MESSAGE));
|
||||
Assert.That(updatedVersion, Has.Property(nameof(Version.previewUrl)).EqualTo(_version.previewUrl));
|
||||
updatedVersion.id.Should().Be(_version.id);
|
||||
updatedVersion.message.Should().Be(NEW_MESSAGE);
|
||||
updatedVersion.previewUrl.Should().Be(_version.previewUrl);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task VersionMoveToModel()
|
||||
{
|
||||
MoveVersionsInput input = new(_project.id, _model2.name, [_version.id]);
|
||||
string id = await Sut.MoveToModel(input);
|
||||
Assert.That(id, Is.EqualTo(_model2.id));
|
||||
|
||||
id.Should().Be(_model2.id);
|
||||
|
||||
Version movedVersion = await Sut.Get(_version.id, _project.id);
|
||||
|
||||
Assert.That(movedVersion, Has.Property(nameof(Version.id)).EqualTo(_version.id));
|
||||
Assert.That(movedVersion, Has.Property(nameof(Version.message)).EqualTo(_version.message));
|
||||
Assert.That(movedVersion, Has.Property(nameof(Version.previewUrl)).EqualTo(_version.previewUrl));
|
||||
movedVersion.id.Should().Be(_version.id);
|
||||
movedVersion.message.Should().Be(_version.message);
|
||||
movedVersion.previewUrl.Should().Be(_version.previewUrl);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task VersionDelete()
|
||||
{
|
||||
DeleteVersionsInput input = new([_version.id], _project.id);
|
||||
|
||||
await Sut.Delete(input);
|
||||
|
||||
var getEx = Assert.ThrowsAsync<AggregateException>(async () => await Sut.Get(_version.id, _project.id));
|
||||
Assert.That(getEx?.InnerExceptions, Has.Exactly(1).TypeOf<SpeckleGraphQLException>());
|
||||
var delEx = Assert.ThrowsAsync<AggregateException>(async () => await Sut.Delete(input));
|
||||
Assert.That(delEx?.InnerExceptions, Has.Exactly(1).TypeOf<SpeckleGraphQLException>());
|
||||
var getEx = await FluentActions
|
||||
.Invoking(async () => await Sut.Get(_version.id, _project.id))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
getEx.WithInnerExceptionExactly<SpeckleGraphQLException>();
|
||||
|
||||
var delEx = await FluentActions
|
||||
.Invoking(async () => await Sut.Delete(input))
|
||||
.Should()
|
||||
.ThrowAsync<AggregateException>();
|
||||
delEx.WithInnerExceptionExactly<SpeckleGraphQLException>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,32 @@
|
||||
using GraphQL.Client.Http;
|
||||
using FluentAssertions;
|
||||
using GraphQL.Client.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration.Credentials;
|
||||
|
||||
public class UserServerInfoTests
|
||||
public class UserServerInfoTests : IAsyncLifetime
|
||||
{
|
||||
private Account _acc;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_acc = await Fixtures.SeedUser();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task IsFrontEnd2True()
|
||||
{
|
||||
ServerInfo? result = await Fixtures
|
||||
.ServiceProvider.GetRequiredService<IAccountManager>()
|
||||
.GetServerInfo(new("https://app.speckle.systems/"));
|
||||
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result!.frontend2, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task IsFrontEnd2False()
|
||||
{
|
||||
ServerInfo? result = await Fixtures
|
||||
.ServiceProvider.GetRequiredService<IAccountManager>()
|
||||
.GetServerInfo(new("https://speckle.xyz/"));
|
||||
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result!.frontend2, Is.False);
|
||||
result.Should().NotBeNull();
|
||||
result.frontend2.Should().BeTrue();
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
@@ -43,27 +35,33 @@ public class UserServerInfoTests
|
||||
/// This is not doable in local server because there is no end-point on this to ping.
|
||||
/// This is a bad sign for mutation.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void GetServerInfo_ExpectFail_CantPing()
|
||||
[Fact]
|
||||
public async Task GetServerInfo_ExpectFail_CantPing()
|
||||
{
|
||||
Uri serverUrl = new(_acc.serverInfo.url);
|
||||
|
||||
Assert.ThrowsAsync<HttpRequestException>(
|
||||
async () => await Fixtures.ServiceProvider.GetRequiredService<IAccountManager>().GetServerInfo(serverUrl)
|
||||
);
|
||||
await FluentActions
|
||||
.Invoking(
|
||||
async () => await Fixtures.ServiceProvider.GetRequiredService<IAccountManager>().GetServerInfo(serverUrl)
|
||||
)
|
||||
.Should()
|
||||
.ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetServerInfo_ExpectFail_NoServer()
|
||||
[Fact]
|
||||
public async Task GetServerInfo_ExpectFail_NoServer()
|
||||
{
|
||||
Uri serverUrl = new("http://invalidserver.local");
|
||||
|
||||
Assert.ThrowsAsync<HttpRequestException>(
|
||||
async () => await Fixtures.ServiceProvider.GetRequiredService<IAccountManager>().GetServerInfo(serverUrl)
|
||||
);
|
||||
await FluentActions
|
||||
.Invoking(
|
||||
async () => await Fixtures.ServiceProvider.GetRequiredService<IAccountManager>().GetServerInfo(serverUrl)
|
||||
)
|
||||
.Should()
|
||||
.ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task GetUserInfo()
|
||||
{
|
||||
Uri serverUrl = new(_acc.serverInfo.url);
|
||||
@@ -71,33 +69,38 @@ public class UserServerInfoTests
|
||||
.ServiceProvider.GetRequiredService<IAccountManager>()
|
||||
.GetUserInfo(_acc.token, serverUrl);
|
||||
|
||||
Assert.That(result.id, Is.EqualTo(_acc.userInfo.id));
|
||||
Assert.That(result.name, Is.EqualTo(_acc.userInfo.name));
|
||||
Assert.That(result.email, Is.EqualTo(_acc.userInfo.email));
|
||||
Assert.That(result.company, Is.EqualTo(_acc.userInfo.company));
|
||||
Assert.That(result.avatar, Is.EqualTo(_acc.userInfo.avatar));
|
||||
result.id.Should().Be(_acc.userInfo.id);
|
||||
result.name.Should().Be(_acc.userInfo.name);
|
||||
result.email.Should().Be(_acc.userInfo.email);
|
||||
result.company.Should().Be(_acc.userInfo.company);
|
||||
result.avatar.Should().Be(_acc.userInfo.avatar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUserInfo_ExpectFail_NoServer()
|
||||
[Fact]
|
||||
public async Task GetUserInfo_ExpectFail_NoServer()
|
||||
{
|
||||
Uri serverUrl = new("http://invalidserver.local");
|
||||
|
||||
Assert.ThrowsAsync<HttpRequestException>(
|
||||
async () => await Fixtures.ServiceProvider.GetRequiredService<IAccountManager>().GetUserInfo("", serverUrl)
|
||||
);
|
||||
await FluentActions
|
||||
.Invoking(
|
||||
async () => await Fixtures.ServiceProvider.GetRequiredService<IAccountManager>().GetUserInfo("", serverUrl)
|
||||
)
|
||||
.Should()
|
||||
.ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUserInfo_ExpectFail_NoUser()
|
||||
[Fact]
|
||||
public async Task GetUserInfo_ExpectFail_NoUser()
|
||||
{
|
||||
Uri serverUrl = new(_acc.serverInfo.url);
|
||||
|
||||
Assert.ThrowsAsync<GraphQLHttpRequestException>(
|
||||
async () =>
|
||||
await Fixtures
|
||||
.ServiceProvider.GetRequiredService<IAccountManager>()
|
||||
.GetUserInfo("Bearer 08913c3c1e7ac65d779d1e1f11b942a44ad9672ca9", serverUrl)
|
||||
);
|
||||
await FluentActions
|
||||
.Invoking(
|
||||
async () =>
|
||||
await Fixtures
|
||||
.ServiceProvider.GetRequiredService<IAccountManager>()
|
||||
.GetUserInfo("Bearer 08913c3c1e7ac65d779d1e1f11b942a44ad9672ca9", serverUrl)
|
||||
)
|
||||
.Should()
|
||||
.ThrowAsync<GraphQLHttpRequestException>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,9 +54,9 @@ public static class Fixtures
|
||||
var seed = Guid.NewGuid().ToString().ToLower();
|
||||
Dictionary<string, string> user = new()
|
||||
{
|
||||
["email"] = $"{seed.Substring(0, 7)}@example.com",
|
||||
["email"] = $"{seed[..7]}@example.com",
|
||||
["password"] = "12ABC3456789DEF0GHO",
|
||||
["name"] = $"{seed.Substring(0, 5)} Name",
|
||||
["name"] = $"{seed[..5]} Name",
|
||||
};
|
||||
|
||||
using var httpClient = new HttpClient(
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Shouldly;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Transports;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Integration;
|
||||
|
||||
public class MemoryTransportTests
|
||||
public class MemoryTransportTests : IDisposable
|
||||
{
|
||||
private readonly MemoryTransport _memoryTransport = new(blobStorageEnabled: true);
|
||||
private IOperations _operations;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public MemoryTransportTests()
|
||||
{
|
||||
CleanData();
|
||||
TypeLoader.Reset();
|
||||
@@ -24,8 +23,7 @@ public class MemoryTransportTests
|
||||
_operations = serviceProvider.GetRequiredService<IOperations>();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown() => CleanData();
|
||||
public void Dispose() => CleanData();
|
||||
|
||||
private void CleanData()
|
||||
{
|
||||
@@ -33,10 +31,11 @@ public class MemoryTransportTests
|
||||
{
|
||||
Directory.Delete(_memoryTransport.BlobStorageFolder, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(_memoryTransport.BlobStorageFolder);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task SendAndReceiveObjectWithBlobs()
|
||||
{
|
||||
var myObject = Fixtures.GenerateSimpleObject();
|
||||
@@ -53,20 +52,24 @@ public class MemoryTransportTests
|
||||
.GetFiles(_memoryTransport.BlobStorageFolder)
|
||||
.Select(fp => fp.Split(Path.DirectorySeparatorChar).Last())
|
||||
.ToList();
|
||||
|
||||
var blobPaths = allFiles
|
||||
.Where(fp => fp.Length > Blob.LocalHashPrefixLength) // excludes things like .DS_store
|
||||
.ToList();
|
||||
|
||||
// Check that there are three downloaded blobs!
|
||||
Assert.That(blobPaths, Has.Count.EqualTo(3));
|
||||
blobPaths.Count.Should().Be(3);
|
||||
|
||||
var objectBlobs = receivedObject["blobs"] as IList<object>;
|
||||
objectBlobs.ShouldNotBeNull();
|
||||
var blobs = objectBlobs.Cast<Blob>().ToList();
|
||||
objectBlobs.Should().NotBeNull();
|
||||
|
||||
var blobs = objectBlobs!.Cast<Blob>().ToList();
|
||||
// Check that we have three blobs
|
||||
Assert.That(blobs, Has.Count.EqualTo(3));
|
||||
blobs.Count.Should().Be(3);
|
||||
|
||||
// Check that received blobs point to local path (where they were received)
|
||||
Assert.That(blobs[0].filePath, Contains.Substring(_memoryTransport.BlobStorageFolder));
|
||||
Assert.That(blobs[1].filePath, Contains.Substring(_memoryTransport.BlobStorageFolder));
|
||||
Assert.That(blobs[2].filePath, Contains.Substring(_memoryTransport.BlobStorageFolder));
|
||||
blobs[0].filePath.Should().Contain(_memoryTransport.BlobStorageFolder);
|
||||
blobs[1].filePath.Should().Contain(_memoryTransport.BlobStorageFolder);
|
||||
blobs[2].filePath.Should().Contain(_memoryTransport.BlobStorageFolder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="altcover" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="NUnit" />
|
||||
<PackageReference Include="NUnit3TestAdapter" />
|
||||
<PackageReference Include="Shouldly" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
global using NUnit.Framework;
|
||||
@@ -4,9 +4,9 @@
|
||||
"net8.0": {
|
||||
"altcover": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.9.3, )",
|
||||
"resolved": "8.9.3",
|
||||
"contentHash": "auKC+pDCkLjfhFkSRaAUBu25BOmlLSqucR7YBs/Lkbdc0XRuJoklWafs1KKp+M+VoJ1f0TeMS6B/FO5IeIcu7w=="
|
||||
"requested": "[9.0.1, )",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA=="
|
||||
},
|
||||
"GitVersion.MsBuild": {
|
||||
"type": "Direct",
|
||||
@@ -16,12 +16,12 @@
|
||||
},
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"type": "Direct",
|
||||
"requested": "[17.11.1, )",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
|
||||
"requested": "[17.12.0, )",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeCoverage": "17.11.1",
|
||||
"Microsoft.TestPlatform.TestHost": "17.11.1"
|
||||
"Microsoft.CodeCoverage": "17.12.0",
|
||||
"Microsoft.TestPlatform.TestHost": "17.12.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
@@ -34,53 +34,34 @@
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"NUnit": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.2.2, )",
|
||||
"resolved": "4.2.2",
|
||||
"contentHash": "mon0OPko28yZ/foVXrhiUvq1LReaGsBdziumyyYGxV/pOE4q92fuYeN+AF+gEU5pCjzykcdBt5l7xobTaiBjsg=="
|
||||
},
|
||||
"NUnit3TestAdapter": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.6.0, )",
|
||||
"resolved": "4.6.0",
|
||||
"contentHash": "R7e1+a4vuV/YS+ItfL7f//rG+JBvVeVLX4mHzFEZo4W1qEKl8Zz27AqvQSAqo+BtIzUCo4aAJMYa56VXS4hudw=="
|
||||
},
|
||||
"PolySharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.15.0, )",
|
||||
"resolved": "1.15.0",
|
||||
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
|
||||
},
|
||||
"Shouldly": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.2.1, )",
|
||||
"resolved": "4.2.1",
|
||||
"contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==",
|
||||
"dependencies": {
|
||||
"DiffEngine": "11.3.0",
|
||||
"EmptyFiles": "4.4.0"
|
||||
}
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.9.6, )",
|
||||
"resolved": "0.9.6",
|
||||
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
|
||||
},
|
||||
"DiffEngine": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.3.0",
|
||||
"contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==",
|
||||
"xunit": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.9.3, )",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
|
||||
"dependencies": {
|
||||
"EmptyFiles": "4.4.0",
|
||||
"System.Management": "6.0.1"
|
||||
"xunit.analyzers": "1.18.0",
|
||||
"xunit.assert": "2.9.3",
|
||||
"xunit.core": "[2.9.3]"
|
||||
}
|
||||
},
|
||||
"EmptyFiles": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw=="
|
||||
"xunit.runner.visualstudio": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.0, )",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "HggUqjQJe8PtDxcP25Q+CnR6Lz4oX3GElhD9V4oU2+75x9HI6A6sxbfKGS4UwU4t4yJaS9fBmAuriz8bQApNjw=="
|
||||
},
|
||||
"GraphQL.Client.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -110,8 +91,8 @@
|
||||
},
|
||||
"Microsoft.CodeCoverage": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core": {
|
||||
"type": "Transitive",
|
||||
@@ -176,21 +157,26 @@
|
||||
},
|
||||
"Microsoft.TestPlatform.ObjectModel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
|
||||
"dependencies": {
|
||||
"System.Reflection.Metadata": "1.6.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.TestPlatform.TestHost": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.11.1",
|
||||
"contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
|
||||
"dependencies": {
|
||||
"Microsoft.TestPlatform.ObjectModel": "17.11.1",
|
||||
"Microsoft.TestPlatform.ObjectModel": "17.12.0",
|
||||
"Newtonsoft.Json": "13.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.1",
|
||||
@@ -226,22 +212,26 @@
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"System.CodeDom": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
|
||||
},
|
||||
"System.Management": {
|
||||
"System.Configuration.ConfigurationManager": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.1",
|
||||
"contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "7T+m0kDSlIPTHIkPMIu6m6tV6qsMqJpvQWW2jIc2qi7sn40qxFo0q+7mEQAhMPXZHMKnWrnv47ntGlM/ejvw3g==",
|
||||
"dependencies": {
|
||||
"System.CodeDom": "6.0.0"
|
||||
"System.Security.Cryptography.ProtectedData": "6.0.0",
|
||||
"System.Security.Permissions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Drawing.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.SystemEvents": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Memory": {
|
||||
@@ -264,6 +254,73 @@
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"System.Security.AccessControl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ=="
|
||||
},
|
||||
"System.Security.Cryptography.ProtectedData": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ=="
|
||||
},
|
||||
"System.Security.Permissions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==",
|
||||
"dependencies": {
|
||||
"System.Security.AccessControl": "6.0.0",
|
||||
"System.Windows.Extensions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Windows.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==",
|
||||
"dependencies": {
|
||||
"System.Drawing.Common": "6.0.0"
|
||||
}
|
||||
},
|
||||
"xunit.abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.3",
|
||||
"contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
|
||||
},
|
||||
"xunit.analyzers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.18.0",
|
||||
"contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
|
||||
},
|
||||
"xunit.assert": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
|
||||
},
|
||||
"xunit.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
|
||||
"dependencies": {
|
||||
"xunit.extensibility.core": "[2.9.3]",
|
||||
"xunit.extensibility.execution": "[2.9.3]"
|
||||
}
|
||||
},
|
||||
"xunit.extensibility.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
|
||||
"dependencies": {
|
||||
"xunit.abstractions": "2.0.3"
|
||||
}
|
||||
},
|
||||
"xunit.extensibility.execution": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.3",
|
||||
"contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
|
||||
"dependencies": {
|
||||
"xunit.extensibility.core": "[2.9.3]"
|
||||
}
|
||||
},
|
||||
"speckle.sdk": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
@@ -283,14 +340,23 @@
|
||||
"speckle.sdk.tests.unit": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"FluentAssertions": "[7.0.0, )",
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Microsoft.NET.Test.Sdk": "[17.11.1, )",
|
||||
"NUnit": "[4.2.2, )",
|
||||
"NUnit3TestAdapter": "[4.6.0, )",
|
||||
"Shouldly": "[4.2.1, )",
|
||||
"Microsoft.NET.Test.Sdk": "[17.12.0, )",
|
||||
"Speckle.DoubleNumerics": "[4.0.1, )",
|
||||
"Speckle.Sdk": "[1.0.0, )",
|
||||
"altcover": "[8.9.3, )"
|
||||
"altcover": "[9.0.1, )",
|
||||
"xunit": "[2.9.3, )",
|
||||
"xunit.runner.visualstudio": "[3.0.0, )"
|
||||
}
|
||||
},
|
||||
"FluentAssertions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[7.0.0, )",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "mTLbcU991EQ1SEmNbVBaGGGJy0YFzvGd1sYJGNZ07nlPKuyHSn1I22aeKzqQXgEiaKyRO6MSCto9eN9VxMwBdA==",
|
||||
"dependencies": {
|
||||
"System.Configuration.ConfigurationManager": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client": {
|
||||
|
||||
@@ -57,7 +57,7 @@ public class GeneralDeserializer : IDisposable
|
||||
null
|
||||
);
|
||||
var o = new ObjectLoader(sqlite, serverObjects, null);
|
||||
var process = new DeserializeProcess(null, o, new ObjectDeserializerFactory(), new(skipCache));
|
||||
using var process = new DeserializeProcess(null, o, new ObjectDeserializerFactory(), new(skipCache));
|
||||
return await process.Deserialize(rootId, default).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,8 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Helpers;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Serialisation;
|
||||
using Speckle.Sdk.Serialisation.V2;
|
||||
using Speckle.Sdk.Serialisation.V2.Receive;
|
||||
using Speckle.Sdk.Transports;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Performance.Benchmarks;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Engines;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Credentials;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
|
||||
using BenchmarkDotNet.Running;
|
||||
using Speckle.Sdk.Tests.Performance.Benchmarks;
|
||||
|
||||
BenchmarkSwitcher.FromAssemblies([typeof(Program).Assembly]).Run(args);
|
||||
// var sut = new GeneralSendTest();
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
using System.Diagnostics;
|
||||
using GraphQL;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Api;
|
||||
|
||||
[TestOf(typeof(Client))]
|
||||
public sealed class GraphQLClientTests : IDisposable
|
||||
{
|
||||
private Client _client;
|
||||
private readonly Client _client;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
public GraphQLClientTests()
|
||||
{
|
||||
var serviceProvider = TestServiceSetup.GetServiceProvider();
|
||||
_client = serviceProvider
|
||||
@@ -28,17 +26,14 @@ public sealed class GraphQLClientTests : IDisposable
|
||||
);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client?.Dispose();
|
||||
}
|
||||
public void Dispose() => _client?.Dispose();
|
||||
|
||||
[Test]
|
||||
public void TestExecuteWithResiliencePoliciesDoesntRetryTaskCancellation()
|
||||
[Fact]
|
||||
public async Task TestExecuteWithResiliencePoliciesDoesntRetryTaskCancellation()
|
||||
{
|
||||
var timer = new Stopwatch();
|
||||
timer.Start();
|
||||
Assert.ThrowsAsync<TaskCanceledException>(async () =>
|
||||
await Assert.ThrowsAsync<TaskCanceledException>(async () =>
|
||||
{
|
||||
var tokenSource = new CancellationTokenSource();
|
||||
tokenSource.Cancel();
|
||||
@@ -55,14 +50,13 @@ public sealed class GraphQLClientTests : IDisposable
|
||||
);
|
||||
});
|
||||
timer.Stop();
|
||||
var elapsed = timer.ElapsedMilliseconds;
|
||||
timer.ElapsedMilliseconds.Should().BeLessThan(1000);
|
||||
|
||||
// the default retry policy would retry 5 times with 1 second jitter backoff each
|
||||
// if the elapsed is less than a second, this was def not retried
|
||||
Assert.That(elapsed, Is.LessThan(1000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task TestExecuteWithResiliencePoliciesRetry()
|
||||
{
|
||||
var counter = 0;
|
||||
@@ -82,8 +76,8 @@ public sealed class GraphQLClientTests : IDisposable
|
||||
});
|
||||
timer.Stop();
|
||||
// The baseline for wait is 1 seconds between the jittered retry
|
||||
Assert.That(timer.ElapsedMilliseconds, Is.GreaterThanOrEqualTo(5000));
|
||||
Assert.That(counter, Is.EqualTo(maxRetryCount));
|
||||
timer.ElapsedMilliseconds.Should().BeGreaterThanOrEqualTo(5000);
|
||||
counter.Should().Be(maxRetryCount);
|
||||
}
|
||||
|
||||
public class FakeGqlResponseModel { }
|
||||
|
||||
@@ -1,55 +1,41 @@
|
||||
using GraphQL;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using GraphQL;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Api;
|
||||
|
||||
public class GraphQLErrorHandlerTests
|
||||
{
|
||||
private static IEnumerable<TestCaseData> ErrorCases()
|
||||
public static IEnumerable<object[]> ErrorCases()
|
||||
{
|
||||
yield return new TestCaseData(typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "FORBIDDEN" } });
|
||||
yield return new TestCaseData(typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "UNAUTHENTICATED" } });
|
||||
yield return new TestCaseData(
|
||||
typeof(SpeckleGraphQLInternalErrorException),
|
||||
new Map { { "code", "INTERNAL_SERVER_ERROR" } }
|
||||
);
|
||||
yield return new TestCaseData(
|
||||
typeof(SpeckleGraphQLStreamNotFoundException),
|
||||
new Map { { "code", "STREAM_NOT_FOUND" } }
|
||||
);
|
||||
yield return new TestCaseData(typeof(SpeckleGraphQLBadInputException), new Map { { "code", "BAD_USER_INPUT" } });
|
||||
yield return new TestCaseData(
|
||||
typeof(SpeckleGraphQLInvalidQueryException),
|
||||
new Map { { "code", "GRAPHQL_PARSE_FAILED" } }
|
||||
);
|
||||
yield return new TestCaseData(
|
||||
typeof(SpeckleGraphQLInvalidQueryException),
|
||||
new Map { { "code", "GRAPHQL_VALIDATION_FAILED" } }
|
||||
);
|
||||
yield return new TestCaseData(typeof(SpeckleGraphQLException), new Map { { "foo", "bar" } });
|
||||
yield return new TestCaseData(typeof(SpeckleGraphQLException), new Map { { "code", "CUSTOM_THING" } });
|
||||
yield return [typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "FORBIDDEN" } }];
|
||||
yield return [typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "UNAUTHENTICATED" } }];
|
||||
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" } }];
|
||||
yield return [typeof(SpeckleGraphQLInvalidQueryException), new Map { { "code", "GRAPHQL_PARSE_FAILED" } }];
|
||||
yield return [typeof(SpeckleGraphQLInvalidQueryException), new Map { { "code", "GRAPHQL_VALIDATION_FAILED" } }];
|
||||
yield return [typeof(SpeckleGraphQLException), new Map { { "foo", "bar" } }];
|
||||
yield return [typeof(SpeckleGraphQLException), new Map { { "code", "CUSTOM_THING" } }];
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(ErrorCases))]
|
||||
[Theory]
|
||||
[MemberData(nameof(ErrorCases))]
|
||||
public void TestExceptionThrowingFromGraphQLErrors(Type exType, Map extensions)
|
||||
{
|
||||
var ex = Assert.Throws<AggregateException>(
|
||||
() =>
|
||||
GraphQLErrorHandler.EnsureGraphQLSuccess(
|
||||
new GraphQLResponse<GraphQLClientTests.FakeGqlResponseModel>
|
||||
{
|
||||
Errors = new GraphQLError[] { new() { Extensions = extensions } },
|
||||
}
|
||||
)
|
||||
new GraphQLResponse<GraphQLClientTests.FakeGqlResponseModel>
|
||||
{
|
||||
Errors = [new() { Extensions = extensions }],
|
||||
}.EnsureGraphQLSuccess()
|
||||
);
|
||||
Assert.That(ex?.InnerExceptions, Has.Exactly(1).TypeOf(exType));
|
||||
ex.InnerExceptions.Count.Should().Be(1);
|
||||
ex.InnerExceptions[0].Should().BeOfType(exType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMaybeThrowsDoesntThrowForNoErrors()
|
||||
{
|
||||
Assert.DoesNotThrow(() => GraphQLErrorHandler.EnsureGraphQLSuccess(new GraphQLResponse<string>()));
|
||||
}
|
||||
[Fact]
|
||||
public void TestMaybeThrowsDoesntThrowForNoErrors() => new GraphQLResponse<string>().EnsureGraphQLSuccess();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Tests.Unit.Host;
|
||||
using Speckle.Sdk.Transports;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Api.Operations;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(Sdk.Api.Operations))]
|
||||
public class Closures
|
||||
{
|
||||
private IOperations _operations;
|
||||
private readonly IOperations _operations;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public Closures()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(TableLegFixture).Assembly);
|
||||
@@ -25,7 +23,7 @@ public class Closures
|
||||
_operations = serviceProvider.GetRequiredService<IOperations>();
|
||||
}
|
||||
|
||||
[Test(Description = "Checks whether closures are generated correctly by the serialiser.")]
|
||||
[Fact(DisplayName = "Checks whether closures are generated correctly by the serialiser.")]
|
||||
public async Task CorrectDecompositionTracking()
|
||||
{
|
||||
var d5 = new Base();
|
||||
@@ -56,31 +54,31 @@ public class Closures
|
||||
var test = await _operations.Receive(sendResult.rootObjId, localTransport: transport);
|
||||
|
||||
test.id.NotNull();
|
||||
Assert.That(d1.GetId(true), Is.EqualTo(test.id));
|
||||
d1.GetId(true).Should().BeEquivalentTo((test.id));
|
||||
|
||||
var d1_ = NotNullExtensions.NotNull(JsonConvert.DeserializeObject<dynamic>(transport.Objects[d1.GetId(true)]));
|
||||
var d2_ = NotNullExtensions.NotNull(JsonConvert.DeserializeObject<dynamic>(transport.Objects[d2.GetId(true)]));
|
||||
var d3_ = NotNullExtensions.NotNull(JsonConvert.DeserializeObject<dynamic>(transport.Objects[d3.GetId(true)]));
|
||||
var d4_ = JsonConvert.DeserializeObject<dynamic>(transport.Objects[d4.GetId(true)]);
|
||||
var d5_ = JsonConvert.DeserializeObject<dynamic>(transport.Objects[d5.GetId(true)]);
|
||||
JsonConvert.DeserializeObject<dynamic>(transport.Objects[d4.GetId(true)]);
|
||||
JsonConvert.DeserializeObject<dynamic>(transport.Objects[d5.GetId(true)]);
|
||||
|
||||
var depthOf_d5_in_d1 = int.Parse((string)d1_.__closure[d5.GetId(true)]);
|
||||
Assert.That(depthOf_d5_in_d1, Is.EqualTo(1));
|
||||
depthOf_d5_in_d1.Should().Be(1);
|
||||
|
||||
var depthOf_d4_in_d1 = int.Parse((string)d1_.__closure[d4.GetId(true)]);
|
||||
Assert.That(depthOf_d4_in_d1, Is.EqualTo(3));
|
||||
depthOf_d4_in_d1.Should().Be(3);
|
||||
|
||||
var depthOf_d5_in_d3 = int.Parse((string)d3_.__closure[d5.GetId(true)]);
|
||||
Assert.That(depthOf_d5_in_d3, Is.EqualTo(2));
|
||||
depthOf_d5_in_d3.Should().Be(2);
|
||||
|
||||
var depthOf_d4_in_d3 = int.Parse((string)d3_.__closure[d4.GetId(true)]);
|
||||
Assert.That(depthOf_d4_in_d3, Is.EqualTo(1));
|
||||
depthOf_d4_in_d3.Should().Be(1);
|
||||
|
||||
var depthOf_d5_in_d2 = int.Parse((string)d2_.__closure[d5.GetId(true)]);
|
||||
Assert.That(depthOf_d5_in_d2, Is.EqualTo(1));
|
||||
depthOf_d5_in_d2.Should().Be(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void DescendantsCounting()
|
||||
{
|
||||
Base myBase = new();
|
||||
@@ -118,18 +116,15 @@ public class Closures
|
||||
|
||||
myBase["@detachTheDictionary"] = dictionary;
|
||||
|
||||
var count = myBase.GetTotalChildrenCount();
|
||||
Assert.That(count, Is.EqualTo(112));
|
||||
myBase.GetTotalChildrenCount().Should().Be(112);
|
||||
|
||||
var tableTest = new DiningTable();
|
||||
var tableKidsCount = tableTest.GetTotalChildrenCount();
|
||||
Assert.That(tableKidsCount, Is.EqualTo(10));
|
||||
tableTest.GetTotalChildrenCount().Should().Be(10);
|
||||
|
||||
// Explicitely test for recurisve references!
|
||||
var recursiveRef = new Base { applicationId = "random" };
|
||||
recursiveRef["@recursive"] = recursiveRef;
|
||||
|
||||
var supriseCount = recursiveRef.GetTotalChildrenCount();
|
||||
Assert.That(supriseCount, Is.EqualTo(2));
|
||||
recursiveRef.GetTotalChildrenCount().Should().Be(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,39 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Transports;
|
||||
using Speckle.Sdk.Transports;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Api.Operations;
|
||||
|
||||
public partial class OperationsReceiveTests
|
||||
{
|
||||
[Test, TestCaseSource(nameof(TestCases))]
|
||||
public void Receive_ObjectsDontExist_ExceptionThrown(string id)
|
||||
[Theory, MemberData(nameof(TestCases))]
|
||||
public async Task Receive_ObjectsDontExist_ExceptionThrown(string id)
|
||||
{
|
||||
MemoryTransport emptyTransport1 = new();
|
||||
MemoryTransport emptyTransport2 = new();
|
||||
Assert.ThrowsAsync<TransportException>(async () =>
|
||||
await Assert.ThrowsAsync<TransportException>(async () =>
|
||||
{
|
||||
await _operations.Receive(id, emptyTransport1, emptyTransport2);
|
||||
});
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TestCases))]
|
||||
public void Receive_ObjectsDontExistNullRemote_ExceptionThrown(string id)
|
||||
[Theory, MemberData(nameof(TestCases))]
|
||||
public async Task Receive_ObjectsDontExistNullRemote_ExceptionThrown(string id)
|
||||
{
|
||||
MemoryTransport emptyTransport = new();
|
||||
Assert.ThrowsAsync<TransportException>(async () =>
|
||||
await Assert.ThrowsAsync<TransportException>(async () =>
|
||||
{
|
||||
await _operations.Receive(id, null, emptyTransport);
|
||||
});
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TestCases))]
|
||||
public void Receive_OperationCanceled_ExceptionThrown(string id)
|
||||
[Theory, MemberData(nameof(TestCases))]
|
||||
public async Task Receive_OperationCanceled_ExceptionThrown(string id)
|
||||
{
|
||||
using CancellationTokenSource ctc = new();
|
||||
ctc.Cancel();
|
||||
|
||||
MemoryTransport emptyTransport2 = new();
|
||||
Assert.CatchAsync<OperationCanceledException>(async () =>
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () =>
|
||||
{
|
||||
await _operations.Receive(id, _testCaseTransport, emptyTransport2, cancellationToken: ctc.Token);
|
||||
});
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Transports;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Api.Operations;
|
||||
|
||||
[TestFixture, TestOf(nameof(Sdk.Api.Operations.Receive))]
|
||||
public sealed partial class OperationsReceiveTests
|
||||
public sealed partial class OperationsReceiveTests : IDisposable
|
||||
{
|
||||
private static readonly Base[] s_testObjects;
|
||||
private IOperations _operations;
|
||||
private readonly IOperations _operations;
|
||||
private readonly MemoryTransport _testCaseTransport;
|
||||
|
||||
static OperationsReceiveTests()
|
||||
{
|
||||
@@ -29,64 +29,57 @@ public sealed partial class OperationsReceiveTests
|
||||
];
|
||||
}
|
||||
|
||||
public static IEnumerable<string> TestCases()
|
||||
public OperationsReceiveTests()
|
||||
{
|
||||
List<string> ret = new();
|
||||
foreach (var s in s_testObjects)
|
||||
Reset();
|
||||
var serviceProvider = TestServiceSetup.GetServiceProvider();
|
||||
_operations = serviceProvider.GetRequiredService<IOperations>();
|
||||
_testCaseTransport = new MemoryTransport();
|
||||
|
||||
// Simulate a one-time setup action
|
||||
foreach (var b in s_testObjects)
|
||||
{
|
||||
ret.Add(s.GetId(true));
|
||||
_ = _operations.Send(b, _testCaseTransport, false).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private MemoryTransport _testCaseTransport;
|
||||
|
||||
private static void Reset()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, Assembly.GetExecutingAssembly());
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task GlobalSetup()
|
||||
public static IEnumerable<object[]> TestCases()
|
||||
{
|
||||
Reset();
|
||||
var serviceProvider = TestServiceSetup.GetServiceProvider();
|
||||
_operations = serviceProvider.GetRequiredService<IOperations>();
|
||||
_testCaseTransport = new MemoryTransport();
|
||||
foreach (var b in s_testObjects)
|
||||
foreach (var s in s_testObjects)
|
||||
{
|
||||
await _operations.Send(b, _testCaseTransport, false);
|
||||
yield return [s.GetId(true)];
|
||||
}
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Reset();
|
||||
var serviceProvider = TestServiceSetup.GetServiceProvider();
|
||||
_operations = serviceProvider.GetRequiredService<IOperations>();
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TestCases))]
|
||||
[Theory]
|
||||
[MemberData(nameof(TestCases))]
|
||||
public async Task Receive_FromLocal_ExistingObjects(string id)
|
||||
{
|
||||
Base result = await _operations.Receive(id, null, _testCaseTransport);
|
||||
|
||||
Assert.That(result.id, Is.EqualTo(id));
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(id, result.id);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TestCases))]
|
||||
[Theory]
|
||||
[MemberData(nameof(TestCases))]
|
||||
public async Task Receive_FromRemote_ExistingObjects(string id)
|
||||
{
|
||||
MemoryTransport localTransport = new();
|
||||
Base result = await _operations.Receive(id, _testCaseTransport, localTransport);
|
||||
|
||||
Assert.That(result.id, Is.EqualTo(id));
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(id, result.id);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TestCases))]
|
||||
[Theory]
|
||||
[MemberData(nameof(TestCases))]
|
||||
public async Task Receive_FromLocal_OnProgressActionCalled(string id)
|
||||
{
|
||||
bool wasCalled = false;
|
||||
@@ -97,6 +90,11 @@ public sealed partial class OperationsReceiveTests
|
||||
onProgressAction: new UnitTestProgress<ProgressArgs>(_ => wasCalled = true)
|
||||
);
|
||||
|
||||
Assert.That(wasCalled, Is.True);
|
||||
Assert.True(wasCalled);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Cleanup resources if necessary
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Transports;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Api.Operations;
|
||||
|
||||
public class SendObjectReferences
|
||||
{
|
||||
private IOperations _operations;
|
||||
private readonly IOperations _operations;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public SendObjectReferences()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(DataChunk).Assembly);
|
||||
@@ -20,34 +20,38 @@ public class SendObjectReferences
|
||||
_operations = serviceProvider.GetRequiredService<IOperations>();
|
||||
}
|
||||
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
[TestCase(10)]
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(10)]
|
||||
public async Task SendObjectsWithApplicationIds(int testDepth)
|
||||
{
|
||||
Base testData = GenerateTestCase(testDepth, true);
|
||||
MemoryTransport transport = new();
|
||||
var result = await _operations.Send(testData, [transport]);
|
||||
|
||||
Assert.That(result.rootObjId, Is.Not.Null);
|
||||
Assert.That(result.rootObjId, Has.Length.EqualTo(32));
|
||||
result.rootObjId.Should().NotBeNull();
|
||||
|
||||
Assert.That(result.convertedReferences, Has.Count.EqualTo(Math.Pow(2, testDepth + 1) - 2));
|
||||
result.rootObjId.Length.Should().Be(32);
|
||||
|
||||
result.convertedReferences.Count.Should().Be((int)(Math.Pow(2, testDepth + 1) - 2));
|
||||
}
|
||||
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
[TestCase(10)]
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(10)]
|
||||
public async Task SendObjectsWithoutApplicationIds(int testDepth)
|
||||
{
|
||||
Base testData = GenerateTestCase(testDepth, false);
|
||||
MemoryTransport transport = new();
|
||||
var result = await _operations.Send(testData, [transport]);
|
||||
|
||||
Assert.That(result.rootObjId, Is.Not.Null);
|
||||
Assert.That(result.rootObjId, Has.Length.EqualTo(32));
|
||||
result.rootObjId.Should().NotBeNull();
|
||||
|
||||
Assert.That(result.convertedReferences, Is.Empty);
|
||||
result.rootObjId.Length.Should().Be(32);
|
||||
|
||||
result.convertedReferences.Should().BeEmpty();
|
||||
}
|
||||
|
||||
private Base GenerateTestCase(int depth, bool withAppId)
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
using System.Collections.Concurrent;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Tests.Unit.Host;
|
||||
using Speckle.Sdk.Transports;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Api.Operations;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class SendReceiveLocal : IDisposable
|
||||
{
|
||||
private IOperations _operations;
|
||||
private readonly IOperations _operations;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public SendReceiveLocal()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly);
|
||||
@@ -25,15 +22,14 @@ public sealed class SendReceiveLocal : IDisposable
|
||||
_operations = serviceProvider.GetRequiredService<IOperations>();
|
||||
}
|
||||
|
||||
private string? _objId01;
|
||||
private string? _commitId02;
|
||||
|
||||
private const int NUM_OBJECTS = 3001;
|
||||
|
||||
private readonly SQLiteTransport _sut = new();
|
||||
|
||||
[Test(Description = "Pushing a commit locally"), Order(1)]
|
||||
public async Task LocalUpload()
|
||||
public void Dispose() => _sut.Dispose();
|
||||
|
||||
[Fact(DisplayName = "Pushing a commit locally")]
|
||||
public async Task LocalUploadAndDownload()
|
||||
{
|
||||
var myObject = new Base();
|
||||
var rand = new Random();
|
||||
@@ -48,24 +44,18 @@ public sealed class SendReceiveLocal : IDisposable
|
||||
}
|
||||
|
||||
using SQLiteTransport localTransport = new();
|
||||
(_objId01, var references) = await _operations.Send(myObject, localTransport, false);
|
||||
(var objId01, var references) = await _operations.Send(myObject, localTransport, false);
|
||||
|
||||
Assert.That(_objId01, Is.Not.Null);
|
||||
Assert.That(references, Has.Count.EqualTo(NUM_OBJECTS));
|
||||
objId01.Should().NotBeNull();
|
||||
references.Count.Should().Be(NUM_OBJECTS);
|
||||
|
||||
TestContext.Out.WriteLine($"Written {NUM_OBJECTS + 1} objects. Commit id is {_objId01}");
|
||||
var commitPulled = await _operations.Receive(objId01.NotNull());
|
||||
|
||||
((List<object>)commitPulled["@items"].NotNull())[0].Should().BeOfType<Point>();
|
||||
((List<object>)commitPulled["@items"].NotNull()).Count.Should().Be(NUM_OBJECTS);
|
||||
}
|
||||
|
||||
[Test(Description = "Pulling a commit locally"), Order(2)]
|
||||
public async Task LocalDownload()
|
||||
{
|
||||
var commitPulled = await _operations.Receive(_objId01.NotNull());
|
||||
|
||||
Assert.That(((List<object>)commitPulled["@items"].NotNull())[0], Is.TypeOf<Point>());
|
||||
Assert.That(((List<object>)commitPulled["@items"].NotNull()), Has.Count.EqualTo(NUM_OBJECTS));
|
||||
}
|
||||
|
||||
[Test(Description = "Pushing and Pulling a commit locally")]
|
||||
[Fact(DisplayName = "Pushing and Pulling a commit locally")]
|
||||
public async Task LocalUploadDownload()
|
||||
{
|
||||
var myObject = new Base();
|
||||
@@ -80,16 +70,15 @@ public sealed class SendReceiveLocal : IDisposable
|
||||
);
|
||||
}
|
||||
|
||||
(_objId01, _) = await _operations.Send(myObject, _sut, false);
|
||||
(var objId01, _) = await _operations.Send(myObject, _sut, false);
|
||||
|
||||
var commitPulled = await _operations.Receive(_objId01);
|
||||
var commitPulled = await _operations.Receive(objId01);
|
||||
List<object> items = (List<object>)commitPulled["@items"].NotNull();
|
||||
|
||||
Assert.That(items, Has.All.TypeOf<Point>());
|
||||
Assert.That(items, Has.Count.EqualTo(NUM_OBJECTS));
|
||||
items.Should().AllSatisfy(x => x.Should().BeOfType<Point>());
|
||||
items.Count.Should().Be(NUM_OBJECTS);
|
||||
}
|
||||
|
||||
[Test(Description = "Pushing and pulling a commit locally"), Order(3)]
|
||||
[Fact(DisplayName = "Pushing and pulling a commit locally")]
|
||||
public async Task LocalUploadDownloadSmall()
|
||||
{
|
||||
var myObject = new Base();
|
||||
@@ -104,16 +93,15 @@ public sealed class SendReceiveLocal : IDisposable
|
||||
);
|
||||
}
|
||||
|
||||
(_objId01, _) = await _operations.Send(myObject, _sut, false);
|
||||
(var objId01, _) = await _operations.Send(myObject, _sut, false);
|
||||
|
||||
Assert.That(_objId01, Is.Not.Null);
|
||||
TestContext.Out.WriteLine($"Written {NUM_OBJECTS + 1} objects. Commit id is {_objId01}");
|
||||
objId01.Should().NotBeNull();
|
||||
|
||||
var objsPulled = await _operations.Receive(_objId01);
|
||||
Assert.That(((List<object>)objsPulled["@items"].NotNull()), Has.Count.EqualTo(30));
|
||||
var objsPulled = await _operations.Receive(objId01);
|
||||
((List<object>)objsPulled["@items"].NotNull()).Count.Should().Be(30);
|
||||
}
|
||||
|
||||
[Test(Description = "Pushing and pulling a commit locally"), Order(3)]
|
||||
[Fact(DisplayName = "Pushing and pulling a commit locally")]
|
||||
public async Task LocalUploadDownloadListDic()
|
||||
{
|
||||
var myList = new List<object> { 1, 2, 3, "ciao" };
|
||||
@@ -128,19 +116,16 @@ public sealed class SendReceiveLocal : IDisposable
|
||||
myObject["@dictionary"] = myDic;
|
||||
myObject["@list"] = myList;
|
||||
|
||||
(_objId01, _) = await _operations.Send(myObject, _sut, false);
|
||||
(var _objId01, _) = await _operations.Send(myObject, _sut, false);
|
||||
|
||||
Assert.That(_objId01, Is.Not.Null);
|
||||
_objId01.Should().NotBeNull();
|
||||
|
||||
var objsPulled = await _operations.Receive(_objId01);
|
||||
Assert.That(
|
||||
((List<object>)((Dictionary<string, object>)objsPulled["@dictionary"].NotNull())["a"]).First(),
|
||||
Is.EqualTo(1)
|
||||
);
|
||||
Assert.That(((List<object>)objsPulled["@list"].NotNull()).Last(), Is.EqualTo("ciao"));
|
||||
((List<object>)((Dictionary<string, object>)objsPulled["@dictionary"].NotNull())["a"]).First().Should().Be(1);
|
||||
((List<object>)objsPulled["@list"].NotNull()).Last().Should().Be("ciao");
|
||||
}
|
||||
|
||||
[Test(Description = "Pushing and pulling a random object, with our without detachment"), Order(3)]
|
||||
[Fact(DisplayName = "Pushing and pulling a random object, with or without detachment")]
|
||||
public async Task UploadDownloadNonCommitObject()
|
||||
{
|
||||
var obj = new Base();
|
||||
@@ -166,32 +151,31 @@ public sealed class SendReceiveLocal : IDisposable
|
||||
((List<Base>)((dynamic)obj)["@LayerC"]).Add(new Point(i, i, i + rand.NextDouble()) { applicationId = i + "baz" });
|
||||
}
|
||||
|
||||
(_objId01, _) = await _operations.Send(obj, _sut, false);
|
||||
(var objId01, _) = await _operations.Send(obj, _sut, false);
|
||||
|
||||
Assert.That(_objId01, Is.Not.Null);
|
||||
TestContext.Out.WriteLine($"Written {NUM_OBJECTS + 1} objects. Commit id is {_objId01}");
|
||||
objId01.Should().NotBeNull();
|
||||
|
||||
var objPulled = await _operations.Receive(_objId01);
|
||||
var objPulled = await _operations.Receive(objId01);
|
||||
|
||||
Assert.That(objPulled, Is.TypeOf<Base>());
|
||||
objPulled.Should().BeOfType<Base>();
|
||||
|
||||
// Note: even if the layers were originally declared as lists of "Base" objects, on deserialisation we cannot know that,
|
||||
// as it's a dynamic property. Dynamic properties, if their content value is ambigous, will default to a common-sense standard.
|
||||
// This specifically manifests in the case of lists and dictionaries: List<AnySpecificType> will become List<object>, and
|
||||
// Dictionary<string, MyType> will deserialize to Dictionary<string,object>.
|
||||
var layerA = ((dynamic)objPulled)["LayerA"] as List<object>;
|
||||
Assert.That(layerA, Has.Count.EqualTo(30));
|
||||
layerA?.Count.Should().Be(30);
|
||||
|
||||
var layerC = (List<object>)((dynamic)objPulled)["@LayerC"];
|
||||
Assert.That(layerC, Has.Count.EqualTo(30));
|
||||
Assert.That(layerC[0], Is.TypeOf<Point>());
|
||||
layerC.Count.Should().Be(30);
|
||||
layerC[0].Should().BeOfType<Point>();
|
||||
|
||||
var layerD = ((dynamic)objPulled)["@LayerD"] as List<object>;
|
||||
Assert.That(layerD, Has.Count.EqualTo(2));
|
||||
layerD?.Count.Should().Be(2);
|
||||
}
|
||||
|
||||
[Test(Description = "Should show progress!"), Order(4)]
|
||||
public async Task UploadProgressReports()
|
||||
[Fact(DisplayName = "Should show progress!")]
|
||||
public async Task UploadAndDownloadProgressReports()
|
||||
{
|
||||
Base myObject = new() { ["items"] = new List<Base>() };
|
||||
var rand = new Random();
|
||||
@@ -203,24 +187,20 @@ public sealed class SendReceiveLocal : IDisposable
|
||||
);
|
||||
}
|
||||
|
||||
(_commitId02, _) = await _operations.Send(myObject, _sut, false);
|
||||
}
|
||||
(var commitId02, _) = await _operations.Send(myObject, _sut, false);
|
||||
|
||||
[Test(Description = "Should show progress!"), Order(5)]
|
||||
public async Task DownloadProgressReports()
|
||||
{
|
||||
ProgressArgs? progress = null;
|
||||
await _operations.Receive(
|
||||
_commitId02.NotNull(),
|
||||
commitId02.NotNull(),
|
||||
onProgressAction: new UnitTestProgress<ProgressArgs>(x =>
|
||||
{
|
||||
progress = x;
|
||||
})
|
||||
);
|
||||
progress.ShouldNotBeNull();
|
||||
progress.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test(Description = "Should not dispose of transports if so specified.")]
|
||||
[Fact(DisplayName = "Should not dispose of transports if so specified.")]
|
||||
public async Task ShouldNotDisposeTransports()
|
||||
{
|
||||
var @base = new Base();
|
||||
@@ -233,9 +213,4 @@ public sealed class SendReceiveLocal : IDisposable
|
||||
_ = await _operations.Receive(sendResult.rootObjId, null, myLocalTransport);
|
||||
await _operations.Receive(sendResult.rootObjId, null, myLocalTransport);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_sut.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
using System.Drawing;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Tests.Unit.Host;
|
||||
using Xunit;
|
||||
using Point = Speckle.Sdk.Tests.Unit.Host.Point;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Api.Operations;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(Sdk.Api.Operations))]
|
||||
public class ObjectSerialization
|
||||
{
|
||||
private IOperations _operations;
|
||||
private readonly IOperations _operations;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public ObjectSerialization()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(DataChunk).Assembly, typeof(ColorMock).Assembly);
|
||||
@@ -26,7 +22,7 @@ public class ObjectSerialization
|
||||
_operations = serviceProvider.GetRequiredService<IOperations>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task IgnoreCircularReferences()
|
||||
{
|
||||
var pt = new Point(1, 2, 3);
|
||||
@@ -36,10 +32,10 @@ public class ObjectSerialization
|
||||
|
||||
var result = await _operations.DeserializeAsync(test);
|
||||
var circle = result["circle"];
|
||||
Assert.That(circle, Is.Null);
|
||||
circle.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task InterfacePropHandling()
|
||||
{
|
||||
Line tail = new() { Start = new Point(0, 0, 0), End = new Point(42, 42, 42) };
|
||||
@@ -75,10 +71,10 @@ public class ObjectSerialization
|
||||
|
||||
var deserialisedFeline = await _operations.DeserializeAsync(result);
|
||||
|
||||
Assert.That(deserialisedFeline.GetId(), Is.EqualTo(cat.GetId())); // If we're getting the same hash... we're probably fine!
|
||||
deserialisedFeline.GetId().Should().Be(cat.GetId());
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task InheritanceTests()
|
||||
{
|
||||
var superPoint = new SuperPoint
|
||||
@@ -92,10 +88,10 @@ public class ObjectSerialization
|
||||
var str = _operations.Serialize(superPoint);
|
||||
var sstr = await _operations.DeserializeAsync(str);
|
||||
|
||||
Assert.That(sstr.speckle_type, Is.EqualTo(superPoint.speckle_type));
|
||||
sstr.speckle_type.Should().Be(superPoint.speckle_type);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ListDynamicProp()
|
||||
{
|
||||
var point = new Point();
|
||||
@@ -111,11 +107,12 @@ public class ObjectSerialization
|
||||
var str = _operations.Serialize(point);
|
||||
var dsrls = await _operations.DeserializeAsync(str);
|
||||
|
||||
var list = dsrls["test"] as List<object>; // NOTE: on dynamically added lists, we cannot infer the inner type and we always fall back to a generic list<object>.
|
||||
Assert.That(list, Has.Count.EqualTo(100));
|
||||
var list = dsrls["test"] as List<object>;
|
||||
list.Should().NotBeNull(); // Ensure the list isn't null in first place
|
||||
list!.Count.Should().Be(100);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ChunkSerialisation()
|
||||
{
|
||||
var baseBasedChunk = new DataChunk() { data = new() };
|
||||
@@ -144,12 +141,12 @@ public class ObjectSerialization
|
||||
var stringChunkDeserialised = (DataChunk)await _operations.DeserializeAsync(stringChunkString);
|
||||
var doubleChunkDeserialised = (DataChunk)await _operations.DeserializeAsync(doubleChunkString);
|
||||
|
||||
Assert.That(baseChunkDeserialised.data, Has.Count.EqualTo(baseBasedChunk.data.Count));
|
||||
Assert.That(stringChunkDeserialised.data, Has.Count.EqualTo(stringBasedChunk.data.Count));
|
||||
Assert.That(doubleChunkDeserialised.data, Has.Count.EqualTo(doubleBasedChunk.data.Count));
|
||||
baseChunkDeserialised.data.Count.Should().Be(baseBasedChunk.data.Count);
|
||||
stringChunkDeserialised.data.Count.Should().Be(stringBasedChunk.data.Count);
|
||||
doubleChunkDeserialised.data.Count.Should().Be(doubleBasedChunk.data.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ObjectWithChunksSerialisation()
|
||||
{
|
||||
const int MAX_NUM = 2020;
|
||||
@@ -174,10 +171,10 @@ public class ObjectSerialization
|
||||
var serialised = _operations.Serialize(mesh);
|
||||
var deserialised = await _operations.DeserializeAsync(serialised);
|
||||
|
||||
Assert.That(mesh.GetId(), Is.EqualTo(deserialised.GetId()));
|
||||
mesh.GetId().Should().Be(deserialised.GetId());
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void EmptyListSerialisationTests()
|
||||
{
|
||||
// NOTE: expected behaviour is that empty lists should serialize as empty lists. Don't ask why, it's complicated.
|
||||
@@ -200,7 +197,7 @@ public class ObjectSerialization
|
||||
&& serialised.Contains("\"nestedList\":[[[]]]")
|
||||
&& serialised.Contains("\"@nestedDetachableList\":[[[]]]");
|
||||
|
||||
Assert.That(isCorrect, Is.EqualTo(true));
|
||||
isCorrect.Should().BeTrue();
|
||||
}
|
||||
|
||||
[SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+DateMock")]
|
||||
@@ -209,7 +206,7 @@ public class ObjectSerialization
|
||||
public DateTime TestField { get; set; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task DateSerialisation()
|
||||
{
|
||||
var date = new DateTime(2020, 1, 14);
|
||||
@@ -218,7 +215,7 @@ public class ObjectSerialization
|
||||
var result = _operations.Serialize(mockBase);
|
||||
var test = (DateMock)await _operations.DeserializeAsync(result);
|
||||
|
||||
Assert.That(test.TestField, Is.EqualTo(date));
|
||||
test.TestField.Should().Be(date);
|
||||
}
|
||||
|
||||
[SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+GUIDMock")]
|
||||
@@ -227,7 +224,7 @@ public class ObjectSerialization
|
||||
public Guid TestField { get; set; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task GuidSerialisation()
|
||||
{
|
||||
var guid = Guid.NewGuid();
|
||||
@@ -236,7 +233,7 @@ public class ObjectSerialization
|
||||
var result = _operations.Serialize(mockBase);
|
||||
var test = (GUIDMock)await _operations.DeserializeAsync(result);
|
||||
|
||||
Assert.That(test.TestField, Is.EqualTo(guid));
|
||||
test.TestField.Should().Be(guid);
|
||||
}
|
||||
|
||||
[SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+ColorMock")]
|
||||
@@ -245,7 +242,7 @@ public class ObjectSerialization
|
||||
public Color TestField { get; set; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task ColorSerialisation()
|
||||
{
|
||||
var color = Color.FromArgb(255, 4, 126, 251);
|
||||
@@ -254,7 +251,7 @@ public class ObjectSerialization
|
||||
var result = _operations.Serialize(mockBase);
|
||||
var test = (ColorMock)await _operations.DeserializeAsync(result);
|
||||
|
||||
Assert.That(test.TestField, Is.EqualTo(color));
|
||||
test.TestField.Should().Be(color);
|
||||
}
|
||||
|
||||
[SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+StringDateTimeRegressionMock")]
|
||||
@@ -263,7 +260,7 @@ public class ObjectSerialization
|
||||
public string TestField { get; set; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task StringDateTimeRegression()
|
||||
{
|
||||
var mockBase = new StringDateTimeRegressionMock { TestField = "2021-11-12T11:32:01" };
|
||||
@@ -271,6 +268,6 @@ public class ObjectSerialization
|
||||
var result = _operations.Serialize(mockBase);
|
||||
var test = (StringDateTimeRegressionMock)await _operations.DeserializeAsync(result);
|
||||
|
||||
Assert.That(test.TestField, Is.EqualTo(mockBase.TestField));
|
||||
test.TestField.Should().Be(mockBase.TestField);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
using Xunit;
|
||||
|
||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||
@@ -1,87 +1,94 @@
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Common;
|
||||
|
||||
public class NotNullTests
|
||||
{
|
||||
[TestCase(null, 0)]
|
||||
[TestCase(new string[0], 0)]
|
||||
[TestCase(new[] { "yay" }, 1)]
|
||||
[Theory]
|
||||
[InlineData(null, 0)]
|
||||
[InlineData(new string[0], 0)]
|
||||
[InlineData(new[] { "yay" }, 1)]
|
||||
public void Empty(string[]? test, int length)
|
||||
{
|
||||
var list = NotNullExtensions.Empty(test).ToList();
|
||||
list.Count.ShouldBe(length);
|
||||
var list = test.Empty().ToList();
|
||||
list.Count.Should().Be(length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void NotNullClass()
|
||||
{
|
||||
var t = NotNullExtensions.NotNull("test");
|
||||
t.ShouldNotBeNull().ShouldBe("test");
|
||||
var t = "test".NotNull();
|
||||
t.Should().Be("test");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void NotNullStruct()
|
||||
{
|
||||
var t = NotNullExtensions.NotNull<int>(2);
|
||||
t.ShouldBe(2);
|
||||
t.Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task NotNullClass_Task()
|
||||
{
|
||||
var t = await NotNullExtensions.NotNull(Task.FromResult<string?>("test"));
|
||||
t.ShouldNotBeNull().ShouldBe("test");
|
||||
var t = await Task.FromResult<string?>("test").NotNull();
|
||||
t.Should().Be("test");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task NotNullStruct_Task()
|
||||
{
|
||||
var t = await NotNullExtensions.NotNull(Task.FromResult<int?>(2));
|
||||
t.ShouldBe(2);
|
||||
var t = await Task.FromResult<int?>(2).NotNull();
|
||||
t.Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task NotNullClass_ValueTask()
|
||||
{
|
||||
var t = await NotNullExtensions.NotNull(ValueTask.FromResult<string?>("test"));
|
||||
t.ShouldNotBeNull().ShouldBe("test");
|
||||
var t = await ValueTask.FromResult<string?>("test").NotNull();
|
||||
t.Should().Be("test");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public async Task NotNullStruct_ValueTask()
|
||||
{
|
||||
var t = await NotNullExtensions.NotNull(ValueTask.FromResult<int?>(2));
|
||||
t.ShouldBe(2);
|
||||
var t = await ValueTask.FromResult<int?>(2).NotNull();
|
||||
t.Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NotNullClass_Exception() =>
|
||||
Assert.Throws<ArgumentNullException>(() => NotNullExtensions.NotNull((string?)null));
|
||||
[Fact]
|
||||
public void NotNullClass_Exception() => FluentActions.Invoking(() => ((string?)null).NotNull());
|
||||
|
||||
[Test]
|
||||
public void NotNullStruct_Exception() =>
|
||||
Assert.Throws<ArgumentNullException>(() => NotNullExtensions.NotNull((int?)null));
|
||||
[Fact]
|
||||
public void NotNullStruct_Exception() => FluentActions.Invoking(() => ((int?)null).NotNull());
|
||||
|
||||
[Test]
|
||||
public void NotNullClass_Task_Exception() =>
|
||||
Assert.ThrowsAsync<ArgumentNullException>(() => NotNullExtensions.NotNull(Task.FromResult((string?)null)));
|
||||
[Fact]
|
||||
public async Task NotNullClass_Task_Exception() =>
|
||||
await FluentActions
|
||||
.Invoking(async () => await Task.FromResult((string?)null).NotNull())
|
||||
.Should()
|
||||
.ThrowAsync<ArgumentNullException>();
|
||||
|
||||
[Test]
|
||||
public void NotNullStruct_Task_Exception() =>
|
||||
Assert.ThrowsAsync<ArgumentNullException>(() => NotNullExtensions.NotNull(Task.FromResult((int?)null)));
|
||||
[Fact]
|
||||
public async Task NotNullStruct_Task_Exception() =>
|
||||
await FluentActions
|
||||
.Invoking(async () => await Task.FromResult((int?)null).NotNull())
|
||||
.Should()
|
||||
.ThrowAsync<ArgumentNullException>();
|
||||
|
||||
[Test]
|
||||
public void NotNullClass_ValueTask_Exception() =>
|
||||
Assert.ThrowsAsync<ArgumentNullException>(
|
||||
async () => await NotNullExtensions.NotNull(ValueTask.FromResult((string?)null))
|
||||
);
|
||||
[Fact]
|
||||
public async Task NotNullClass_ValueTask_Exception() =>
|
||||
await FluentActions
|
||||
.Invoking(async () => await ValueTask.FromResult((string?)null).NotNull())
|
||||
.Should()
|
||||
.ThrowAsync<ArgumentNullException>();
|
||||
|
||||
[Test]
|
||||
public void NotNullStruct_ValueTask_Exception() =>
|
||||
Assert.ThrowsAsync<ArgumentNullException>(
|
||||
async () => await NotNullExtensions.NotNull(ValueTask.FromResult((int?)null))
|
||||
);
|
||||
[Fact]
|
||||
public async Task NotNullStruct_ValueTask_Exception() =>
|
||||
await FluentActions
|
||||
.Invoking(async () => await ValueTask.FromResult((int?)null).NotNull())
|
||||
.Should()
|
||||
.ThrowAsync<ArgumentNullException>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Speckle.Sdk.Dependencies;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Common;
|
||||
|
||||
public class RangeFromTests
|
||||
{
|
||||
[Fact]
|
||||
public void EnsureRange()
|
||||
{
|
||||
var list = EnumerableExtensions.RangeFrom(1, 4).ToArray();
|
||||
Assert.Equal([1, 2, 3, 4], list);
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,98 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Common;
|
||||
|
||||
[TestOf(typeof(Units))]
|
||||
public class UnitsTest
|
||||
{
|
||||
private const double EPS = 0.00022;
|
||||
|
||||
public static List<string> OfficiallySupportedUnits => Units.SupportedUnits;
|
||||
public static List<string> NotSupportedUnits => ["feeters", "liters", "us_ft"];
|
||||
public static List<string?> ConversionSupport => [.. Units.SupportedUnits, null];
|
||||
|
||||
[Test, Combinatorial]
|
||||
[DefaultFloatingPointTolerance(EPS)]
|
||||
public void TestUnitConversion(
|
||||
[ValueSource(nameof(ConversionSupport))] string? from,
|
||||
[ValueSource(nameof(ConversionSupport))] string? to
|
||||
)
|
||||
public static List<string> NotSupportedUnits => ["feeters", "liters", "us_ft"];
|
||||
|
||||
public static List<string?> ConversionSupport => Units.SupportedUnits.Concat([null]).ToList();
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ConversionSupportGenerator))]
|
||||
public void TestUnitConversion(string? from, string? to)
|
||||
{
|
||||
var forwards = Units.GetConversionFactor(from, to);
|
||||
var backwards = Units.GetConversionFactor(to, from);
|
||||
|
||||
Assert.That(
|
||||
backwards * forwards,
|
||||
Is.EqualTo(1d),
|
||||
$"Behaviour says that 1{from} == {forwards}{to}, and 1{to} == {backwards}{from}"
|
||||
);
|
||||
(backwards * forwards)
|
||||
.Should()
|
||||
.BeApproximately(1d, EPS, $"Behaviour says that 1{from} == {forwards}{to}, and 1{to} == {backwards}{from}");
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(OfficiallySupportedUnits))]
|
||||
[Theory]
|
||||
[MemberData(nameof(OfficiallySupportedUnitsGenerator))]
|
||||
public void IsUnitSupported_ReturnsTrue_AllSupportedUnits(string unit)
|
||||
{
|
||||
bool res = Units.IsUnitSupported(unit);
|
||||
Assert.That(res, Is.True);
|
||||
res.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(NotSupportedUnits))]
|
||||
[Theory]
|
||||
[MemberData(nameof(NotSupportedUnitsGenerator))]
|
||||
public void IsUnitSupported_ReturnsFalse_NotSupportedUnits(string unit)
|
||||
{
|
||||
bool res = Units.IsUnitSupported(unit);
|
||||
Assert.That(res, Is.False);
|
||||
res.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(OfficiallySupportedUnits))]
|
||||
[Theory]
|
||||
[MemberData(nameof(OfficiallySupportedUnitsGenerator))]
|
||||
public void GetUnitsFromString_ReturnsSupported(string unit)
|
||||
{
|
||||
var lower = Units.GetUnitsFromString(unit);
|
||||
var upper = Units.GetUnitsFromString(unit?.ToUpperInvariant());
|
||||
string? lower = Units.GetUnitsFromString(unit);
|
||||
string? upper = Units.GetUnitsFromString(unit.ToUpperInvariant());
|
||||
|
||||
Assert.That(lower, Is.EqualTo(unit));
|
||||
Assert.That(upper, Is.EqualTo(unit));
|
||||
lower.Should().Be(unit);
|
||||
upper.Should().Be(unit);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(NotSupportedUnits))]
|
||||
public void GetUnitsFromString_ThrowsUnSupported(string unit)
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _ = Units.GetUnitsFromString(unit));
|
||||
}
|
||||
[Theory]
|
||||
[MemberData(nameof(NotSupportedUnitsGenerator))]
|
||||
public void GetUnitsFromString_ThrowsUnSupported(string unit) =>
|
||||
FluentActions.Invoking(() => Units.GetUnitsFromString(unit)).Should().Throw<ArgumentOutOfRangeException>();
|
||||
|
||||
[TestCaseSource(nameof(OfficiallySupportedUnits))]
|
||||
[Theory]
|
||||
[MemberData(nameof(OfficiallySupportedUnitsGenerator))]
|
||||
public void UnitEncoding_RoundTrip(string unit)
|
||||
{
|
||||
var encoded = Units.GetEncodingFromUnit(unit);
|
||||
var res = Units.GetUnitFromEncoding(encoded);
|
||||
|
||||
Assert.That(res, Is.EqualTo(unit));
|
||||
res.Should().Be(unit);
|
||||
}
|
||||
|
||||
// Generators for MemberData
|
||||
public static IEnumerable<object[]> OfficiallySupportedUnitsGenerator()
|
||||
{
|
||||
foreach (var unit in OfficiallySupportedUnits)
|
||||
{
|
||||
yield return [unit];
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> NotSupportedUnitsGenerator()
|
||||
{
|
||||
foreach (var unit in NotSupportedUnits)
|
||||
{
|
||||
yield return [unit];
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object?[]> ConversionSupportGenerator()
|
||||
{
|
||||
foreach (var from in ConversionSupport)
|
||||
{
|
||||
foreach (var to in ConversionSupport)
|
||||
{
|
||||
yield return [from, to];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,66 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Host;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Credentials;
|
||||
|
||||
public class AccountServerMigrationTests
|
||||
public class AccountServerMigrationTests : IDisposable
|
||||
{
|
||||
private readonly List<Account> _accountsToCleanUp = new();
|
||||
private readonly List<Account> _accountsToCleanUp = [];
|
||||
|
||||
public static IEnumerable<TestCaseData> MigrationTestCase()
|
||||
public static IEnumerable<object[]> MigrationTestCases()
|
||||
{
|
||||
const string OLD_URL = "https://old.example.com";
|
||||
const string NEW_URL = "https://new.example.com";
|
||||
const string OTHER_URL = "https://other.example.com";
|
||||
|
||||
Account oldAccount = CreateTestAccount(OLD_URL, null, new(NEW_URL));
|
||||
string accountId = oldAccount.userInfo.id; // new account user must match old account user id
|
||||
Account newAccount = CreateTestAccount(NEW_URL, new(OLD_URL), null, accountId);
|
||||
Account otherAccount = CreateTestAccount(OTHER_URL, null, null);
|
||||
|
||||
List<Account> givenAccounts = new() { oldAccount, newAccount, otherAccount };
|
||||
List<Account> givenAccounts = [oldAccount, newAccount, otherAccount];
|
||||
|
||||
yield return new TestCaseData(givenAccounts, NEW_URL, new[] { newAccount })
|
||||
.SetName("Get New")
|
||||
.SetDescription("When requesting for new account, ensure only this account is returned");
|
||||
yield return [givenAccounts, NEW_URL, new[] { newAccount }];
|
||||
|
||||
yield return new TestCaseData(givenAccounts, OLD_URL, new[] { newAccount })
|
||||
.SetName("Get New via Old")
|
||||
.SetDescription("When requesting for old account, ensure migrated account is returned first");
|
||||
yield return [givenAccounts, OLD_URL, new[] { newAccount }];
|
||||
|
||||
var reversed = Enumerable.Reverse(givenAccounts).ToList();
|
||||
var reversed = givenAccounts.AsEnumerable().Reverse().ToList();
|
||||
|
||||
yield return new TestCaseData(reversed, OLD_URL, new[] { newAccount })
|
||||
.SetName("Get New via Old (Reversed order)")
|
||||
.SetDescription("Account order shouldn't matter");
|
||||
yield return [reversed, OLD_URL, new[] { newAccount }];
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(MigrationTestCase))]
|
||||
[Theory]
|
||||
[MemberData(nameof(MigrationTestCases))]
|
||||
public void TestServerMigration(IList<Account> accounts, string requestedUrl, IList<Account> expectedSequence)
|
||||
{
|
||||
// Add accounts to the local setup
|
||||
AddAccounts(accounts);
|
||||
var serviceProvider = TestServiceSetup.GetServiceProvider();
|
||||
|
||||
var serviceProvider = TestServiceSetup.GetServiceProvider();
|
||||
var result = serviceProvider.GetRequiredService<IAccountManager>().GetAccounts(requestedUrl).ToList();
|
||||
|
||||
Assert.That(result, Is.EquivalentTo(expectedSequence));
|
||||
// Assert the result using Shouldly
|
||||
result.Should().BeEquivalentTo(expectedSequence);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
public void Dispose()
|
||||
{
|
||||
//Clean up any of the test accounts we made
|
||||
// Clean up accounts after each test
|
||||
foreach (var acc in _accountsToCleanUp)
|
||||
{
|
||||
Fixtures.DeleteLocalAccount(acc.id);
|
||||
}
|
||||
|
||||
_accountsToCleanUp.Clear();
|
||||
}
|
||||
|
||||
private static Account CreateTestAccount(string url, Uri? movedFrom, Uri? movedTo, string? id = null)
|
||||
{
|
||||
id ??= Guid.NewGuid().ToString();
|
||||
|
||||
return new Account
|
||||
{
|
||||
token = "myToken",
|
||||
@@ -83,7 +81,7 @@ public class AccountServerMigrationTests
|
||||
|
||||
private void AddAccounts(IEnumerable<Account> accounts)
|
||||
{
|
||||
foreach (Account account in accounts)
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
_accountsToCleanUp.Add(account);
|
||||
Fixtures.UpdateOrSaveAccount(account);
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Host;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Credentials;
|
||||
|
||||
[TestFixture]
|
||||
public class CredentialInfrastructure
|
||||
public class CredentialInfrastructure : IDisposable
|
||||
{
|
||||
private IAccountManager _accountManager;
|
||||
private readonly IAccountManager _accountManager;
|
||||
private static readonly Account s_testAccount1;
|
||||
private static readonly Account s_testAccount2;
|
||||
private static readonly Account s_testAccount3;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public static void SetUp()
|
||||
static CredentialInfrastructure()
|
||||
{
|
||||
s_testAccount1 = new Account
|
||||
{
|
||||
@@ -42,76 +42,69 @@ public class CredentialInfrastructure
|
||||
name = "Test Account 3",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public CredentialInfrastructure()
|
||||
{
|
||||
Fixtures.UpdateOrSaveAccount(s_testAccount1);
|
||||
Fixtures.UpdateOrSaveAccount(s_testAccount2);
|
||||
Fixtures.SaveLocalAccount(s_testAccount3);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Setup2()
|
||||
{
|
||||
var serviceProvider = TestServiceSetup.GetServiceProvider();
|
||||
_accountManager = serviceProvider.GetRequiredService<IAccountManager>();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public static void TearDown()
|
||||
public void Dispose()
|
||||
{
|
||||
_accountManager.Dispose();
|
||||
Fixtures.DeleteLocalAccount(s_testAccount1.id);
|
||||
Fixtures.DeleteLocalAccount(s_testAccount2.id);
|
||||
Fixtures.DeleteLocalAccount(s_testAccount3.id);
|
||||
Fixtures.DeleteLocalAccountFile();
|
||||
}
|
||||
|
||||
private static Account s_testAccount1,
|
||||
s_testAccount2,
|
||||
s_testAccount3;
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void GetAllAccounts()
|
||||
{
|
||||
var accs = _accountManager.GetAccounts().ToList();
|
||||
Assert.That(accs, Has.Count.GreaterThanOrEqualTo(3)); // Tests are adding three accounts, you might have extra accounts on your machine when testing :D
|
||||
accs.Count.Should().BeGreaterThanOrEqualTo(3); // Tests are adding three accounts, there might be extra accounts locally
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void GetAccount_ById()
|
||||
{
|
||||
var result = _accountManager.GetAccount(s_testAccount1.id);
|
||||
|
||||
Assert.That(result, Is.EqualTo(s_testAccount1));
|
||||
result.Should().Be(s_testAccount1); // Uses `Shouldly` for a clean assertion
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAccount_ById_ThrowsWhenNotFound()
|
||||
{
|
||||
Assert.Throws<SpeckleAccountManagerException>(() => _accountManager.GetAccount("Non_existent_id"));
|
||||
}
|
||||
[Fact]
|
||||
public void GetAccount_ById_ThrowsWhenNotFound() =>
|
||||
FluentActions
|
||||
.Invoking(() => _accountManager.GetAccount("Non_existent_id"))
|
||||
.Should()
|
||||
.Throw<SpeckleAccountManagerException>();
|
||||
|
||||
public static IEnumerable<Account> TestCases()
|
||||
{
|
||||
SetUp();
|
||||
return new[] { s_testAccount1, s_testAccount2, s_testAccount3 };
|
||||
}
|
||||
public static TheoryData<Account> TestCases() => new() { s_testAccount1, s_testAccount2, s_testAccount3 };
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(TestCases))]
|
||||
[Theory]
|
||||
[MemberData(nameof(TestCases))]
|
||||
public void GetAccountsForServer(Account target)
|
||||
{
|
||||
var accs = _accountManager.GetAccounts(target.serverInfo.url).ToList();
|
||||
|
||||
Assert.That(accs, Has.Count.EqualTo(1));
|
||||
accs.Count.Should().Be(1);
|
||||
|
||||
var acc = accs[0];
|
||||
|
||||
Assert.That(acc, Is.Not.SameAs(target), "We expect new objects (no reference equality)");
|
||||
Assert.That(acc.serverInfo.company, Is.EqualTo(target.serverInfo.company));
|
||||
Assert.That(acc.serverInfo.url, Is.EqualTo(target.serverInfo.url));
|
||||
Assert.That(acc.refreshToken, Is.EqualTo(target.refreshToken));
|
||||
Assert.That(acc.token, Is.EqualTo(target.token));
|
||||
acc.Should().NotBeSameAs(target); // We expect new objects (no reference equality)
|
||||
acc.serverInfo.company.Should().Be(target.serverInfo.company);
|
||||
acc.serverInfo.url.Should().Be(target.serverInfo.url);
|
||||
acc.refreshToken.Should().Be(target.refreshToken);
|
||||
acc.token.Should().Be(target.token);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void EnsureLocalIdentifiers_AreUniqueAcrossServers()
|
||||
{
|
||||
// Accounts with the same user ID in different servers should always result in different local identifiers.
|
||||
@@ -128,6 +121,6 @@ public class CredentialInfrastructure
|
||||
userInfo = new UserInfo { id = id },
|
||||
}.GetLocalIdentifier();
|
||||
|
||||
Assert.That(acc1, Is.Not.EqualTo(acc2));
|
||||
acc1.Should().NotBe(acc2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,13 +28,7 @@ public abstract class Fixtures
|
||||
File.WriteAllText(s_accountPath, json);
|
||||
}
|
||||
|
||||
public static void DeleteLocalAccount(string id)
|
||||
{
|
||||
s_accountStorage.DeleteObject(id);
|
||||
}
|
||||
public static void DeleteLocalAccount(string id) => s_accountStorage.DeleteObject(id);
|
||||
|
||||
public static void DeleteLocalAccountFile()
|
||||
{
|
||||
File.Delete(s_accountPath);
|
||||
}
|
||||
public static void DeleteLocalAccountFile() => File.Delete(s_accountPath);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Helpers;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(nameof(SpecklePathProvider))]
|
||||
public class SpecklePathTests
|
||||
{
|
||||
[Test]
|
||||
[Fact]
|
||||
public void TestUserApplicationDataPath()
|
||||
{
|
||||
var userPath = SpecklePathProvider.UserApplicationDataPath();
|
||||
@@ -39,10 +38,10 @@ public class SpecklePathTests
|
||||
throw new NotImplementedException("Your OS platform is not supported");
|
||||
}
|
||||
|
||||
Assert.That(userPath, Does.Match(pattern));
|
||||
userPath.Should().MatchRegex(pattern);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void TestInstallApplicationDataPath()
|
||||
{
|
||||
var installPath = SpecklePathProvider.InstallApplicationDataPath;
|
||||
@@ -78,6 +77,6 @@ public class SpecklePathTests
|
||||
throw new NotImplementedException("Your OS platform is not supported");
|
||||
}
|
||||
|
||||
Assert.That(installPath, Does.Match(pattern));
|
||||
installPath.Should().MatchRegex(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Host;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Host;
|
||||
|
||||
public class HostApplicationTests
|
||||
{
|
||||
private static List<HostAppVersion> s_hostAppVersion = Enum.GetValues<HostAppVersion>().ToList();
|
||||
public static TheoryData<HostAppVersion> HostAppVersionData => new(Enum.GetValues<HostAppVersion>().ToList());
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(s_hostAppVersion))]
|
||||
[Theory]
|
||||
[MemberData(nameof(HostAppVersionData))]
|
||||
public void HostAppVersionParsingTests(HostAppVersion appVersion)
|
||||
{
|
||||
appVersion.ToString().StartsWith('v').ShouldBeTrue();
|
||||
// Assert that the string representation starts with 'v'
|
||||
appVersion.ToString().StartsWith('v').Should().BeTrue();
|
||||
|
||||
// Assert that the parsed version is a positive integer
|
||||
var version = HostApplications.GetVersion(appVersion);
|
||||
int.Parse(version).ShouldBePositive();
|
||||
int.Parse(version).Should().BePositive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,38 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
using Speckle.Newtonsoft.Json.Linq;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Dependencies.Serialization;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Serialisation;
|
||||
using Speckle.Sdk.Serialisation.V2;
|
||||
using Speckle.Sdk.Serialisation.V2.Send;
|
||||
using Speckle.Sdk.Transports;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Models;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(Base))]
|
||||
[TestOf(typeof(DynamicBase))]
|
||||
public class BaseTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public BaseTests()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(BaseTests).Assembly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CanGetSetDynamicItemProp()
|
||||
{
|
||||
var @base = new Base();
|
||||
@base["Item"] = "Item";
|
||||
|
||||
Assert.That(@base["Item"], Is.EqualTo("Item"));
|
||||
@base["Item"].Should().Be("Item");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CanGetSetTypedItemProp()
|
||||
{
|
||||
var @base = new ObjectWithItemProp { Item = "baz" };
|
||||
|
||||
Assert.That(@base["Item"], Is.EqualTo("baz"));
|
||||
Assert.That(@base.Item, Is.EqualTo("baz"));
|
||||
@base["Item"].Should().Be("baz");
|
||||
@base.Item.Should().Be("baz");
|
||||
}
|
||||
|
||||
[Test(Description = "Checks if validation is performed in property names")]
|
||||
[Fact(DisplayName = "Checks if validation is performed in property names")]
|
||||
public void CanValidatePropNames()
|
||||
{
|
||||
dynamic @base = new Base();
|
||||
@@ -54,27 +42,35 @@ public class BaseTests
|
||||
|
||||
// Only single leading @ allowed
|
||||
@base["@something"] = "A";
|
||||
Assert.Throws<InvalidPropNameException>(() =>
|
||||
{
|
||||
@base["@@@something"] = "Testing";
|
||||
});
|
||||
FluentActions
|
||||
.Invoking(() =>
|
||||
{
|
||||
@base["@@@something"] = "Testing";
|
||||
})
|
||||
.Should()
|
||||
.Throw<InvalidPropNameException>();
|
||||
|
||||
// Invalid chars: ./
|
||||
Assert.Throws<InvalidPropNameException>(() =>
|
||||
{
|
||||
@base["some.thing"] = "Testing";
|
||||
});
|
||||
Assert.Throws<InvalidPropNameException>(() =>
|
||||
{
|
||||
@base["some/thing"] = "Testing";
|
||||
});
|
||||
|
||||
FluentActions
|
||||
.Invoking(() =>
|
||||
{
|
||||
@base["some.thing"] = "Testing";
|
||||
})
|
||||
.Should()
|
||||
.Throw<InvalidPropNameException>();
|
||||
FluentActions
|
||||
.Invoking(() =>
|
||||
{
|
||||
@base["some/thing"] = "Testing";
|
||||
})
|
||||
.Should()
|
||||
.Throw<InvalidPropNameException>();
|
||||
// Trying to change a class member value will throw exceptions.
|
||||
//Assert.Throws<Exception>(() => { @base["speckle_type"] = "Testing"; });
|
||||
//Assert.Throws<Exception>(() => { @base["id"] = "Testing"; });
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CountDynamicChunkables()
|
||||
{
|
||||
const int MAX_NUM = 3000;
|
||||
@@ -92,10 +88,10 @@ public class BaseTests
|
||||
@base["@(1000)cc2"] = customChunkArr;
|
||||
|
||||
var num = @base.GetTotalChildrenCount();
|
||||
Assert.That(num, Is.EqualTo(MAX_NUM / 1000 * 2 + 1));
|
||||
num.Should().Be(MAX_NUM / 1000 * 2 + 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CountTypedChunkables()
|
||||
{
|
||||
const int MAX_NUM = 3000;
|
||||
@@ -114,33 +110,33 @@ public class BaseTests
|
||||
|
||||
var num = @base.GetTotalChildrenCount();
|
||||
var actualNum = 1 + MAX_NUM / 300 + MAX_NUM / 1000;
|
||||
Assert.That(num, Is.EqualTo(actualNum));
|
||||
num.Should().Be(actualNum);
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that no ignored or obsolete properties are returned")]
|
||||
[Fact(DisplayName = "Checks that no ignored or obsolete properties are returned")]
|
||||
public void CanGetMemberNames()
|
||||
{
|
||||
var @base = new SampleObject();
|
||||
var dynamicProp = "dynamicProp";
|
||||
@base[dynamicProp] = 123;
|
||||
var names = @base.GetMembers().Keys;
|
||||
Assert.That(names, Has.No.Member(nameof(@base.IgnoredSchemaProp)));
|
||||
Assert.That(names, Has.No.Member(nameof(@base.ObsoleteSchemaProp)));
|
||||
Assert.That(names, Has.Member(dynamicProp));
|
||||
Assert.That(names, Has.Member(nameof(@base.attachedProp)));
|
||||
names.Should().NotContain(nameof(@base.IgnoredSchemaProp));
|
||||
names.Should().NotContain(nameof(@base.ObsoleteSchemaProp));
|
||||
names.Should().Contain(dynamicProp);
|
||||
names.Should().Contain(nameof(@base.attachedProp));
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that only instance properties are returned, excluding obsolete and ignored.")]
|
||||
[Fact(DisplayName = "Checks that only instance properties are returned, excluding obsolete and ignored.")]
|
||||
public void CanGetMembers_OnlyInstance()
|
||||
{
|
||||
var @base = new SampleObject();
|
||||
@base["dynamicProp"] = 123;
|
||||
|
||||
var names = @base.GetMembers(DynamicBaseMemberType.Instance).Keys;
|
||||
Assert.That(names, Has.Member(nameof(@base.attachedProp)));
|
||||
names.Should().Contain(nameof(@base.attachedProp));
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that only dynamic properties are returned")]
|
||||
[Fact(DisplayName = "Checks that only dynamic properties are returned")]
|
||||
public void CanGetMembers_OnlyDynamic()
|
||||
{
|
||||
var @base = new SampleObject();
|
||||
@@ -148,33 +144,33 @@ public class BaseTests
|
||||
@base[dynamicProp] = 123;
|
||||
|
||||
var names = @base.GetMembers(DynamicBaseMemberType.Dynamic).Keys;
|
||||
Assert.That(names, Has.Member(dynamicProp));
|
||||
Assert.That(names, Has.Count.EqualTo(1));
|
||||
names.Should().Contain(dynamicProp);
|
||||
names.Count.Should().Be(1);
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that all typed properties (including ignored ones) are returned")]
|
||||
[Fact(DisplayName = "Checks that all typed properties (including ignored ones) are returned")]
|
||||
public void CanGetMembers_OnlyInstance_IncludeIgnored()
|
||||
{
|
||||
var @base = new SampleObject();
|
||||
@base["dynamicProp"] = 123;
|
||||
|
||||
var names = @base.GetMembers(DynamicBaseMemberType.Instance | DynamicBaseMemberType.SchemaIgnored).Keys;
|
||||
Assert.That(names, Has.Member(nameof(@base.IgnoredSchemaProp)));
|
||||
Assert.That(names, Has.Member(nameof(@base.attachedProp)));
|
||||
names.Should().Contain(nameof(@base.IgnoredSchemaProp));
|
||||
names.Should().Contain(nameof(@base.attachedProp));
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
[Fact(DisplayName = "Checks that all typed properties (including obsolete ones) are returned")]
|
||||
public void CanGetMembers_OnlyInstance_IncludeObsolete()
|
||||
{
|
||||
var @base = new SampleObject();
|
||||
@base["dynamicProp"] = 123;
|
||||
|
||||
var names = @base.GetMembers(DynamicBaseMemberType.Instance | DynamicBaseMemberType.Obsolete).Keys;
|
||||
Assert.That(names, Has.Member(nameof(@base.ObsoleteSchemaProp)));
|
||||
Assert.That(names, Has.Member(nameof(@base.attachedProp)));
|
||||
names.Should().Contain(nameof(@base.ObsoleteSchemaProp));
|
||||
names.Should().Contain(nameof(@base.attachedProp));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CanGetDynamicMembers()
|
||||
{
|
||||
var @base = new SampleObject();
|
||||
@@ -182,11 +178,11 @@ public class BaseTests
|
||||
@base[dynamicProp] = null;
|
||||
|
||||
var names = @base.GetDynamicMemberNames();
|
||||
Assert.That(names, Has.Member(dynamicProp));
|
||||
Assert.That(@base[dynamicProp], Is.Null);
|
||||
names.Should().Contain(dynamicProp);
|
||||
@base[dynamicProp].Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CanSetDynamicMembers()
|
||||
{
|
||||
var @base = new SampleObject();
|
||||
@@ -194,19 +190,19 @@ public class BaseTests
|
||||
var value = "something";
|
||||
// Can create a new dynamic member
|
||||
@base[key] = value;
|
||||
Assert.That(value, Is.EqualTo((string)@base[key].NotNull()));
|
||||
value.Should().Be((string)@base[key].NotNull());
|
||||
|
||||
// Can overwrite existing
|
||||
value = "some other value";
|
||||
@base[key] = value;
|
||||
Assert.That(value, Is.EqualTo((string)@base[key].NotNull()));
|
||||
value.Should().Be((string)@base[key].NotNull());
|
||||
|
||||
// Accepts null values
|
||||
@base[key] = null;
|
||||
Assert.That(@base[key], Is.Null);
|
||||
@base[key].Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CanShallowCopy()
|
||||
{
|
||||
var sample = new SampleObject();
|
||||
@@ -217,8 +213,8 @@ public class BaseTests
|
||||
var sampleMembers = sample.GetMembers(selectedMembers);
|
||||
var copyMembers = copy.GetMembers(selectedMembers);
|
||||
|
||||
Assert.That(copyMembers.Keys, Is.EquivalentTo(sampleMembers.Keys));
|
||||
Assert.That(copyMembers.Values, Is.EquivalentTo(sampleMembers.Values));
|
||||
copyMembers.Keys.Should().BeEquivalentTo(sampleMembers.Keys);
|
||||
copyMembers.Values.Should().BeEquivalentTo(sampleMembers.Values);
|
||||
}
|
||||
|
||||
[SpeckleType("Speckle.Core.Tests.Unit.Models.BaseTests+SampleObject")]
|
||||
|
||||
@@ -1,46 +1,44 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Models.Extensions;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(nameof(BaseExtensions))]
|
||||
public class BaseExtensionsTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public BaseExtensionsTests()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("myDynamicProp")]
|
||||
[TestCase("elements")]
|
||||
[Theory]
|
||||
[InlineData("myDynamicProp")]
|
||||
[InlineData("elements")]
|
||||
public void GetDetachedPropName_Dynamic(string propertyName)
|
||||
{
|
||||
var data = new TestBase();
|
||||
|
||||
var result = data.GetDetachedPropName(propertyName);
|
||||
var expected = $"@{propertyName}";
|
||||
Assert.That(result, Is.EqualTo(expected));
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(nameof(TestBase.myProperty))]
|
||||
[TestCase(nameof(TestBase.myOtherProperty))]
|
||||
[Theory]
|
||||
[InlineData(nameof(TestBase.myProperty))]
|
||||
[InlineData(nameof(TestBase.myOtherProperty))]
|
||||
public void GetDetachedPropName_Instance(string propertyName)
|
||||
{
|
||||
var data = new TestBase();
|
||||
var result = data.GetDetachedPropName(propertyName);
|
||||
|
||||
Assert.That(result, Is.EqualTo(propertyName));
|
||||
result.Should().Be(propertyName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void TraverseWithPath()
|
||||
{
|
||||
var collection = new Collection() { name = "collection" };
|
||||
@@ -51,17 +49,18 @@ public class BaseExtensionsTests
|
||||
|
||||
var basePaths = collection.TraverseWithPath((obj => obj is not Collection)).ToList();
|
||||
|
||||
Assert.That(basePaths.Count, Is.EqualTo(3));
|
||||
Assert.That(basePaths[0].Item2.speckle_type, Is.EqualTo("Speckle.Core.Models.Collections.Collection"));
|
||||
Assert.That(basePaths[0].Item2["name"], Is.EqualTo("collection"));
|
||||
Assert.That(basePaths[0].Item1, Is.EqualTo(new List<string>()));
|
||||
basePaths.Count.Should().Be(3);
|
||||
|
||||
Assert.That(basePaths[1].Item2.speckle_type, Is.EqualTo("Speckle.Core.Models.Collections.Collection"));
|
||||
Assert.That(basePaths[1].Item2["name"], Is.EqualTo("subCollection"));
|
||||
Assert.That(basePaths[1].Item1, Is.EqualTo(new List<string>() { "collection" }));
|
||||
basePaths[0].Item2.speckle_type.Should().Be("Speckle.Core.Models.Collections.Collection");
|
||||
basePaths[0].Item2["name"].Should().Be("collection");
|
||||
basePaths[0].Item1.Should().BeEquivalentTo(new List<string>());
|
||||
|
||||
Assert.That(basePaths[2].Item2.speckle_type, Is.EqualTo("Base"));
|
||||
Assert.That(basePaths[2].Item1, Is.EqualTo(new List<string>() { "collection", "subCollection" }));
|
||||
basePaths[1].Item2.speckle_type.Should().Be("Speckle.Core.Models.Collections.Collection");
|
||||
basePaths[1].Item2["name"].Should().Be("subCollection");
|
||||
basePaths[1].Item1.Should().BeEquivalentTo(new List<string>() { "collection" });
|
||||
|
||||
basePaths[2].Item2.speckle_type.Should().Be("Base");
|
||||
basePaths[2].Item1.Should().BeEquivalentTo(new List<string>() { "collection", "subCollection" });
|
||||
}
|
||||
|
||||
[SpeckleType("Speckle.Core.Tests.Unit.Models.Extensions.BaseExtensionsTests+TestBase")]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Models.Extensions;
|
||||
|
||||
[TestOf(typeof(BaseExtensions))]
|
||||
public class DisplayValueTests
|
||||
{
|
||||
private const string PAYLOAD = "This is my payload";
|
||||
@@ -22,28 +22,35 @@ public class DisplayValueTests
|
||||
TypeLoader.Initialize(typeof(Base).Assembly);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
[Fact]
|
||||
public void Setup() => Reset();
|
||||
|
||||
[TestCaseSource(nameof(TestCases))]
|
||||
[Theory]
|
||||
[MemberData(nameof(TestCases))]
|
||||
public void TestTryGetDisplayValue_WithValue(Base testCase)
|
||||
{
|
||||
var res = testCase.TryGetDisplayValue();
|
||||
|
||||
Assert.That(res, Has.Count.EqualTo(1));
|
||||
Assert.That(res, Has.One.Items.TypeOf<Base>().With.Property(nameof(Base.applicationId)).EqualTo(PAYLOAD));
|
||||
// Assert collection count
|
||||
res?.Count.Should().Be(1);
|
||||
|
||||
// Assert the single item matches the expected type and property
|
||||
var displayValue = res?[0];
|
||||
displayValue.Should().NotBeNull();
|
||||
displayValue!.applicationId.Should().Be(PAYLOAD);
|
||||
}
|
||||
|
||||
public static IEnumerable<Base> TestCases()
|
||||
public static IEnumerable<object[]> TestCases()
|
||||
{
|
||||
var listOfBase = new List<object> { s_displayValue }; //This is what our deserializer will output
|
||||
var listOfBase = new List<object> { s_displayValue }; // This is what our deserializer will output
|
||||
var listOfMesh = new List<Base> { s_displayValue };
|
||||
yield return new Base { ["@displayValue"] = s_displayValue };
|
||||
yield return new Base { ["displayValue"] = s_displayValue };
|
||||
yield return new Base { ["@displayValue"] = listOfBase };
|
||||
yield return new Base { ["displayValue"] = listOfBase };
|
||||
yield return new TypedDisplayValue { displayValue = s_displayValue };
|
||||
yield return new TypedDisplayValueList { displayValue = listOfMesh };
|
||||
|
||||
yield return [new Base { ["@displayValue"] = s_displayValue }];
|
||||
yield return [new Base { ["displayValue"] = s_displayValue }];
|
||||
yield return [new Base { ["@displayValue"] = listOfBase }];
|
||||
yield return [new Base { ["displayValue"] = listOfBase }];
|
||||
yield return [new TypedDisplayValue { displayValue = s_displayValue }];
|
||||
yield return [new TypedDisplayValueList { displayValue = listOfMesh }];
|
||||
}
|
||||
|
||||
[SpeckleType("Speckle.Core.Tests.Unit.Models.Extensions.DisplayValueTests+TypedDisplayValue")]
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Models.Extensions;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(BaseExtensions))]
|
||||
public class ExceptionTests
|
||||
{
|
||||
[Test]
|
||||
[Fact]
|
||||
public void CanPrintAllInnerExceptions()
|
||||
{
|
||||
// Test with a single exception
|
||||
var ex = new Exception("Some error");
|
||||
var exMsg = ex.ToFormattedString();
|
||||
Assert.That(exMsg, Is.Not.Null);
|
||||
exMsg.Should().NotBeNull();
|
||||
|
||||
// Test with an inner exception
|
||||
var ex2 = new Exception("One or more errors occurred", ex);
|
||||
var ex2Msg = ex2.ToFormattedString();
|
||||
Assert.That(ex2Msg, Is.Not.Null);
|
||||
ex2Msg.Should().NotBeNull();
|
||||
|
||||
// Test with an aggregate exception
|
||||
var ex3 = new AggregateException("One or more errors occurred", ex2);
|
||||
var ex3Msg = ex3.ToFormattedString();
|
||||
|
||||
Assert.That(ex3Msg, Is.Not.Null);
|
||||
ex3Msg.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
using Speckle.Sdk.Tests.Unit.Host;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Models.GraphTraversal;
|
||||
|
||||
[TestFixture, TestOf(typeof(Sdk.Models.GraphTraversal.GraphTraversal))]
|
||||
public class GraphTraversalTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public GraphTraversalTests()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(TraversalMock).Assembly);
|
||||
@@ -23,7 +21,7 @@ public class GraphTraversalTests
|
||||
return sut.Traverse(testCase);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void Traverse_TraversesListMembers()
|
||||
{
|
||||
var traverseListsRule = TraversalRule
|
||||
@@ -38,23 +36,23 @@ public class GraphTraversalTests
|
||||
|
||||
TraversalMock testCase = new()
|
||||
{
|
||||
ListChildren = new List<Base> { expectTraverse },
|
||||
ListChildren = [expectTraverse],
|
||||
DictChildren = new Dictionary<string, Base> { ["myprop"] = expectIgnored },
|
||||
Child = expectIgnored,
|
||||
};
|
||||
|
||||
var ret = Traverse(testCase, traverseListsRule).Select(b => b.Current).ToList();
|
||||
|
||||
//Assert expected members present
|
||||
Assert.That(ret, Has.Exactly(1).Items.EqualTo(testCase));
|
||||
Assert.That(ret, Has.Exactly(1).Items.EqualTo(expectTraverse));
|
||||
// Assert expected members present
|
||||
ret.Should().Contain(testCase);
|
||||
ret.Should().Contain(expectTraverse);
|
||||
|
||||
//Assert unexpected members not present
|
||||
Assert.That(ret, Has.No.Member(expectIgnored));
|
||||
Assert.That(ret, Has.Count.EqualTo(2));
|
||||
// Assert unexpected members not present
|
||||
ret.Should().NotContain(expectIgnored);
|
||||
ret.Count.Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void Traverse_TraversesDictMembers()
|
||||
{
|
||||
var traverseListsRule = TraversalRule
|
||||
@@ -69,23 +67,23 @@ public class GraphTraversalTests
|
||||
|
||||
TraversalMock testCase = new()
|
||||
{
|
||||
ListChildren = new List<Base> { expectIgnored },
|
||||
ListChildren = [expectIgnored],
|
||||
DictChildren = new Dictionary<string, Base> { ["myprop"] = expectTraverse },
|
||||
Child = expectIgnored,
|
||||
};
|
||||
|
||||
var ret = Traverse(testCase, traverseListsRule).Select(b => b.Current).ToList();
|
||||
|
||||
//Assert expected members present
|
||||
Assert.That(ret, Has.Exactly(1).Items.EqualTo(testCase));
|
||||
Assert.That(ret, Has.Exactly(1).Items.EqualTo(expectTraverse));
|
||||
// Assert expected members present
|
||||
ret.Should().Contain(testCase);
|
||||
ret.Should().Contain(expectTraverse);
|
||||
|
||||
//Assert unexpected members not present
|
||||
Assert.That(ret, Has.No.Member(expectIgnored));
|
||||
Assert.That(ret, Has.Count.EqualTo(2));
|
||||
// Assert unexpected members not present
|
||||
ret.Should().NotContain(expectIgnored);
|
||||
ret.Count.Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void Traverse_TraversesDynamic()
|
||||
{
|
||||
var traverseListsRule = TraversalRule
|
||||
@@ -105,16 +103,16 @@ public class GraphTraversalTests
|
||||
|
||||
var ret = Traverse(testCase, traverseListsRule).Select(b => b.Current).ToList();
|
||||
|
||||
//Assert expected members present
|
||||
Assert.That(ret, Has.Exactly(1).Items.EqualTo(testCase));
|
||||
Assert.That(ret, Has.Exactly(2).Items.EqualTo(expectTraverse));
|
||||
// Assert expected members present
|
||||
ret.Should().Contain(testCase);
|
||||
ret.Count(x => x == expectTraverse).Should().Be(2);
|
||||
|
||||
//Assert unexpected members not present
|
||||
Assert.That(ret, Has.No.Member(expectIgnored));
|
||||
Assert.That(ret, Has.Count.EqualTo(3));
|
||||
// Assert unexpected members not present
|
||||
ret.Should().NotContain(expectIgnored);
|
||||
ret.Count.Should().Be(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Fact]
|
||||
public void Traverse_ExclusiveRule()
|
||||
{
|
||||
var expectTraverse = new Base { id = "List Member" };
|
||||
@@ -134,12 +132,12 @@ public class GraphTraversalTests
|
||||
|
||||
var ret = Traverse(testCase, traverseListsRule).Select(b => b.Current).ToList();
|
||||
|
||||
//Assert expected members present
|
||||
Assert.That(ret, Has.Exactly(1).Items.EqualTo(testCase));
|
||||
Assert.That(ret, Has.Exactly(2).Items.EqualTo(expectTraverse));
|
||||
// Assert expected members present
|
||||
ret.Should().Contain(testCase);
|
||||
ret.Count(x => x == expectTraverse).Should().Be(2);
|
||||
|
||||
//Assert unexpected members not present
|
||||
Assert.That(ret, Has.No.Member(expectIgnored));
|
||||
Assert.That(ret, Has.Count.EqualTo(3));
|
||||
// Assert unexpected members not present
|
||||
ret.Should().NotContain(expectIgnored);
|
||||
ret.Count.Should().Be(3);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-19
@@ -1,62 +1,71 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Models.GraphTraversal;
|
||||
|
||||
[TestOf(typeof(TraversalContextExtensions))]
|
||||
// Mark test class for xUnit
|
||||
public class TraversalContextExtensionsTests
|
||||
{
|
||||
public static int[] TestDepths => new[] { 1, 2, 10 };
|
||||
|
||||
private TraversalContext? CreateLinkedList(int depth, Func<int, Base> createBaseFunc)
|
||||
{
|
||||
if (depth <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TraversalContext(createBaseFunc(depth), $"{depth}", CreateLinkedList(depth - 1, createBaseFunc));
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(TestDepths))]
|
||||
[Theory] // replaces [TestCaseSource]
|
||||
[MemberData(nameof(GetTestDepths))]
|
||||
public void GetPropertyPath_ReturnsSequentialPath(int depth)
|
||||
{
|
||||
var testData = CreateLinkedList(depth, i => new()).NotNull();
|
||||
var testData = CreateLinkedList(depth, i => new Base()).NotNull();
|
||||
|
||||
var path = TraversalContextExtensions.GetPropertyPath(testData);
|
||||
var path = testData.GetPropertyPath();
|
||||
|
||||
var expected = Enumerable.Range(1, depth).Select(i => i.ToString());
|
||||
|
||||
Assert.That(path, Is.EquivalentTo(expected));
|
||||
path.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(TestDepths))]
|
||||
[Theory]
|
||||
[MemberData(nameof(GetTestDepths))]
|
||||
public void GetAscendant(int depth)
|
||||
{
|
||||
var testData = CreateLinkedList(depth, i => new()).NotNull();
|
||||
var testData = CreateLinkedList(depth, i => new Base()).NotNull();
|
||||
|
||||
var all = TraversalContextExtensions.GetAscendants(testData).ToArray();
|
||||
var all = testData.GetAscendants().ToArray();
|
||||
|
||||
Assert.That(all, Has.Length.EqualTo(depth));
|
||||
all.Length.Should().Be(depth);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(TestDepths))]
|
||||
[Theory]
|
||||
[MemberData(nameof(GetTestDepths))]
|
||||
public void GetAscendantOfType_AllBase(int depth)
|
||||
{
|
||||
var testData = CreateLinkedList(depth, i => new()).NotNull();
|
||||
var testData = CreateLinkedList(depth, i => new Base()).NotNull();
|
||||
|
||||
var all = TraversalContextExtensions.GetAscendantOfType<Base>(testData).ToArray();
|
||||
var all = testData.GetAscendantOfType<Base>().ToArray();
|
||||
|
||||
Assert.That(all, Has.Length.EqualTo(depth));
|
||||
all.Length.Should().Be(depth);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(TestDepths))]
|
||||
[Theory]
|
||||
[MemberData(nameof(GetTestDepths))]
|
||||
public void GetAscendantOfType_EveryOtherIsCollection(int depth)
|
||||
{
|
||||
var testData = CreateLinkedList(depth, i => i % 2 == 0 ? new Base() : new Collection()).NotNull();
|
||||
|
||||
var all = TraversalContextExtensions.GetAscendantOfType<Collection>(testData).ToArray();
|
||||
var all = testData.GetAscendantOfType<Collection>().ToArray();
|
||||
|
||||
Assert.That(all, Has.Length.EqualTo(Math.Ceiling(depth / 2.0)));
|
||||
all.Length.Should().Be((int)Math.Ceiling(depth / 2.0));
|
||||
}
|
||||
|
||||
// Providing the test depths to [MemberData] for xUnit
|
||||
public static IEnumerable<object[]> GetTestDepths() => new[] { 1, 2, 10 }.Select(depth => new object[] { depth });
|
||||
}
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Tests.Unit.Host;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Models;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(Base))]
|
||||
// Removed [TestFixture] and [TestOf] annotations as they are NUnit specific
|
||||
public class Hashing
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public Hashing()
|
||||
{
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(DiningTable).Assembly);
|
||||
}
|
||||
|
||||
[Test(Description = "Checks that hashing (as represented by object ids) actually works.")]
|
||||
public void HashChangeCheck()
|
||||
[Fact(DisplayName = "Checks that hashing (as represented by object IDs) actually works.")]
|
||||
public void HashChangeCheck_Test()
|
||||
{
|
||||
var table = new DiningTable();
|
||||
var secondTable = new DiningTable();
|
||||
|
||||
Assert.That(secondTable.GetId(), Is.EqualTo(table.GetId()));
|
||||
secondTable.GetId().Should().Be(table.GetId(), "Object IDs of identical objects should match.");
|
||||
|
||||
((dynamic)secondTable).testProp = "wonderful";
|
||||
|
||||
Assert.That(secondTable.GetId(), Is.Not.EqualTo(table.GetId()));
|
||||
secondTable.GetId().Should().NotBe(table.GetId(), "Changing a property should alter the object ID.");
|
||||
}
|
||||
|
||||
[Test(
|
||||
Description = "Tests the convention that dynamic properties that have key names prepended with '__' are ignored."
|
||||
)]
|
||||
public void IgnoredDynamicPropertiesCheck()
|
||||
[Fact(DisplayName = "Verifies that dynamic properties with '__' prefix are ignored during hashing.")]
|
||||
public void IgnoredDynamicPropertiesCheck_Test()
|
||||
{
|
||||
var table = new DiningTable();
|
||||
var originalHash = table.GetId();
|
||||
|
||||
((dynamic)table).__testProp = "wonderful";
|
||||
|
||||
Assert.That(table.GetId(), Is.EqualTo(originalHash));
|
||||
table
|
||||
.GetId()
|
||||
.Should()
|
||||
.Be(originalHash, "Hashing of table should not change when '__' prefixed properties are added.");
|
||||
}
|
||||
|
||||
[Test(Description = "Rather stupid test as results vary wildly even on one machine.")]
|
||||
public void HashingPerformance()
|
||||
[Fact(DisplayName = "Performance test: Hash computation time for large and small objects.")]
|
||||
public void HashingPerformance_Test()
|
||||
{
|
||||
var polyline = new Polyline();
|
||||
|
||||
@@ -62,7 +62,7 @@ public class Hashing
|
||||
_ = polyline.GetId();
|
||||
|
||||
var diff1 = stopWatch.ElapsedMilliseconds - stopWatchStep;
|
||||
Assert.That(diff1, Is.LessThan(300), $"Hashing shouldn't take that long ({diff1} ms) for the test object used.");
|
||||
diff1.Should().BeLessThan(300, $"Hashing shouldn't take that long ({diff1} ms) for the test object used.");
|
||||
Console.WriteLine($"Big obj hash duration: {diff1} ms");
|
||||
|
||||
var pt = new Point
|
||||
@@ -75,12 +75,12 @@ public class Hashing
|
||||
_ = pt.GetId();
|
||||
|
||||
var diff2 = stopWatch.ElapsedMilliseconds - stopWatchStep;
|
||||
Assert.That(diff2, Is.LessThan(10), $"Hashing shouldn't take that long ({diff2} ms)for the point object used.");
|
||||
diff2.Should().BeLessThan(10, $"Hashing shouldn't take that long ({diff2} ms) for the point object used.");
|
||||
Console.WriteLine($"Small obj hash duration: {diff2} ms");
|
||||
}
|
||||
|
||||
[Test(Description = "The hash of a decomposed object is different that that of a non-decomposed object.")]
|
||||
public void DecompositionHashes()
|
||||
[Fact(DisplayName = "Verifies that decomposed and non-decomposed objects have different hashes.")]
|
||||
public void DecompositionHashes_Test()
|
||||
{
|
||||
var table = new DiningTable();
|
||||
((dynamic)table)["@decomposeMePlease"] = new Point();
|
||||
@@ -88,6 +88,6 @@ public class Hashing
|
||||
var hash1 = table.GetId();
|
||||
var hash2 = table.GetId(true);
|
||||
|
||||
Assert.That(hash2, Is.Not.EqualTo(hash1));
|
||||
hash2.Should().NotBe(hash1, "Hash values should differ for decomposed and non-decomposed objects.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models;
|
||||
using TestModels;
|
||||
using Speckle.Sdk.Tests.Unit.Models.TestModels;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Models
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(Base))]
|
||||
public class SpeckleTypeTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public SpeckleTypeTests()
|
||||
{
|
||||
// Setup logic during test class initialization
|
||||
TypeLoader.Reset();
|
||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Foo).Assembly);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(s_cases))]
|
||||
public void SpeckleTypeIsProperlyBuilt(Base foo, string expectedType)
|
||||
{
|
||||
Assert.That(foo.speckle_type, Is.EqualTo(expectedType));
|
||||
}
|
||||
[Theory]
|
||||
[MemberData(nameof(Cases))]
|
||||
public void SpeckleTypeIsProperlyBuilt(Base foo, string expectedType) => foo.speckle_type.Should().Be(expectedType);
|
||||
|
||||
private static readonly object[] s_cases =
|
||||
{
|
||||
new object[] { new Base(), "Base" },
|
||||
new object[] { new Foo(), "TestModels.Foo" },
|
||||
new object[] { new Bar(), "TestModels.Foo:TestModels.Bar" },
|
||||
new object[] { new Baz(), "TestModels.Foo:TestModels.Bar:TestModels.Baz" },
|
||||
};
|
||||
public static IEnumerable<object[]> Cases =>
|
||||
new List<object[]>
|
||||
{
|
||||
new object[] { new Base(), "Base" },
|
||||
new object[] { new Foo(), "TestModels.Foo" },
|
||||
new object[] { new Bar(), "TestModels.Foo:TestModels.Bar" },
|
||||
new object[] { new Baz(), "TestModels.Foo:TestModels.Bar:TestModels.Baz" },
|
||||
};
|
||||
}
|
||||
|
||||
namespace TestModels
|
||||
{
|
||||
[SpeckleType("TestModels.Foo")]
|
||||
public class Foo : Base { }
|
||||
|
||||
[SpeckleType("TestModels.Bar")]
|
||||
public class Bar : Foo { }
|
||||
|
||||
[SpeckleType("TestModels.Baz")]
|
||||
public class Baz : Bar { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace TestModels
|
||||
{
|
||||
[SpeckleType("TestModels.Foo")]
|
||||
public class Foo : Base { }
|
||||
|
||||
[SpeckleType("TestModels.Bar")]
|
||||
public class Bar : Foo { }
|
||||
|
||||
[SpeckleType("TestModels.Baz")]
|
||||
public class Baz : Bar { }
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Speckle.Sdk.Tests.Unit.Models;
|
||||
|
||||
[TestFixture, TestOf(typeof(BaseExtensions))]
|
||||
public class TraversalTests
|
||||
{
|
||||
[Test, Description("Tests that provided breaker rules are respected")]
|
||||
[Fact(DisplayName = "Tests that provided breaker rules are respected")]
|
||||
public void TestFlattenWithBreaker()
|
||||
{
|
||||
//Setup
|
||||
@@ -32,13 +32,19 @@ public class TraversalTests
|
||||
var ret = root.Flatten(BreakRule).ToList();
|
||||
|
||||
//Test
|
||||
Assert.That(ret, Has.Count.EqualTo(3));
|
||||
Assert.That(ret, Is.Unique);
|
||||
Assert.That(ret.Where(BreakRule), Is.Not.Empty);
|
||||
Assert.That(ret, Has.No.Member(Contains.Substring("should have ignored me")));
|
||||
ret.Count.Should().Be(3);
|
||||
|
||||
ret.Should().OnlyHaveUniqueItems();
|
||||
|
||||
ret.Where(BreakRule).Should().NotBeEmpty();
|
||||
|
||||
ret.Should().NotContain(x => x.id == "should have ignored me");
|
||||
}
|
||||
|
||||
[Test, TestCase(5, 5), TestCase(5, 10), TestCase(10, 5), Description("Tests breaking after a fixed number of items")]
|
||||
[Theory(DisplayName = "Tests breaking after a fixed number of items")]
|
||||
[InlineData(5, 5)]
|
||||
[InlineData(5, 10)]
|
||||
[InlineData(10, 5)]
|
||||
public void TestBreakerFixed(int nestDepth, int flattenDepth)
|
||||
{
|
||||
//Setup
|
||||
@@ -56,11 +62,12 @@ public class TraversalTests
|
||||
var ret = rootObject.Flatten(_ => ++counter >= flattenDepth).ToList();
|
||||
|
||||
//Test
|
||||
Assert.That(ret, Has.Count.EqualTo(Math.Min(flattenDepth, nestDepth)));
|
||||
Assert.That(ret, Is.Unique);
|
||||
ret.Count.Should().Be(Math.Min(flattenDepth, nestDepth));
|
||||
|
||||
ret.Should().OnlyHaveUniqueItems();
|
||||
}
|
||||
|
||||
[Test, Timeout(2000), Description("Tests that the flatten function does not get stuck on circular references")]
|
||||
[Fact(DisplayName = "Tests that the flatten function does not get stuck on circular references")]
|
||||
public void TestCircularReference()
|
||||
{
|
||||
//Setup
|
||||
@@ -76,12 +83,14 @@ public class TraversalTests
|
||||
var ret = objectA.Flatten().ToList();
|
||||
|
||||
//Test
|
||||
Assert.That(ret, Is.Unique);
|
||||
Assert.That(ret, Is.EquivalentTo(new[] { objectA, objectB, objectC }));
|
||||
Assert.That(ret, Has.Count.EqualTo(3));
|
||||
ret.Should().OnlyHaveUniqueItems();
|
||||
|
||||
ret.Should().BeEquivalentTo([objectA, objectB, objectC]);
|
||||
|
||||
ret.Count.Should().Be(3);
|
||||
}
|
||||
|
||||
[Test, Description("Tests that the flatten function correctly handles (non circular) duplicates")]
|
||||
[Fact(DisplayName = "Tests that the flatten function correctly handles (non circular) duplicates")]
|
||||
public void TestDuplicates()
|
||||
{
|
||||
//Setup
|
||||
@@ -95,8 +104,10 @@ public class TraversalTests
|
||||
var ret = objectA.Flatten().ToList();
|
||||
|
||||
//Test
|
||||
Assert.That(ret, Is.Unique);
|
||||
Assert.That(ret, Is.EquivalentTo(new[] { objectA, objectB }));
|
||||
Assert.That(ret, Has.Count.EqualTo(2));
|
||||
ret.Should().OnlyHaveUniqueItems();
|
||||
|
||||
ret.Should().BeEquivalentTo([objectA, objectB]);
|
||||
|
||||
ret.Count.Should().Be(2);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user