From 92830fd521119c210de67faf0482152404cbf5ad Mon Sep 17 00:00:00 2001 From: jonaslagoni Date: Sun, 16 Oct 2022 19:23:50 +0200 Subject: [PATCH] feat: fix some small issues, updated dependency and added tests --- .../DefaultPluginInformation.cs | 2 +- .../MessageQueue/GamingApiMessageQueue.cs | 10 +-- .../Oxide.Ext.GamingApi.csproj | 35 ++++---- .../AsyncVerifyForMoqExtensions.cs | 75 ++++++++++++++++ .../GamingApiMessageQueueTests.cs | 88 +++++++++++++++++++ .../Oxide.Ext.GamingApiTests.csproj | 14 +++ Oxide.Ext.GamingApiTests/app.config | 2 +- Oxide.Ext.GamingApiTests/packages.config | 5 ++ 8 files changed, 206 insertions(+), 25 deletions(-) create mode 100644 Oxide.Ext.GamingApiTests/AsyncVerifyForMoqExtensions.cs create mode 100644 Oxide.Ext.GamingApiTests/GamingApiMessageQueueTests.cs diff --git a/Oxide.Ext.GamingApi/DefaultPluginInformation.cs b/Oxide.Ext.GamingApi/DefaultPluginInformation.cs index a5da95b..0ed553f 100644 --- a/Oxide.Ext.GamingApi/DefaultPluginInformation.cs +++ b/Oxide.Ext.GamingApi/DefaultPluginInformation.cs @@ -48,7 +48,7 @@ public static string GetServerId() if (value == null) { Interface.Oxide.LogInfo($"{envName} environment variable not sat using default value."); - return "" + 0; + return "0"; } else { diff --git a/Oxide.Ext.GamingApi/MessageQueue/GamingApiMessageQueue.cs b/Oxide.Ext.GamingApi/MessageQueue/GamingApiMessageQueue.cs index 467db61..d945fa9 100644 --- a/Oxide.Ext.GamingApi/MessageQueue/GamingApiMessageQueue.cs +++ b/Oxide.Ext.GamingApi/MessageQueue/GamingApiMessageQueue.cs @@ -132,13 +132,12 @@ private void CallMessageInQueue(object source, ElapsedEventArgs e) actionToCall(() => { //Message was successfull + currentMessageCount--; }, () => { //If action was not successfull, add it do the retryMessageQueue - AddMessageToQueue(retryMessageQueue, messageImportance, actionToCall); - shouldRetryMessageQueue = true; + AddToRetryMessageQueue(messageImportance, actionToCall); }); - currentMessageCount--; return; } } @@ -153,10 +152,8 @@ private void CallMessageInQueue(object source, ElapsedEventArgs e) }, () => { //If action was not successfull, add it do the retryMessageQueue - AddMessageToQueue(retryMessageQueue, messageImportance, actionToCall); - shouldRetryMessageQueue = true; + AddToRetryMessageQueue(messageImportance, actionToCall); }); - currentMessageCount--; return; } else @@ -188,6 +185,7 @@ private void AddMessageToQueue(Dictionary action) { AddMessageToQueue(retryMessageQueue, importance, action); + shouldRetryMessageQueue = true; } public void AddToMessageQueue(MessageImportance importance, Action action) { diff --git a/Oxide.Ext.GamingApi/Oxide.Ext.GamingApi.csproj b/Oxide.Ext.GamingApi/Oxide.Ext.GamingApi.csproj index c51ecde..86816f8 100644 --- a/Oxide.Ext.GamingApi/Oxide.Ext.GamingApi.csproj +++ b/Oxide.Ext.GamingApi/Oxide.Ext.GamingApi.csproj @@ -13,12 +13,12 @@ 1 - - - + + + - + False ..\dependencies\Oxide.Core.dll @@ -35,18 +35,19 @@ ..\dependencies\Rust.UI.dll - - - - - - - - - - - - + + + + + + + + + + + + + ..\dependencies\Unity.TextMeshPro.dll @@ -230,6 +231,6 @@ ..\dependencies\UnityEngine.XRModule.dll - + \ No newline at end of file diff --git a/Oxide.Ext.GamingApiTests/AsyncVerifyForMoqExtensions.cs b/Oxide.Ext.GamingApiTests/AsyncVerifyForMoqExtensions.cs new file mode 100644 index 0000000..c01ff97 --- /dev/null +++ b/Oxide.Ext.GamingApiTests/AsyncVerifyForMoqExtensions.cs @@ -0,0 +1,75 @@ +using Moq; +using NUnit.Framework; +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace AsyncVerifyForMoq +{ + /// + /// Allows to do a async Verify() that wait for the Verification expression to become verified within a given timeout. + /// + public static class MoqAsyncVerifyExtensions + { + const int GRANULARITY = 100; + + /// + /// Prevents having multiple implementations for the timeout handling. + /// + /// The verification action that is called. + /// The timeout within the Verification should be successful. + /// + public async static Task SomeVerifyWithTimeout(Action action, TimeSpan timeout) + { + var stopWatch = new Stopwatch(); + stopWatch.Start(); + Exception verificationException = null; + + while (stopWatch.Elapsed < timeout) + { + try + { + action(); + return; + } + catch (Exception mockException) + { + verificationException = mockException; + await Task.Delay(GRANULARITY); + } + } + throw new TimeoutException($"Verification expression always failed within given timespan of {timeout}.", verificationException); + } + + /// + /// Extension for Mock allowing a asynchronous Verify() that should be successful in a specified time period. + /// + /// Type to mock, which can be an interface, a class, or a delegate. + /// Result type of the expression used for verification. + /// The mock that is used as this for this extension + /// Expression to verify. + /// The number of times a method is expected to be called as in the regular Verify() call. + /// The timeout within the verify should be successful. + /// A task to wait on. The task will fail with a TimeoutException in case the Verify() was not successful within the given timeout. + public async static Task AsyncVerify(this Mock mock, Expression> expression, Times times, TimeSpan timeout) where T : class + { + await SomeVerifyWithTimeout(() => mock.Verify(expression, times), timeout); + } + + /// + /// Extension for Mock allowing a asynchronous Verify() that should be successful in a specified time period. + /// Variant for expressions without a return value and one parameter. + /// + /// Type to mock, which can be an interface, a class, or a delegate. + /// The mock that is used as this for this extension + /// Expression to verify. + /// The number of times a method is expected to be called as in the regular Verify() call. + /// The timeout within the verify should be successful. + /// A task to wait on. The task will fail with a TimeoutException in case the Verify() was not successful within the given timeout. + public async static Task AsyncVerify(this Mock mock, Expression> expression, Times times, TimeSpan timeout) where T : class + { + await SomeVerifyWithTimeout(() => mock.Verify(expression, times), timeout); + } + } +} \ No newline at end of file diff --git a/Oxide.Ext.GamingApiTests/GamingApiMessageQueueTests.cs b/Oxide.Ext.GamingApiTests/GamingApiMessageQueueTests.cs new file mode 100644 index 0000000..24c86ff --- /dev/null +++ b/Oxide.Ext.GamingApiTests/GamingApiMessageQueueTests.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Text.Json; +using System.Threading.Tasks; +using NUnit.Framework; +using Oxide.Ext.GamingApi.MessageQueue; +using System; +using AsyncVerifyForMoq; +using Moq; + +namespace Oxide.Ext.GamingApi.Tests +{ + [TestFixture()] + public class GamingApiMessageQueueTest + { + GamingApiMessageQueue queue; + + [SetUp()] + public void setup() + { + queue = GamingApiMessageQueue.Instance; + } + + [Test()] + public async Task shouldConsumeMessage() + { + var timeSpan = TimeSpan.FromSeconds(10); + var mock = new Mock>(); + mock.Setup((m) => m(It.IsAny(), It.IsAny())) + .Callback((Action success, Action failure) => success()); + + queue.AddToMessageQueue(MessageImportance.LOW, mock.Object); + + var asyncVerification = new List() + { + MoqAsyncVerifyExtensions.SomeVerifyWithTimeout(() => + { + //Cannot use assertion as it will be caught by the test itself + if(0 != queue.GetCurrentMessagesInQueue()) throw new Exception($"{queue.GetCurrentMessagesInQueue()}"); + }, timeSpan) + }; + await Task.WhenAll(asyncVerification); + } + [Test()] + public async Task shouldConsumeRetryMessage() + { + var timeSpan = TimeSpan.FromSeconds(10); + var mock = new Mock>(); + mock.Setup((m) => m(It.IsAny(), It.IsAny())) + .Callback((Action success, Action failure) => success()); + + queue.AddToRetryMessageQueue(MessageImportance.LOW, mock.Object); + + var asyncVerification = new List() + { + MoqAsyncVerifyExtensions.SomeVerifyWithTimeout(() => + { + //Cannot use assertion as it will be caught by the test itself + if(0 != queue.GetCurrentMessagesInQueue()) throw new Exception($"{queue.GetCurrentMessagesInQueue()}"); + }, timeSpan) + }; + await Task.WhenAll(asyncVerification); + } + [Test()] + public async Task shouldBeAbleToProcessMessagesInDueTime() + { + var timeSpan = TimeSpan.FromSeconds(12); + queue.SetInterval(1000); + var mock = new Mock>(); + mock.Setup((m) => m(It.IsAny(), It.IsAny())) + .Callback((Action success, Action failure) => success()); + + for (int i = 0; i < 10; i++) + { + queue.AddToRetryMessageQueue(MessageImportance.LOW, mock.Object); + } + + var asyncVerification = new List() + { + MoqAsyncVerifyExtensions.SomeVerifyWithTimeout(() => + { + //Cannot use assertion as it will be caught by the test itself + if(0 != queue.GetCurrentMessagesInQueue()) throw new Exception($"{queue.GetCurrentMessagesInQueue()}"); + }, timeSpan) + }; + await Task.WhenAll(asyncVerification); + } + } +} \ No newline at end of file diff --git a/Oxide.Ext.GamingApiTests/Oxide.Ext.GamingApiTests.csproj b/Oxide.Ext.GamingApiTests/Oxide.Ext.GamingApiTests.csproj index 3fae44f..0a16745 100644 --- a/Oxide.Ext.GamingApiTests/Oxide.Ext.GamingApiTests.csproj +++ b/Oxide.Ext.GamingApiTests/Oxide.Ext.GamingApiTests.csproj @@ -40,10 +40,16 @@ 4 + + ..\packages\Castle.Core.5.1.0\lib\netstandard2.0\Castle.Core.dll + ..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + ..\packages\Moq.4.18.2\lib\netstandard2.0\Moq.dll + ..\packages\NATS.Client.1.0.1\lib\net46\NATS.Client.dll True @@ -64,6 +70,9 @@ ..\packages\System.Collections.Specialized.4.3.0\lib\net46\System.Collections.Specialized.dll + + ..\packages\System.Diagnostics.EventLog.4.7.0\lib\net461\System.Diagnostics.EventLog.dll + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll @@ -89,6 +98,9 @@ ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll + + ..\packages\System.Security.Principal.Windows.4.7.0\lib\net461\System.Security.Principal.Windows.dll + ..\packages\System.Text.Encodings.Web.5.0.1\lib\net461\System.Text.Encodings.Web.dll @@ -111,6 +123,8 @@ + + diff --git a/Oxide.Ext.GamingApiTests/app.config b/Oxide.Ext.GamingApiTests/app.config index 8b2af02..cbf054f 100644 --- a/Oxide.Ext.GamingApiTests/app.config +++ b/Oxide.Ext.GamingApiTests/app.config @@ -4,7 +4,7 @@ - + diff --git a/Oxide.Ext.GamingApiTests/packages.config b/Oxide.Ext.GamingApiTests/packages.config index c03250e..3034e1d 100644 --- a/Oxide.Ext.GamingApiTests/packages.config +++ b/Oxide.Ext.GamingApiTests/packages.config @@ -1,7 +1,9 @@  + + @@ -9,14 +11,17 @@ + + +