Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c7ecfdc
Tweaking logging and generating coverage + test results
aaronpowell Aug 20, 2024
4b334bb
More waiting on services to be ready
aaronpowell Aug 20, 2024
b0ba31e
that's the thing I wanted
aaronpowell Aug 21, 2024
d0d38a7
adding test reporter
aaronpowell Aug 22, 2024
6feca43
merging the two actions together
aaronpowell Aug 22, 2024
0dd7449
Test reporting always runs
aaronpowell Aug 22, 2024
d5703e1
Disabling test parallelism
aaronpowell Aug 29, 2024
5a33086
Using collections to disable parallelisation
aaronpowell Aug 29, 2024
fca5927
Moving to use a class fixture for aspire setup rather than inheritance
aaronpowell Aug 29, 2024
e0ae3b7
Removing some old prototype code
aaronpowell Aug 29, 2024
cf70cf3
Using the DistributedApplicationFactory which I didn't know existed
aaronpowell Aug 29, 2024
13941e4
Slight reorder
aaronpowell Aug 29, 2024
de893bb
Trying a theory for swa tests
aaronpowell Aug 29, 2024
b638fe0
Extending the devserver timeout when waiting for the frontend and api…
aaronpowell Aug 29, 2024
dc5ca38
Using an options object
aaronpowell Aug 29, 2024
2445d8c
Splitting the tests up as it wasn't helping
aaronpowell Aug 29, 2024
206433b
Making maven quiet and removing the Java stub test
aaronpowell Aug 29, 2024
be133f0
massive timeout
aaronpowell Aug 29, 2024
e558156
Debug logging
aaronpowell Aug 29, 2024
c1d9bae
Bringing in an extension from Aspire core to wait for log stream
aaronpowell Aug 30, 2024
d840d3e
Updating the aspire version
aaronpowell Aug 30, 2024
a1d00e8
Adding a much longer timeout
aaronpowell Aug 30, 2024
a07d4d5
Adding a warning on the methods that they are experimental and from t…
aaronpowell Aug 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 50 additions & 9 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ jobs:
build:
runs-on: ubuntu-latest
env:
DCP_PRESERVE_EXECUTABLE_LOGS: "yes"
DCP_DIAGNOSTICS_LOG_FOLDER: ${{ github.workspace }}/diagnostics
DOTNET_CONFIGURATION: Release

steps:
- uses: actions/checkout@v4
Expand All @@ -23,12 +22,30 @@ jobs:
with:
dotnet-version: 8.0.x
- uses: actions/setup-java@v4
name: Set up Java
with:
distribution: "microsoft"
java-version: "21"
- uses: actions/setup-node@v4
name: Set up Node.js
with:
node-version: "latest"

- uses: actions/cache@v4
name: Cache NuGet packages
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('Directory.Packages.props') }}
restore-keys: |
${{ runner.os }}-nuget-
- uses: actions/cache@v4
name: Cache Java Docker images
with:
path: /var/lib/docker/image
key: ${{ runner.os }}-docker-${{ hashFiles('examples/java/CommunityToolkit.Aspire.Java.Spring.Maven/Dockerfile') }}
restore-keys: |
${{ runner.os }}-docker-

- name: Install Aspire workload
run: dotnet workload install aspire
- name: Setup .NET dev certs
Expand All @@ -40,16 +57,40 @@ jobs:
npm install -g @azure/static-web-apps-cli
cd examples/swa/CommunityToolkit.Aspire.StaticWebApps.WebApp
npm ci

- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
run: dotnet build --no-restore --configuration ${{ env.DOTNET_CONFIGURATION }}
- name: Test
run: dotnet test --no-build --verbosity normal
- name: Upload build artifact
uses: actions/upload-artifact@v3
if: failure()
run: dotnet test --no-build --configuration ${{ env.DOTNET_CONFIGURATION }} --collect "XPlat Code Coverage" --results-directory test-results --logger trx

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: build-output
name: test-results
path: |
${{ github.workspace }}/diagnostics/**
${{ github.workspace }}/test-results/**

test-reporting:
permissions:
contents: read
actions: read
checks: write
runs-on: ubuntu-latest
needs: build
if: ${{ always() }}
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
merge-multiple: true

- name: Test Report
uses: dorny/test-reporter@v1
if: success() || failure()
with:
name: ".NET Tests"
path: "*.trx"
reporter: dotnet-trx
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ obj
.azure
appsettings.*.json
*.orig
test-results
TestResults
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<AspireVersion>8.1.0</AspireVersion>
<AspireVersion>8.2.0</AspireVersion>
<AspNetCoreVersion>8.0.7</AspNetCoreVersion>
<OpenTelemetryVersion>1.9.0</OpenTelemetryVersion>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

<Target Name="PublishRunWebpack" AfterTargets="Build">
<!-- As part of publishing, ensure the Java app is freshly built -->
<Exec WorkingDirectory="$(JavaAppRoot)" Command="./mvnw clean package" />
<Exec WorkingDirectory="$(JavaAppRoot)" Command="./mvnw --quiet clean package" />
</Target>

</Project>

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,24 @@ public static class SwaAppHostingExtension
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to add the resource to.</param>
/// <param name="name">The name of the resource.</param>
/// <param name="options">The <see cref="JavaAppContainerResourceOptions"/> to configure the Java application.</param>"
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
/// <remarks>This resource will not be included in the published manifest.</remarks>
public static IResourceBuilder<SwaResource> AddSwaEmulator(this IDistributedApplicationBuilder builder, string name, int port = 4280)
public static IResourceBuilder<SwaResource> AddSwaEmulator(this IDistributedApplicationBuilder builder, string name) =>
builder.AddSwaEmulator(name, new SwaResourceOptions());

/// <summary>
/// Adds a Static Web Apps emulator to the application.
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to add the resource to.</param>
/// <param name="name">The name of the resource.</param>
/// <param name="options">The <see cref="SwaResourceOptions"/> to configure the SWA CLI.</param>"
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
/// <remarks>This resource will not be included in the published manifest.</remarks>
public static IResourceBuilder<SwaResource> AddSwaEmulator(this IDistributedApplicationBuilder builder, string name, SwaResourceOptions options)
{
var resource = new SwaResource(name, Environment.CurrentDirectory);
return builder.AddResource(resource)
.WithHttpEndpoint(isProxied: false, port: port)
.WithHttpEndpoint(isProxied: false, port: options.Port)
.WithArgs(ctx =>
{
ctx.Args.Add("start");
Expand All @@ -35,7 +45,10 @@ public static IResourceBuilder<SwaResource> AddSwaEmulator(this IDistributedAppl
}

ctx.Args.Add("--port");
ctx.Args.Add(port.ToString());
ctx.Args.Add(options.Port.ToString());

ctx.Args.Add("--devserver-timeout");
ctx.Args.Add(options.DevServerTimeout.ToString());
})
.ExcludeFromManifest();
}
Expand All @@ -58,3 +71,9 @@ public static IResourceBuilder<SwaResource> WithAppResource(this IResourceBuilde
public static IResourceBuilder<SwaResource> WithApiResource(this IResourceBuilder<SwaResource> builder, IResourceBuilder<IResourceWithEndpoints> apiResource) =>
builder.WithAnnotation<SwaApiEndpointAnnotation>(new(apiResource), ResourceAnnotationMutationBehavior.Replace);
}

public class SwaResourceOptions
{
public int Port { get; set; } = 4280;
public int DevServerTimeout { get; set; } = 60;
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public void TargetPort_Can_Be_Overridden()
{
var builder = DistributedApplication.CreateBuilder();

builder.AddSwaEmulator("swa", port: 1234);
SwaResourceOptions options = new() { Port = 1234 };
builder.AddSwaEmulator("swa", options);

using var app = builder.Build();

Expand All @@ -46,7 +47,7 @@ public void TargetPort_Can_Be_Overridden()
Assert.Equal("swa", resource.Name);

var httpEndpoint = resource.GetEndpoint("http");
Assert.Equal(1234, httpEndpoint.TargetPort);
Assert.Equal(options.Port, httpEndpoint.TargetPort);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@
using FluentAssertions;
using System.Net;
using System.Net.Http.Json;
using Xunit.Abstractions;

namespace CommunityToolkit.Aspire.Hosting.Azure.StaticWebApps.Tests;

public class SwaHostingComponentTests(ITestOutputHelper testOutput) : AspireIntegrationTest<Projects.CommunityToolkit_Aspire_StaticWebApps_AppHost>(testOutput)
#pragma warning disable CTASPIRESRC001
public class SwaHostingComponentTests(AspireIntegrationTestFixture<Projects.CommunityToolkit_Aspire_StaticWebApps_AppHost> fixture) : IClassFixture<AspireIntegrationTestFixture<Projects.CommunityToolkit_Aspire_StaticWebApps_AppHost>>
{
[Fact]
public async Task EmulatorLaunchesOnDefaultPort()
public async Task CanAccessFrontendSuccessfully()
{
var httpClient = app.CreateHttpClient("swa");
var httpClient = fixture.CreateHttpClient("swa");

await ResourceNotificationService.WaitForResourceAsync("swa", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
await fixture.App.WaitForTextAsync("Azure Static Web Apps emulator started", "swa").WaitAsync(TimeSpan.FromSeconds(30));

var response = await httpClient.GetAsync("/");

response.StatusCode.Should().Be(HttpStatusCode.OK);
}

[Fact]
public async Task CanAccessApi()
public async Task CanAccessApiSuccessfully()
{
var httpClient = app.CreateHttpClient("swa");
var httpClient = fixture.CreateHttpClient("swa");

await ResourceNotificationService.WaitForResourceAsync("swa", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
await fixture.App.WaitForTextAsync("Azure Static Web Apps emulator started", "swa").WaitAsync(TimeSpan.FromSeconds(30));

var response = await httpClient.GetAsync("/api/weather");

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Net;
using CommunityToolkit.Aspire.Testing;
using FluentAssertions;

namespace CommunityToolkit.Aspire.Java.Hosting.EndToEndTests;

#pragma warning disable CTASPIRESRC001
public class JavaHostingComponentTests(AspireIntegrationTestFixture<Projects.CommunityToolkit_Aspire_Java_AppHost> fixture) : IClassFixture<AspireIntegrationTestFixture<Projects.CommunityToolkit_Aspire_Java_AppHost>>
{
[Theory]
[InlineData("containerapp")]
[InlineData("executableapp")]
public async Task ResourceWillRespondWithOk(string resourceName)
{
var httpClient = fixture.CreateHttpClient(resourceName);

await fixture.App.WaitForTextAsync("Started SpringMavenApplication", resourceName).WaitAsync(TimeSpan.FromMinutes(5));

var response = await httpClient.GetAsync("/");

response.StatusCode.Should().Be(HttpStatusCode.OK);
}
}

This file was deleted.

34 changes: 20 additions & 14 deletions tests/CommunityToolkit.Aspire.Testing/AspireIntegrationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,36 @@
using Aspire.Hosting.ApplicationModel;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;
namespace CommunityToolkit.Aspire.Testing;

public abstract class AspireIntegrationTest<T>(ITestOutputHelper testOutput) : IAsyncLifetime
where T : class
public class AspireIntegrationTestFixture<TEntryPoint>() : DistributedApplicationFactory(typeof(TEntryPoint), []), IAsyncLifetime where TEntryPoint : class
{
protected DistributedApplication app = null!;
protected ResourceNotificationService ResourceNotificationService => app.Services.GetRequiredService<ResourceNotificationService>();
public ResourceNotificationService ResourceNotificationService => App.Services.GetRequiredService<ResourceNotificationService>();

public async Task DisposeAsync() => await app.DisposeAsync();
public DistributedApplication App { get; private set; } = null!;

public async Task InitializeAsync()
protected override void OnBuilt(DistributedApplication application)
{
var appHost = await DistributedApplicationTestingBuilder.CreateAsync<T>();
App = application;
base.OnBuilt(application);
}

appHost.Services
.AddLogging(builder =>
protected override void OnBuilderCreated(DistributedApplicationBuilder applicationBuilder)
{
applicationBuilder.Services.AddLogging(builder =>
{
builder.AddXUnit(testOutput);
builder.SetMinimumLevel(LogLevel.Trace);
builder.AddXUnit();
if (Environment.GetEnvironmentVariable("RUNNER_DEBUG") is not null or "1")
builder.SetMinimumLevel(LogLevel.Trace);
else
builder.SetMinimumLevel(LogLevel.Information);
})
.ConfigureHttpClientDefaults(clientBuilder => clientBuilder.AddStandardResilienceHandler());

app = await appHost.BuildAsync();
await app.StartAsync();
base.OnBuilderCreated(applicationBuilder);
}

public async Task InitializeAsync() => await StartAsync();

async Task IAsyncLifetime.DisposeAsync() => await DisposeAsync();
}
Loading