error tests expansion
.NET Test / .NET tests (push) Failing after 2m5s

This commit is contained in:
2026-07-12 18:06:53 +04:00
parent 2dbfd781ec
commit e8ae93799e
23 changed files with 1190 additions and 156 deletions
+1
View File
@@ -2,6 +2,7 @@ namespace Just.Railway;
public static partial class Ensure
{
[return: NotNull]
public delegate Error ErrorFactory(string valueExpression);
public const string DefaultErrorType = "EnsureFailed";
+13 -7
View File
@@ -14,7 +14,13 @@ public abstract class Error : IEquatable<Error>, IComparable<Error>
/// </summary>
/// <param name="thisException">Exception</param>
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Error New(Exception thisException) => new ExceptionalError(thisException);
public static Error New(Exception thisException) => thisException switch
{
AggregateException { InnerExceptions.Count: > 1 } aggegateException => new ManyErrors(aggegateException.InnerExceptions.Select(x => Error.New(x))),
AggregateException { InnerExceptions.Count: 1 } aggegateException => new ExceptionalError(aggegateException.InnerExceptions[0]),
_ => new ExceptionalError(thisException),
};
/// <summary>
/// Create a <see cref="ExceptionalError"/> with an overriden detail. This can be useful for sanitising the display message
/// when internally we're carrying the exception.
@@ -85,7 +91,7 @@ public abstract class Error : IEquatable<Error>, IComparable<Error>
[Pure] public abstract int Count { get; }
[Pure] public abstract bool IsEmpty { get; }
[Pure] public abstract bool IsExpected { get; }
[Pure] public abstract bool IsExeptional { get; }
[Pure] public abstract bool IsExceptional { get; }
[Pure] public Error Append(Error? next)
{
@@ -166,7 +172,7 @@ public sealed class ExpectedError : Error
[Pure] public override int Count => 1;
[Pure] public override bool IsEmpty => false;
[Pure] public override bool IsExpected => true;
[Pure] public override bool IsExeptional => false;
[Pure] public override bool IsExceptional => false;
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public override IEnumerable<Error> ToEnumerable()
@@ -208,7 +214,7 @@ public sealed class ExceptionalError : Error
[Pure] public override int Count => 1;
[Pure] public override bool IsEmpty => false;
[Pure] public override bool IsExpected => false;
[Pure] public override bool IsExeptional => true;
[Pure] public override bool IsExceptional => true;
[Pure] public override Exception ToException() => Exception ?? base.ToException();
@@ -220,7 +226,7 @@ public sealed class ExceptionalError : Error
private static ImmutableDictionary<string, string> ExtractExtensionData(Exception exception)
{
if (!(exception.Data?.Count > 0))
if (exception.Data is null || exception.Data.Count == 0)
return ImmutableDictionary<string, string>.Empty;
var values = GetGenericExtData(exception);
@@ -276,7 +282,7 @@ public sealed class ManyErrors : Error, IEnumerable<Error>, IReadOnlyList<Error>
{
var unpackedErrors = ImmutableArray.CreateBuilder<Error>();
foreach (var err in errors)
foreach (var err in errors.Where(x => x is not null))
{
if (err.IsEmpty) continue;
@@ -309,7 +315,7 @@ public sealed class ManyErrors : Error, IEnumerable<Error>, IReadOnlyList<Error>
[Pure] public override int Count => _errors.Length;
[Pure] public override bool IsEmpty => _errors.IsEmpty;
[Pure] public override bool IsExpected => _errors.All(static x => x.IsExpected);
[Pure] public override bool IsExeptional => _errors.Any(static x => x.IsExeptional);
[Pure] public override bool IsExceptional => _errors.Any(static x => x.IsExceptional);
[Pure] public Error this[int index] => _errors[index];
+4 -3
View File
@@ -8,7 +8,7 @@ public sealed class ErrorJsonConverter : JsonConverter<Error>
{
return reader.TokenType switch
{
JsonTokenType.StartObject => ToExpectedError(ReadOne(ref reader)),
JsonTokenType.StartObject => ToExpectedError(ReadOne(ref reader)), // deserialization always produce ExpectedError by design
JsonTokenType.StartArray => ReadMany(ref reader),
JsonTokenType.None => null,
JsonTokenType.Null => null,
@@ -42,6 +42,7 @@ public sealed class ErrorJsonConverter : JsonConverter<Error>
{
errors.Add(ToExpectedError(ReadOne(ref reader)));
}
else if (reader.TokenType == JsonTokenType.EndArray) break;
}
return new ManyErrors(errors.ToImmutable());
}
@@ -73,11 +74,11 @@ public sealed class ErrorJsonConverter : JsonConverter<Error>
if (string.IsNullOrEmpty(propvalue))
break;
if (propname == "type" || string.Equals(propname, "type", StringComparison.InvariantCultureIgnoreCase))
if (string.Equals(propname, "type", StringComparison.InvariantCultureIgnoreCase))
{
type = propvalue;
}
else if (propname == "msg" || string.Equals(propname, "msg", StringComparison.InvariantCultureIgnoreCase))
else if (string.Equals(propname, "msg", StringComparison.InvariantCultureIgnoreCase))
{
message = propvalue;
}
+1 -1
View File
@@ -11,7 +11,7 @@
<Description>Base for railway-oriented programming in .NET. Package includes Result object, Error class and most of the common extensions.</Description>
<PackageTags>railway-oriented;functional;result-pattern;result-object;error-handling</PackageTags>
<Authors>JustFixMe</Authors>
<Copyright>Copyright (c) 2023-2025 JustFixMe</Copyright>
<Copyright>Copyright (c) 2023-2026 JustFixMe</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/JustFixMe/Just.Railway/</RepositoryUrl>
+4 -4
View File
@@ -64,7 +64,7 @@ public readonly partial struct Result : IEquatable<Result>
public static implicit operator Result(Error error) => new(error ?? throw new ArgumentNullException(nameof(error)));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Result(Exception exception) => new(
new ExceptionalError(exception ?? throw new ArgumentNullException(nameof(exception))));
Error.New(exception ?? throw new ArgumentNullException(nameof(exception))));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Result<SuccessUnit>(Result result) => result.State switch
{
@@ -75,8 +75,8 @@ public readonly partial struct Result : IEquatable<Result>
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Result(SuccessUnit _) => new(null);
[Pure] public bool IsSuccess => Error is null;
[Pure] public bool IsFailure => Error is not null;
[Pure] public bool IsSuccess => State == ResultState.Success;
[Pure] public bool IsFailure => State == ResultState.Error;
[Pure] public bool TryGetValue([MaybeNullWhen(false)]out SuccessUnit? u, [MaybeNullWhen(true), NotNullWhen(false)]out Error? error)
{
@@ -236,7 +236,7 @@ public readonly struct Result<T> : IEquatable<Result<T>>
if (typeof(R).IsAssignableFrom(typeof(T)) && Value is null)
return default(R)!;
return (R)(object)Value!;
return (R)(object)Value!; // throws InvalidCastException by design. should not use cast on incompatible types
}
default: throw new ResultNotInitializedException();
+14 -2
View File
@@ -98,7 +98,7 @@ public static partial class ResultExtensions
case ResultState.Success:
if (hasErrors) goto afterLoop;
break;
default: throw new ResultNotInitializedException(nameof(results));
}
}
@@ -145,7 +145,19 @@ public static partial class ResultExtensions
}
public static async Task<Result<IEnumerable<T>>> Merge<T>(this IEnumerable<Task<Result<T>>> tasks)
{
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
var taskList = tasks.ToList();
var results = new Result<T>[taskList.Count];
for (int i = 0; i < taskList.Count; i++)
{
var task = await Task.WhenAny(taskList);
taskList.Remove(task);
results[i] = task.IsCompletedSuccessfully
? task.Result
: task.Exception!;
}
return results.Merge();
}