Skip to content

Commit

Permalink
dotnet: add plugin examples
Browse files Browse the repository at this point in the history
  • Loading branch information
YOU54F committed Jul 29, 2024
1 parent 3181d0e commit 3a14488
Show file tree
Hide file tree
Showing 70 changed files with 4,407 additions and 134 deletions.
51 changes: 49 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,55 @@ dotnet_grpc_client_run:
dotnet run --project dotnet/Grpc/GrpcGreeterClient
dotnet_grpc_provider_run:
dotnet run --project dotnet/Grpc/GrpcGreeter

dotnet: dotnet_grpc_client_test dotnet_grpc_provider_test
dotnet_grpc:
make dotnet_grpc_client_test
make dotnet_grpc_provider_test

dotnet_tcp_client_run:
dotnet run --project dotnet/Tcp/TcpClient
dotnet_tcp_provider_run:
dotnet run --project dotnet/Tcp/TcpListener
dotnet_tcp_client_test:
$(LOAD_PATH) dotnet test dotnet/Tcp/TcpClient.Tests
dotnet_tcp_provider_test:
$(LOAD_PATH) dotnet test dotnet/Tcp/TcpListener.Tests
dotnet_tcp:
make dotnet_tcp_client_test
make dotnet_tcp_provider_test

dotnet_avro_client_run:
dotnet run --project dotnet/Avro/AvroClient
dotnet_avro_provider_run:
dotnet run --project dotnet/Avro/AvroProvider
dotnet_avro_client_test:
$(LOAD_PATH) dotnet test dotnet/Avro/AvroClient.Tests
dotnet_avro_provider_test:
$(LOAD_PATH) dotnet test dotnet/Avro/AvroProvider.Tests
dotnet_avro:
make dotnet_avro_client_test
make dotnet_avro_provider_test

dotnet_protobuf_client_run:
dotnet run --project dotnet/Protobuf/RouteGuideClient
dotnet_protobuf_provider_run:
dotnet run --project dotnet/Protobuf/RouteGuideServer
dotnet_protobuf_client_test:
$(LOAD_PATH) dotnet test dotnet/Protobuf/RouteGuideClient.Tests
dotnet_protobuf_provider_test:
$(LOAD_PATH) dotnet test dotnet/Protobuf/RouteGuideServer.Tests
dotnet_protobuf:
make dotnet_protobuf_client_test
make dotnet_protobuf_provider_test

dotnet_plugin_client_test:
$(LOAD_PATH) dotnet test dotnet/Plugin/FooPluginConsumer.Tests
dotnet_plugin_install_local:
cd dotnet/PactDotnetPlugin && make install_local
dotnet_plugin:
make dotnet_plugin_install_local
make dotnet_plugin_client_test

dotnet: dotnet_grpc dotnet_tcp dotnet_avro dotnet_protobuf dotnet_plugin_client_test dotnet_plugin
alpine_php:
docker run --platform=${DOCKER_DEFAULT_PLATFORM} -v ${PWD}:/app --rm alpine sh -c 'apk add php make php83-ffi libgcc protoc && cd /app && make php'

