109 lines
3.5 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|