-
Notifications
You must be signed in to change notification settings - Fork 273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Isolated Entity Tests #2612
Open
sebastianburckhardt
wants to merge
36
commits into
v2.x
Choose a base branch
from
core-entities/isolated-tests
base: v2.x
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Isolated Entity Tests #2612
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
c36d3d9
udpate readme.
sebastianburckhardt 10111e0
update durability provider class for new core-entities support. (#2570)
sebastianburckhardt 6ff3e7b
update DurableClient to take advantage of native entity queries (#2571)
sebastianburckhardt d1d6074
implement passthrough middleware for entities (#2572)
sebastianburckhardt eb961e0
implement entity queries for grpc listener (#2573)
sebastianburckhardt cc7b93a
Various fixes (#2585)
sebastianburckhardt db60e7f
simplify how entities are excluded from instance queries (#2586)
sebastianburckhardt 2c5a7e5
add an entity example to the DotNetIsolated smoke test project. (#2584)
sebastianburckhardt ac6e0d2
Entities: Add worker side entity trigger and logic (#2576)
jviau 6e9d615
Merge commit '2455636cca50cfb4d1543633d28657437bbb5173' into feature/…
sebastianburckhardt 6251fca
another small fix that got lost somewhere. (#2596)
sebastianburckhardt 06d0713
Update packages and version for entities preview (#2599)
jviau 5429a27
Switch to Microsoft.DurableTask.Grpc (#2605)
jviau 991c8f0
Fix grpc core (#2616)
jviau 9f4cb5b
new test suite for isolated entities.
sebastianburckhardt 0e26d15
pass entity parameters for task orchestration. (#2611)
sebastianburckhardt 07ecbc8
Core entities/various fixes and updates (#2619)
sebastianburckhardt 62d7049
Update to entities preview 2 (#2620)
jviau 9e311a6
Add callback handler for entity dispatching (#2624)
jviau 1edc10e
Merge branch 'feature/core-entities' into core-entities/isolated-tests
sebastianburckhardt a6b3622
propagate changes
sebastianburckhardt c4a89b0
Core entities/propagate changes (#2625)
sebastianburckhardt 565d548
Rev dependencies to entities-preview.2 (#2627)
jviau cc0d0ed
Call EnsureLegalAccess from EntityFeature in dotnet-isolated (#2633)
jviau c545e42
create a better error message in situations where client entity funct…
sebastianburckhardt 105948d
Merge branch 'feature/core-entities' into core-entities/isolated-tests
sebastianburckhardt 9971383
address PR feedback
sebastianburckhardt 219d3e8
Merge branch 'dev' into core-entities/isolated-tests
sebastianburckhardt 82a1e8c
fix merge error
sebastianburckhardt d62c836
Merge branch 'dev' into core-entities/isolated-tests
sebastianburckhardt a4c370c
update SignalThenPoll test so it passes a non-null input, so that we …
sebastianburckhardt 4171037
Merge branch 'dev' into core-entities/isolated-tests
sebastianburckhardt 417f650
add test for faulty critical section.
sebastianburckhardt 37ef463
add distinction on whether backend supports implicit deletion
sebastianburckhardt 179f7e1
add non-entity tests for failure propagation by activities and suborc…
sebastianburckhardt 8123d17
refine the entity error tests to check for nested failure details (in…
sebastianburckhardt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
|
||
using System.Runtime.Serialization; | ||
using System.Text; | ||
using System.Text.RegularExpressions; | ||
using Azure.Core; | ||
using Microsoft.Azure.Functions.Worker; | ||
using Microsoft.Azure.Functions.Worker.Extensions.DurableTask; | ||
using Microsoft.Azure.Functions.Worker.Http; | ||
using Microsoft.DurableTask; | ||
using Microsoft.DurableTask.Client; | ||
using Microsoft.DurableTask.Entities; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace IsolatedEntities; | ||
|
||
/// <summary> | ||
/// Provides an http trigger to run functional tests for entities. | ||
/// </summary> | ||
public static class HttpTriggers | ||
{ | ||
[Function(nameof(RunAllTests))] | ||
public static async Task<HttpResponseData> RunAllTests( | ||
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "tests/")] HttpRequestData request, | ||
[DurableClient] DurableTaskClient client, | ||
FunctionContext executionContext) | ||
{ | ||
var context = new TestContext(client, executionContext); | ||
string result = await TestRunner.RunAsync(context, filter: null); | ||
HttpResponseData response = request.CreateResponse(System.Net.HttpStatusCode.OK); | ||
response.WriteString(result); | ||
return response; | ||
} | ||
|
||
[Function(nameof(RunFilteredTests))] | ||
public static async Task<HttpResponseData> RunFilteredTests( | ||
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "tests/{filter}")] HttpRequestData request, | ||
[DurableClient] DurableTaskClient client, | ||
FunctionContext executionContext, | ||
string filter) | ||
{ | ||
var context = new TestContext(client, executionContext); | ||
string result = await TestRunner.RunAsync(context, filter); | ||
HttpResponseData response = request.CreateResponse(System.Net.HttpStatusCode.OK); | ||
response.WriteString(result); | ||
return response; | ||
} | ||
|
||
[Function(nameof(ListAllTests))] | ||
public static async Task<HttpResponseData> ListAllTests( | ||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "tests/")] HttpRequestData request, | ||
[DurableClient] DurableTaskClient client, | ||
FunctionContext executionContext) | ||
{ | ||
var context = new TestContext(client, executionContext); | ||
string result = await TestRunner.RunAsync(context, filter: null, listOnly: true); | ||
HttpResponseData response = request.CreateResponse(System.Net.HttpStatusCode.OK); | ||
response.WriteString(result); | ||
return response; | ||
} | ||
|
||
[Function(nameof(ListFilteredTests))] | ||
public static async Task<HttpResponseData> ListFilteredTests( | ||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "tests/{filter}")] HttpRequestData request, | ||
[DurableClient] DurableTaskClient client, | ||
FunctionContext executionContext, | ||
string filter) | ||
{ | ||
var context = new TestContext(client, executionContext); | ||
string result = await TestRunner.RunAsync(context, filter, listOnly: true); | ||
HttpResponseData response = request.CreateResponse(System.Net.HttpStatusCode.OK); | ||
response.WriteString(result); | ||
return response; | ||
} | ||
} | ||
|
||
|
||
|
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,69 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Runtime.Serialization; | ||
using System.Text; | ||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
using System.Threading.Tasks; | ||
using Azure.Core.Serialization; | ||
|
||
namespace IsolatedEntities | ||
{ | ||
internal static class CustomSerialization | ||
{ | ||
public static ProblematicObject CreateUnserializableObject() | ||
{ | ||
return new ProblematicObject(serializable: false, deserializable: false); | ||
} | ||
|
||
public static ProblematicObject CreateUndeserializableObject() | ||
{ | ||
return new ProblematicObject(serializable: true, deserializable: false); | ||
} | ||
|
||
public class ProblematicObject | ||
{ | ||
public ProblematicObject(bool serializable = true, bool deserializable = true) | ||
{ | ||
this.Serializable = serializable; | ||
this.Deserializable = deserializable; | ||
} | ||
|
||
public bool Serializable { get; set; } | ||
|
||
public bool Deserializable { get; set; } | ||
} | ||
|
||
public class ProblematicObjectJsonConverter : JsonConverter<ProblematicObject> | ||
{ | ||
public override ProblematicObject Read( | ||
ref Utf8JsonReader reader, | ||
Type typeToConvert, | ||
JsonSerializerOptions options) | ||
{ | ||
bool deserializable = reader.GetBoolean(); | ||
if (!deserializable) | ||
{ | ||
throw new JsonException("problematic object: is not deserializable"); | ||
} | ||
return new ProblematicObject(serializable: true, deserializable: true); | ||
} | ||
|
||
public override void Write( | ||
Utf8JsonWriter writer, | ||
ProblematicObject value, | ||
JsonSerializerOptions options) | ||
{ | ||
if (!value.Serializable) | ||
{ | ||
throw new JsonException("problematic object: is not serializable"); | ||
} | ||
writer.WriteBooleanValue(value.Deserializable); | ||
} | ||
} | ||
} | ||
} |
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,19 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace IsolatedEntities; | ||
|
||
internal abstract class Test | ||
{ | ||
public virtual string Name => this.GetType().Name; | ||
|
||
public abstract Task RunAsync(TestContext context); | ||
|
||
public virtual TimeSpan Timeout => TimeSpan.FromSeconds(30); | ||
} |
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,36 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.Azure.Functions.Worker; | ||
using Microsoft.DurableTask.Client; | ||
using Microsoft.DurableTask.Client.Entities; | ||
using Microsoft.DurableTask.Entities; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace IsolatedEntities; | ||
|
||
internal class TestContext | ||
{ | ||
public TestContext(DurableTaskClient client, FunctionContext executionContext) | ||
{ | ||
this.ExecutionContext = executionContext; | ||
this.Client = client; | ||
this.Logger = executionContext.GetLogger(nameof(IsolatedEntities)); | ||
} | ||
|
||
public FunctionContext ExecutionContext { get; } | ||
|
||
public DurableTaskClient Client { get; } | ||
|
||
public ILogger Logger { get; } | ||
|
||
public CancellationToken CancellationToken { get; set; } | ||
|
||
public bool BackendSupportsImplicitEntityDeletion { get; set; } = false; // false for Azure Storage, true for Netherite and MSSQL | ||
} |
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 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.DurableTask.Client.Entities; | ||
using Microsoft.DurableTask.Entities; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace IsolatedEntities; | ||
|
||
internal static class TestContextExtensions | ||
{ | ||
public static async Task<T> WaitForEntityStateAsync<T>( | ||
this TestContext context, | ||
EntityInstanceId entityInstanceId, | ||
TimeSpan? timeout = null, | ||
Func<T, string?>? describeWhatWeAreWaitingFor = null) | ||
{ | ||
if (timeout == null) | ||
{ | ||
timeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(30); | ||
} | ||
|
||
Stopwatch sw = Stopwatch.StartNew(); | ||
|
||
EntityMetadata? response; | ||
|
||
do | ||
{ | ||
response = await context.Client.Entities.GetEntityAsync(entityInstanceId, includeState: true); | ||
|
||
if (response != null) | ||
{ | ||
if (describeWhatWeAreWaitingFor == null) | ||
{ | ||
break; | ||
} | ||
else | ||
{ | ||
var waitForResult = describeWhatWeAreWaitingFor(response.State.ReadAs<T>()); | ||
|
||
if (string.IsNullOrEmpty(waitForResult)) | ||
{ | ||
break; | ||
} | ||
else | ||
{ | ||
context.Logger.LogInformation($"Waiting for {entityInstanceId} : {waitForResult}"); | ||
} | ||
} | ||
} | ||
else | ||
{ | ||
context.Logger.LogInformation($"Waiting for {entityInstanceId} to have state."); | ||
} | ||
|
||
await Task.Delay(TimeSpan.FromMilliseconds(100)); | ||
} | ||
while (sw.Elapsed < timeout); | ||
|
||
if (response != null) | ||
{ | ||
string serializedState = response.State.Value; | ||
context.Logger.LogInformation($"Found state: {serializedState}"); | ||
return response.State.ReadAs<T>(); | ||
} | ||
else | ||
{ | ||
throw new TimeoutException($"Durable entity '{entityInstanceId}' still doesn't have any state!"); | ||
} | ||
} | ||
} |
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,57 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
|
||
using System.Linq; | ||
using System.Text; | ||
using System.Text.RegularExpressions; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace IsolatedEntities; | ||
|
||
internal static class TestRunner | ||
{ | ||
public static async Task<string> RunAsync(TestContext context, string? filter = null, bool listOnly = false) | ||
{ | ||
var output = new StringBuilder(); | ||
|
||
foreach (var test in All.GetAllTests()) | ||
{ | ||
if (filter == null || test.Name.ToLowerInvariant().Equals(filter.ToLowerInvariant())) | ||
{ | ||
if (listOnly) | ||
{ | ||
output.AppendLine(test.Name); | ||
} | ||
else | ||
{ | ||
context.Logger.LogWarning("------------ starting {testName}", test.Name); | ||
|
||
// if debugging, time out after 60m | ||
// otherwise, time out either when the http request times out or when the individual test time limit is exceeded | ||
using CancellationTokenSource cancellationTokenSource | ||
= Debugger.IsAttached ? new() : CancellationTokenSource.CreateLinkedTokenSource(context.ExecutionContext.CancellationToken); | ||
cancellationTokenSource.CancelAfter(Debugger.IsAttached ? TimeSpan.FromMinutes(60) : test.Timeout); | ||
context.CancellationToken = cancellationTokenSource.Token; | ||
|
||
try | ||
{ | ||
await test.RunAsync(context); | ||
output.AppendLine($"PASSED {test.Name}"); | ||
} | ||
catch (Exception ex) | ||
{ | ||
context.Logger.LogError(ex, "test {testName} failed", test.Name); | ||
output.AppendLine($"FAILED {test.Name} {ex.ToString()}"); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return output.ToString(); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit surprised that we're writing our own test runner. I'm not strictly against it (plus this is relatively simple) but why why aren't we scaffolding this with our usual testing framework?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has to run inside a function app. I had no appetite for spending a lot of time figuring out how to get a unit testing framework running inside a function app. Also, my past experience with those frameworks is not great. Takes a lot to get them to do what you want, even if what you want is rather simple. As demonstrated with this little bit of code here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I am not sure we should be adding our own test runner. What I recommend is using xunit test fixtures. You can encapsulate starting the function app via this fixture and then author tests which pull in and start the fixture (this starting the function app), and then dispatch HTTP to the function app to run a given scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand what you are suggesting, we would additionally create a separate xunit test for each test and then have that test invoke the app's unit test via http call?
Would that be a separate project (given that the function app is a special kind of project)? Would there be any way to avoid having to redefine all tests in both projects?