Expand Down
22 changes: 22 additions & 0 deletions dotnet/Avro/AvroClient.Tests/AvroClient.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="xunit" Version="2.6.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AvroClient\AvroClient.csproj" />
<ProjectReference Include="..\..\Pact\Pact.csproj" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>
108 changes: 108 additions & 0 deletions dotnet/Avro/AvroClient.Tests/AvroClientTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System;
using System.Text.Json;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
using PactFfi;
using System.Runtime.InteropServices;
using System.IO;
namespace AvroClient.Tests
{
public class AvroClientTests
{

[Fact]
public async Task ReturnsMismatchWhenNoAvroClientRequestMade()
{

_ = Pact.LogToStdOut(3);
var host = "0.0.0.0";
var pact = Pact.NewPact("AvroConsumer", "AvroProvider");
var interaction = Pact.NewInteraction(pact, "A request to do get some Avro stuff");
Pact.WithSpecification(pact, Pact.PactSpecification.V4);
var content = $@"{{
""pact:avro"":""{Path.Join(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "AvroClient", "user.avsc").Replace("\\", "\\\\")}"",
""pact:record-name"": ""User"",
""pact:content-type"": ""avro/binary"",
""id"": ""matching(number, 1)"",
""username"": ""notEmpty('matt')""
}}";
Pact.PluginAdd(pact, "avro", "0.0.5");
Pact.WithRequest(interaction,"GET", "/avro");
Pact.ResponseStatus(interaction,200);
Pact.PluginInteractionContents(interaction, Pact.InteractionPart.Response, "avro/binary", content);

var port = Pact.CreateMockServerForTransport(pact, host, 0, null, null);
Console.WriteLine("Port: " + port);

var matched = Pact.MockServerMatched(port);
Console.WriteLine("Matched: " + matched);
matched.Should().BeFalse();

var MismatchesPtr = Pact.MockServerMismatches(port);
var MismatchesString = Marshal.PtrToStringAnsi(MismatchesPtr);
Console.WriteLine("Mismatches: " + MismatchesString);
var MismatchesJson = JsonSerializer.Deserialize<JsonElement>(MismatchesString);
var ErrorString = MismatchesJson[0].GetProperty("type").GetString();
var ExpectedPath = MismatchesJson[0].GetProperty("path").GetString();

ErrorString.Should().Be("missing-request");
ExpectedPath.Should().Be("/avro");

Pact.CleanupMockServer(port);
Pact.PluginCleanup(pact);
await Task.Delay(1);
}
[Fact]
public async Task WritesPactWhenGrpcClientRequestMade()
{

_ = Pact.LogToStdOut(3);
var host = "0.0.0.0";
var pact = Pact.NewPact("AvroConsumer", "AvroProvider");
var interaction = Pact.NewInteraction(pact, "A request to do get some Avro stuff");
Pact.WithSpecification(pact, Pact.PactSpecification.V4);
var content = $@"{{
""pact:avro"":""{Path.Join(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "AvroClient", "user.avsc").Replace("\\", "\\\\")}"",
""pact:record-name"": ""User"",
""pact:content-type"": ""avro/binary"",
""id"": ""matching(number, 1)"",
""username"": ""notEmpty('matt')""
}}";
Pact.PluginAdd(pact, "avro", "0.0.5");
Pact.WithRequest(interaction,"GET", "/avro");
Pact.ResponseStatus(interaction,200);
Pact.PluginInteractionContents(interaction, Pact.InteractionPart.Response, "avro/binary", content);

var port = Pact.CreateMockServerForTransport(pact, host, 0, null, null);
Console.WriteLine("Port: " + port);

// act - call avro client
var result = await AvroClient.RunAvroClient("http://localhost:" + port);
Console.WriteLine("Result: " + result);
// assert
result.Id.Should().Be(1);
result.Username.Should().Be("matt");

// pact - internal assert
var matched = Pact.MockServerMatched(port);
Console.WriteLine("Matched: " + matched);
matched.Should().BeTrue();

var MismatchesPtr = Pact.MockServerMismatches(port);
var MismatchesString = Marshal.PtrToStringAnsi(MismatchesPtr);
Console.WriteLine("Mismatches: " + MismatchesString);

MismatchesString.Should().Be("[]");

// pact - internal finalise and cleanup
var writeRes = Pact.WritePactFileForPort(port, "../../../../pacts", false);
Console.WriteLine("WriteRes: " + writeRes);
Pact.CleanupMockServer(port);
Pact.PluginCleanup(pact);
}



}
}
37 changes: 37 additions & 0 deletions dotnet/Avro/AvroClient/AvroClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Avro;
using Avro.IO;
using Avro.Specific;

