Add gRPC server example with file download

This commit is contained in:
2024-09-25 19:27:20 +02:00
commit 179d4a8dc4
14 changed files with 480 additions and 0 deletions

233
TestGrpc.Test/UnitTest1.cs Normal file
View File

@@ -0,0 +1,233 @@
using Grpc.Core;
using Grpc.Net.Client;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using Xunit.Abstractions;
namespace TestGrpc.Test;
public class UnitTest1 : IntegrationTestBase
{
public UnitTest1(GrpcTestFixture<Startup> fixture, ITestOutputHelper outputHelper)
: base(fixture, outputHelper)
{
}
[Fact]
public async Task Test1()
{
// Arrange
var client = new Greeter.GreeterClient(Channel);
// Act
using var response = client.DownloadFile(new FileRequest { FilePath = "Files/test.txt" });
using var memoryStream = new MemoryStream();
var receivedFileName = string.Empty;
await foreach (var chunk in response.ResponseStream.ReadAllAsync())
{
if (!string.IsNullOrEmpty(chunk.FileName))
{
receivedFileName = chunk.FileName;
}
memoryStream.Write(chunk.Chunk.Span);
}
memoryStream.Seek(0, SeekOrigin.Begin);
using var reader = new StreamReader(memoryStream);
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
Console.WriteLine(line);
}
// Assert
Assert.Equal("test.txt", receivedFileName);
}
}
internal class ForwardingLoggerProvider : ILoggerProvider
{
private readonly LogMessage _logAction;
public ForwardingLoggerProvider(LogMessage logAction)
{
_logAction = logAction;
}
public ILogger CreateLogger(string categoryName)
{
return new ForwardingLogger(categoryName, _logAction);
}
public void Dispose()
{
}
internal class ForwardingLogger : ILogger
{
private readonly string _categoryName;
private readonly LogMessage _logAction;
public ForwardingLogger(string categoryName, LogMessage logAction)
{
_categoryName = categoryName;
_logAction = logAction;
}
public IDisposable BeginScope<TState>(TState state)
{
return null!;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
_logAction(logLevel, _categoryName, eventId, formatter(state, exception), exception);
}
}
}
internal class GrpcTestContext<TStartup> : IDisposable where TStartup : class
{
private readonly Stopwatch _stopwatch;
private readonly GrpcTestFixture<TStartup> _fixture;
private readonly ITestOutputHelper _outputHelper;
public GrpcTestContext(GrpcTestFixture<TStartup> fixture, ITestOutputHelper outputHelper)
{
_stopwatch = Stopwatch.StartNew();
_fixture = fixture;
_outputHelper = outputHelper;
_fixture.LoggedMessage += WriteMessage;
}
private void WriteMessage(LogLevel logLevel, string category, EventId eventId, string message, Exception? exception)
{
var log = $"{_stopwatch.Elapsed.TotalSeconds:N3}s {category} - {logLevel}: {message}";
if (exception != null)
{
log += Environment.NewLine + exception.ToString();
}
_outputHelper.WriteLine(log);
}
public void Dispose()
{
_fixture.LoggedMessage -= WriteMessage;
}
}
public delegate void LogMessage(LogLevel logLevel, string categoryName, EventId eventId, string message, Exception? exception);
public class GrpcTestFixture<TStartup> : IDisposable where TStartup : class
{
private TestServer? _server;
private IHost? _host;
private HttpMessageHandler? _handler;
private Action<IWebHostBuilder>? _configureWebHost;
public event LogMessage? LoggedMessage;
public GrpcTestFixture()
{
LoggerFactory = new LoggerFactory();
LoggerFactory.AddProvider(new ForwardingLoggerProvider((logLevel, category, eventId, message, exception) =>
{
LoggedMessage?.Invoke(logLevel, category, eventId, message, exception);
}));
}
public void ConfigureWebHost(Action<IWebHostBuilder> configure)
{
_configureWebHost = configure;
}
private void EnsureServer()
{
if (_host == null)
{
var builder = new HostBuilder()
.ConfigureServices(services =>
{
services.AddSingleton<ILoggerFactory>(LoggerFactory);
})
.ConfigureWebHostDefaults(webHost =>
{
webHost
.UseTestServer()
.UseStartup<TStartup>();
_configureWebHost?.Invoke(webHost);
});
_host = builder.Start();
_server = _host.GetTestServer();
_handler = _server.CreateHandler();
}
}
public LoggerFactory LoggerFactory { get; }
public HttpMessageHandler Handler
{
get
{
EnsureServer();
return _handler!;
}
}
public void Dispose()
{
_handler?.Dispose();
_host?.Dispose();
_server?.Dispose();
}
public IDisposable GetTestContext(ITestOutputHelper outputHelper)
{
return new GrpcTestContext<TStartup>(this, outputHelper);
}
}
public class IntegrationTestBase : IClassFixture<GrpcTestFixture<Startup>>, IDisposable
{
private GrpcChannel? _channel;
private IDisposable? _testContext;
protected GrpcTestFixture<Startup> Fixture { get; set; }
protected ILoggerFactory LoggerFactory => Fixture.LoggerFactory;
protected GrpcChannel Channel => _channel ??= CreateChannel();
protected GrpcChannel CreateChannel()
{
return GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
{
LoggerFactory = LoggerFactory,
HttpHandler = Fixture.Handler
});
}
public IntegrationTestBase(GrpcTestFixture<Startup> fixture, ITestOutputHelper outputHelper)
{
Fixture = fixture;
_testContext = Fixture.GetTestContext(outputHelper);
}
public void Dispose()
{
_testContext?.Dispose();
_channel = null;
}
}