Skip to content

Commit

Permalink
Merge pull request #607 from Particular/empty-lists-1.0
Browse files Browse the repository at this point in the history
Add support for empty collections in saga data (release-1.0)
  • Loading branch information
DavidBoike committed May 3, 2024
2 parents 16ef1b5 + a0f290c commit 6258af5
Show file tree
Hide file tree
Showing 16 changed files with 805 additions and 31 deletions.
Expand Up @@ -10,7 +10,7 @@

<ItemGroup>
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.103" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.302.25" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="NServiceBus.Testing" Version="8.0.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
Expand Down
Expand Up @@ -10,3 +10,6 @@ dotnet_diagnostic.PS0013.severity = suggestion

# Persistence library doesn't need saga analyzers
dotnet_diagnostic.NSB0004.severity = none

# IDE0028: Simplify collection initialization
dotnet_diagnostic.IDE0028.severity = suggestion
Expand Up @@ -4,12 +4,12 @@
<TargetFrameworks>net481;net8.0</TargetFrameworks>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\NServiceBusTests.snk</AssemblyOriginatorKeyFile>
<LangVersion>10.0</LangVersion>
<LangVersion>12.0</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.103" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.302.25" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="NServiceBus" Version="8.0.3" />
<PackageReference Include="NServiceBus.Testing" Version="8.0.1" />
Expand All @@ -21,7 +21,7 @@
<ItemGroup>
<ProjectReference Include="..\NServiceBus.Persistence.DynamoDB\NServiceBus.Persistence.DynamoDB.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\NServiceBus.Persistence.DynamoDB.Tests\ClientFactory.cs" />
</ItemGroup>
Expand Down
@@ -0,0 +1,190 @@
namespace NServiceBus.PersistenceTesting;

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
using Sagas;

