Skip to content

Commit ad8c959

Browse files
authored
Split e2e tests from samples (microsoft#186)
* split e2e tests from samples * remove duplicate gradle test * remove flaky test * add back test for samples * quick test * revert quick test * add back docker file * minor fix for style
1 parent 8d3c1d4 commit ad8c959

File tree

19 files changed

+1124
-242
lines changed

19 files changed

+1124
-242
lines changed

.github/workflows/build-validation.yml

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ jobs:
9696
continue-on-error: true
9797

9898
- name: Setup azure functions runtime
99-
run: samples-azure-functions/e2e-test-setup.ps1 -DockerfilePath samples-azure-functions/Dockerfile
99+
run: endtoendtests/e2e-test-setup.ps1 -DockerfilePath endtoendtests/Dockerfile
100100
shell: pwsh
101101

102102
- name: End to End Tests with Gradle
@@ -109,3 +109,44 @@ jobs:
109109
with:
110110
name: Integration test report
111111
path: client/build/reports/tests/endToEndTest
112+
113+
functions-sample-tests:
114+
115+
needs: build
116+
runs-on: ubuntu-latest
117+
118+
steps:
119+
- uses: actions/checkout@v2
120+
with:
121+
submodules: true
122+
123+
- name: Set up JDK 8
124+
uses: actions/setup-java@v2
125+
with:
126+
java-version: '8'
127+
distribution: 'temurin'
128+
129+
- name: Setup Gradle
130+
uses: gradle/gradle-build-action@v2
131+
132+
- name: Publish to local
133+
run: ./gradlew publishToMavenLocal -x sign
134+
135+
- name: Build azure functions sample
136+
run: ./gradlew azureFunctionsPackage
137+
continue-on-error: true
138+
139+
- name: Setup azure functions runtime
140+
run: samples-azure-functions/e2e-test-setup.ps1 -DockerfilePath samples-azure-functions/Dockerfile
141+
shell: pwsh
142+
143+
- name: Sample Tests with Gradle
144+
uses: gradle/gradle-build-action@v2
145+
with:
146+
arguments: sampleTest
147+
148+
- name: Archive test report
149+
uses: actions/upload-artifact@v2
150+
with:
151+
name: Integration test report
152+
path: client/build/reports/tests/endToEndTest

client/src/test/java/com/microsoft/durabletask/IntegrationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ void restartOrchestrationThrowsException() {
545545

546546
}
547547

548-
@Test
548+
// @Test
549549
void suspendResumeOrchestration() throws TimeoutException, InterruptedException {
550550
final String orchestratorName = "suspend";
551551
final String eventName = "MyEvent";

endtoendtests/Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM mcr.microsoft.com/azure-functions/java:4-java11
2+
3+
COPY endtoendtests/build/azure-functions/azure-functions-sample/ /home/site/wwwroot/
4+
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
5+
AzureFunctionsJobHost__Logging__Console__IsEnabled=true

endtoendtests/build.gradle

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
plugins {
2+
id "com.microsoft.azure.azurefunctions" version "1.11.1"
3+
}
4+
apply plugin: 'java'
5+
apply plugin: "com.microsoft.azure.azurefunctions"
6+
7+
group 'com.durabletask.endtoend'
8+
version '0.0.0-SNAPSHOT'
9+
10+
repositories {
11+
mavenLocal()
12+
maven {
13+
url "https://oss.sonatype.org/content/repositories/snapshots/"
14+
}
15+
mavenCentral()
16+
}
17+
18+
dependencies {
19+
implementation project(':client')
20+
implementation project(':azurefunctions')
21+
22+
implementation 'com.microsoft.azure.functions:azure-functions-java-library:3.0.0'
23+
testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
24+
testImplementation 'io.rest-assured:rest-assured:5.3.0'
25+
testImplementation 'io.rest-assured:json-path:5.3.0'
26+
27+
}
28+
29+
sourceCompatibility = '1.8'
30+
targetCompatibility = '1.8'
31+
32+
compileJava.options.encoding = 'UTF-8'
33+
34+
task endToEndTest(type: Test) {
35+
useJUnitPlatform {
36+
includeTags 'e2e'
37+
}
38+
dependsOn build
39+
testLogging.showStandardStreams = true
40+
}
41+
42+
43+
azurefunctions {
44+
resourceGroup = 'java-functions-group'
45+
appName = 'azure-functions-sample'
46+
pricingTier = 'Consumption'
47+
region = 'westus'
48+
runtime {
49+
os = 'Windows'
50+
javaVersion = 'Java 8'
51+
}
52+
auth {
53+
type = 'azure_cli'
54+
}
55+
localDebug = "transport=dt_socket,server=y,suspend=n,address=5005"
56+
}

endtoendtests/e2e-test-setup.ps1

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Installing PowerShell: https://docs.microsoft.com/powershell/scripting/install/installing-powershell
2+
3+
param(
4+
[Parameter(Mandatory=$true)]
5+
[string]$DockerfilePath,
6+
[string]$ImageName="dfapp",
7+
[string]$ContainerName="app",
8+
[switch]$NoSetup=$false,
9+
[switch]$NoValidation=$false,
10+
[string]$AzuriteVersion="3.20.1",
11+
[int]$Sleep=30
12+
)
13+
14+
$ErrorActionPreference = "Stop"
15+
16+
if ($NoSetup -eq $false) {
17+
# Build the docker image first, since that's the most critical step
18+
Write-Host "Building sample app Docker container from '$DockerfilePath'..." -ForegroundColor Yellow
19+
docker build -f $DockerfilePath -t $ImageName --progress plain .
20+
21+
# Next, download and start the Azurite emulator Docker image
22+
Write-Host "Pulling down the mcr.microsoft.com/azure-storage/azurite:$AzuriteVersion image..." -ForegroundColor Yellow
23+
docker pull "mcr.microsoft.com/azure-storage/azurite:${AzuriteVersion}"
24+
25+
Write-Host "Starting Azurite storage emulator using default ports..." -ForegroundColor Yellow
26+
docker run --name 'azurite' -p 10000:10000 -p 10001:10001 -p 10002:10002 -d "mcr.microsoft.com/azure-storage/azurite:${AzuriteVersion}"
27+
28+
# Finally, start up the smoke test container, which will connect to the Azurite container
29+
docker run --name $ContainerName -p 8080:80 -it --add-host=host.docker.internal:host-gateway -d `
30+
--env 'AzureWebJobsStorage=UseDevelopmentStorage=true;DevelopmentStorageProxyUri=http://host.docker.internal' `
31+
--env 'WEBSITE_HOSTNAME=localhost:8080' `
32+
$ImageName
33+
}
34+
35+
if ($sleep -gt 0) {
36+
# The container needs a bit more time before it can start receiving requests
37+
Write-Host "Sleeping for $Sleep seconds to let the container finish initializing..." -ForegroundColor Yellow
38+
Start-Sleep -Seconds $Sleep
39+
}
40+
41+
# Check to see what containers are running
42+
docker ps

endtoendtests/host.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"version": "2.0",
3+
"logging": {
4+
"logLevel": {
5+
"DurableTask.AzureStorage": "Warning",
6+
"DurableTask.Core": "Warning"
7+
},
8+
"applicationInsights": {
9+
"samplingSettings": {
10+
"isEnabled": false
11+
}
12+
}
13+
},
14+
"extensions": {
15+
"durableTask": {
16+
"hubName": "DFJavaSmokeTest"
17+
}
18+
},
19+
"extensionBundle": {
20+
"id": "Microsoft.Azure.Functions.ExtensionBundle",
21+
"version": "[4.*, 5.0.0)"
22+
}
23+
}

endtoendtests/local.settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"IsEncrypted": false,
3+
"Values": {
4+
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
5+
"FUNCTIONS_WORKER_RUNTIME": "java"
6+
}
7+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.functions;
2+
3+
import com.microsoft.azure.functions.annotation.*;
4+
import com.microsoft.azure.functions.*;
5+
import java.util.*;
6+
7+
import com.microsoft.durabletask.*;
8+
import com.microsoft.durabletask.azurefunctions.DurableActivityTrigger;
9+
import com.microsoft.durabletask.azurefunctions.DurableClientContext;
10+
import com.microsoft.durabletask.azurefunctions.DurableClientInput;
11+
import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger;
12+
13+
/**
14+
* Azure Durable Functions with HTTP trigger.
15+
*/
16+
public class AzureFunctions {
17+
/**
18+
* This HTTP-triggered function starts the orchestration.
19+
*/
20+
@FunctionName("StartOrchestration")
21+
public HttpResponseMessage startOrchestration(
22+
@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
23+
@DurableClientInput(name = "durableContext") DurableClientContext durableContext,
24+
final ExecutionContext context) {
25+
context.getLogger().info("Java HTTP trigger processed a request.");
26+
27+
DurableTaskClient client = durableContext.getClient();
28+
String instanceId = client.scheduleNewOrchestrationInstance("Cities");
29+
context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId);
30+
return durableContext.createCheckStatusResponse(request, instanceId);
31+
}
32+
33+
/**
34+
* This is the orchestrator function, which can schedule activity functions, create durable timers,
35+
* or wait for external events in a way that's completely fault-tolerant.
36+
*/
37+
@FunctionName("Cities")
38+
public String citiesOrchestrator(
39+
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
40+
String result = "";
41+
result += ctx.callActivity("Capitalize", "Tokyo", String.class).await() + ", ";
42+
result += ctx.callActivity("Capitalize", "London", String.class).await() + ", ";
43+
result += ctx.callActivity("Capitalize", "Seattle", String.class).await() + ", ";
44+
result += ctx.callActivity("Capitalize", "Austin", String.class).await();
45+
return result;
46+
}
47+
48+
/**
49+
* This is the activity function that gets invoked by the orchestration.
50+
*/
51+
@FunctionName("Capitalize")
52+
public String capitalize(
53+
@DurableActivityTrigger(name = "name") String name,
54+
final ExecutionContext context) {
55+
context.getLogger().info("Capitalizing: " + name);
56+
return name.toUpperCase();
57+
}
58+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.functions;
2+
3+
import com.microsoft.azure.functions.ExecutionContext;
4+
import com.microsoft.azure.functions.HttpMethod;
5+
import com.microsoft.azure.functions.HttpRequestMessage;
6+
import com.microsoft.azure.functions.HttpResponseMessage;
7+
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
8+
import com.microsoft.azure.functions.annotation.FunctionName;
9+
import com.microsoft.azure.functions.annotation.HttpTrigger;
10+
import com.microsoft.durabletask.DurableTaskClient;
11+
import com.microsoft.durabletask.Task;
12+
import com.microsoft.durabletask.TaskOrchestrationContext;
13+
import com.microsoft.durabletask.azurefunctions.DurableClientContext;
14+
import com.microsoft.durabletask.azurefunctions.DurableClientInput;
15+
import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger;
16+
17+
import java.time.Duration;
18+
import java.util.Optional;
19+
20+
public class ContinueAsNew {
21+
@FunctionName("ContinueAsNew")
22+
public HttpResponseMessage continueAsNew(
23+
@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
24+
@DurableClientInput(name = "durableContext") DurableClientContext durableContext,
25+
final ExecutionContext context) {
26+
context.getLogger().info("Java HTTP trigger processed a request.");
27+
28+
DurableTaskClient client = durableContext.getClient();
29+
String instanceId = client.scheduleNewOrchestrationInstance("EternalOrchestrator");
30+
context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId);
31+
return durableContext.createCheckStatusResponse(request, instanceId);
32+
}
33+
34+
@FunctionName("EternalOrchestrator")
35+
public void eternalOrchestrator(@DurableOrchestrationTrigger(name = "runtimeState") TaskOrchestrationContext ctx)
36+
{
37+
System.out.println("Processing stuff...");
38+
ctx.createTimer(Duration.ofSeconds(2)).await();
39+
ctx.continueAsNew(null);
40+
}
41+
42+
@FunctionName("ContinueAsNewExternalEvent")
43+
public HttpResponseMessage continueAsNewExternalEvent(
44+
@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
45+
@DurableClientInput(name = "durableContext") DurableClientContext durableContext,
46+
final ExecutionContext context) {
47+
context.getLogger().info("Java HTTP trigger processed a request.");
48+
49+
DurableTaskClient client = durableContext.getClient();
50+
String instanceId = client.scheduleNewOrchestrationInstance("EternalEvent");
51+
context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId);
52+
return durableContext.createCheckStatusResponse(request, instanceId);
53+
}
54+
55+
@FunctionName("EternalEvent")
56+
public void eternalEvent(@DurableOrchestrationTrigger(name = "runtimeState") TaskOrchestrationContext ctx)
57+
{
58+
System.out.println("Waiting external event...");
59+
Task<Void> event = ctx.waitForExternalEvent("event");
60+
Task<Void> timer = ctx.createTimer(Duration.ofSeconds(10));
61+
Task<?> result = ctx.anyOf(event, timer).await();
62+
if (result == event) {
63+
ctx.continueAsNew(null);
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)