fix stat issue with concurrent threads

fix potential lost penalty if server does not response to kick request
make sure that broadcast only shows one custom say name
add unit tests
This commit is contained in:
RaidMax 2020-04-21 17:34:00 -05:00
parent 3ae2e42718
commit 9e74dac5ed
11 changed files with 554 additions and 126 deletions

View File

@ -1096,158 +1096,111 @@ namespace IW4MAdmin
return logPath.FixDirectoryCharacters(); 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 // ensure player gets warned if command not performed on them in game
if (Target.ClientNumber < 0) targetClient = targetClient.ClientNumber < 0 ?
{ Manager.GetActiveClients()
var ingameClient = Manager.GetActiveClients() .FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
.FirstOrDefault(c => c.ClientId == Target.ClientId); targetClient;
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);
}
var newPenalty = new EFPenalty() var newPenalty = new EFPenalty()
{ {
Type = EFPenalty.PenaltyType.Warning, Type = EFPenalty.PenaltyType.Warning,
Expires = DateTime.UtcNow, Expires = DateTime.UtcNow,
Offender = Target, Offender = targetClient,
Punisher = Origin, Punisher = targetOrigin,
Offense = Reason, Offense = reason,
Link = Target.AliasLink 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) if (targetClient.IsIngame)
{
// ensure player gets kicked if command not performed on them in game
if (Target.ClientNumber < 0)
{ {
var ingameClient = Manager.GetActiveClients() if (targetClient.Warnings >= 4)
.FirstOrDefault(c => c.ClientId == Target.ClientId);
if (ingameClient != null)
{ {
await Kick(Reason, ingameClient, Origin); targetClient.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this));
return; 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 string message = $"^1{loc["SERVER_WARNING"]} ^7[^3{targetClient.Warnings}^7]: ^3{targetClient.Name}^7, {reason}";
// await Target.CurrentServer.OnClientDisconnected(Target); targetClient.CurrentServer.Broadcast(message);
var e = new GameEvent() }
{ }
Type = GameEvent.EventType.PreDisconnect,
Origin = Target,
Owner = this
};
Manager.GetEventHandler().AddEvent(e); public override async Task Kick(string Reason, EFClient targetClient, EFClient originClient)
#endif {
targetClient = targetClient.ClientNumber < 0 ?
Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
targetClient;
var newPenalty = new EFPenalty() var newPenalty = new EFPenalty()
{ {
Type = EFPenalty.PenaltyType.Kick, Type = EFPenalty.PenaltyType.Kick,
Expires = DateTime.UtcNow, Expires = DateTime.UtcNow,
Offender = Target, Offender = targetClient,
Offense = Reason, Offense = Reason,
Punisher = Origin, Punisher = originClient,
Link = Target.AliasLink 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 // ensure player gets kicked if command not performed on them in the same server
if (Target.ClientNumber < 0) targetClient = targetClient.ClientNumber < 0 ?
{ Manager.GetActiveClients()
var ingameClient = Manager.GetActiveClients() .FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
.FirstOrDefault(c => c.ClientId == Target.ClientId); targetClient;
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
var newPenalty = new EFPenalty() var newPenalty = new EFPenalty()
{ {
Type = EFPenalty.PenaltyType.TempBan, Type = EFPenalty.PenaltyType.TempBan,
Expires = DateTime.UtcNow + length, Expires = DateTime.UtcNow + length,
Offender = Target, Offender = targetClient,
Offense = Reason, Offense = Reason,
Punisher = Origin, Punisher = originClient,
Link = Target.AliasLink 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 // ensure player gets kicked if command not performed on them in the same server
if (targetClient.ClientNumber < 0) targetClient = targetClient.ClientNumber < 0 ?
{ Manager.GetActiveClients()
EFClient ingameClient = null; .FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
targetClient;
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
}
EFPenalty newPenalty = new EFPenalty() EFPenalty newPenalty = new EFPenalty()
{ {
@ -1260,8 +1213,16 @@ namespace IW4MAdmin
IsEvadedOffense = isEvade IsEvadedOffense = isEvade
}; };
Logger.WriteDebug($"Creating ban penalty for {targetClient}");
targetClient.SetLevel(Permission.Banned, originClient); 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) override public async Task Unban(string reason, EFClient Target, EFClient Origin)

View File

@ -9,7 +9,7 @@ namespace IW4MAdmin.Application.RconParsers
/// generic implementation of the IRConParserConfiguration /// generic implementation of the IRConParserConfiguration
/// allows script plugins to generate dynamic RCon configurations /// allows script plugins to generate dynamic RCon configurations
/// </summary> /// </summary>
sealed internal class DynamicRConParserConfiguration : IRConParserConfiguration public class DynamicRConParserConfiguration : IRConParserConfiguration
{ {
public CommandPrefix CommandPrefixes { get; set; } public CommandPrefix CommandPrefixes { get; set; }
public ParserRegex Status { get; set; } public ParserRegex Status { get; set; }

View File

@ -82,7 +82,10 @@ namespace IW4MAdmin.Plugins.Stats.Models
KillStreak = 0; KillStreak = 0;
DeathStreak = 0; DeathStreak = 0;
LastScore = 0; LastScore = 0;
SessionScores.Add(0); lock (SessionScores)
{
SessionScores.Add(0);
}
Team = IW4Info.Team.None; Team = IW4Info.Team.None;
} }
[NotMapped] [NotMapped]

View File

@ -124,6 +124,9 @@ namespace SharedLibraryCore.Database.Models
[NotMapped] [NotMapped]
public string IPAddressString => IPAddress.ConvertIPtoString(); public string IPAddressString => IPAddress.ConvertIPtoString();
[NotMapped]
public bool IsIngame => ClientNumber >= 0;
[NotMapped] [NotMapped]
public virtual IDictionary<int, long> LinkedAccounts { get; set; } public virtual IDictionary<int, long> LinkedAccounts { get; set; }

View File

@ -121,8 +121,7 @@ namespace SharedLibraryCore
/// <param name="message">Message to be sent to all players</param> /// <param name="message">Message to be sent to all players</param>
public GameEvent Broadcast(string message, EFClient sender = null) 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 #if DEBUG == true
Logger.WriteVerbose(message.StripColors()); Logger.WriteVerbose(message.StripColors());
#endif #endif
@ -196,14 +195,14 @@ namespace SharedLibraryCore
/// </summary> /// </summary>
/// <param name="Reason">Reason for kicking</param> /// <param name="Reason">Reason for kicking</param>
/// <param name="Target">EFClient to kick</param> /// <param name="Target">EFClient to kick</param>
abstract protected Task Kick(String Reason, EFClient Target, EFClient Origin); abstract public Task Kick(String Reason, EFClient Target, EFClient Origin);
/// <summary> /// <summary>
/// Temporarily ban a player ( default 1 hour ) from the server /// Temporarily ban a player ( default 1 hour ) from the server
/// </summary> /// </summary>
/// <param name="Reason">Reason for banning the player</param> /// <param name="Reason">Reason for banning the player</param>
/// <param name="Target">The player to ban</param> /// <param name="Target">The player to ban</param>
abstract protected Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin); abstract public Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin);
/// <summary> /// <summary>
/// Perm ban a player from the server /// Perm ban a player from the server
@ -211,9 +210,9 @@ namespace SharedLibraryCore
/// <param name="Reason">The reason for the ban</param> /// <param name="Reason">The reason for the ban</param>
/// <param name="Target">The person to ban</param> /// <param name="Target">The person to ban</param>
/// <param name="Origin">The person who banned the target</param> /// <param name="Origin">The person who banned the target</param>
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);
/// <summary> /// <summary>
/// Unban a player by npID / GUID /// Unban a player by npID / GUID
@ -307,7 +306,7 @@ namespace SharedLibraryCore
public bool CustomCallback { get; protected set; } public bool CustomCallback { get; protected set; }
public string WorkingDirectory { get; protected set; } public string WorkingDirectory { get; protected set; }
public IRConConnection RemoteConnection { 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 IEventParser EventParser { get; set; }
public string LogPath { get; protected set; } public string LogPath { get; protected set; }
public bool RestartRequested { get; set; } public bool RestartRequested { get; set; }

View File

@ -12,7 +12,7 @@ namespace SharedLibraryCore.Services
{ {
public class PenaltyService : Interfaces.IEntityService<EFPenalty> public class PenaltyService : Interfaces.IEntityService<EFPenalty>
{ {
public async Task<EFPenalty> Create(EFPenalty newEntity) public virtual async Task<EFPenalty> Create(EFPenalty newEntity)
{ {
using (var context = new DatabaseContext()) 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()) using (var context = new DatabaseContext())
{ {

View File

@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore.Storage;
#endif #endif
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -875,6 +876,30 @@ namespace SharedLibraryCore
return new[] { deltaX, deltaY }; return new[] { deltaX, deltaY };
} }
/// <summary>
/// attempts to create and persist a penalty
/// </summary>
/// <param name="penalty"></param>
/// <param name="penaltyService"></param>
/// <param name="logger"></param>
/// <returns>true of the creat succeeds, false otherwise</returns>
public static async Task<bool> TryCreatePenalty(this EFPenalty penalty, IEntityService<EFPenalty> 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; public static bool ShouldHideLevel(this Permission perm) => perm == Permission.Flagged;
/// <summary> /// <summary>

View File

@ -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
};
}
}

View File

@ -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"
}
};
}
}

View File

@ -1,12 +1,55 @@
using IW4MAdmin; using IW4MAdmin;
using IW4MAdmin.Application.Misc; using IW4MAdmin.Application.Misc;
using SharedLibraryCore.Interfaces;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework; 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 namespace ApplicationTests
{ {
[TestFixture] [TestFixture]
public class IW4MServerTests 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<ILogger>();
fakeManager = A.Fake<IManager>();
fakeRConConnection = A.Fake<IRConConnection>();
var rconConnectionFactory = A.Fake<IRConConnectionFactory>();
A.CallTo(() => rconConnectionFactory.CreateConnection(A<string>.Ignored, A<int>.Ignored, A<string>.Ignored))
.Returns(fakeRConConnection);
var fakeTranslationLookup = A.Fake<ITranslationLookup>();
fakeRConParser = A.Fake<IRConParser>();
A.CallTo(() => fakeRConParser.Configuration)
.Returns(ConfigurationGenerators.CreateRConParserConfiguration(A.Fake<IParserRegexFactory>()));
mockEventHandler = new MockEventHandler();
A.CallTo(() => fakeManager.GetEventHandler())
.Returns(mockEventHandler);
serviceProvider = new ServiceCollection()
.AddSingleton(new IW4MServer(fakeManager, ConfigurationGenerators.CreateServerConfiguration(), fakeTranslationLookup, rconConnectionFactory)
{
RconParser = fakeRConParser
})
.BuildServiceProvider();
}
[Test] [Test]
public void Test_GenerateLogPath_Basic() public void Test_GenerateLogPath_Basic()
{ {
@ -133,5 +176,337 @@ namespace ApplicationTests
Assert.AreEqual(expected, generated); Assert.AreEqual(expected, generated);
} }
#region BAN
[Test]
public async Task Test_BanCreatesPenalty()
{
var fakePenaltyService = A.Fake<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
var target = ClientGenerators.CreateBasicClient(server);
var origin = ClientGenerators.CreateBasicClient(server);
await server.Ban("test reason", target, origin);
A.CallTo(() => fakePenaltyService.Create(A<EFPenalty>.Ignored))
.MustHaveHappenedOnceExactly();
}
[Test]
public async Task Test_BanExecutesKickCommand()
{
var fakePenaltyService = A.Fake<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
var target = ClientGenerators.CreateBasicClient(server);
var origin = ClientGenerators.CreateBasicClient(server);
await server.TempBan("test reason", TimeSpan.Zero, target, origin);
A.CallTo(() => fakePenaltyService.Create(A<EFPenalty>.Ignored))
.MustHaveHappenedOnceExactly();
}
[Test]
public async Task Test_TempBanExecutesKickCommand()
{
var fakePenaltyService = A.Fake<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
var target = ClientGenerators.CreateBasicClient(server);
var origin = ClientGenerators.CreateBasicClient(server);
await server.Kick("test reason", target, origin);
A.CallTo(() => fakePenaltyService.Create(A<EFPenalty>.Ignored))
.MustHaveHappenedOnceExactly();
}
[Test]
public async Task Test_KickExecutesKickCommand()
{
var fakePenaltyService = A.Fake<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
var target = ClientGenerators.CreateBasicClient(server);
var origin = ClientGenerators.CreateBasicClient(server);
await server.Warn("test reason", target, origin);
A.CallTo(() => fakePenaltyService.Create(A<EFPenalty>.Ignored))
.MustHaveHappenedOnceExactly();
}
[Test]
public async Task Test_WarnBroadCastMessageForIngameClient()
{
var fakePenaltyService = A.Fake<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
A.CallTo(() => fakePenaltyService.RemoveActivePenalties(A<int>.Ignored))
.Returns(Task.CompletedTask);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
A.CallTo(() => fakePenaltyService.RemoveActivePenalties(A<int>.Ignored))
.Returns(Task.CompletedTask);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<PenaltyService>();
A.CallTo(() => fakeManager.GetPenaltyService())
.Returns(fakePenaltyService);
A.CallTo(() => fakePenaltyService.RemoveActivePenalties(A<int>.Ignored))
.Returns(Task.CompletedTask);
var server = serviceProvider.GetRequiredService<IW4MServer>();
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<EFPenalty>.Ignored))
.MustHaveHappened();
}
#endregion
} }
} }

View File

@ -0,0 +1,16 @@
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using System.Collections.Generic;
namespace ApplicationTests.Mocks
{
class MockEventHandler : IEventHandler
{
public IList<GameEvent> Events = new List<GameEvent>();
public void AddEvent(GameEvent gameEvent)
{
Events.Add(gameEvent);
}
}
}