public void WithSource_FailedResultWith2Errors_ShouldUpdateOnlyNeededSource()
        {
            var errorSource1 = SourceBuilder.BuildErrorSource <FakeCreateCommand>(c => c.AggregateRootId);
            var errorSource2 = SourceBuilder.BuildErrorSource <FakeCreateCommand>(c => c.SomeArray[0].SomeInternalArray[0].SomeInt);

            var executionErrors = new[]
            {
                new ExecutionError(new ErrorCodeInfo(nameof(Core), FixtureUtils.String(), FixtureUtils.String()), errorSource1),
                new ExecutionError(new ErrorCodeInfo(nameof(Core), FixtureUtils.String(), FixtureUtils.String()), errorSource2)
            };

            var result = new CreationResult <DummyEntry>(executionErrors)
                         .WithSource <DummyEntry, FakeCreateCommand>(c => c.SomeArray[3]);

            result.Entry.Should().BeNull();

            var internalErrors = result.Errors.ToArray();

            internalErrors[0].CodeInfo.Should().Be(executionErrors[0].CodeInfo);
            internalErrors[0].Code.Should().Be(executionErrors[0].Code);
            internalErrors[0].Message.Should().Be(executionErrors[0].Message);
            internalErrors[0].Source.Should().Be(errorSource1);

            internalErrors[1].CodeInfo.Should().Be(executionErrors[1].CodeInfo);
            internalErrors[1].Code.Should().Be(executionErrors[1].Code);
            internalErrors[1].Message.Should().Be(executionErrors[1].Message);
            internalErrors[1].Source.Should().Be("FakeCreateCommand.SomeArray.3.SomeInternalArray.0.SomeInt");
        }
        /// <summary>
        /// Converts Creation Result to Creation Result with tracking errors containing correct source in fail case.
        /// </summary>
        /// <typeparam name="TResult">Type of entity to be created.</typeparam>
        /// <typeparam name="TCommand">Command type.</typeparam>
        /// <param name="creationResult">Creation Result.</param>
        /// <param name="getProperty">Property of command to fill as source.</param>
        /// <returns>Modified creation result.</returns>
        public static CreationResult <TResult> ForCommand <TResult, TCommand>(
            this CreationResult <TResult> creationResult,
            Expression <Func <TCommand, object> > getProperty)
            where TCommand : ICommand
            where TResult : class
        {
            if (creationResult.Entry != null)
            {
                return(creationResult);
            }

            var source = SourceBuilder.BuildErrorSource(getProperty);
            var firstExecutionError = creationResult.Errors.First();

            // It's the first error and we should fill correct source path for this error.
            if (string.IsNullOrEmpty(firstExecutionError.Source))
            {
                var updatedError = new ExecutionError(firstExecutionError.CodeInfo, source, firstExecutionError.Message);
                return(new CreationResult <TResult>(updatedError));
            }

            // There are error with filled source => that we need to create copy of error with another source
            var newError = new ExecutionError(firstExecutionError.CodeInfo, source, firstExecutionError.Message);

            var allErrors = creationResult.Errors.ToList();

            allErrors.Add(newError);

            return(new CreationResult <TResult>(allErrors));
        }
        public void ForCommand_ResultWithErrors_ShouldReturnErrorWithCorrectSource()
        {
            var errorCode      = new ErrorCodeInfo(nameof(Core), FixtureUtils.String(), FixtureUtils.String());
            var creationResult = CreationResult.Failed <DummyEntry>(errorCode);

            var result = creationResult.ForCommand <DummyEntry, FakeCreateCommand>(c => c.Data);

            result.Entry.Should().BeNull();

            var internalError = result.Errors.Single();

            internalError.CodeInfo.Should().Be(errorCode);
            internalError.Code.Should().Be(errorCode.Code);
            internalError.Message.Should().Be(errorCode.Message);
            internalError.Source.Should().Be(SourceBuilder.BuildErrorSource <FakeCreateCommand>(c => c.Data));
        }
        public void ForCommand_ResultWith2Errors_ShouldReturn2Errors()
        {
            var errorCode      = new ErrorCodeInfo(nameof(Core), FixtureUtils.String(), FixtureUtils.String());
            var creationResult = CreationResult.Failed <DummyEntry>(errorCode);

            var result = creationResult
                         .ForCommand <DummyEntry, FakeCreateCommand>(c => c)
                         .ForCommand <DummyEntry, FakeCreateCommand>(c => c.Data);

            result.Entry.Should().BeNull();
            result.Errors.Count.Should().Be(2);

            var expectedError1 = new ExecutionError(errorCode, SourceBuilder.BuildErrorSource <FakeCreateCommand>(c => c));
            var expectedError2 = new ExecutionError(errorCode, SourceBuilder.BuildErrorSource <FakeCreateCommand>(c => c.Data));

            result.Errors.Should().ContainEquivalentOf(expectedError1);
            result.Errors.Should().ContainEquivalentOf(expectedError2);
        }
        /// <summary>
        /// Extension for Creation Result that fixes path with incorrect indices.
        /// </summary>
        /// <typeparam name="TResult">Type of entity to be created.</typeparam>
        /// <typeparam name="TCommand">Command type.</typeparam>
        /// <param name="creationResult">Creation Result.</param>
        /// <param name="getProperty">Property of command to fill as source.</param>
        /// <returns>Modified creation result.</returns>
        public static CreationResult <TResult> WithSource <TResult, TCommand>(
            this CreationResult <TResult> creationResult,
            Expression <Func <TCommand, object> > getProperty)
            where TCommand : ICommand
            where TResult : class
        {
            if (creationResult.Entry != null)
            {
                return(creationResult);
            }

            var newSource = SourceBuilder.BuildErrorSource(getProperty);
            var allErrors = new List <ExecutionError>();

            foreach (var error in creationResult.Errors)
            {
                var updatedError = UpdateErrorWithSource(error, newSource);

                allErrors.Add(updatedError);
            }

            return(new CreationResult <TResult>(allErrors));
        }
        /// <summary>
        /// Extension that fills sources for general error + internal error if exist.
        /// </summary>
        /// <typeparam name="TCommand">Type of command.</typeparam>
        /// <param name="executionResult">Failed Execution Result.</param>
        /// <param name="getProprty">Get property expression.</param>
        /// <returns>Filed FailedResult with correct Source.</returns>
        public static IExecutionResult ForCommand <TCommand>(this IExecutionResult executionResult, Expression <Func <TCommand, object> > getProprty)
            where TCommand : ICommand
        {
            if (!(executionResult is FailedResult failedResult))
            {
                return(executionResult);
            }

            if (failedResult.Details.Count == 0)
            {
                var source = SourceBuilder.BuildErrorSource(getProprty);
                return(new FailedResult(failedResult.CodeInfo, source, failedResult.Message));
            }

            var result = new FailedResult(failedResult.CodeInfo, typeof(TCommand).Name, failedResult.Message);

            var internalError    = failedResult.Details.First();
            var internalSource   = SourceBuilder.BuildErrorSource(getProprty);
            var newInternalError = new ExecutionError(internalError.CodeInfo, internalSource, internalError.Message);

            result.AddError(newInternalError);

            return(result);
        }
        public void BuildErrorSource_ArrayTypeExpressionWithConstantIndexParameter_SuccessTest()
        {
            var result = SourceBuilder.BuildErrorSource <TestSourceCommand>(c => c.SomeArray[3].SomeString);

            result.Should().Be($"TestSourceCommand.SomeArray.3.SomeString");
        }
        public void BuildErrorSource_ArrayTypeExpressionWithIndexParameter_SuccessTest(int index)
        {
            var result = SourceBuilder.BuildErrorSource <TestSourceCommand>(c => c.SomeArray[index].SomeString);

            result.Should().Be($"TestSourceCommand.SomeArray.{index}.SomeString");
        }
        public void BuildErrorSource_SimpleIntTypeExpression_SuccessTest()
        {
            var result = SourceBuilder.BuildErrorSource <TestSourceCommand>(c => c.SomeInt);

            result.Should().Be("TestSourceCommand.SomeInt");
        }