zcbot/windows-node/Zcbot.WindowsNode.Tests/NodeSessionTests.cs

109 lines
3.5 KiB
C#

using System.Net.WebSockets;
namespace Zcbot.WindowsNode.Tests;
public sealed class NodeSessionTests
{
[Fact]
public async Task SessionReconnectsAfterNetworkFailureAndSendsHelloBeforeRecovery()
{
var config = new NodeConfig(
new Uri("https://node.example/"),
Guid.NewGuid(),
Guid.NewGuid(),
"test-node",
"secret",
30,
[]);
var socket = new ClosingWebSocket();
var attempts = 0;
var events = new List<string>();
using var stop = new CancellationTokenSource();
var session = new NodeSession(
config,
() => new { available_slots = 0 },
(client, cancellationToken) =>
{
events.Add("recovery");
stop.Cancel();
return Task.CompletedTask;
},
(_, _) => Task.CompletedTask,
(_, _, _) => Task.CompletedTask,
(state, _) => events.Add(state.ToString()),
(_, _) =>
{
attempts++;
return attempts == 1
? Task.FromException<NodeProtocolClient>(new WebSocketException("offline"))
: Task.FromResult(new NodeProtocolClient(socket));
},
(_, _) => Task.CompletedTask);
await session.RunAsync(stop.Token);
Assert.Equal(2, attempts);
Assert.Equal(["Connecting", "Offline", "Connecting", "Online", "recovery"], events);
Assert.Single(socket.SentTypes);
Assert.Equal("hello", socket.SentTypes[0]);
}
private sealed class ClosingWebSocket : WebSocket
{
private WebSocketState state = WebSocketState.Open;
internal List<string> SentTypes { get; } = [];
public override WebSocketCloseStatus? CloseStatus => WebSocketCloseStatus.NormalClosure;
public override string? CloseStatusDescription => "done";
public override WebSocketState State => state;
public override string? SubProtocol => null;
public override void Abort() => state = WebSocketState.Aborted;
public override Task CloseAsync(
WebSocketCloseStatus closeStatus,
string? statusDescription,
CancellationToken cancellationToken)
{
state = WebSocketState.Closed;
return Task.CompletedTask;
}
public override Task CloseOutputAsync(
WebSocketCloseStatus closeStatus,
string? statusDescription,
CancellationToken cancellationToken)
{
state = WebSocketState.CloseSent;
return Task.CompletedTask;
}
public override void Dispose() => state = WebSocketState.Closed;
public override Task<WebSocketReceiveResult> ReceiveAsync(
ArraySegment<byte> buffer,
CancellationToken cancellationToken) => Task.FromResult(
new WebSocketReceiveResult(
0,
WebSocketMessageType.Close,
true,
WebSocketCloseStatus.NormalClosure,
"done"));
public override Task SendAsync(
ArraySegment<byte> buffer,
WebSocketMessageType messageType,
bool endOfMessage,
CancellationToken cancellationToken)
{
var message = NodeProtocolCodec.Decode(buffer.ToArray());
SentTypes.Add(message!.Type);
return Task.CompletedTask;
}
}
}