namespace AvroClient
{
public class AvroClient
{
static async Task Main(string[] args)
{
await RunAvroClient();
}

public static async Task<User> RunAvroClient(string avroProviderUrl = "http://localhost:8080")
{
// Load the user schema from the avsc file
var schema = (RecordSchema)Schema.Parse(File.ReadAllText(Path.Join(Directory.GetCurrentDirectory(), ".." , "..", "..", "..","AvroClient","user.avsc")));

// Make an HTTP request to the AvroProvider
var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
var response = await client.GetAsync(avroProviderUrl + "/avro");

// Get the response from the AvroProvider
var responseStream = await response.Content.ReadAsStreamAsync();

// Deserialize the Avro binary data from the response stream
var reader = new BinaryDecoder(responseStream);
var avroReader = new SpecificDefaultReader(schema, schema);
var deserializedUser = new User();
avroReader.Read(deserializedUser, reader);

// Print the deserialized user object
Console.WriteLine($"Deserialized User: Id={deserializedUser.Id}, Username={deserializedUser.Username}");
return deserializedUser;
}
}
}
15 changes: 15 additions & 0 deletions dotnet/Avro/AvroClient/AvroClient.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- to avoid code-signing on macos due to Failed to create CoreCLR, HRESULT: 0x8007000C -->
<!-- codesign -f \-\-remove-signature ./bin/Debug/net8.0/GrpcGreeter -->
<UseAppHost>false</UseAppHost>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Apache.Avro" Version="1.11.3" />
</ItemGroup>
</Project>
5 changes: 5 additions & 0 deletions dotnet/Avro/AvroClient/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<RunWorkingDirectory>$(TargetDir)</RunWorkingDirectory>
</PropertyGroup>
</Project>
40 changes: 40 additions & 0 deletions dotnet/Avro/AvroClient/User.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Avro;
using Avro.Specific;
// Define the User class based on the user.avsc schema

public class User : ISpecificRecord
{
public static Schema _SCHEMA = Schema.Parse(File.ReadAllText("user.avsc"));

public virtual Schema Schema => _SCHEMA;

public long Id { get; set; }
public string Username { get; set; } = string.Empty;

Schema ISpecificRecord.Schema => throw new NotImplementedException();

object ISpecificRecord.Get(int fieldPos)
{
return fieldPos switch
{
0 => Id,
1 => Username,
_ => throw new AvroRuntimeException("Invalid field index"),
};
}

void ISpecificRecord.Put(int fieldPos, object fieldValue)
{
switch (fieldPos)
{
case 0:
Id = (long)fieldValue;
break;
case 1:
Username = (string)fieldValue;
break;
default:
throw new AvroRuntimeException("Invalid field index");
}
}
}
9 changes: 9 additions & 0 deletions dotnet/Avro/AvroClient/user.avsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "record",
"name": "User",
"namespace": "io.pact",
"fields" : [
{"name": "id", "type": "long"},
{"name": "username", "type": "string"}
]
}
22 changes: 22 additions & 0 deletions dotnet/Avro/AvroProvider.Tests/AvroProvider.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="xunit" Version="2.6.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AvroProvider\AvroProvider.csproj" />
<ProjectReference Include="..\..\Pact\Pact.csproj" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>
50 changes: 50 additions & 0 deletions dotnet/Avro/AvroProvider.Tests/AvroProviderTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using FluentAssertions;
using Xunit;
using PactFfi;
using System.Threading.Tasks;

namespace AvroProvider.Tests
{
public class AvroProviderTests
{

[Fact]
public void ReturnsVerificationFailureWhenNoRunningProvider()
{
_ = Pact.LogToStdOut(3);

var verifier = Pact.VerifierNewForApplication("pact-dotnet","0.0.0");
Pact.VerifierSetProviderInfo(verifier,"AvroProvider",null,null,8081,null);
Pact.VerifierAddFileSource(verifier,"../../../../pacts/AvroConsumer-AvroProvider.json");
var VerifierExecuteResult = Pact.VerifierExecute(verifier);
VerifierExecuteResult.Should().Be(1);
}
[Fact]
public async Task ReturnsVerificationSuccessRunningProviderAsync()
{
_ = Pact.LogToStdOut(3);
ushort port = 8080;
var verifier = Pact.VerifierNewForApplication("pact-dotnet", "0.0.0");
Pact.VerifierSetProviderInfo(verifier,"AvroProvider",null,null,port,null);
Pact.VerifierAddFileSource(verifier,"../../../../pacts/AvroConsumer-AvroProvider.json");

// // Arrange
// // Setup our app to run before our verifier executes
// // Setup a cancellation token so we can shutdown the app after
var cts = new System.Threading.CancellationTokenSource();
var token = cts.Token;
var runAppTask = Task.Run(async () =>
{
await AvroProvider.StartServer(token, "http://localhost:" + port + "/");
}, token);

// Act
var VerifierExecuteResult = Pact.VerifierExecute(verifier);
VerifierExecuteResult.Should().Be(0);
Pact.VerifierShutdown(verifier);

// After test execution, signal the task to terminate
cts.Cancel();
}
}
}
Loading

0 comments on commit 3a14488

Please sign in to comment.