mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-21 20:17:22 +01:00
Use IReady event for gateway tests
- Fix manual connect invocations
This commit is contained in:
@@ -29,5 +29,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Result> RespondAsync(IMessageCreate gatewayEvent, CancellationToken ct) => targetResponder.RespondAsync(gatewayEvent, ct);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Result> RespondAsync(IReady gatewayEvent, CancellationToken ct = default) => targetResponder.RespondAsync(gatewayEvent, ct);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -18,7 +17,6 @@ using Remora.Discord.Gateway.Extensions;
|
||||
using Remora.Results;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Host.System;
|
||||
@@ -50,11 +48,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// </summary>
|
||||
readonly IAssemblyInformationProvider assemblyInformationProvider;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IAsyncDelayer"/> for the <see cref="DiscordProvider"/>.
|
||||
/// </summary>
|
||||
readonly IAsyncDelayer asyncDelayer;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ServiceProvider"/> containing Discord services.
|
||||
/// </summary>
|
||||
@@ -80,6 +73,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// </summary>
|
||||
CancellationTokenSource gatewayCts;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="TaskCompletionSource{TResult}"/> for the initial gateway connection event.
|
||||
/// </summary>
|
||||
TaskCompletionSource<object> gatewayReadyTcs;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Task"/> representing the lifetime of the client.
|
||||
/// </summary>
|
||||
@@ -149,19 +147,16 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// </summary>
|
||||
/// <param name="jobManager">The <see cref="IJobManager"/> for the <see cref="Provider"/>.</param>
|
||||
/// <param name="assemblyInformationProvider">The value of <see cref="assemblyInformationProvider"/>.</param>
|
||||
/// <param name="asyncDelayer">The value of <see cref="asyncDelayer"/>.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> for the <see cref="Provider"/>.</param>
|
||||
/// <param name="chatBot">The <see cref="ChatBot"/> for the <see cref="Provider"/>.</param>
|
||||
public DiscordProvider(
|
||||
IJobManager jobManager,
|
||||
IAssemblyInformationProvider assemblyInformationProvider,
|
||||
IAsyncDelayer asyncDelayer,
|
||||
ILogger<DiscordProvider> logger,
|
||||
ChatBot chatBot)
|
||||
: base(jobManager, logger, chatBot)
|
||||
{
|
||||
this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
|
||||
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
|
||||
|
||||
mappedChannels = new List<ulong>();
|
||||
|
||||
@@ -579,6 +574,13 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
return Result.FromSuccess();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Result> RespondAsync(IReady readyEvent, CancellationToken cancellationToken)
|
||||
{
|
||||
gatewayReadyTcs?.TrySetResult(null);
|
||||
return Task.FromResult(Result.FromSuccess());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task Connect(CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -592,20 +594,15 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
var gatewayCancellationToken = gatewayCts.Token;
|
||||
var gatewayClient = serviceProvider.GetRequiredService<DiscordGatewayClient>();
|
||||
|
||||
// reconnects keep happening until we stop or it faults, our auto-reconnector will handle the latter
|
||||
var gatewayTaskLocal = gatewayClient.RunAsync(gatewayCancellationToken);
|
||||
|
||||
// HACK: The gateway connection status isn't public, how 'bout we read it anyway?
|
||||
GatewayConnectionStatus connectionStatus;
|
||||
var connectionStatusField = gatewayClient.GetType().GetField("_connectionStatus", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
do
|
||||
gatewayReadyTcs = new TaskCompletionSource<object>();
|
||||
using (cancellationToken.Register(() => gatewayReadyTcs.TrySetCanceled()))
|
||||
{
|
||||
await asyncDelayer.Delay(TimeSpan.FromMilliseconds(250), cancellationToken).ConfigureAwait(false);
|
||||
connectionStatus = (GatewayConnectionStatus)connectionStatusField.GetValue(gatewayClient);
|
||||
}
|
||||
while (!gatewayTaskLocal.IsCompleted && connectionStatus != GatewayConnectionStatus.Connected);
|
||||
// reconnects keep happening until we stop or it faults, our auto-reconnector will handle the latter
|
||||
gatewayTask = gatewayClient.RunAsync(gatewayCancellationToken);
|
||||
|
||||
await Task.WhenAny(gatewayReadyTcs.Task, gatewayTask).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
gatewayTask = gatewayTaskLocal;
|
||||
if (gatewayTask.IsCompleted)
|
||||
{
|
||||
await DisconnectImpl(cancellationToken).ConfigureAwait(false);
|
||||
@@ -630,7 +627,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
if (gatewayTask != null)
|
||||
await DisconnectImpl(default).ConfigureAwait(false); // DCT: Musn't abort
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// <summary>
|
||||
/// Combined interface for the <see cref="IResponder"/> types used by TGS.
|
||||
/// </summary>
|
||||
interface IDiscordResponders : IResponder<IMessageCreate>
|
||||
interface IDiscordResponders : IResponder<IMessageCreate>, IResponder<IReady>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
ChatProvider.Discord => new DiscordProvider(
|
||||
jobManager,
|
||||
assemblyInformationProvider,
|
||||
asyncDelayer,
|
||||
loggerFactory.CreateLogger<DiscordProvider>(),
|
||||
settings),
|
||||
_ => throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid ChatProvider: {0}", settings.Provider)),
|
||||
|
||||
@@ -7,7 +7,6 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Host.System;
|
||||
@@ -19,7 +18,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests
|
||||
{
|
||||
ChatBot testToken1;
|
||||
IJobManager mockJobManager;
|
||||
IAsyncDelayer mockDel;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
@@ -41,10 +39,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests
|
||||
.Setup(x => x.WaitForJobCompletion(It.IsNotNull<Job>(), It.IsAny<User>(), It.IsAny<CancellationToken>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
mockJobManager = mockSetup.Object;
|
||||
|
||||
var mockDelSetup = new Mock<IAsyncDelayer>();
|
||||
mockDelSetup.Setup(x => x.Delay(It.IsAny<TimeSpan>(), It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);
|
||||
mockDel = mockDelSetup.Object;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -53,14 +47,13 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests
|
||||
if (testToken1 == null)
|
||||
Assert.Inconclusive("Required environment variable TGS4_TEST_DISCORD_TOKEN isn't set!");
|
||||
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new DiscordProvider(null, null, null, null, null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new DiscordProvider(mockJobManager, null, null, null, null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new DiscordProvider(null, null, null, null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new DiscordProvider(mockJobManager, null, null, null));
|
||||
var mockAss = new Mock<IAssemblyInformationProvider>();
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new DiscordProvider(mockJobManager, mockAss.Object, null, null, null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new DiscordProvider(mockJobManager, mockAss.Object, mockDel, null, null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new DiscordProvider(mockJobManager, mockAss.Object, null, null));
|
||||
var mockLogger = new Mock<ILogger<DiscordProvider>>();
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new DiscordProvider(mockJobManager, null, null, mockLogger.Object, null));
|
||||
await new DiscordProvider(mockJobManager, mockAss.Object, mockDel, mockLogger.Object, testToken1).DisposeAsync();
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new DiscordProvider(mockJobManager, null, mockLogger.Object, null));
|
||||
await new DiscordProvider(mockJobManager, mockAss.Object, mockLogger.Object, testToken1).DisposeAsync();
|
||||
}
|
||||
|
||||
static Task InvokeConnect(IProvider provider, CancellationToken cancellationToken = default) => (Task)provider.GetType().GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(provider, new object[] { cancellationToken });
|
||||
@@ -69,7 +62,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests
|
||||
public async Task TestConnectWithFakeTokenFails()
|
||||
{
|
||||
var mockLogger = new Mock<ILogger<DiscordProvider>>();
|
||||
await using var provider = new DiscordProvider(mockJobManager, Mock.Of<IAssemblyInformationProvider>(), mockDel, mockLogger.Object, new ChatBot
|
||||
await using var provider = new DiscordProvider(mockJobManager, Mock.Of<IAssemblyInformationProvider>(), mockLogger.Object, new ChatBot
|
||||
{
|
||||
ReconnectionInterval = 1,
|
||||
ConnectionString = "asdf"
|
||||
@@ -85,17 +78,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests
|
||||
Assert.Inconclusive("Required environment variable TGS4_TEST_DISCORD_TOKEN isn't set!");
|
||||
|
||||
var mockLogger = new Mock<ILogger<DiscordProvider>>();
|
||||
await using var provider = new DiscordProvider(mockJobManager, Mock.Of<IAssemblyInformationProvider>(), mockDel, mockLogger.Object, testToken1);
|
||||
await using var provider = new DiscordProvider(mockJobManager, Mock.Of<IAssemblyInformationProvider>(), mockLogger.Object, testToken1);
|
||||
Assert.IsFalse(provider.Connected);
|
||||
await provider.Disconnect(default).ConfigureAwait(false);
|
||||
Assert.IsFalse(provider.Connected);
|
||||
await InvokeConnect(provider).ConfigureAwait(false);
|
||||
Assert.IsTrue(provider.Connected);
|
||||
await InvokeConnect(provider).ConfigureAwait(false);
|
||||
Assert.IsTrue(provider.Connected);
|
||||
|
||||
await provider.Disconnect(default).ConfigureAwait(false);
|
||||
Assert.IsFalse(provider.Connected);
|
||||
await provider.Disconnect(default).ConfigureAwait(false);
|
||||
Assert.IsFalse(provider.Connected);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user