public class When_saving_saga_with_empty_list : SagaPersisterTests
{
[Test]
public async Task ShouldSave()
{
var sagaData = new EmptyCollectionsSagaData { SomeId = Guid.NewGuid().ToString() };
await SaveSaga(sagaData);

var read = await GetById<EmptyCollectionsSagaData>(sagaData.Id);
Assert.That(read, Is.Not.Null);
Assert.That(read.SomeId, Is.EqualTo(sagaData.SomeId));

AssertEverythingEmpty(read);
}

[Test]
public async Task ShouldUpdate()
{
var memStreams = Enumerable.Range(0, 5)
.Select(i => new MemoryStream(Encoding.UTF8.GetBytes($"Hello world {i}")))
.ToArray();

var sagaData = new EmptyCollectionsSagaData
{
SomeId = Guid.NewGuid().ToString(),
StringList = ["a", "b"],
StringArray = ["c", "d"],
RecordList = [new("e", 1.2), new("f", 3.4)],
RecordArray = [new("g", 5.6), new("h", 7.8)],
SimpleDict = new Dictionary<string, int>
{
["i"] = 9,
["j"] = 10,
},
Ints = [11, 12],
Doubles = [13.4, 15.6],
Floats = new HashSet<float>([1.234f, 5.678f]).ToImmutableHashSet(),
Bytes = new SortedSet<byte>([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64]).ToImmutableSortedSet(),
Shorts = [1, 2, 3, 4],
UShorts = [1, 2, 3, 4],
Longs = new HashSet<long>([3147483647, 4147483647, 5147483647]).ToImmutableHashSet(),
ULongs = new HashSet<ulong>([3147483647, 4147483647, 5147483647, 18446744073709551615]).ToImmutableSortedSet(),
UInts = [2147483647, 4294967295],
SBytes = [0x0F, 0x10],
Decimals = new HashSet<decimal>([1.234m, 5.678m]).ToImmutableHashSet(),
HashSetOfMemoryStreams = new HashSet<MemoryStream>(memStreams),
ImmutableHashSetOfStreams = new HashSet<MemoryStream>(memStreams).ToImmutableHashSet(),
HashSetOfString = ["a", "b", "c", "d"],
SortedSetOfString = ["a", "b", "c", "d"],
ImmutableHashSetOfString = new HashSet<string>(["a", "b", "c", "d"]).ToImmutableHashSet(),
ImmutableSortedSetOfString = new SortedSet<string>(["a", "b", "c", "d"]).ToImmutableSortedSet(),

};
await SaveSaga(sagaData);

var context = configuration.GetContextBagForSagaStorage();
using (var session = configuration.CreateStorageSession())
{
await session.Open(context);
var read = await configuration.SagaStorage.Get<EmptyCollectionsSagaData>("SomeId", sagaData.SomeId, session, context);

Assert.That(read, Is.Not.Null);
Assert.That(read.SomeId, Is.EqualTo(sagaData.SomeId));

read.StringList = [];
read.StringArray = [];
read.RecordList = [];
read.RecordArray = [];
read.SimpleDict = [];
read.Ints = [];
read.Doubles = [];
read.Floats = ImmutableHashSet<float>.Empty;
read.Bytes = ImmutableSortedSet<byte>.Empty;
read.Shorts = [];
read.UShorts = [];
read.Longs = ImmutableHashSet<long>.Empty;
read.ULongs = ImmutableSortedSet<ulong>.Empty;
read.UInts = [];
read.SBytes = [];
read.Decimals = ImmutableHashSet<decimal>.Empty;
read.HashSetOfMemoryStreams = [];
read.ImmutableHashSetOfStreams = ImmutableHashSet<MemoryStream>.Empty;
read.HashSetOfString = [];
read.SortedSetOfString = [];
read.ImmutableHashSetOfString = ImmutableHashSet<string>.Empty;
read.ImmutableSortedSetOfString = ImmutableSortedSet<string>.Empty;

await configuration.SagaStorage.Update(read, session, context);
await session.CompleteAsync();
}

var read2 = await GetById<EmptyCollectionsSagaData>(sagaData.Id);
Assert.That(read2, Is.Not.Null);
Assert.That(read2.SomeId, Is.EqualTo(sagaData.SomeId));

AssertEverythingEmpty(read2);
}

void AssertEverythingEmpty(EmptyCollectionsSagaData data) => Assert.Multiple(() =>
{
Assert.That(data.StringList, Is.Empty);
Assert.That(data.StringArray, Is.Empty);
Assert.That(data.RecordList, Is.Empty);
Assert.That(data.RecordArray, Is.Empty);
Assert.That(data.SimpleDict, Is.Empty);
Assert.That(data.Ints, Is.Empty);
Assert.That(data.Doubles, Is.Empty);
Assert.That(data.Floats, Is.Empty);
Assert.That(data.Bytes, Is.Empty);
Assert.That(data.Shorts, Is.Empty);
Assert.That(data.UShorts, Is.Empty);
Assert.That(data.Longs, Is.Empty);
Assert.That(data.ULongs, Is.Empty);
Assert.That(data.UInts, Is.Empty);
Assert.That(data.SBytes, Is.Empty);
Assert.That(data.Decimals, Is.Empty);
Assert.That(data.HashSetOfMemoryStreams, Is.Empty);
Assert.That(data.ImmutableHashSetOfStreams, Is.Empty);
Assert.That(data.HashSetOfString, Is.Empty);
Assert.That(data.SortedSetOfString, Is.Empty);
Assert.That(data.ImmutableHashSetOfString, Is.Empty);
Assert.That(data.ImmutableSortedSetOfString, Is.Empty);
});

// Even though not used, need this class so saga data will get picked up by mapper
public class EmptyCollectionsSaga : Saga<EmptyCollectionsSagaData>,
IAmStartedByMessages<TestMessage>
{
public Task Handle(TestMessage message, IMessageHandlerContext context) => throw new NotImplementedException();

protected override void ConfigureHowToFindSaga(SagaPropertyMapper<EmptyCollectionsSagaData> mapper)
{
mapper.MapSaga(s => s.SomeId)
.ToMessage<TestMessage>(m => m.SomeId);
}
}

public class EmptyCollectionsSagaData : ContainSagaData
{
public string SomeId { get; set; } = "Test";

public List<string> StringList { get; set; } = [];
public string[] StringArray { get; set; } = [];
public List<SimpleType> RecordList { get; set; } = [];
public SimpleType[] RecordArray { get; set; } = [];
public Dictionary<string, int> SimpleDict { get; set; } = [];
public HashSet<int> Ints { get; set; } = [];
public SortedSet<double> Doubles { get; set; } = [];
public ImmutableHashSet<float> Floats { get; set; } = ImmutableHashSet<float>.Empty;
public ImmutableSortedSet<byte> Bytes { get; set; } = ImmutableSortedSet<byte>.Empty;
public HashSet<short> Shorts { get; set; } = [];
public SortedSet<ushort> UShorts { get; set; } = [];
public ImmutableHashSet<long> Longs { get; set; } = ImmutableHashSet<long>.Empty;
public ImmutableSortedSet<ulong> ULongs { get; set; } = ImmutableSortedSet<ulong>.Empty;
public HashSet<uint> UInts { get; set; } = [];
public SortedSet<sbyte> SBytes { get; set; } = [];
public ImmutableHashSet<decimal> Decimals { get; set; } = ImmutableHashSet<decimal>.Empty;
#pragma warning disable PS0025 // It is a test
public HashSet<MemoryStream> HashSetOfMemoryStreams { get; set; } = [];
public ImmutableHashSet<MemoryStream> ImmutableHashSetOfStreams { get; set; } = ImmutableHashSet<MemoryStream>.Empty;
#pragma warning restore PS0025
public HashSet<string> HashSetOfString { get; set; } = [];
public SortedSet<string> SortedSetOfString { get; set; } = [];
public ImmutableHashSet<string> ImmutableHashSetOfString { get; set; } = ImmutableHashSet<string>.Empty;
public ImmutableSortedSet<string> ImmutableSortedSetOfString { get; set; } = ImmutableSortedSet<string>.Empty;
}

public record SimpleType(string Id, double Value);

public class TestMessage : ICommand
{
public string SomeId { get; set; }
}

public When_saving_saga_with_empty_list(TestVariant param) : base(param)
{
}
}
Expand Up @@ -9,7 +9,7 @@

