-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add operation instrumentation * Add additional test detail * Add exception instrumentation to operation invoker Remove a bunch of comments from the test now that these things are captured in GitHub issue #13 Refactored tests partly to enable sharing between the two sets of instrumentation tests, but also to clarify which bits of shared functionality the non-instrumentation tests are relying on. (It had become a bit hazy.)
- Loading branch information
1 parent
8f15a1e
commit 5f653f3
Showing
30 changed files
with
967 additions
and
130 deletions.
There are no files selected for viewing
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
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,77 @@ | ||
namespace Menes.Specs.Bindings | ||
{ | ||
using System.Collections.Generic; | ||
using System.Reflection; | ||
using System.Threading.Tasks; | ||
using Corvus.SpecFlow.Extensions; | ||
using Menes.Internal; | ||
using Menes.Specs.Fakes; | ||
using Menes.Specs.Steps.TestClasses; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Moq; | ||
using NUnit.Framework; | ||
using TechTalk.SpecFlow; | ||
|
||
/// <summary> | ||
/// Provides tests with the ability to invoke simple test OpenApi operations by specifying the | ||
/// <c>@useZeroArgumentTestOperations</c> tag. Must be used in conjunction with <c>@perScenarioContainer</c>. | ||
/// Also defines a test operation used by some tests. | ||
/// </summary> | ||
/// <remarks> | ||
/// <para> | ||
/// When the <c>@useZeroArgumentTestOperations</c> tag is specified, the scenario binding in this class configures | ||
/// the <see cref="IOpenApiParameterBuilder{TRequest}"/> mock to return an empty dictionary regardless of its | ||
/// inputs. This is necessary to enable the <see cref="OpenApiOperationInvoker{TRequest, TResponse}"/> to be able | ||
/// to invoke the operation. (The default behaviour if this particular mock is unconfigured is that it will return | ||
/// null, causing the attempt to invoke the operation to fail. In normal non-test operation, the real builder works | ||
/// out what to do based on the contents of the OpenApi spec.) | ||
/// </para> | ||
/// </remarks> | ||
[Binding] | ||
public class TestOperationBindings : IOpenApiService | ||
{ | ||
private static readonly MethodInfo operationMethodInfo = typeof(TestOperationBindings).GetMethod(nameof(TestOperation), BindingFlags.NonPublic | BindingFlags.Instance); | ||
private readonly ScenarioContext scenarioContext; | ||
|
||
public TestOperationBindings(ScenarioContext scenarioContext) | ||
{ | ||
this.scenarioContext = scenarioContext; | ||
} | ||
|
||
[BeforeScenario("@useZeroArgumentTestOperations", Order = ContainerBeforeFeatureOrder.ServiceProviderAvailable)] | ||
public static void InitializeMocksToEnableInvocation(ScenarioContext scenarioContext) | ||
{ | ||
OperationInvokerTestContext invokerContext = ContainerBindings.GetServiceProvider(scenarioContext).GetRequiredService<OperationInvokerTestContext>(); | ||
|
||
invokerContext.ParameterBuilder | ||
.Setup(pb => pb.BuildParametersAsync(It.IsAny<object>(), It.IsAny<OpenApiOperationPathTemplate>())) | ||
.ReturnsAsync(new Dictionary<string, object>()); | ||
} | ||
|
||
[Given("the operation locator maps the operation id '(.*)' to an operation named '(.*)'")] | ||
public void GivenTheOperationLocatorMapsTheOperationIdToAnOperationNamed(string operationId, string operationName) | ||
{ | ||
Assert.AreEqual(nameof(TestOperation), operationName, "Menes uses the method name as the operation name, so this test can only work if the operation specified in the spec matches the name of the operation method supplied"); | ||
|
||
var operation = new OpenApiServiceOperation( | ||
this, | ||
operationMethodInfo, | ||
new Mock<IOpenApiConfiguration>().Object); | ||
this.InvokerContext.OperationLocator | ||
.Setup(m => m.TryGetOperation(operationId, out operation)) | ||
.Returns(true); | ||
} | ||
|
||
protected private OperationInvokerTestContext InvokerContext | ||
=> ContainerBindings.GetServiceProvider(this.scenarioContext).GetRequiredService<OperationInvokerTestContext>(); | ||
|
||
protected private FakeInstrumentationProvider InstrumentationProvider | ||
=> ContainerBindings.GetServiceProvider(this.scenarioContext).GetRequiredService<FakeInstrumentationProvider>(); | ||
|
||
private Task TestOperation() | ||
{ | ||
this.InvokerContext.ReportedOperationCountWhenOperationBodyInvoked = this.InstrumentationProvider.Operations.Count; | ||
return this.InvokerContext.OperationCompletionSource.GetTask(); | ||
} | ||
} | ||
} |
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,42 @@ | ||
namespace Menes.Specs.Fakes | ||
{ | ||
using System; | ||
using Corvus.Monitoring.Instrumentation; | ||
|
||
/// <summary> | ||
/// Describes exception instrumentation that was delivered to one of our fakes. | ||
/// </summary> | ||
/// <remarks> | ||
/// <para> | ||
/// This represents a single call to <see cref="IExceptionsInstrumentation.ReportException(Exception, AdditionalInstrumentationDetail)"/>. | ||
/// </para> | ||
/// </remarks> | ||
public class ExceptionDetail | ||
{ | ||
public ExceptionDetail( | ||
Exception x, | ||
AdditionalInstrumentationDetail additionalDetail, | ||
OperationDetail operationInProgressAtTime) | ||
{ | ||
this.Exception = x; | ||
this.AdditionalDetail = additionalDetail; | ||
this.OperationInProgressAtTime = operationInProgressAtTime; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the exception passed to the call to <see cref="IExceptionsInstrumentation.ReportException(Exception, AdditionalInstrumentationDetail)"/>. | ||
/// </summary> | ||
public Exception Exception { get; } | ||
|
||
/// <summary> | ||
/// Gets the additional instrumentation detail (if any) passed to the call to | ||
/// <see cref="IExceptionsInstrumentation.ReportException(Exception, AdditionalInstrumentationDetail)"/>. | ||
/// </summary> | ||
public AdditionalInstrumentationDetail AdditionalDetail { get; } | ||
|
||
/// <summary> | ||
/// Gets the <see cref="OperationDetail"/> that was in progress at the instant this exception was reported. | ||
/// </summary> | ||
public OperationDetail OperationInProgressAtTime { get; } | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
Solutions/Menes.Specs/Fakes/FakeInstrumentationProvider.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,53 @@ | ||
// <copyright file="FakeInstrumentationProvider.cs" company="Endjin"> | ||
// Copyright (c) Endjin. All rights reserved. | ||
// </copyright> | ||
|
||
namespace Menes.Specs.Fakes | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using Corvus.Monitoring.Instrumentation; | ||
using NUnit.Framework; | ||
|
||
/// <summary> | ||
/// Implements instrumentation interfaces, and reports what they see. | ||
/// </summary> | ||
internal class FakeInstrumentationProvider : IOperationsInstrumentation, IExceptionsInstrumentation | ||
{ | ||
private readonly List<OperationDetail> operations = new List<OperationDetail>(); | ||
private readonly List<ExceptionDetail> exceptions = new List<ExceptionDetail>(); | ||
private readonly Stack<OperationDetail> operationsInProgress = new Stack<OperationDetail>(); | ||
|
||
/// <summary> | ||
/// Gets a list of the operations supplied to the fake <see cref="IOperationsInstrumentation"/> implementation. | ||
/// </summary> | ||
public IReadOnlyList<OperationDetail> Operations => this.operations; | ||
|
||
/// <summary> | ||
/// Gets a list of the exceptions supplied to the fake <see cref="IExceptionsInstrumentation"/> implementation. | ||
/// </summary> | ||
public IReadOnlyList<ExceptionDetail> Exceptions => this.exceptions; | ||
|
||
/// <inheritdoc/> | ||
public IOperationInstance StartOperation(string name, AdditionalInstrumentationDetail additionalDetail = null) | ||
{ | ||
var result = new OperationDetail( | ||
name, | ||
additionalDetail, | ||
detailJustPopped => | ||
{ | ||
OperationDetail current = this.operationsInProgress.Pop(); | ||
Assert.AreSame(detailJustPopped, current); | ||
}); | ||
this.operationsInProgress.Push(result); | ||
this.operations.Add(result); | ||
return result; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public void ReportException(Exception x, AdditionalInstrumentationDetail additionalDetail = null) | ||
{ | ||
this.exceptions.Add(new ExceptionDetail(x, additionalDetail, this.operationsInProgress.Peek())); | ||
} | ||
} | ||
} |
Oops, something went wrong.