Skip to content

Commit dadd022

Browse files
authored
[Durable Functions] Add E2E test (#372)
1 parent 0f672be commit dadd022

File tree

12 files changed

+180
-2
lines changed

12 files changed

+180
-2
lines changed

azure-pipelines-1.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ steps:
2929
AzureWebJobsServiceBus: $(AzureWebJobsServiceBus)
3030
AzureWebJobsEventHubSender: $(AzureWebJobsEventHubSender)
3131
FUNCTIONS_WORKER_RUNTIME : "powershell"
32+
PSWorkerEnableExperimentalDurableFunctions: "true"
3233
continueOnError: true
3334
displayName: 'Running E2ETest'
3435

azure-pipelines-2.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ steps:
2424
AzureWebJobsEventHubSender: $(AzureWebJobsEventHubSender)
2525
FUNCTIONS_WORKER_RUNTIME : "powershell"
2626
FunctionAppUrl: $(FunctionAppUrl)
27+
PSWorkerEnableExperimentalDurableFunctions: "true"
2728
continueOnError: true
2829
displayName: 'Running E2ETest'
2930

azure-pipelines.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ steps:
3232
AzureWebJobsServiceBus: $(AzureWebJobsServiceBus)
3333
AzureWebJobsEventHubSender: $(AzureWebJobsEventHubSender)
3434
FUNCTIONS_WORKER_RUNTIME : "powershell"
35+
PSWorkerEnableExperimentalDurableFunctions: "true"
3536
displayName: 'Running E2ETest'
3637

3738
- task: CopyFiles@2
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Azure.Functions.PowerShell.Tests.E2E
5+
{
6+
using System;
7+
using System.Net;
8+
using System.Threading.Tasks;
9+
using Xunit;
10+
11+
using System.Net.Http;
12+
using Newtonsoft.Json;
13+
14+
[Collection(Constants.FunctionAppCollectionName)]
15+
public class DurableEndToEndTests
16+
{
17+
private readonly FunctionAppFixture _fixture;
18+
19+
public DurableEndToEndTests(FunctionAppFixture fixture)
20+
{
21+
this._fixture = fixture;
22+
}
23+
24+
[Fact]
25+
public async Task DurableClientFollowsAsyncPattern()
26+
{
27+
var initialResponse = await Utilities.GetHttpTriggerResponse("DurableClient", queryString: string.Empty);
28+
Assert.Equal(HttpStatusCode.Accepted, initialResponse.StatusCode);
29+
30+
var location = initialResponse.Headers.Location;
31+
Assert.NotNull(location);
32+
33+
var initialResponseBody = await initialResponse.Content.ReadAsStringAsync();
34+
dynamic initialResponseBodyObject = JsonConvert.DeserializeObject(initialResponseBody);
35+
Assert.NotNull(initialResponseBodyObject.id);
36+
var statusQueryGetUri = (string)initialResponseBodyObject.statusQueryGetUri;
37+
Assert.Equal(location.ToString(), statusQueryGetUri);
38+
Assert.NotNull(initialResponseBodyObject.sendEventPostUri);
39+
Assert.NotNull(initialResponseBodyObject.purgeHistoryDeleteUri);
40+
Assert.NotNull(initialResponseBodyObject.terminatePostUri);
41+
Assert.NotNull(initialResponseBodyObject.rewindPostUri);
42+
43+
var orchestrationCompletionTimeout = TimeSpan.FromSeconds(60);
44+
var startTime = DateTime.UtcNow;
45+
46+
using (var httpClient = new HttpClient())
47+
{
48+
while (true)
49+
{
50+
var statusResponse = await httpClient.GetAsync(statusQueryGetUri);
51+
switch (statusResponse.StatusCode)
52+
{
53+
case HttpStatusCode.Accepted:
54+
{
55+
var statusResponseBody = await GetResponseBodyAsync(statusResponse);
56+
var runtimeStatus = (string)statusResponseBody.runtimeStatus;
57+
Assert.True(
58+
runtimeStatus == "Running" || runtimeStatus == "Pending",
59+
$"Unexpected runtime status: {runtimeStatus}");
60+
61+
if (DateTime.UtcNow > startTime + orchestrationCompletionTimeout)
62+
{
63+
Assert.True(false, $"The orchestration has not completed after {orchestrationCompletionTimeout}");
64+
}
65+
66+
await Task.Delay(TimeSpan.FromSeconds(2));
67+
break;
68+
}
69+
70+
case HttpStatusCode.OK:
71+
{
72+
var statusResponseBody = await GetResponseBodyAsync(statusResponse);
73+
Assert.Equal("Completed", (string)statusResponseBody.runtimeStatus);
74+
Assert.Equal("Hello Tokyo", statusResponseBody.output[0].ToString());
75+
Assert.Equal("Hello Seattle", statusResponseBody.output[1].ToString());
76+
Assert.Equal("Hello London", statusResponseBody.output[2].ToString());
77+
return;
78+
}
79+
80+
default:
81+
Assert.True(false, $"Unexpected orchestration status code: {statusResponse.StatusCode}");
82+
break;
83+
}
84+
}
85+
}
86+
}
87+
88+
private static async Task<dynamic> GetResponseBodyAsync(HttpResponseMessage response)
89+
{
90+
var responseBody = await response.Content.ReadAsStringAsync();
91+
return JsonConvert.DeserializeObject(responseBody);
92+
}
93+
}
94+
}

test/E2E/Azure.Functions.PowerShellWorker.E2E/Azure.Functions.PowerShellWorker.E2E/Utilities.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public static async Task<string> InvokeHttpTrigger(string functionName, string q
5959
return await response.Content.ReadAsStringAsync();
6060
}
6161

62-
private static async Task<HttpResponseMessage> GetHttpTriggerResponse(string functionName, string queryString)
62+
public static async Task<HttpResponseMessage> GetHttpTriggerResponse(string functionName, string queryString)
6363
{
6464
string uri = $"api/{functionName}{queryString}";
6565
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"bindings": [
3+
{
4+
"name": "name",
5+
"type": "activityTrigger",
6+
"direction": "in"
7+
}
8+
]
9+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
param($name)
2+
3+
Write-Host "DurableActivity($name) started"
4+
Start-Sleep -Seconds 5
5+
Write-Host "DurableActivity($name) finished"
6+
return "Hello $name"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"bindings": [
3+
{
4+
"authLevel": "anonymous",
5+
"type": "httpTrigger",
6+
"direction": "in",
7+
"name": "Request",
8+
"methods": [
9+
"get",
10+
"post"
11+
]
12+
},
13+
{
14+
"type": "http",
15+
"direction": "out",
16+
"name": "Response"
17+
},
18+
{
19+
"name": "starter",
20+
"type": "orchestrationClient",
21+
"direction": "in"
22+
}
23+
]
24+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using namespace System.Net
2+
3+
param($Request, $TriggerMetadata)
4+
5+
Write-Host "DurableClient started"
6+
7+
$ErrorActionPreference = 'Stop'
8+
9+
$InstanceId = Start-NewOrchestration -FunctionName 'DurableOrchestrator' -InputObject 'Hello'
10+
Write-Host "Started orchestration with ID = '$InstanceId'"
11+
12+
$Response = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId
13+
Push-OutputBinding -Name Response -Value $Response
14+
15+
Write-Host "DurableClient completed"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"bindings": [
3+
{
4+
"name": "Context",
5+
"type": "orchestrationTrigger",
6+
"direction": "in"
7+
}
8+
]
9+
}

0 commit comments

Comments
 (0)