mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
145 lines
4.5 KiB
C#
145 lines
4.5 KiB
C#
using MinecraftClient.ChatBots;
|
|
using MinecraftClient.Scripting;
|
|
|
|
namespace MinecraftClient.Tests;
|
|
|
|
public sealed class AutoRelogRetryPolicyTests
|
|
{
|
|
[Fact]
|
|
public void DefaultConfigurationUsesUnlimitedRetries()
|
|
{
|
|
Assert.Equal(-1, new AutoRelog.Configs().Retries);
|
|
}
|
|
|
|
[Fact]
|
|
public void UnlimitedRetriesNeverExhaust()
|
|
{
|
|
var policy = new AutoRelogRetryPolicy(new ManualTimeProvider());
|
|
|
|
for (int attempt = 1; attempt <= 100; attempt++)
|
|
{
|
|
Assert.True(policy.TryReserveAttempt(-1, out int retriesLeft));
|
|
Assert.Equal(-1, retriesLeft);
|
|
Assert.Equal(attempt, policy.Attempts);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ZeroRetriesDisablesReconnect()
|
|
{
|
|
var policy = new AutoRelogRetryPolicy(new ManualTimeProvider());
|
|
|
|
Assert.False(policy.TryReserveAttempt(0, out int retriesLeft));
|
|
Assert.Equal(0, retriesLeft);
|
|
Assert.Equal(0, policy.Attempts);
|
|
}
|
|
|
|
[Fact]
|
|
public void FiniteRetryLimitIsExact()
|
|
{
|
|
var policy = new AutoRelogRetryPolicy(new ManualTimeProvider());
|
|
|
|
Assert.True(policy.TryReserveAttempt(3, out int firstRetriesLeft));
|
|
Assert.True(policy.TryReserveAttempt(3, out int secondRetriesLeft));
|
|
Assert.True(policy.TryReserveAttempt(3, out int thirdRetriesLeft));
|
|
Assert.False(policy.TryReserveAttempt(3, out int exhaustedRetriesLeft));
|
|
|
|
Assert.Equal(2, firstRetriesLeft);
|
|
Assert.Equal(1, secondRetriesLeft);
|
|
Assert.Equal(0, thirdRetriesLeft);
|
|
Assert.Equal(0, exhaustedRetriesLeft);
|
|
}
|
|
|
|
[Fact]
|
|
public void RejectedRestartDoesNotConsumeRetry()
|
|
{
|
|
var policy = new AutoRelogRetryPolicy(new ManualTimeProvider());
|
|
|
|
Assert.True(policy.TryReserveAttempt(1, out _));
|
|
policy.RollBackReservedAttempt();
|
|
|
|
Assert.True(policy.TryReserveAttempt(1, out int retriesLeft));
|
|
Assert.Equal(0, retriesLeft);
|
|
}
|
|
|
|
[Fact]
|
|
public void StableConnectionResetsRetryBudget()
|
|
{
|
|
var timeProvider = new ManualTimeProvider();
|
|
var policy = new AutoRelogRetryPolicy(timeProvider);
|
|
Assert.True(policy.TryReserveAttempt(1, out _));
|
|
|
|
policy.MarkJoined();
|
|
timeProvider.Advance(AutoRelogRetryPolicy.StableConnectionThreshold - TimeSpan.FromMilliseconds(1));
|
|
Assert.False(policy.ResetAfterStableConnection());
|
|
|
|
timeProvider.Advance(TimeSpan.FromMilliseconds(1));
|
|
Assert.True(policy.ResetAfterStableConnection());
|
|
Assert.Equal(0, policy.Attempts);
|
|
Assert.True(policy.TryReserveAttempt(1, out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void TransportLossAlwaysReconnectsWhenEnabled()
|
|
{
|
|
bool reconnect = AutoRelogRetryPolicy.ShouldReconnect(
|
|
ChatBot.DisconnectReason.ConnectionLost,
|
|
"A transport-specific error without a configured phrase",
|
|
ignoreKickMessage: false,
|
|
["Server is restarting"],
|
|
out string? matchedMessage);
|
|
|
|
Assert.True(reconnect);
|
|
Assert.Null(matchedMessage);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(ChatBot.DisconnectReason.InGameKick)]
|
|
[InlineData(ChatBot.DisconnectReason.LoginRejected)]
|
|
public void ServerMessageMatchingIsCaseInsensitive(ChatBot.DisconnectReason reason)
|
|
{
|
|
bool reconnect = AutoRelogRetryPolicy.ShouldReconnect(
|
|
reason,
|
|
"THE SERVER IS RESTARTING NOW",
|
|
ignoreKickMessage: false,
|
|
["server is restarting"],
|
|
out string? matchedMessage);
|
|
|
|
Assert.True(reconnect);
|
|
Assert.Equal("server is restarting", matchedMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void UserLogoutNeverReconnects()
|
|
{
|
|
Assert.False(AutoRelogRetryPolicy.ShouldReconnect(
|
|
ChatBot.DisconnectReason.UserLogout,
|
|
"Server is restarting",
|
|
ignoreKickMessage: true,
|
|
["Server is restarting"],
|
|
out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void IgnoreKickMessageAllowsNonmatchingServerKick()
|
|
{
|
|
Assert.True(AutoRelogRetryPolicy.ShouldReconnect(
|
|
ChatBot.DisconnectReason.InGameKick,
|
|
"Administrative removal",
|
|
ignoreKickMessage: true,
|
|
[],
|
|
out _));
|
|
}
|
|
|
|
private sealed class ManualTimeProvider : TimeProvider
|
|
{
|
|
private DateTimeOffset utcNow = DateTimeOffset.UnixEpoch;
|
|
|
|
public override DateTimeOffset GetUtcNow() => utcNow;
|
|
|
|
internal void Advance(TimeSpan duration)
|
|
{
|
|
utcNow += duration;
|
|
}
|
|
}
|
|
}
|