diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 498679fba..3442d792f 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -1096,158 +1096,111 @@ namespace IW4MAdmin return logPath.FixDirectoryCharacters(); } - protected override async Task Warn(String Reason, EFClient Target, EFClient Origin) + public override async Task Warn(string reason, EFClient targetClient, EFClient targetOrigin) { // ensure player gets warned if command not performed on them in game - if (Target.ClientNumber < 0) - { - var ingameClient = Manager.GetActiveClients() - .FirstOrDefault(c => c.ClientId == Target.ClientId); - - if (ingameClient != null) - { - await Warn(Reason, ingameClient, Origin); - return; - } - } - - else - { - if (Target.Warnings >= 4) - { - Target.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this)); - return; - } - - string message = $"^1{loc["SERVER_WARNING"]} ^7[^3{Target.Warnings}^7]: ^3{Target.Name}^7, {Reason}"; - Target.CurrentServer.Broadcast(message); - } + targetClient = targetClient.ClientNumber < 0 ? + Manager.GetActiveClients() + .FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient : + targetClient; var newPenalty = new EFPenalty() { Type = EFPenalty.PenaltyType.Warning, Expires = DateTime.UtcNow, - Offender = Target, - Punisher = Origin, - Offense = Reason, - Link = Target.AliasLink + Offender = targetClient, + Punisher = targetOrigin, + Offense = reason, + Link = targetClient.AliasLink }; - await Manager.GetPenaltyService().Create(newPenalty); - } + Logger.WriteDebug($"Creating warn penalty for {targetClient}"); + await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), Manager.GetLogger(0)); - protected override async Task Kick(String Reason, EFClient Target, EFClient Origin) - { - // ensure player gets kicked if command not performed on them in game - if (Target.ClientNumber < 0) + if (targetClient.IsIngame) { - var ingameClient = Manager.GetActiveClients() - .FirstOrDefault(c => c.ClientId == Target.ClientId); - - if (ingameClient != null) + if (targetClient.Warnings >= 4) { - await Kick(Reason, ingameClient, Origin); + targetClient.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this)); return; } - } -#if !DEBUG - else - { - string formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, Target.ClientNumber, $"{loc["SERVER_KICK_TEXT"]} - ^5{Reason}^7"); - await Target.CurrentServer.ExecuteCommandAsync(formattedKick); - } -#endif -#if DEBUG - // await Target.CurrentServer.OnClientDisconnected(Target); - var e = new GameEvent() - { - Type = GameEvent.EventType.PreDisconnect, - Origin = Target, - Owner = this - }; + string message = $"^1{loc["SERVER_WARNING"]} ^7[^3{targetClient.Warnings}^7]: ^3{targetClient.Name}^7, {reason}"; + targetClient.CurrentServer.Broadcast(message); + } + } - Manager.GetEventHandler().AddEvent(e); -#endif + public override async Task Kick(string Reason, EFClient targetClient, EFClient originClient) + { + targetClient = targetClient.ClientNumber < 0 ? + Manager.GetActiveClients() + .FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient : + targetClient; var newPenalty = new EFPenalty() { Type = EFPenalty.PenaltyType.Kick, Expires = DateTime.UtcNow, - Offender = Target, + Offender = targetClient, Offense = Reason, - Punisher = Origin, - Link = Target.AliasLink + Punisher = originClient, + Link = targetClient.AliasLink }; - await Manager.GetPenaltyService().Create(newPenalty); + Logger.WriteDebug($"Creating kick penalty for {targetClient}"); + await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), Manager.GetLogger(0)); + + if (targetClient.IsIngame) + { + var e = new GameEvent() + { + Type = GameEvent.EventType.PreDisconnect, + Origin = targetClient, + Owner = this + }; + + Manager.GetEventHandler().AddEvent(e); + + string formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_KICK_TEXT"]} - ^5{Reason}^7"); + await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick); + } } - protected override async Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin) + public override async Task TempBan(string Reason, TimeSpan length, EFClient targetClient, EFClient originClient) { - // ensure player gets banned if command not performed on them in game - if (Target.ClientNumber < 0) - { - var ingameClient = Manager.GetActiveClients() - .FirstOrDefault(c => c.ClientId == Target.ClientId); - - if (ingameClient != null) - { - await TempBan(Reason, length, ingameClient, Origin); - return; - } - } -#if !DEBUG - else - { - string formattedKick = String.Format(RconParser.Configuration.CommandPrefixes.Kick, Target.ClientNumber, $"^7{loc["SERVER_TB_TEXT"]}- ^5{Reason}"); - await Target.CurrentServer.ExecuteCommandAsync(formattedKick); - } -#else - await Target.CurrentServer.OnClientDisconnected(Target); -#endif + // ensure player gets kicked if command not performed on them in the same server + targetClient = targetClient.ClientNumber < 0 ? + Manager.GetActiveClients() + .FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient : + targetClient; var newPenalty = new EFPenalty() { Type = EFPenalty.PenaltyType.TempBan, Expires = DateTime.UtcNow + length, - Offender = Target, + Offender = targetClient, Offense = Reason, - Punisher = Origin, - Link = Target.AliasLink + Punisher = originClient, + Link = targetClient.AliasLink }; - await Manager.GetPenaltyService().Create(newPenalty); + Logger.WriteDebug($"Creating tempban penalty for {targetClient}"); + await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), Manager.GetLogger(0)); + + if (targetClient.IsIngame) + { + string formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"^7{loc["SERVER_TB_TEXT"]}- ^5{Reason}"); + await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick); + } } - override protected async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false) + override public async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false) { - // ensure player gets banned if command not performed on them in game - if (targetClient.ClientNumber < 0) - { - EFClient ingameClient = null; - - ingameClient = Manager.GetServers() - .Select(s => s.GetClientsAsList()) - .FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) != null) - ?.First(c => c.ClientId == targetClient.ClientId); - - if (ingameClient != null) - { - await Ban(reason, ingameClient, originClient, isEvade); - return; - } - } - - else - { -#if !DEBUG - string formattedString = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{reason} ^7{loc["SERVER_BAN_APPEAL"].FormatExt(Website)}^7"); - await targetClient.CurrentServer.ExecuteCommandAsync(formattedString); -#else - await targetClient.CurrentServer.OnClientDisconnected(targetClient); -#endif - } + // ensure player gets kicked if command not performed on them in the same server + targetClient = targetClient.ClientNumber < 0 ? + Manager.GetActiveClients() + .FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient : + targetClient; EFPenalty newPenalty = new EFPenalty() { @@ -1260,8 +1213,16 @@ namespace IW4MAdmin IsEvadedOffense = isEvade }; + Logger.WriteDebug($"Creating ban penalty for {targetClient}"); targetClient.SetLevel(Permission.Banned, originClient); - await Manager.GetPenaltyService().Create(newPenalty); + await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), Manager.GetLogger(0)); + + if (targetClient.IsIngame) + { + Logger.WriteDebug($"Attempting to kicking newly banned client {targetClient}"); + string formattedString = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{reason} ^7{loc["SERVER_BAN_APPEAL"].FormatExt(Website)}^7"); + await targetClient.CurrentServer.ExecuteCommandAsync(formattedString); + } } override public async Task Unban(string reason, EFClient Target, EFClient Origin) diff --git a/Application/RconParsers/DynamicRConParserConfiguration.cs b/Application/RconParsers/DynamicRConParserConfiguration.cs index 12309f458..79df12504 100644 --- a/Application/RconParsers/DynamicRConParserConfiguration.cs +++ b/Application/RconParsers/DynamicRConParserConfiguration.cs @@ -9,7 +9,7 @@ namespace IW4MAdmin.Application.RconParsers /// generic implementation of the IRConParserConfiguration /// allows script plugins to generate dynamic RCon configurations /// - sealed internal class DynamicRConParserConfiguration : IRConParserConfiguration + public class DynamicRConParserConfiguration : IRConParserConfiguration { public CommandPrefix CommandPrefixes { get; set; } public ParserRegex Status { get; set; } diff --git a/Plugins/Stats/Models/EFClientStatistics.cs b/Plugins/Stats/Models/EFClientStatistics.cs index c406956c7..635aa1710 100644 --- a/Plugins/Stats/Models/EFClientStatistics.cs +++ b/Plugins/Stats/Models/EFClientStatistics.cs @@ -82,7 +82,10 @@ namespace IW4MAdmin.Plugins.Stats.Models KillStreak = 0; DeathStreak = 0; LastScore = 0; - SessionScores.Add(0); + lock (SessionScores) + { + SessionScores.Add(0); + } Team = IW4Info.Team.None; } [NotMapped] diff --git a/SharedLibraryCore/PartialEntities/EFClient.cs b/SharedLibraryCore/PartialEntities/EFClient.cs index b25a94eaa..7dca84d57 100644 --- a/SharedLibraryCore/PartialEntities/EFClient.cs +++ b/SharedLibraryCore/PartialEntities/EFClient.cs @@ -124,6 +124,9 @@ namespace SharedLibraryCore.Database.Models [NotMapped] public string IPAddressString => IPAddress.ConvertIPtoString(); + [NotMapped] + public bool IsIngame => ClientNumber >= 0; + [NotMapped] public virtual IDictionary LinkedAccounts { get; set; } diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 2ca24a086..ee176b7ca 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -121,8 +121,7 @@ namespace SharedLibraryCore /// Message to be sent to all players public GameEvent Broadcast(string message, EFClient sender = null) { - string formattedMessage = String.Format(RconParser.Configuration.CommandPrefixes.Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}"); - + string formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Say, $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}"); #if DEBUG == true Logger.WriteVerbose(message.StripColors()); #endif @@ -196,14 +195,14 @@ namespace SharedLibraryCore /// /// Reason for kicking /// EFClient to kick - abstract protected Task Kick(String Reason, EFClient Target, EFClient Origin); + abstract public Task Kick(String Reason, EFClient Target, EFClient Origin); /// /// Temporarily ban a player ( default 1 hour ) from the server /// /// Reason for banning the player /// The player to ban - abstract protected Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin); + abstract public Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin); /// /// Perm ban a player from the server @@ -211,9 +210,9 @@ namespace SharedLibraryCore /// The reason for the ban /// The person to ban /// The person who banned the target - abstract protected Task Ban(String Reason, EFClient Target, EFClient Origin, bool isEvade = false); + abstract public Task Ban(String Reason, EFClient Target, EFClient Origin, bool isEvade = false); - abstract protected Task Warn(String Reason, EFClient Target, EFClient Origin); + abstract public Task Warn(String Reason, EFClient Target, EFClient Origin); /// /// Unban a player by npID / GUID @@ -307,7 +306,7 @@ namespace SharedLibraryCore public bool CustomCallback { get; protected set; } public string WorkingDirectory { get; protected set; } public IRConConnection RemoteConnection { get; protected set; } - public IRConParser RconParser { get; protected set; } + public IRConParser RconParser { get; set; } public IEventParser EventParser { get; set; } public string LogPath { get; protected set; } public bool RestartRequested { get; set; } diff --git a/SharedLibraryCore/Services/PenaltyService.cs b/SharedLibraryCore/Services/PenaltyService.cs index 61105180a..0939d55cd 100644 --- a/SharedLibraryCore/Services/PenaltyService.cs +++ b/SharedLibraryCore/Services/PenaltyService.cs @@ -12,7 +12,7 @@ namespace SharedLibraryCore.Services { public class PenaltyService : Interfaces.IEntityService { - public async Task Create(EFPenalty newEntity) + public virtual async Task Create(EFPenalty newEntity) { using (var context = new DatabaseContext()) { @@ -181,7 +181,7 @@ namespace SharedLibraryCore.Services } } - public async Task RemoveActivePenalties(int aliasLinkId) + public virtual async Task RemoveActivePenalties(int aliasLinkId) { using (var context = new DatabaseContext()) { diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 69e7bd96b..ad6a342b4 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore.Storage; #endif using SharedLibraryCore.Database.Models; using SharedLibraryCore.Helpers; +using SharedLibraryCore.Interfaces; using System; using System.Collections.Generic; using System.Diagnostics; @@ -875,6 +876,30 @@ namespace SharedLibraryCore return new[] { deltaX, deltaY }; } + /// + /// attempts to create and persist a penalty + /// + /// + /// + /// + /// true of the creat succeeds, false otherwise + public static async Task TryCreatePenalty(this EFPenalty penalty, IEntityService penaltyService, ILogger logger) + { + try + { + await penaltyService.Create(penalty); + return true; + } + + catch (Exception e) + { + logger.WriteWarning($"Could not create penalty of type {penalty.Type.ToString()}"); + logger.WriteDebug(e.GetExceptionInfo()); + } + + return false; + } + public static bool ShouldHideLevel(this Permission perm) => perm == Permission.Flagged; /// diff --git a/Tests/ApplicationTests/Fixtures/ClientGenerators.cs b/Tests/ApplicationTests/Fixtures/ClientGenerators.cs new file mode 100644 index 000000000..29c0c41fe --- /dev/null +++ b/Tests/ApplicationTests/Fixtures/ClientGenerators.cs @@ -0,0 +1,24 @@ +using SharedLibraryCore; +using SharedLibraryCore.Database.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ApplicationTests.Fixtures +{ + public class ClientGenerators + { + public static EFClient CreateBasicClient(Server currentServer, bool isIngame = true) => new EFClient() + { + ClientId = 1, + CurrentAlias = new EFAlias() + { + Name = "BasicClient", + IPAddress = "127.0.0.1".ConvertToIP(), + }, + Level = EFClient.Permission.User, + ClientNumber = isIngame ? 0 : -1, + CurrentServer = currentServer + }; + } +} diff --git a/Tests/ApplicationTests/Fixtures/ConfigurationGenerators.cs b/Tests/ApplicationTests/Fixtures/ConfigurationGenerators.cs new file mode 100644 index 000000000..7e3b70a70 --- /dev/null +++ b/Tests/ApplicationTests/Fixtures/ConfigurationGenerators.cs @@ -0,0 +1,22 @@ +using IW4MAdmin.Application.RconParsers; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ApplicationTests.Fixtures +{ + public class ConfigurationGenerators + { + public static ServerConfiguration CreateServerConfiguration() => new ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 }; + public static IRConParserConfiguration CreateRConParserConfiguration(IParserRegexFactory factory) => new DynamicRConParserConfiguration(factory) + { + CommandPrefixes = new SharedLibraryCore.RCon.CommandPrefix() + { + Kick = "kick", + Say = "say" + } + }; + } +} diff --git a/Tests/ApplicationTests/IW4MServerTests.cs b/Tests/ApplicationTests/IW4MServerTests.cs index e6607a31f..9688a1b4b 100644 --- a/Tests/ApplicationTests/IW4MServerTests.cs +++ b/Tests/ApplicationTests/IW4MServerTests.cs @@ -1,12 +1,55 @@ using IW4MAdmin; using IW4MAdmin.Application.Misc; +using SharedLibraryCore.Interfaces; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using FakeItEasy; +using System; +using ApplicationTests.Fixtures; +using SharedLibraryCore.Services; +using SharedLibraryCore.Database.Models; +using System.Threading.Tasks; +using ApplicationTests.Mocks; +using System.Linq; namespace ApplicationTests { [TestFixture] public class IW4MServerTests { + private IServiceProvider serviceProvider; + private ILogger fakeLogger; + private IManager fakeManager; + private IRConConnection fakeRConConnection; + private IRConParser fakeRConParser; + private MockEventHandler mockEventHandler; + + [SetUp] + public void Setup() + { + fakeLogger = A.Fake(); + fakeManager = A.Fake(); + fakeRConConnection = A.Fake(); + var rconConnectionFactory = A.Fake(); + A.CallTo(() => rconConnectionFactory.CreateConnection(A.Ignored, A.Ignored, A.Ignored)) + .Returns(fakeRConConnection); + var fakeTranslationLookup = A.Fake(); + fakeRConParser = A.Fake(); + A.CallTo(() => fakeRConParser.Configuration) + .Returns(ConfigurationGenerators.CreateRConParserConfiguration(A.Fake())); + + mockEventHandler = new MockEventHandler(); + A.CallTo(() => fakeManager.GetEventHandler()) + .Returns(mockEventHandler); + + serviceProvider = new ServiceCollection() + .AddSingleton(new IW4MServer(fakeManager, ConfigurationGenerators.CreateServerConfiguration(), fakeTranslationLookup, rconConnectionFactory) + { + RconParser = fakeRConParser + }) + .BuildServiceProvider(); + } + [Test] public void Test_GenerateLogPath_Basic() { @@ -133,5 +176,337 @@ namespace ApplicationTests Assert.AreEqual(expected, generated); } + + #region BAN + [Test] + public async Task Test_BanCreatesPenalty() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + + await server.Ban("test reason", target, origin); + + A.CallTo(() => fakePenaltyService.Create(A.Ignored)) + .MustHaveHappenedOnceExactly(); + } + + [Test] + public async Task Test_BanExecutesKickCommand() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + + await server.Ban("test reason", target, origin); + + A.CallTo(() => fakeRConParser.ExecuteCommandAsync(fakeRConConnection, "kick")) + .MustHaveHappenedOnceExactly(); + } + + [Test] + public async Task Test_BanQueuesSetLevelEvent() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + + await server.Ban("test reason", target, origin); + + Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Type == SharedLibraryCore.GameEvent.EventType.ChangePermission && + _event.Origin == origin && + _event.Target == target && + (EFClient.Permission)_event.Extra == EFClient.Permission.Banned)); + } + + [Test] + public async Task Test_BanFindsIngameClientToExecuteFor() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var origin = ClientGenerators.CreateBasicClient(server); + var target = ClientGenerators.CreateBasicClient(server, isIngame: false); + var ingameTarget = ClientGenerators.CreateBasicClient(server); + + A.CallTo(() => fakeManager.GetActiveClients()) + .Returns(new[] { ingameTarget }); + + await server.Ban("test reason", target, origin); + + Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Target == ingameTarget)); + } + #endregion + + #region TEMPBAN + [Test] + public async Task Test_TempBanCreatesPenalty() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + + await server.TempBan("test reason", TimeSpan.Zero, target, origin); + + A.CallTo(() => fakePenaltyService.Create(A.Ignored)) + .MustHaveHappenedOnceExactly(); + } + + [Test] + public async Task Test_TempBanExecutesKickCommand() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + + await server.TempBan("test reason", TimeSpan.Zero, target, origin); + + A.CallTo(() => fakeRConParser.ExecuteCommandAsync(fakeRConConnection, "kick")) + .MustHaveHappenedOnceExactly(); + } + + [Test] + public async Task Test_TempBanFindsIngameClientToExecuteFor() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var origin = ClientGenerators.CreateBasicClient(server); + var target = ClientGenerators.CreateBasicClient(server, isIngame: false); + + var ingameTarget = ClientGenerators.CreateBasicClient(server); + + A.CallTo(() => fakeManager.GetActiveClients()) + .Returns(new[] { ingameTarget }); + + await server.TempBan("test reason", TimeSpan.Zero, target, origin); + + A.CallTo(() => fakeRConParser.ExecuteCommandAsync(fakeRConConnection, "kick")) + .MustHaveHappenedOnceExactly(); + } + #endregion + + #region KICK + [Test] + public async Task Test_KickCreatesPenalty() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + + await server.Kick("test reason", target, origin); + + A.CallTo(() => fakePenaltyService.Create(A.Ignored)) + .MustHaveHappenedOnceExactly(); + } + + [Test] + public async Task Test_KickExecutesKickCommand() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + + await server.Kick("test reason", target, origin); + + A.CallTo(() => fakeRConParser.ExecuteCommandAsync(fakeRConConnection, "kick")) + .MustHaveHappenedOnceExactly(); + } + + [Test] + public async Task Test_KickQueuesPredisconnectEvent() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + + await server.Kick("test reason", target, origin); + + Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Type == SharedLibraryCore.GameEvent.EventType.PreDisconnect && _event.Origin == target)); + } + + [Test] + public async Task Test_KickFindsIngameClientToExecuteFor() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var origin = ClientGenerators.CreateBasicClient(server); + var target = ClientGenerators.CreateBasicClient(server, isIngame: false); + + var ingameTarget = ClientGenerators.CreateBasicClient(server); + + A.CallTo(() => fakeManager.GetActiveClients()) + .Returns(new[] { ingameTarget }); + + await server.Kick("test reason", target, origin); + + // kick creates a pre disconnect event + Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Origin == ingameTarget)); + } + #endregion + + #region WARN + [Test] + public async Task Test_WarnCreatesPenalty() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + + await server.Warn("test reason", target, origin); + + A.CallTo(() => fakePenaltyService.Create(A.Ignored)) + .MustHaveHappenedOnceExactly(); + } + + [Test] + public async Task Test_WarnBroadCastMessageForIngameClient() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + + await server.Warn("test reason", target, origin); + + Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Type == SharedLibraryCore.GameEvent.EventType.Broadcast)); + } + + [Test] + public async Task Test_WarnLimitReachedQueuesKickEvent() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + + var server = serviceProvider.GetRequiredService(); + var target = ClientGenerators.CreateBasicClient(server); + var origin = ClientGenerators.CreateBasicClient(server); + target.Warnings = 5; + + await server.Warn("test reason", target, origin); + + Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Type == SharedLibraryCore.GameEvent.EventType.Kick && _event.Target == target)); + } + #endregion + + #region UNBAN + [Test] + public async Task Test_UnbanQueuesSetLevelEvent() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + A.CallTo(() => fakePenaltyService.RemoveActivePenalties(A.Ignored)) + .Returns(Task.CompletedTask); + + var server = serviceProvider.GetRequiredService(); + var origin = ClientGenerators.CreateBasicClient(server); + var target = ClientGenerators.CreateBasicClient(server); + + target.Level = EFClient.Permission.Banned; + target.AliasLink = new EFAliasLink(); + + await server.Unban("test reason", target, origin); + + Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Type == SharedLibraryCore.GameEvent.EventType.ChangePermission && _event.Target == target)); + } + + [Test] + public async Task Test_UnbanRemovedActivePenalties() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + A.CallTo(() => fakePenaltyService.RemoveActivePenalties(A.Ignored)) + .Returns(Task.CompletedTask); + + var server = serviceProvider.GetRequiredService(); + var origin = ClientGenerators.CreateBasicClient(server); + var target = ClientGenerators.CreateBasicClient(server); + + target.Level = EFClient.Permission.Banned; + target.AliasLink = new EFAliasLink() + { + AliasLinkId = 1 + }; + + await server.Unban("test reason", target, origin); + + A.CallTo(() => fakePenaltyService.RemoveActivePenalties(target.AliasLink.AliasLinkId)) + .MustHaveHappened(); + } + + [Test] + public async Task Test_UnbanCreatesPenalty() + { + var fakePenaltyService = A.Fake(); + A.CallTo(() => fakeManager.GetPenaltyService()) + .Returns(fakePenaltyService); + A.CallTo(() => fakePenaltyService.RemoveActivePenalties(A.Ignored)) + .Returns(Task.CompletedTask); + + var server = serviceProvider.GetRequiredService(); + var origin = ClientGenerators.CreateBasicClient(server); + var target = ClientGenerators.CreateBasicClient(server); + + target.Level = EFClient.Permission.Banned; + target.AliasLink = new EFAliasLink() + { + AliasLinkId = 1 + }; + + await server.Unban("test reason", target, origin); + + A.CallTo(() => fakePenaltyService.Create(A.Ignored)) + .MustHaveHappened(); + } + #endregion } } diff --git a/Tests/ApplicationTests/Mocks/EventHandler.cs b/Tests/ApplicationTests/Mocks/EventHandler.cs new file mode 100644 index 000000000..fda78c697 --- /dev/null +++ b/Tests/ApplicationTests/Mocks/EventHandler.cs @@ -0,0 +1,16 @@ +using SharedLibraryCore; +using SharedLibraryCore.Interfaces; +using System.Collections.Generic; + +namespace ApplicationTests.Mocks +{ + class MockEventHandler : IEventHandler + { + public IList Events = new List(); + + public void AddEvent(GameEvent gameEvent) + { + Events.Add(gameEvent); + } + } +}