Add audit API client
- cleanup - generate PCAP API
This commit is contained in:
parent
bb46b7f58a
commit
15106dbfd6
13 changed files with 151 additions and 58 deletions
73
src/INetMock.Client/Audit/Client/AuditApiClient.cs
Normal file
73
src/INetMock.Client/Audit/Client/AuditApiClient.cs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Grpc.Core;
|
||||||
|
using Grpc.Net.Client;
|
||||||
|
using INetMock.Client.Audit.Serialization;
|
||||||
|
using INetMock.Client.Rpc;
|
||||||
|
|
||||||
|
namespace INetMock.Client.Audit.Client
|
||||||
|
{
|
||||||
|
public class AuditApiClient : IAuditApiClient
|
||||||
|
{
|
||||||
|
private readonly Rpc.Audit.AuditClient _auditClient;
|
||||||
|
|
||||||
|
public AuditApiClient(string address, GrpcChannelOptions? options = null) : this(new Uri(address), options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuditApiClient(Uri address, GrpcChannelOptions? options = null) : this(
|
||||||
|
GrpcChannel.ForAddress(address, options ?? new GrpcChannelOptions()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuditApiClient(ChannelBase channel)
|
||||||
|
{
|
||||||
|
_auditClient = new Rpc.Audit.AuditClient(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<string>> ListSinksAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var sinks = await _auditClient.ListSinksAsync(new ListSinksRequest(), Metadata.Empty, null, token);
|
||||||
|
return sinks.Sinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> RegisterFileSinkAsync(string targetPath, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var resp = await _auditClient.RegisterFileSinkAsync(
|
||||||
|
new RegisterFileSinkRequest {TargetPath = targetPath},
|
||||||
|
Metadata.Empty,
|
||||||
|
null,
|
||||||
|
token
|
||||||
|
);
|
||||||
|
|
||||||
|
return resp.ResolvedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> RemoveFileSinkAsync(string targetPath, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var resp = await _auditClient.RemoveFileSinkAsync(new RemoveFileSinkRequest
|
||||||
|
{
|
||||||
|
TargetPath = targetPath
|
||||||
|
},
|
||||||
|
Metadata.Empty,
|
||||||
|
null,
|
||||||
|
token
|
||||||
|
);
|
||||||
|
|
||||||
|
return resp.SinkGotRemoved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IProtoEventReader EventStreamAsync(string watcherName, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var stream = _auditClient.WatchEvents(
|
||||||
|
new WatchEventsRequest {WatcherName = watcherName},
|
||||||
|
Metadata.Empty,
|
||||||
|
null,
|
||||||
|
token
|
||||||
|
);
|
||||||
|
return new EventServerStreamReader(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
@ -9,11 +8,13 @@ using INetMock.Client.Audit.Details;
|
||||||
namespace INetMock.Client.Audit
|
namespace INetMock.Client.Audit
|
||||||
{
|
{
|
||||||
public abstract record EventDetails;
|
public abstract record EventDetails;
|
||||||
|
|
||||||
public record EmptyDetails : EventDetails;
|
public record EmptyDetails : EventDetails;
|
||||||
|
|
||||||
public record GenericDetails : EventDetails
|
public record GenericDetails : EventDetails
|
||||||
{
|
{
|
||||||
private readonly Any? _detailsAny;
|
private readonly Any? _detailsAny;
|
||||||
|
|
||||||
public GenericDetails()
|
public GenericDetails()
|
||||||
{
|
{
|
||||||
_detailsAny = null;
|
_detailsAny = null;
|
||||||
|
@ -27,14 +28,16 @@ namespace INetMock.Client.Audit
|
||||||
public static implicit operator HttpDetails(GenericDetails gd)
|
public static implicit operator HttpDetails(GenericDetails gd)
|
||||||
{
|
{
|
||||||
if (gd._detailsAny == null || gd._detailsAny.Value == null) return new();
|
if (gd._detailsAny == null || gd._detailsAny.Value == null) return new();
|
||||||
if(!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName)) throw new InvalidOperationException();
|
if (!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName))
|
||||||
|
throw new InvalidOperationException();
|
||||||
return new HttpDetails(gd._detailsAny);
|
return new HttpDetails(gd._detailsAny);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static implicit operator DnsDetails(GenericDetails gd)
|
public static implicit operator DnsDetails(GenericDetails gd)
|
||||||
{
|
{
|
||||||
if (gd._detailsAny == null || gd._detailsAny.Value == null) return new();
|
if (gd._detailsAny == null || gd._detailsAny.Value == null) return new();
|
||||||
if(!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName)) throw new InvalidOperationException();
|
if (!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName))
|
||||||
|
throw new InvalidOperationException();
|
||||||
return new DnsDetails(gd._detailsAny);
|
return new DnsDetails(gd._detailsAny);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +46,6 @@ namespace INetMock.Client.Audit
|
||||||
{
|
{
|
||||||
public HttpDetails()
|
public HttpDetails()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpDetails(Any? any)
|
public HttpDetails(Any? any)
|
||||||
|
@ -51,14 +53,14 @@ namespace INetMock.Client.Audit
|
||||||
if (any == null || any.Value == null) return;
|
if (any == null || any.Value == null) return;
|
||||||
|
|
||||||
var detailsEntity = HTTPDetailsEntity.Parser.ParseFrom(any.Value);
|
var detailsEntity = HTTPDetailsEntity.Parser.ParseFrom(any.Value);
|
||||||
|
|
||||||
Method = new HttpMethod(detailsEntity.Method.ToString());
|
Method = new HttpMethod(detailsEntity.Method.ToString());
|
||||||
Host = detailsEntity.Host;
|
Host = detailsEntity.Host;
|
||||||
Uri = detailsEntity.Uri;
|
Uri = detailsEntity.Uri;
|
||||||
Proto = detailsEntity.Proto;
|
Proto = detailsEntity.Proto;
|
||||||
Headers = new INetMockHttpHeaders(detailsEntity.Headers);
|
Headers = new INetMockHttpHeaders(detailsEntity.Headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpMethod Method { get; init; }
|
public HttpMethod Method { get; init; }
|
||||||
public string Host { get; init; }
|
public string Host { get; init; }
|
||||||
public string Uri { get; init; }
|
public string Uri { get; init; }
|
||||||
|
@ -70,16 +72,15 @@ namespace INetMock.Client.Audit
|
||||||
{
|
{
|
||||||
public DnsDetails()
|
public DnsDetails()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DNSOpCode OpCode { get; init; }
|
public DNSOpCode OpCode { get; init; }
|
||||||
|
|
||||||
public IReadOnlyList<DNSQuestionEntity> Questions { get; init; }
|
public IReadOnlyList<DNSQuestionEntity> Questions { get; init; }
|
||||||
|
|
||||||
public DnsDetails(Any? any)
|
public DnsDetails(Any? any)
|
||||||
{
|
{
|
||||||
if(any == null || any.Value == null) return;
|
if (any == null || any.Value == null) return;
|
||||||
|
|
||||||
var entity = DNSDetailsEntity.Parser.ParseFrom(any.Value);
|
var entity = DNSDetailsEntity.Parser.ParseFrom(any.Value);
|
||||||
OpCode = entity.Opcode;
|
OpCode = entity.Opcode;
|
||||||
|
|
|
@ -7,13 +7,12 @@ namespace INetMock.Client.Audit
|
||||||
{
|
{
|
||||||
public Event()
|
public Event()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Event(EventEntity entity)
|
public Event(EventEntity entity)
|
||||||
{
|
{
|
||||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
||||||
|
|
||||||
var details = (new T(), entity.Application) switch
|
var details = (new T(), entity.Application) switch
|
||||||
{
|
{
|
||||||
(GenericDetails, _) => new GenericDetails(entity.ProtocolDetails) as T,
|
(GenericDetails, _) => new GenericDetails(entity.ProtocolDetails) as T,
|
||||||
|
@ -33,7 +32,7 @@ namespace INetMock.Client.Audit
|
||||||
Application = entity.Application;
|
Application = entity.Application;
|
||||||
Details = details;
|
Details = details;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Id { get; init; }
|
public long Id { get; init; }
|
||||||
public DateTimeOffset Timestamp { get; init; }
|
public DateTimeOffset Timestamp { get; init; }
|
||||||
public TransportProtocol Transport { get; init; }
|
public TransportProtocol Transport { get; init; }
|
||||||
|
@ -69,10 +68,10 @@ namespace INetMock.Client.Audit
|
||||||
_ => new EmptyDetails() as T
|
_ => new EmptyDetails() as T
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (details == null) return null;
|
if (details == null) return null;
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
Id = ge.Id,
|
Id = ge.Id,
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Grpc.Core;
|
|
||||||
|
|
||||||
namespace INetMock.Client.Audit
|
|
||||||
{
|
|
||||||
public sealed class EventServerStreamReader : IProtoEventReader
|
|
||||||
{
|
|
||||||
private readonly IAsyncStreamReader<EventEntity> _asyncEventStream;
|
|
||||||
|
|
||||||
public EventServerStreamReader(IAsyncStreamReader<EventEntity> asyncEventStream)
|
|
||||||
{
|
|
||||||
_asyncEventStream = asyncEventStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<EventEntity?> ReadAsync(CancellationToken token = default)
|
|
||||||
{
|
|
||||||
if (!await _asyncEventStream.MoveNext(token)) return null;
|
|
||||||
return _asyncEventStream.Current;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
14
src/INetMock.Client/Audit/IAuditApiClient.cs
Normal file
14
src/INetMock.Client/Audit/IAuditApiClient.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace INetMock.Client.Audit
|
||||||
|
{
|
||||||
|
public interface IAuditApiClient
|
||||||
|
{
|
||||||
|
IProtoEventReader EventStreamAsync(string watcherName, CancellationToken token = default);
|
||||||
|
Task<IEnumerable<string>> ListSinksAsync(CancellationToken token = default);
|
||||||
|
Task<string> RegisterFileSinkAsync(string targetPath, CancellationToken token = default);
|
||||||
|
Task<bool> RemoveFileSinkAsync(string targetPath, CancellationToken token = default);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,10 +5,10 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace INetMock.Client.Audit
|
namespace INetMock.Client.Audit
|
||||||
{
|
{
|
||||||
public interface IEventReader<T>: IDisposable, IAsyncDisposable where T : EventDetails, new()
|
public interface IEventReader<T> : IDisposable, IAsyncDisposable where T : EventDetails, new()
|
||||||
{
|
{
|
||||||
IAsyncEnumerable<Event<T>> ReadAllAsync(CancellationToken token = default);
|
IAsyncEnumerable<Event<T>> ReadAllAsync(CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Grpc.Core;
|
||||||
|
|
||||||
|
namespace INetMock.Client.Audit.Serialization
|
||||||
|
{
|
||||||
|
public sealed class EventServerStreamReader : IProtoEventReader
|
||||||
|
{
|
||||||
|
private readonly AsyncServerStreamingCall<EventEntity> _asyncEventStream;
|
||||||
|
|
||||||
|
public EventServerStreamReader(AsyncServerStreamingCall<EventEntity> asyncEventStream)
|
||||||
|
{
|
||||||
|
_asyncEventStream = asyncEventStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<EventEntity?> ReadAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (!await _asyncEventStream.ResponseStream.MoveNext(token)) return null;
|
||||||
|
return _asyncEventStream.ResponseStream.Current;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => _asyncEventStream.Dispose();
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
_asyncEventStream.Dispose();
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace INetMock.Client.Audit
|
namespace INetMock.Client.Audit.Serialization
|
||||||
{
|
{
|
||||||
public sealed class GenericReader : IEventReader<GenericDetails>
|
public sealed class GenericReader : IEventReader<GenericDetails>
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,8 @@ namespace INetMock.Client.Audit
|
||||||
_reader = reader;
|
_reader = reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async IAsyncEnumerable<Event<GenericDetails>> ReadAllAsync([EnumeratorCancellation] CancellationToken token = default)
|
public async IAsyncEnumerable<Event<GenericDetails>> ReadAllAsync(
|
||||||
|
[EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
|
@ -5,14 +5,14 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
||||||
namespace INetMock.Client.Audit
|
namespace INetMock.Client.Audit.Serialization
|
||||||
{
|
{
|
||||||
public sealed class ProtoReader : IProtoEventReader
|
public sealed class ProtoReader : IProtoEventReader
|
||||||
{
|
{
|
||||||
private readonly MemoryPool<byte> _memoryPool;
|
private readonly MemoryPool<byte> _memoryPool;
|
||||||
private readonly Stream _sourceStream;
|
private readonly Stream _sourceStream;
|
||||||
private readonly bool _keepStreamOpen;
|
private readonly bool _keepStreamOpen;
|
||||||
|
|
||||||
public ProtoReader(Stream sourceStream, bool keepStreamOpen = false)
|
public ProtoReader(Stream sourceStream, bool keepStreamOpen = false)
|
||||||
{
|
{
|
||||||
_memoryPool = MemoryPool<byte>.Shared;
|
_memoryPool = MemoryPool<byte>.Shared;
|
||||||
|
@ -41,10 +41,10 @@ namespace INetMock.Client.Audit
|
||||||
|
|
||||||
var entity = new EventEntity();
|
var entity = new EventEntity();
|
||||||
entity.MergeFrom(msgMem.ToArray());
|
entity.MergeFrom(msgMem.ToArray());
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
if (!_keepStreamOpen)
|
if (!_keepStreamOpen)
|
|
@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace INetMock.Client.Audit
|
namespace INetMock.Client.Audit.Serialization
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configures how the TypedReader proceeds with mismatching entities.
|
/// Configures how the TypedReader proceeds with mismatching entities.
|
|
@ -19,5 +19,6 @@
|
||||||
<Protobuf Include="proto/audit/details/http_details.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
|
<Protobuf Include="proto/audit/details/http_details.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
|
||||||
<Protobuf Include="proto/rpc/audit.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
|
<Protobuf Include="proto/rpc/audit.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
|
||||||
<Protobuf Include="proto/rpc/health.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
|
<Protobuf Include="proto/rpc/health.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
|
||||||
|
<Protobuf Include="proto/rpc/pcap.proto" GrpcServices="Client" AdditionalImportDirs="./proto/" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using INetMock.Client.Audit;
|
using INetMock.Client.Audit;
|
||||||
|
using INetMock.Client.Audit.Serialization;
|
||||||
using INetMock.Client.Test.Hex;
|
using INetMock.Client.Test.Hex;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace INetMock.Client.Test.Audit
|
namespace INetMock.Client.Test.Audit.Serialization
|
||||||
{
|
{
|
||||||
public class GenericReaderTest
|
public class GenericReaderTest
|
||||||
{
|
{
|
||||||
|
@ -23,7 +24,7 @@ namespace INetMock.Client.Test.Audit
|
||||||
public async void TestRead_HttpEvent_Success()
|
public async void TestRead_HttpEvent_Success()
|
||||||
{
|
{
|
||||||
await using var protoReader = new ProtoReader(new MemoryStream(_httpEventPayloadBytes));
|
await using var protoReader = new ProtoReader(new MemoryStream(_httpEventPayloadBytes));
|
||||||
await using var reader = new GenericReader(protoReader);
|
await using IEventReader<GenericDetails> reader = new GenericReader(protoReader);
|
||||||
await foreach (var ev in reader.ReadAllAsync())
|
await foreach (var ev in reader.ReadAllAsync())
|
||||||
{
|
{
|
||||||
Assert.NotNull(ev);
|
Assert.NotNull(ev);
|
||||||
|
@ -44,7 +45,7 @@ namespace INetMock.Client.Test.Audit
|
||||||
{
|
{
|
||||||
await using var memStream = new MemoryStream(_dnsEventPayloadBytes);
|
await using var memStream = new MemoryStream(_dnsEventPayloadBytes);
|
||||||
await using var protoReader = new ProtoReader(new MemoryStream(_dnsEventPayloadBytes));
|
await using var protoReader = new ProtoReader(new MemoryStream(_dnsEventPayloadBytes));
|
||||||
await using var reader = new GenericReader(protoReader);
|
await using IEventReader<GenericDetails> reader = new GenericReader(protoReader);
|
||||||
await foreach (var ev in reader.ReadAllAsync())
|
await foreach (var ev in reader.ReadAllAsync())
|
||||||
{
|
{
|
||||||
Assert.NotNull(ev);
|
Assert.NotNull(ev);
|
|
@ -1,9 +1,10 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using INetMock.Client.Audit;
|
using INetMock.Client.Audit;
|
||||||
|
using INetMock.Client.Audit.Serialization;
|
||||||
using INetMock.Client.Test.Hex;
|
using INetMock.Client.Test.Hex;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace INetMock.Client.Test.Audit
|
namespace INetMock.Client.Test.Audit.Serialization
|
||||||
{
|
{
|
||||||
public class TypedReaderTest
|
public class TypedReaderTest
|
||||||
{
|
{
|
||||||
|
@ -23,7 +24,7 @@ namespace INetMock.Client.Test.Audit
|
||||||
public async void TestRead_HttpEvent_Success()
|
public async void TestRead_HttpEvent_Success()
|
||||||
{
|
{
|
||||||
await using var protoReader = new ProtoReader(new MemoryStream(_httpEventPayloadBytes));
|
await using var protoReader = new ProtoReader(new MemoryStream(_httpEventPayloadBytes));
|
||||||
await using var reader = new TypedReader<HttpDetails>(protoReader, DropMode.DropEntity);
|
await using IEventReader<HttpDetails> reader = new TypedReader<HttpDetails>(protoReader, DropMode.DropEntity);
|
||||||
await foreach (var ev in reader.ReadAllAsync())
|
await foreach (var ev in reader.ReadAllAsync())
|
||||||
{
|
{
|
||||||
Assert.NotNull(ev);
|
Assert.NotNull(ev);
|
||||||
|
@ -34,7 +35,7 @@ namespace INetMock.Client.Test.Audit
|
||||||
public async void TestRead_DnsEvent_Success()
|
public async void TestRead_DnsEvent_Success()
|
||||||
{
|
{
|
||||||
await using var protoReader = new ProtoReader(new MemoryStream(_dnsEventPayloadBytes));
|
await using var protoReader = new ProtoReader(new MemoryStream(_dnsEventPayloadBytes));
|
||||||
await using var reader = new TypedReader<DnsDetails>(protoReader, DropMode.DropEntity);
|
await using IEventReader<DnsDetails> reader = new TypedReader<DnsDetails>(protoReader, DropMode.DropEntity);
|
||||||
await foreach (var ev in reader.ReadAllAsync())
|
await foreach (var ev in reader.ReadAllAsync())
|
||||||
{
|
{
|
||||||
Assert.NotNull(ev);
|
Assert.NotNull(ev);
|
Loading…
Reference in a new issue