diff --git a/Services/Donut/DonutFlipSubscriptionService.Tests.cs b/Services/Donut/DonutFlipSubscriptionService.Tests.cs new file mode 100644 index 00000000..a8372b7f --- /dev/null +++ b/Services/Donut/DonutFlipSubscriptionService.Tests.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Coflnet.Payments.Client.Api; +using Coflnet.Sky.Commands; +using Coflnet.Sky.Core; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging.Abstractions; +using Moq; +using NUnit.Framework; + +namespace Coflnet.Sky.ModCommands.Services.Donut; + +public class DonutFlipSubscriptionServiceTests +{ + [Test] + public async Task ClosingOldConnectionKeepsReplacementSubscription() + { + var userApi = new Mock(); + userApi.Setup(u => u.UserUserIdOwnsProductSlugUntilGetAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(DateTime.UtcNow.AddDays(1)); + var service = new DonutFlipSubscriptionService(userApi.Object, new ConfigurationBuilder().Build(), NullLogger.Instance); + var oldConnection = CreateConnection(); + var replacement = CreateConnection(); + + await service.RefreshSubscriptionAsync(oldConnection.Object); + await service.RefreshSubscriptionAsync(replacement.Object); + // the old socket closes after the replacement with the same id registered + service.RemoveConnection(oldConnection.Object); + await service.DeliverAsync(new LowPricedAuction + { + Auction = new SaveAuction(), + AdditionalProps = new Dictionary { { "server", DonutServerContext.Name } } + }); + + replacement.Verify(c => c.SendFlip(It.IsAny()), Times.Once); + } + + private static Mock CreateConnection() + { + var connection = new Mock(); + connection.SetupGet(c => c.Id).Returns(42); + connection.SetupGet(c => c.UserId).Returns("1"); + connection.SetupGet(c => c.GameServer).Returns(DonutServerContext.Name); + connection.Setup(c => c.SendFlip(It.IsAny())).ReturnsAsync(true); + return connection; + } +} diff --git a/Services/Donut/DonutFlipSubscriptionService.cs b/Services/Donut/DonutFlipSubscriptionService.cs index 62c04ba1..73f3c76c 100644 --- a/Services/Donut/DonutFlipSubscriptionService.cs +++ b/Services/Donut/DonutFlipSubscriptionService.cs @@ -61,7 +61,8 @@ public async Task RefreshSubscriptionAsync(IFlipConnection connection) public void RemoveConnection(IFlipConnection connection) { - subscribers.TryRemove(connection.Id, out _); + // A replacement connection reuses the same id, only remove the subscription this connection owns + subscribers.TryRemove(new(connection.Id, connection)); } public async Task DeliverAsync(LowPricedAuction flip) @@ -74,12 +75,12 @@ public async Task DeliverAsync(LowPricedAuction flip) try { if (!await entry.Value.SendFlip(flip).ConfigureAwait(false)) - subscribers.TryRemove(entry.Key, out _); + subscribers.TryRemove(entry); } catch (Exception e) { logger.LogError(e, "Failed to send Donut flip to connection {ConnectionId}", entry.Key); - subscribers.TryRemove(entry.Key, out _); + subscribers.TryRemove(entry); } } }