<ItemGroup>
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.103" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.302.25" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="NServiceBus.Testing" Version="8.0.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
Expand Down
3 changes: 3 additions & 0 deletions src/NServiceBus.Persistence.DynamoDB.Tests/.editorconfig
Expand Up @@ -5,3 +5,6 @@ dotnet_diagnostic.CA2007.severity = none

# Justification: Tests don't support cancellation and don't need to forward IMessageHandlerContext.CancellationToken
dotnet_diagnostic.NSB0002.severity = suggestion

# IDE0028: Simplify collection initialization
dotnet_diagnostic.IDE0028.severity = suggestion
Expand Up @@ -14,105 +14,126 @@
"BOOL": false,
"IsBOOLSet": false,
"BS": [],
"IsBSSet": false,
"L": [],
"IsLSet": false,
"M": {},
"IsMSet": false,
"N": null,
"NS": [],
"IsNSSet": false,
"NULL": false,
"S": "OUTBOX#SchemaVersionTest#FFC8A2FD-0335-47C8-A29D-9EEA6C8445D8",
"SS": []
"SS": [],
"IsSSSet": false
},
"SK": {
"B": null,
"BOOL": false,
"IsBOOLSet": false,
"BS": [],
"IsBSSet": false,
"L": [],
"IsLSet": false,
"M": {},
"IsMSet": false,
"N": null,
"NS": [],
"IsNSSet": false,
"NULL": false,
"S": "OUTBOX#METADATA#FFC8A2FD-0335-47C8-A29D-9EEA6C8445D8",
"SS": []
"SS": [],
"IsSSSet": false
},
"OperationsCount": {
"B": null,
"BOOL": false,
"IsBOOLSet": false,
"BS": [],
"IsBSSet": false,
"L": [],
"IsLSet": false,
"M": {},
"IsMSet": false,
"N": "0",
"NS": [],
"IsNSSet": false,
"NULL": false,
"S": null,
"SS": []
"SS": [],
"IsSSSet": false
},
"Dispatched": {
"B": null,
"BOOL": false,
"IsBOOLSet": true,
"BS": [],
"IsBSSet": false,
"L": [],
"IsLSet": false,
"M": {},
"IsMSet": false,
"N": null,
"NS": [],
"IsNSSet": false,
"NULL": false,
"S": null,
"SS": []
"SS": [],
"IsSSSet": false
},
"DispatchedAt": {
"B": null,
"BOOL": false,
"IsBOOLSet": false,
"BS": [],
"IsBSSet": false,
"L": [],
"IsLSet": false,
"M": {},
"IsMSet": false,
"N": null,
"NS": [],
"IsNSSet": false,
"NULL": true,
"S": null,
"SS": []
"SS": [],
"IsSSSet": false
},
"SchemaVersion": {
"B": null,
"BOOL": false,
"IsBOOLSet": false,
"BS": [],
"IsBSSet": false,
"L": [],
"IsLSet": false,
"M": {},
"IsMSet": false,
"N": null,
"NS": [],
"IsNSSet": false,
"NULL": false,
"S": "1.0",
"SS": []
"SS": [],
"IsSSSet": false
},
"ExpiresAt": {
"B": null,
"BOOL": false,
"IsBOOLSet": false,
"BS": [],
"IsBSSet": false,
"L": [],
"IsLSet": false,
"M": {},
"IsMSet": false,
"N": null,
"NS": [],
"IsNSSet": false,
"NULL": true,
"S": null,
"SS": []
"SS": [],
"IsSSSet": false
}
},
"ReturnValuesOnConditionCheckFailure": null,
Expand Down

0 comments on commit 6258af5

Please sign in to comment.