Skip to content

Commit 4d3ecc9

Browse files
committed
fix: preserve non-null collection result payloads
1 parent 032ae4c commit 4d3ecc9

3 files changed

Lines changed: 27 additions & 1 deletion

File tree

‎ManagedCode.Communication.Tests/Results/ResultSuccessValueContractTests.cs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Text.Json;
3+
using ManagedCode.Communication.CollectionResultT;
34
using Shouldly;
45

56
namespace ManagedCode.Communication.Tests.Results;
@@ -57,6 +58,23 @@ public void Failure_WithNullPayload_PreservesFailure()
5758
result.Problem.ShouldNotBeNull();
5859
}
5960

61+
[Test]
62+
public void CollectionSuccess_WithNullInitializer_PreservesEmptyNonNullCollection()
63+
{
64+
var result = CollectionResult<string>.Succeed(Array.Empty<string>()) with { Collection = null! };
65+
result.IsSuccess.ShouldBeTrue();
66+
result.Collection.ShouldBeEmpty();
67+
result.Value.ShouldBeSameAs(result.Collection);
68+
}
69+
70+
[Test]
71+
public void CollectionSuccess_FromDefaultStruct_PreservesNonNullCollection()
72+
{
73+
var result = default(CollectionResult<string>) with { IsSuccess = true };
74+
result.Collection.ShouldBeEmpty();
75+
result.Value.ShouldBeSameAs(result.Collection);
76+
}
77+
6078
[Test]
6179
public void Deserialize_WithNullSuccessPayload_RejectsInvalidResult()
6280
{

‎ManagedCode.Communication/CollectionResultT/CollectionResult.cs‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ namespace ManagedCode.Communication.CollectionResultT;
1717
[DebuggerDisplay("IsSuccess: {IsSuccess}; Count: {Collection?.Length ?? 0}; Problem: {Problem?.Title}")]
1818
public partial struct CollectionResult<T> : IResultCollection<T>, ICollectionResultFactory<CollectionResult<T>, T>
1919
{
20+
private T[]? _collection;
21+
2022
private CollectionResult(bool isSuccess, IEnumerable<T>? collection, int pageNumber, int pageSize, int totalItems, Problem? problem) : this(
2123
isSuccess, collection?.ToArray(), pageNumber, pageSize, totalItems, problem)
2224
{
@@ -70,7 +72,11 @@ internal static CollectionResult<T> CreateFailed(Problem problem, T[]? collectio
7072
[JsonPropertyName(CommunicationJsonNames.Collection)]
7173
[JsonPropertyOrder(2)]
7274
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
73-
public T[] Collection { get; init; } = [];
75+
public T[] Collection
76+
{
77+
get => _collection ?? [];
78+
init => _collection = value;
79+
}
7480

7581
/// <summary>
7682
/// Gets the collection as a Value property, for <see cref="IResult{T}" /> compatibility.

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ replacing a successful payload with null in an initializer, throws `ArgumentNull
125125
C# type argument does not allow a successful null payload. `Result<int>.Succeed()` still carries zero,
126126
while `Result<string>.Succeed()` has no valid default payload and throws. For an operation with no
127127
data, return `Result.Succeed()`; for optional data, return a real value or an appropriate failure.
128+
`CollectionResult<T>.Collection` always exposes a non-null collection, including default struct values
129+
and null initializer input; missing collections normalize to empty, matching its existing factories.
128130

129131
`Result` and `Result<T>` are structs. Their state is fixed after construction, but referenced payloads and
130132
`Problem` objects remain mutable. Avoid changing a shared problem after returning or publishing it.

0 commit comments

Comments
 (0)