Skip to content

Commit

Permalink
test: add pact verification
Browse files Browse the repository at this point in the history
todo: shutdown app cleanly
  • Loading branch information
YOU54F committed Nov 21, 2024
1 parent e27c8c4 commit 3153cbc
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 4 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/ProviderPactVerify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Provider-Pact-Verification
on:
push:

jobs:
test:
runs-on: ubuntu-latest
env:
application_folder_consumer: smartbearcoin-payments-ui
application_folder_provider_tests: smartbearcoin-payments-ui
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: cd ${{ env.application_folder_consumer }} && npm ci
- run: cd ${{ env.application_folder_consumer }} && npm test
- run: cd ${{ env.application_folder_provider_tests }} && dotnet test
env:
PACT_URL: ../../../../smartbearcoin-payments-ui/pacts/SmartBearCoin-Payments-UI-SmartBearCoin-Payee-Provider.json
90 changes: 90 additions & 0 deletions provider_azure_function_tests/PactVerificationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using PactNet.Infrastructure.Outputters;
using PactNet.Output.Xunit;
using PactNet.Verifier;
using PactNet;
using Xunit.Abstractions;

namespace provider_azure_function_tests;

public class ProviderApiTests : IDisposable
{
private string _providerUri { get; }
private ITestOutputHelper _outputHelper { get; }
private System.Threading.Tasks.Task<TemporaryAzureFunctionsApplication> _app { get; }

public ProviderApiTests(ITestOutputHelper output)
{
_outputHelper = output;
_providerUri = "http://localhost:7071/api";
_app = TemporaryAzureFunctionsApplication.StartNewAsync(new DirectoryInfo("../../../../provider_azure_function"));
}

[Fact]
public void EnsureProviderApiHonoursPactWithConsumer()
{

// Wait for the Azure Functions application to start
_app.Wait(15000);

// Arrange
var config = new PactVerifierConfig
{

// NOTE: We default to using a ConsoleOutput,
// however xUnit 2 does not capture the console output,
// so a custom outputter is required.
Outputters = new List<IOutput>
{
new XunitOutput(_outputHelper),
new ConsoleOutput()
},

// Output verbose verification logs to the test output
LogLevel = PactLogLevel.Information,
};

string providerName = "SmartBearCoin-Payee-Provider";
IPactVerifier pactVerifier = new PactVerifier(providerName, config);
string pactUrl = Environment.GetEnvironmentVariable("PACT_URL");

pactVerifier.WithHttpEndpoint(new Uri(_providerUri))
.WithFileSource(new FileInfo(pactUrl))
.Verify();

// _app.Dispose();

}

#region IDisposable Support

private bool _disposed = false; // To detect redundant calls

protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}

if (disposing)
{
Console.WriteLine("SAF IS A BOSS");
_app.Dispose();
}

_disposed = true;
}

// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);

GC.SuppressFinalize(this);
}

#endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System.Diagnostics;
using Polly;
using Polly.Retry;

public class TemporaryAzureFunctionsApplication : IAsyncDisposable
{
private readonly Process _application;
private static readonly HttpClient HttpClient = new HttpClient();

private TemporaryAzureFunctionsApplication(Process application)
{
_application = application;
}

public static async Task<TemporaryAzureFunctionsApplication> StartNewAsync(DirectoryInfo projectDirectory)
{
int port = 7071;
Process app = StartApplication(port, projectDirectory);
await WaitUntilTriggerIsAvailableAsync($"http://localhost:{port}/");

return new TemporaryAzureFunctionsApplication(app);
}

private static Process StartApplication(int port, DirectoryInfo projectDirectory)
{
var appInfo = new ProcessStartInfo("func", $"start --port {port}")
{
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = projectDirectory.FullName
};

var app = new Process { StartInfo = appInfo };
app.Start();
return app;
}

private static async Task WaitUntilTriggerIsAvailableAsync(string endpoint)
{
AsyncRetryPolicy retryPolicy =
Policy.Handle<Exception>()
.WaitAndRetryForeverAsync(index => TimeSpan.FromMilliseconds(500));

PolicyResult<HttpResponseMessage> result =
await Policy.TimeoutAsync(TimeSpan.FromSeconds(30))
.WrapAsync(retryPolicy)
.ExecuteAndCaptureAsync(() => HttpClient.GetAsync(endpoint));

if (result.Outcome == OutcomeType.Failure)
{
throw new InvalidOperationException(
"The Azure Functions project doesn't seem to be running, "
+ "please check any build or runtime errors that could occur during startup");
}
}

public ValueTask DisposeAsync()
{
if (!_application.HasExited)
{
_application.Kill(entireProcessTree: true);
}

_application.Dispose();
return ValueTask.CompletedTask;
}
}
33 changes: 33 additions & 0 deletions provider_azure_function_tests/provider_azure_function_tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Polly" Version="8.5.0" />

<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.6.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="PactNet" Version="5.0.0" />
<PackageReference Include="PactNet.Output.Xunit" Version="1.0.0" />

<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\provider_azure_function\AzureFunctions.csproj" />
</ItemGroup>
</Project>
8 changes: 4 additions & 4 deletions smartbearcoin-payments-ui/src/api.pact.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('test with pact', () => {
.withRequest({
method: 'GET',
path: '/payees',
query: { country_of_registration: like('DE'), name: like('test') },
query: { country_of_registration: like('IE'), name: like('LTD') },
headers: {
'x-Authorization': like('Bearer 1234')
}
Expand All @@ -43,21 +43,21 @@ describe('test with pact', () => {
});
return providerWithConsumerA.executeTest((mockserver) => {
const client = new API(mockserver.url);
return client.getPayees('DE', 'foo').then((res) => {
return client.getPayees('IE', 'LTD').then((res) => {
expect(res).toEqual(expectedPayees);
});
});
});
it('should return a particular payee', () => {
const id = '592b4ece-c7a2-46ff-b380-96fd1638852a';
const id = '1e331a0f-29bd-4b6b-8b21-8b87ed653c6b';
const expectedPayee = {
account_name: 'account_name',
any_bic: 'VHO7ZKQT',
bank_account_currency: 'EUR',
bank_code: 'bank_code',
bank_name: 'bank_name',
iban: 'IE01AIBK935955939393',
id: '592b4ece-c7a2-46ff-b380-96fd1638852a',
id,
name: 'name'
};
providerWithConsumerA
Expand Down

0 comments on commit 3153cbc

Please sign in to comment.