Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Propagate DocumentClientException from ProcessChangesAsync (#127)
Fix DocumentClientExceptions happening in user code and affecting Processor logic
- Loading branch information
Showing
12 changed files
with
400 additions
and
11 deletions.
There are no files selected for viewing
60 changes: 60 additions & 0 deletions
60
src/DocumentDB.ChangeFeedProcessor.UnitTests/Exceptions/ObserverExceptionTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
//---------------------------------------------------------------- | ||
// Copyright (c) Microsoft Corporation. Licensed under the MIT license. | ||
//---------------------------------------------------------------- | ||
|
||
using System; | ||
using System.IO; | ||
using System.Runtime.Serialization.Formatters.Binary; | ||
using Microsoft.Azure.Documents.ChangeFeedProcessor.Exceptions; | ||
using Xunit; | ||
|
||
namespace Microsoft.Azure.Documents.ChangeFeedProcessor.UnitTests.Exceptions | ||
{ | ||
[Trait("Category", "Gated")] | ||
public class ObserverExceptionTests | ||
{ | ||
[Fact] | ||
public void ValidateConstructor() | ||
{ | ||
Exception exception = new Exception("randomMessage"); | ||
var ex = new ObserverException(exception); | ||
Assert.Equal(exception.Message, ex.InnerException.Message); | ||
Assert.Equal(exception, ex.InnerException); | ||
} | ||
|
||
// Tests the GetObjectData method and the serialization ctor. | ||
[Fact] | ||
public void ValidateSerialization_AllFields() | ||
{ | ||
Exception exception = new Exception("randomMessage"); | ||
var originalException = new ObserverException(exception); | ||
var buffer = new byte[4096]; | ||
var formatter = new BinaryFormatter(); | ||
var stream1 = new MemoryStream(buffer); | ||
var stream2 = new MemoryStream(buffer); | ||
|
||
formatter.Serialize(stream1, originalException); | ||
var deserializedException = (ObserverException)formatter.Deserialize(stream2); | ||
|
||
Assert.Equal(originalException.Message, deserializedException.Message); | ||
Assert.Equal(originalException.InnerException.Message, deserializedException.InnerException.Message); | ||
} | ||
|
||
// Make sure that when some fields are not set, serialization is not broken. | ||
[Fact] | ||
public void ValidateSerialization_NullFields() | ||
{ | ||
var originalException = new ObserverException(null); | ||
var buffer = new byte[4096]; | ||
var formatter = new BinaryFormatter(); | ||
var stream1 = new MemoryStream(buffer); | ||
var stream2 = new MemoryStream(buffer); | ||
|
||
formatter.Serialize(stream1, originalException); | ||
var deserializedException = (ObserverException)formatter.Deserialize(stream2); | ||
|
||
Assert.Equal(originalException.Message, deserializedException.Message); | ||
Assert.Null(deserializedException.InnerException); | ||
} | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
...ssor.UnitTests/FeedProcessor/ObserverExceptionWrappingChangeFeedObserverDecoratorTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
//---------------------------------------------------------------- | ||
// Copyright (c) Microsoft Corporation. Licensed under the MIT license. | ||
//---------------------------------------------------------------- | ||
|
||
namespace Microsoft.Azure.Documents.ChangeFeedProcessor.UnitTests.FeedProcessor | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Azure.Documents.ChangeFeedProcessor.Exceptions; | ||
using Microsoft.Azure.Documents.ChangeFeedProcessor.FeedProcessing; | ||
using Microsoft.Azure.Documents.ChangeFeedProcessor.UnitTests.Utils; | ||
using Moq; | ||
using Xunit; | ||
|
||
[Trait("Category", "Gated")] | ||
public class ObserverExceptionWrappingChangeFeedObserverDecoratorTests | ||
{ | ||
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); | ||
private readonly IChangeFeedObserver observer; | ||
private readonly IChangeFeedObserverContext changeFeedObserverContext; | ||
private readonly FeedProcessing.ObserverExceptionWrappingChangeFeedObserverDecorator observerWrapper; | ||
private readonly List<Document> documents; | ||
|
||
public ObserverExceptionWrappingChangeFeedObserverDecoratorTests() | ||
{ | ||
this.observer = Mock.Of<IChangeFeedObserver>(); | ||
this.changeFeedObserverContext = Mock.Of<IChangeFeedObserverContext>(); | ||
this.observerWrapper = new FeedProcessing.ObserverExceptionWrappingChangeFeedObserverDecorator(observer); | ||
|
||
var document = new Document(); | ||
documents = new List<Document> { document }; | ||
} | ||
|
||
[Fact] | ||
public async Task OpenAsync_ShouldCallOpenAsync() | ||
{ | ||
await observerWrapper.OpenAsync(this.changeFeedObserverContext); | ||
|
||
Mock.Get(observer) | ||
.Verify(feedObserver => feedObserver | ||
.OpenAsync(It.IsAny<IChangeFeedObserverContext>()), | ||
Times.Once); | ||
} | ||
|
||
[Fact] | ||
public async Task CloseAsync_ShouldCallCloseAsync() | ||
{ | ||
await observerWrapper.CloseAsync(this.changeFeedObserverContext, ChangeFeedObserverCloseReason.Shutdown); | ||
|
||
Mock.Get(observer) | ||
.Verify(feedObserver => feedObserver | ||
.CloseAsync(It.IsAny<IChangeFeedObserverContext>(), | ||
It.Is<ChangeFeedObserverCloseReason>(reason => reason == ChangeFeedObserverCloseReason.Shutdown)), | ||
Times.Once); | ||
} | ||
|
||
[Fact] | ||
public async Task ProcessChangesAsync_ShouldPassDocumentsToProcessChangesAsync() | ||
{ | ||
await observerWrapper.ProcessChangesAsync(this.changeFeedObserverContext, this.documents, cancellationTokenSource.Token); | ||
|
||
Mock.Get(observer) | ||
.Verify(feedObserver => feedObserver | ||
.ProcessChangesAsync(It.IsAny<IChangeFeedObserverContext>(), | ||
It.Is<IReadOnlyList<Document>>(list => list.SequenceEqual(documents)), | ||
It.IsAny<CancellationToken>() | ||
), | ||
Times.Once); | ||
} | ||
|
||
[Fact] | ||
public async Task ProcessChangesAsync_ShouldThrow_IfObserverThrows() | ||
{ | ||
Mock.Get(observer) | ||
.SetupSequence(feedObserver => feedObserver | ||
.ProcessChangesAsync(It.IsAny<IChangeFeedObserverContext>(), It.IsAny<IReadOnlyList<Document>>(), It.IsAny<CancellationToken>())) | ||
.Throws(new Exception()); | ||
|
||
Exception exception = await Record.ExceptionAsync(() => observerWrapper.ProcessChangesAsync(this.changeFeedObserverContext, this.documents, cancellationTokenSource.Token)); | ||
Assert.IsAssignableFrom<ObserverException>(exception); | ||
Assert.IsAssignableFrom<Exception>(exception.InnerException); | ||
|
||
Mock.Get(observer) | ||
.Verify(feedObserver => feedObserver | ||
.ProcessChangesAsync(It.IsAny<IChangeFeedObserverContext>(), | ||
It.Is<IReadOnlyList<Document>>(list => list.SequenceEqual(documents)), | ||
It.IsAny<CancellationToken>() | ||
), | ||
Times.Once); | ||
} | ||
|
||
[Fact] | ||
public async Task ProcessChangesAsync_ShouldThrow_IfObserverThrowsDocumentClientException() | ||
{ | ||
Mock.Get(observer) | ||
.SetupSequence(feedObserver => feedObserver | ||
.ProcessChangesAsync(It.IsAny<IChangeFeedObserverContext>(), It.IsAny<IReadOnlyList<Document>>(), It.IsAny<CancellationToken>())) | ||
.Throws(DocumentExceptionHelpers.CreateRequestRateTooLargeException()); | ||
|
||
Exception exception = await Record.ExceptionAsync(() => observerWrapper.ProcessChangesAsync(this.changeFeedObserverContext, this.documents, cancellationTokenSource.Token)); | ||
Assert.IsAssignableFrom<ObserverException>(exception); | ||
Assert.IsAssignableFrom<DocumentClientException>(exception.InnerException); | ||
|
||
Mock.Get(observer) | ||
.Verify(feedObserver => feedObserver | ||
.ProcessChangesAsync(It.IsAny<IChangeFeedObserverContext>(), | ||
It.Is<IReadOnlyList<Document>>(list => list.SequenceEqual(documents)), | ||
It.IsAny<CancellationToken>() | ||
), | ||
Times.Once); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.