diff --git a/FruitBank.sln b/FruitBank.sln
index 84afd68..7c62890 100644
--- a/FruitBank.sln
+++ b/FruitBank.sln
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.32002.185
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.11222.15
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{E4ACA93B-D3DE-4557-B069-F1DB42925A4B}"
EndProject
@@ -87,6 +87,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nop.Plugin.Misc.MangoCore",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mango.Nop.Data", "..\NopCommerce.Common\4.70\Libraries\Mango.Nop.Data\Mango.Nop.Data.csproj", "{EE44B558-F1DA-433A-BD4C-D275986A4679}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mango.Sandbox.EndPoints", "Tests\Mango.Sandbox\Mango.Sandbox.EndPoints\Mango.Sandbox.EndPoints.csproj", "{D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mango.Sandbox.EndPoints.Tests", "Tests\Mango.Sandbox\Mango.Sandbox.EndPoints.Tests\Mango.Sandbox.EndPoints.Tests.csproj", "{B8491E5C-DBB5-1594-052E-744D78D7A4DE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -507,6 +511,30 @@ Global
{EE44B558-F1DA-433A-BD4C-D275986A4679}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{EE44B558-F1DA-433A-BD4C-D275986A4679}.Release|x86.ActiveCfg = Release|Any CPU
{EE44B558-F1DA-433A-BD4C-D275986A4679}.Release|x86.Build.0 = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Debug|x86.Build.0 = Debug|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|x86.ActiveCfg = Release|Any CPU
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C}.Release|x86.Build.0 = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Debug|x86.Build.0 = Debug|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|x86.ActiveCfg = Release|Any CPU
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -550,6 +578,8 @@ Global
{3E893AC2-29F1-48FC-B33F-F73C6EE2BE90} = {0742FDF3-0F2E-4C64-9521-E58A7FF2ED26}
{3976CB1D-8080-4B84-8C01-1F98BFCAF2B3} = {0742FDF3-0F2E-4C64-9521-E58A7FF2ED26}
{EE44B558-F1DA-433A-BD4C-D275986A4679} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {D22DB269-2490-4A3D-B0B4-2CD2BB626F9C} = {E8FC6874-E230-468A-9685-4747354B92FF}
+ {B8491E5C-DBB5-1594-052E-744D78D7A4DE} = {E8FC6874-E230-468A-9685-4747354B92FF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EE72A8B2-332A-4175-9319-6726D36E9D25}
diff --git a/Libraries/Nop.Core/Nop.Core.csproj b/Libraries/Nop.Core/Nop.Core.csproj
index f3c9b34..4fa9460 100644
--- a/Libraries/Nop.Core/Nop.Core.csproj
+++ b/Libraries/Nop.Core/Nop.Core.csproj
@@ -19,10 +19,10 @@
-
-
-
-
+
+
+
+
diff --git a/Libraries/Nop.Data/Nop.Data.csproj b/Libraries/Nop.Data/Nop.Data.csproj
index 296705f..6e923c2 100644
--- a/Libraries/Nop.Data/Nop.Data.csproj
+++ b/Libraries/Nop.Data/Nop.Data.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/Plugins/Nop.Plugin.ExternalAuth.Facebook/Nop.Plugin.ExternalAuth.Facebook.csproj b/Plugins/Nop.Plugin.ExternalAuth.Facebook/Nop.Plugin.ExternalAuth.Facebook.csproj
index 05bd7f2..2a3d667 100644
--- a/Plugins/Nop.Plugin.ExternalAuth.Facebook/Nop.Plugin.ExternalAuth.Facebook.csproj
+++ b/Plugins/Nop.Plugin.ExternalAuth.Facebook/Nop.Plugin.ExternalAuth.Facebook.csproj
@@ -58,7 +58,7 @@
-
+
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/Mango.Sandbox.EndPoints.Tests.csproj b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/Mango.Sandbox.EndPoints.Tests.csproj
new file mode 100644
index 0000000..bfbf974
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/Mango.Sandbox.EndPoints.Tests.csproj
@@ -0,0 +1,39 @@
+
+
+
+ net9.0
+ enable
+ enable
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\..\FruitBankHybridApp\FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Core.dll
+
+
+ ..\..\..\..\FruitBankHybridApp\FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Services.dll
+
+
+ ..\..\..\..\FruitBankHybridApp\FruitBank.Common.Server\bin\Debug\net9.0\FruitBank.Common.dll
+
+
+
+
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/SignalRClientSandbox.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/SignalRClientSandbox.cs
new file mode 100644
index 0000000..03e2970
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/SignalRClientSandbox.cs
@@ -0,0 +1,32 @@
+using AyCode.Core.Loggers;
+using AyCode.Services.SignalRs;
+
+namespace Mango.Sandbox.EndPoints.Tests;
+
+///
+/// SignalR kliens a Sandbox teszteléshez.
+/// Az AcSignalRClientBase-bõl származik, a GetByIdAsync, GetAllAsync, PostDataAsync, GetAllIntoAsync metódusokat örökli.
+///
+public class SignalRClientSandbox : AcSignalRClientBase
+{
+ public SignalRClientSandbox(string hubUrl)
+ : base(hubUrl, new Logger())
+ {
+ }
+
+ //protected override ISignalResponseMessage DeserializeResponseMessage(byte[] messageBytes)
+ //{
+ // var responseMessage = base.DeserializeResponseMessage(messageBytes);
+ // if (responseMessage is SignalResponseJsonMessage jsonMessage)
+ // {
+ // Console.WriteLine(jsonMessage.ResponseData);
+ // }
+ // return responseMessage;
+ //}
+
+ protected override Task MessageReceived(int messageTag, byte[] messageBytes)
+ {
+ Console.WriteLine($"[SignalRClientSandbox] Push message received: tag={messageTag}");
+ return Task.CompletedTask;
+ }
+}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/SignalRClientToEndpointTest.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/SignalRClientToEndpointTest.cs
new file mode 100644
index 0000000..5ffcad1
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints.Tests/SignalRClientToEndpointTest.cs
@@ -0,0 +1,502 @@
+using AyCode.Services.SignalRs;
+using Mango.Sandbox.EndPoints;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Mango.Sandbox.EndPoints.Tests;
+
+///
+/// Komplex SignalR tesztek a SignalRClientSandbox használatával.
+/// Az AcSignalRClientBase GetByIdAsync, GetAllAsync, PostDataAsync, GetAllIntoAsync metódusait teszteli.
+/// FONTOS: A SANDBOX-ot manuálisan kell elindÃtani a tesztek futtatása elÅ‘tt!
+///
+[TestClass]
+public class SignalRClientToEndpointTest
+{
+ private static readonly string HubUrl = "http://localhost:59579/fbHub";
+ private static SignalRClientSandbox _client = null!;
+
+ [ClassInitialize]
+ public static async Task ClassInitialize(TestContext context)
+ {
+ // Sandbox ellenőrzés
+ //using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
+ //try
+ //{
+ // var response = await httpClient.GetAsync("http://localhost:59579/health");
+ // Assert.IsTrue(response.IsSuccessStatusCode,
+ // "SANDBOX not running! Start: dotnet run --project Mango.Sandbox.EndPoints --urls http://localhost:59579");
+ //}
+ //catch (Exception ex)
+ //{
+ // Assert.Fail($"SANDBOX not running! {ex.Message}");
+ //}
+
+ _client = new SignalRClientSandbox(HubUrl);
+ await _client.StartConnection();
+ }
+
+ [ClassCleanup]
+ public static async Task ClassCleanup()
+ {
+ if (_client != null)
+ await _client.StopConnection();
+ }
+
+ #region GetAllAsync Tesztek - Paraméter nélkül
+
+ [TestMethod]
+ public async Task GetAllAsync_NoParams_ReturnsTestItems()
+ {
+ // Act - GetAllAsync(tag)
+ var result = await _client.GetAllAsync>(TestSignalRTags.GetTestItems);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Count > 0);
+ Console.WriteLine($"[GetAllAsync] Received {result.Count} items");
+ }
+
+ [TestMethod]
+ public async Task GetAllAsync_NoParams_Callback_ReturnsTestItems()
+ {
+ // Arrange
+ List? receivedItems = null;
+ var tcs = new TaskCompletionSource();
+
+ // Act - GetAllAsync(tag, callback)
+ await _client.GetAllAsync>(TestSignalRTags.GetTestItems, response =>
+ {
+ receivedItems = response.GetResponseData>();
+ tcs.SetResult(true);
+ return Task.CompletedTask;
+ });
+
+ await Task.WhenAny(tcs.Task, Task.Delay(5000));
+
+ // Assert
+ Assert.IsNotNull(receivedItems);
+ Assert.IsTrue(receivedItems.Count > 0);
+ }
+
+ [TestMethod]
+ public async Task GetAllAsync_HandleNoParams_ReturnsOk()
+ {
+ // Act - paraméter nélküli metódus
+ var result = await _client.GetAllAsync(TestSignalRTags.NoParams);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual("OK", result);
+ }
+
+ #endregion
+
+ #region GetByIdAsync Tesztek - Egyetlen ID
+
+ [TestMethod]
+ public async Task GetByIdAsync_SingleInt_ReturnsFormattedString()
+ {
+ // Act - GetByIdAsync(tag, id)
+ var result = await _client.GetByIdAsync(TestSignalRTags.SingleIntParam, 42);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual("Received: 42", result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_String_ReturnsEcho()
+ {
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.StringParam, "Hello World");
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual("Echo: Hello World", result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_Bool_ReturnsTrue()
+ {
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.BoolParam, true);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_Guid_ReturnsSameGuid()
+ {
+ // Arrange
+ var guid = Guid.NewGuid();
+
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.GuidParam, guid);
+
+ // Assert
+ Assert.AreEqual(guid, result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_Decimal_ReturnsDoubled()
+ {
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.DecimalParam, 123.45m);
+
+ // Assert
+ Assert.AreEqual(246.90m, result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_Long_ReturnsSameLong()
+ {
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.LongParam, 9223372036854775807L);
+
+ // Assert
+ Assert.AreEqual(9223372036854775807L, result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_Double_ReturnsSameDouble()
+ {
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.DoubleParam, 3.14159);
+
+ // Assert
+ Assert.AreEqual(3.14159, result, 0.00001);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_Enum_ReturnsSameEnum()
+ {
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.EnumParam, (int)TestStatus.Active);
+
+ // Assert
+ Assert.AreEqual(TestStatus.Active, result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_SingleInt_Callback_ReturnsFormattedString()
+ {
+ // Arrange
+ string? receivedResult = null;
+ var tcs = new TaskCompletionSource();
+
+ // Act - GetByIdAsync(tag, callback, id)
+ await _client.GetByIdAsync(TestSignalRTags.SingleIntParam, response =>
+ {
+ receivedResult = response.GetResponseData();
+ tcs.SetResult(true);
+ return Task.CompletedTask;
+ }, 100);
+
+ await Task.WhenAny(tcs.Task, Task.Delay(5000));
+
+ // Assert
+ Assert.IsNotNull(receivedResult);
+ Assert.AreEqual("Received: 100", receivedResult);
+ }
+
+ #endregion
+
+ #region GetByIdAsync Tesztek - Több ID (object[])
+
+ [TestMethod]
+ public async Task GetByIdAsync_TwoInts_ReturnsSum()
+ {
+ // Act - GetByIdAsync(tag, ids[])
+ var result = await _client.GetByIdAsync(TestSignalRTags.TwoIntParams, new object[] { 10, 20 });
+
+ // Assert
+ Assert.AreEqual(30, result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_MultipleTypes_ReturnsFormattedString()
+ {
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.MultipleTypesParams, new object[] { true, "test", 123 });
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual("True-test-123", result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_FiveParams_ReturnsFormattedString()
+ {
+ // Arrange
+ var guid = Guid.NewGuid();
+
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.FiveParams, new object[] { 1, "text", true, guid, 99.99m });
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual($"1-text-True-{guid}-99.99", result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_TwoInts_Callback_ReturnsSum()
+ {
+ // Arrange
+ int? receivedResult = null;
+ var tcs = new TaskCompletionSource();
+
+ // Act - GetByIdAsync(tag, callback, ids[])
+ await _client.GetByIdAsync(TestSignalRTags.TwoIntParams, response =>
+ {
+ receivedResult = response.GetResponseData();
+ tcs.SetResult(true);
+ return Task.CompletedTask;
+ }, new object[] { 5, 7 });
+
+ await Task.WhenAny(tcs.Task, Task.Delay(5000));
+
+ // Assert
+ Assert.IsNotNull(receivedResult);
+ Assert.AreEqual(12, receivedResult.Value);
+ }
+
+ #endregion
+
+ #region GetAllAsync Tesztek - ContextParams-szal
+
+ [TestMethod]
+ public async Task GetAllAsync_WithContextParams_IntArray_ReturnsDoubled()
+ {
+ // Act - GetAllAsync(tag, contextParams[])
+ var result = await _client.GetAllAsync(TestSignalRTags.IntArrayParam, new object[] { new[] { 1, 2, 3 } });
+
+ // Assert
+ Assert.IsNotNull(result);
+ CollectionAssert.AreEqual(new[] { 2, 4, 6 }, result);
+ }
+
+ [TestMethod]
+ public async Task GetAllAsync_WithContextParams_StringList_ReturnsUppercased()
+ {
+ // Act
+ var result = await _client.GetAllAsync>(TestSignalRTags.StringListParam, new object[] { new List { "apple", "banana" } });
+
+ // Assert
+ Assert.IsNotNull(result);
+ CollectionAssert.AreEqual(new List { "APPLE", "BANANA" }, result);
+ }
+
+ [TestMethod]
+ public async Task GetAllAsync_WithContextParams_Callback_IntArray()
+ {
+ // Arrange
+ int[]? receivedResult = null;
+ var tcs = new TaskCompletionSource();
+
+ // Act - GetAllAsync(tag, callback, contextParams[])
+ await _client.GetAllAsync(TestSignalRTags.IntArrayParam, response =>
+ {
+ receivedResult = response.GetResponseData();
+ tcs.SetResult(true);
+ return Task.CompletedTask;
+ }, new object[] { new[] { 5, 10, 15 } });
+
+ await Task.WhenAny(tcs.Task, Task.Delay(5000));
+
+ // Assert
+ Assert.IsNotNull(receivedResult);
+ CollectionAssert.AreEqual(new[] { 10, 20, 30 }, receivedResult);
+ }
+
+ #endregion
+
+ #region PostDataAsync Tesztek - Komplex Objektumok
+
+ [TestMethod]
+ public async Task PostDataAsync_TestEchoRequest_ReturnsEchoResponse()
+ {
+ // Arrange
+ var request = new TestEchoRequest { Id = 42, Name = "TestName" };
+
+ // Act - PostDataAsync(tag, data)
+ var result = await _client.PostDataAsync(TestSignalRTags.EchoTag, request);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual(42, result.Id);
+ Assert.AreEqual("TestName", result.Name);
+ Assert.IsTrue(result.EchoedSuccessfully);
+ }
+
+ [TestMethod]
+ public async Task PostDataAsync_TestOrderItem_ReturnsProcessedItem()
+ {
+ // Arrange
+ var item = new TestOrderItem { Id = 1, ProductName = "TestProduct", Quantity = 5, UnitPrice = 10m };
+
+ // Act
+ var result = await _client.PostDataAsync(TestSignalRTags.TestOrderItemParam, item);
+ //await Task.Delay(1000);
+ // Assert
+ Assert.IsNotNull(result);
+ //Assert.AreEqual("Processed: TestProduct", result.ProductName);
+ Assert.AreEqual(item.Quantity * 2, result.Quantity); // doubled
+ }
+
+ [TestMethod]
+ public async Task PostDataAsync_TestOrder_ReturnsNestedOrder()
+ {
+ // Arrange
+ var order = new TestOrder
+ {
+ Id = 100,
+ CustomerName = "Test Customer",
+ OrderDate = DateTime.UtcNow,
+ Status = TestStatus.Active,
+ Items = [new() { Id = 1, ProductName = "Item1", Quantity = 2, UnitPrice = 10m }]
+ };
+
+ // Act
+ var result = await _client.PostDataAsync(TestSignalRTags.TestOrderParam, order);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual(100, result.Id);
+ Assert.AreEqual("Test Customer", result.CustomerName);
+ Assert.AreEqual(1, result.Items.Count);
+ }
+
+ [TestMethod]
+ public async Task PostDataAsync_SharedTag_ReturnsTag()
+ {
+ // Arrange
+ var tag = new SharedTag { Id = 1, Name = "Tag1", Description = "Desc", IsActive = true };
+
+ // Act
+ var result = await _client.PostDataAsync(TestSignalRTags.SharedTagParam, tag);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual("Tag1", result.Name);
+ Assert.AreEqual("Desc", result.Description);
+ }
+
+ [TestMethod]
+ public async Task PostDataAsync_Callback_TestEchoRequest()
+ {
+ // Arrange
+ var request = new TestEchoRequest { Id = 99, Name = "CallbackTest" };
+ TestEchoResponse? receivedResult = null;
+ var tcs = new TaskCompletionSource();
+
+ // Act - PostDataAsync(tag, data, callback)
+ await _client.PostDataAsync(TestSignalRTags.EchoTag, request, response =>
+ {
+ receivedResult = response.GetResponseData();
+ tcs.SetResult(true);
+ return Task.CompletedTask;
+ });
+
+ await Task.WhenAny(tcs.Task, Task.Delay(5000));
+
+ // Assert
+ Assert.IsNotNull(receivedResult);
+ Assert.AreEqual(99, receivedResult.Id);
+ }
+
+ #endregion
+
+ #region Edge Case Tesztek
+
+ [TestMethod]
+ public async Task GetByIdAsync_EmptyString_ReturnsEchoOfEmpty()
+ {
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.StringParam, "");
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual("Echo: ", result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_Zero_ReturnsFormattedString()
+ {
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.SingleIntParam, 0);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual("Received: 0", result);
+ }
+
+ [TestMethod]
+ public async Task GetByIdAsync_NegativeInt_ReturnsFormattedString()
+ {
+ // Act
+ var result = await _client.GetByIdAsync(TestSignalRTags.SingleIntParam, -42);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual("Received: -42", result);
+ }
+
+ [TestMethod]
+ public async Task GetAllAsync_EmptyArray_ReturnsEmptyArray()
+ {
+ // Act
+ var result = await _client.GetAllAsync(TestSignalRTags.IntArrayParam, new object[] { Array.Empty() });
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual(0, result.Length);
+ }
+
+ #endregion
+
+ #region Binary Serialization Tesztek - AcBinaryDeserializationException reprodukálása
+
+ ///
+ /// Reprodukálja a GetMeasuringUsers hibát: AcBinaryDeserializationException: Invalid interned string index
+ /// A hiba akkor jelentkezik, amikor a szerver Binary formátumban küldi a választ (List),
+ /// de a kliens deszerializálásnál az interned string index hibás.
+ ///
+ [TestMethod]
+ public async Task GetAllAsync_CustomerDtoList_BinaryDeserialization_ThrowsInternedStringIndexError()
+ {
+ // Act - GetAllAsync>(tag)
+ // Ez a hÃvás a GetMeasuringUsers-höz hasonló forgatókönyvet reprodukál
+ var result = await _client.GetAllAsync>(TestSignalRTags.GetCustomerDtoList);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Count > 0, "Should return at least one CustomerDto");
+
+ // Ellenőrizzük, hogy minden CustomerDto megfelelően deszerializálódott
+ foreach (var dto in result)
+ {
+ Assert.IsTrue(dto.Id > 0, $"CustomerDto.Id should be > 0, got {dto.Id}");
+ Assert.IsFalse(string.IsNullOrEmpty(dto.Username), "CustomerDto.Username should not be empty");
+ Assert.IsFalse(string.IsNullOrEmpty(dto.Email), "CustomerDto.Email should not be empty");
+ Console.WriteLine($"[GetAllAsync_CustomerDtoList] Id={dto.Id}, Username={dto.Username}, Email={dto.Email}, FullName={dto.FullName}");
+ }
+ }
+
+ ///
+ /// Nagyobb CustomerDto lista tesztelése - több interned string a szerializációban
+ ///
+ [TestMethod]
+ public async Task GetAllAsync_LargeCustomerDtoList_BinaryDeserialization_Success()
+ {
+ // Act
+ var result = await _client.GetAllAsync>(TestSignalRTags.GetLargeCustomerDtoList);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Count >= 10, $"Should return at least 10 CustomerDtos, got {result.Count}");
+ Console.WriteLine($"[GetAllAsync_LargeCustomerDtoList] Received {result.Count} items");
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/DevAdminSignalRHubSandbox.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/DevAdminSignalRHubSandbox.cs
new file mode 100644
index 0000000..00adf64
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/DevAdminSignalRHubSandbox.cs
@@ -0,0 +1,43 @@
+using AyCode.Core.Enums;
+using AyCode.Core.Extensions;
+using AyCode.Core.Loggers;
+using AyCode.Models.Server.DynamicMethods;
+using AyCode.Services.Server.SignalRs;
+using AyCode.Services.SignalRs;
+using FruitBank.Common.Interfaces;
+using FruitBank.Common.Loggers;
+using FruitBank.Common.Server.Interfaces;
+using FruitBank.Common.Server.Services.SignalRs;
+using FruitBank.Common.SignalRs;
+using Mango.Nop.Core.Loggers;
+using Microsoft.Extensions.Configuration;
+//using Nop.Plugin.Misc.FruitBankPlugin.Controllers;
+
+namespace Mango.Sandbox.EndPoints;
+
+///
+/// Egyszerûsített SignalR Hub a teszteléshez.
+/// Ez a Hub nem függ a 3 eredeti endpoint-tól (IFruitBankDataControllerServer, ICustomOrderSignalREndpointServer, IStockSignalREndpointServer).
+///
+public class DevAdminSignalRHubSandbox : AcWebSignalRHubWithSessionBase>
+{
+ public DevAdminSignalRHubSandbox(IConfiguration configuration, ITestSignalREndpointServer testSignalREndpoint, IEnumerable logWriters)
+ : base(configuration, new Logger(logWriters.ToArray()))
+ {
+ SerializerOptions = new AcBinarySerializerOptions();
+ DynamicMethodCallModels.Add(new AcDynamicMethodCallModel(testSignalREndpoint));
+ }
+
+ //protected override Task SendMessageToClient(IAcSignalRHubItemServer sendTo, int messageTag, ISignalRMessage message, int? requestId = null)
+ //{
+ // Console.WriteLine(((SignalResponseJsonMessage)message).ResponseDataJson);
+
+ // return base.SendMessageToClient(sendTo, messageTag, message, requestId);
+ //}
+}
+
+// ===========================================
+// === EREDETI KÓD - KIKOMMENTEZVE ===
+// ===========================================
+// A helyes using: FruitBank.Common.Server.Services.SignalRs (nem AyCode.Services.SignalRs!)
+// Az AcWebSignalRHubWithSessionBase a FruitBank.Common.Server projektben van definiálva.
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Logger.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Logger.cs
new file mode 100644
index 0000000..7f5ea04
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Logger.cs
@@ -0,0 +1,40 @@
+using AyCode.Core.Enums;
+using AyCode.Core.Loggers;
+
+namespace Mango.Sandbox.EndPoints;
+
+
+public interface ILogger : ILogger
+{
+}
+public interface ILogger : IAcLoggerBase
+{
+}
+
+public class Logger : Logger, ILogger
+{
+ //public Logger() : base(typeof(TCategory).Name)
+ //{ }
+
+ public Logger(params IAcLogWriterBase[] logWriters) : base(typeof(TCategory).Name, logWriters)
+ { }
+
+ public Logger(AppType appType, AyCode.Core.Loggers.LogLevel logLevel, params IAcLogWriterBase[] logWriters) : base(appType, logLevel, typeof(TCategory).Name, logWriters)
+ { }
+}
+
+public class Logger : AcLoggerBase, ILogger
+{
+ public Logger(params IAcLogWriterBase[] logWriters) : this(null, logWriters)
+ { }
+
+ public Logger(string? categoryName) : base(categoryName)
+ { }
+
+ public Logger(string? categoryName, params IAcLogWriterBase[] logWriters) : base(categoryName, logWriters)
+ { }
+
+ public Logger(AppType appType, AyCode.Core.Loggers.LogLevel logLevel, string? categoryName, params IAcLogWriterBase[] logWriters) : base(appType, logLevel, categoryName, logWriters)
+ { }
+}
+
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Mango.Sandbox.EndPoints.csproj b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Mango.Sandbox.EndPoints.csproj
new file mode 100644
index 0000000..57aa986
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Mango.Sandbox.EndPoints.csproj
@@ -0,0 +1,136 @@
+
+
+
+ net9.0
+ enable
+ enable
+
+ false
+
+ $([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..\..\..'))\
+ $([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..\..\..\..'))\
+ $(FruitBankRoot)Presentation\Nop.Web\Plugins\Misc.FruitBankPlugin\
+ $(SourceRoot)FruitBankHybridApp\
+
+ H:\Applications\Aycode\Source\AyCode.Core\
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(AyCodeRoot)AyCode.Core\bin\FruitBank\Debug\net9.0\AyCode.Core.dll
+
+
+ $(AyCodeRoot)AyCode.Core.Server\bin\FruitBank\Debug\net9.0\AyCode.Core.Server.dll
+
+
+ $(AyCodeRoot)AyCode.Database\bin\FruitBank\Debug\net9.0\AyCode.Database.dll
+
+
+ $(AyCodeRoot)AyCode.Entities\bin\FruitBank\Debug\net9.0\AyCode.Entities.dll
+
+
+ $(AyCodeRoot)AyCode.Entities.Server\bin\FruitBank\Debug\net9.0\AyCode.Entities.Server.dll
+
+
+ $(AyCodeRoot)AyCode.Interfaces\bin\FruitBank\Debug\net9.0\AyCode.Interfaces.dll
+
+
+ $(AyCodeRoot)AyCode.Interfaces.Server\bin\FruitBank\Debug\net9.0\AyCode.Interfaces.Server.dll
+
+
+ $(AyCodeRoot)AyCode.Models.Server\bin\FruitBank\Debug\net9.0\AyCode.Models.Server.dll
+
+
+ $(AyCodeRoot)AyCode.Services\bin\FruitBank\Debug\net9.0\AyCode.Services.dll
+
+
+ $(AyCodeRoot)AyCode.Services.Server\bin\FruitBank\Debug\net9.0\AyCode.Services.Server.dll
+
+
+ $(AyCodeRoot)AyCode.Utils\bin\FruitBank\Debug\net9.0\AyCode.Utils.dll
+
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\FruitBank.Common.dll
+
+
+ $(FruitBankHybridRoot)FruitBank.Common.Server\bin\Debug\net9.0\FruitBank.Common.Server.dll
+
+
+
+
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Program.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Program.cs
new file mode 100644
index 0000000..5eac8f7
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Program.cs
@@ -0,0 +1,126 @@
+using AyCode.Core.Loggers;
+using FruitBank.Common;
+using FruitBank.Common.Server.Services.Loggers;
+using FruitBank.Common.Server.Services.SignalRs;
+using Mango.Sandbox.EndPoints;
+using Microsoft.AspNetCore.Http.Connections;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// ===========================================
+// === MINIMÁLIS KONFIGURÁCIÓ A SIGNALR HUB TESZTELÉSÉHEZ ===
+// ===========================================
+
+Console.WriteLine("[SANDBOX] Starting minimal SignalR Hub test configuration...");
+
+// ===========================================
+// === CORS ===
+// ===========================================
+
+builder.Services.AddCors(options =>
+{
+ options.AddDefaultPolicy(policy =>
+ {
+ policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
+ });
+
+ options.AddPolicy("SignalR", policy =>
+ {
+ policy.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
+ });
+});
+
+// ===========================================
+// === DI VALIDÁCIÓ KIKAPCSOLÁSA ===
+// ===========================================
+
+builder.Host.UseDefaultServiceProvider(options =>
+{
+ options.ValidateScopes = false;
+ options.ValidateOnBuild = false;
+});
+
+// ===========================================
+// === ALAPVETÕ INFRASTRUKTÚRA ===
+// ===========================================
+
+builder.Services.AddHttpContextAccessor();
+builder.Services.AddMemoryCache();
+builder.Services.AddDistributedMemoryCache(); // IDistributedCache a Session-höz
+builder.Services.AddSession();
+
+// ===========================================
+// === LOGGER - Csak ConsoleLogWriter a teszteléshez ===
+// ===========================================
+
+builder.Services.AddScoped();
+
+// ===========================================
+// === TESZT ENDPOINT ===
+// ===========================================
+
+builder.Services.AddScoped();
+
+// ===========================================
+// === SIGNALR KONFIGURÁCIÓ ===
+// ===========================================
+
+builder.Services.AddSignalR(hubOptions =>
+{
+ hubOptions.EnableDetailedErrors = true;
+ hubOptions.MaximumReceiveMessageSize = 30_000_000;
+ hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(FruitBankConstClient.SignalRKeepAliveIntervalSecond);
+ hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(FruitBankConstClient.SignarlRTimeoutIntervalSecond);
+ hubOptions.StatefulReconnectBufferSize = 30_000_000;
+});
+
+// ===========================================
+// === APP BUILD ===
+// ===========================================
+
+var app = builder.Build();
+
+app.UseCors("SignalR");
+app.UseSession();
+
+// ===========================================
+// === SIGNALR HUB REGISZTRÁCIÓ ===
+// ===========================================
+
+var fruitBankHubEndPoint = $"/{FruitBankConstClient.DefaultHubName}";
+
+// SANDBOX TESZT HUB - egyszerûsített, nincs Nop függõség
+app.MapHub(fruitBankHubEndPoint, options =>
+{
+ options.Transports = HttpTransportType.WebSockets;
+ options.WebSockets.CloseTimeout = TimeSpan.FromSeconds(10);
+ options.AllowStatefulReconnects = true;
+ options.TransportMaxBufferSize = 30_000_000;
+ options.ApplicationMaxBufferSize = 30_000_000;
+ options.TransportSendTimeout = TimeSpan.FromSeconds(60);
+});
+
+var loggerHubEndPoint = $"/{FruitBankConstClient.LoggerHubName}";
+app.MapHub(loggerHubEndPoint, options =>
+{
+ options.AllowStatefulReconnects = false;
+});
+
+app.MapGet("/", () => "SANDBOX is running!");
+app.MapGet("/health", () => Results.Ok(new { status = "healthy", timestamp = DateTime.UtcNow }));
+
+// ===========================================
+// === CONSOLE OUTPUT ===
+// ===========================================
+
+Console.Title = "[SB] - SignalR Test";
+
+Console.WriteLine("===========================================");
+Console.WriteLine(" FRUITBANK SANDBOX - SignalR Test Mode");
+Console.WriteLine("===========================================");
+Console.WriteLine($" Base URL: http://localhost:59579");
+Console.WriteLine($" SignalR Hub: {fruitBankHubEndPoint}");
+Console.WriteLine($" Logger Hub: {loggerHubEndPoint}");
+Console.WriteLine("===========================================");
+
+app.Run();
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullAuthenticationPluginManager.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullAuthenticationPluginManager.cs
new file mode 100644
index 0000000..c449f9c
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullAuthenticationPluginManager.cs
@@ -0,0 +1,30 @@
+using Nop.Core.Domain.Customers;
+//using Nop.Services.Authentication.External;
+//using Nop.Services.Plugins;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+//public class NullAuthenticationPluginManager : IAuthenticationPluginManager
+//{
+// public Task LoadPluginBySystemNameAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task> LoadAllPluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(List systemNames, Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public bool IsPluginActive(IExternalAuthenticationMethod plugin, List systemNames) => false;
+
+// public bool IsPluginActive(IExternalAuthenticationMethod plugin) => false;
+
+// public Task IsPluginActiveAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(false);
+
+// public Task GetPluginLogoUrlAsync(IExternalAuthenticationMethod plugin)
+// => Task.FromResult(string.Empty);
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullDiscountPluginManager.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullDiscountPluginManager.cs
new file mode 100644
index 0000000..d33b4bb
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullDiscountPluginManager.cs
@@ -0,0 +1,26 @@
+using Nop.Core.Domain.Customers;
+using Nop.Core.Domain.Discounts;
+//using Nop.Services.Discounts;
+//using Nop.Services.Plugins;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+///
+/// Null implementation of IDiscountPluginManager for SANDBOX
+///
+//public class NullDiscountPluginManager : IDiscountPluginManager
+//{
+// public Task LoadPluginBySystemNameAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task> LoadAllPluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(List systemNames, Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public bool IsPluginActive(IDiscountRequirementRule plugin, List systemNames) => false;
+
+// public Task GetPluginLogoUrlAsync(IDiscountRequirementRule plugin)
+// => Task.FromResult(string.Empty);
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullExchangeRatePluginManager.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullExchangeRatePluginManager.cs
new file mode 100644
index 0000000..5bd7fe6
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullExchangeRatePluginManager.cs
@@ -0,0 +1,31 @@
+using Nop.Core.Domain.Customers;
+//using Nop.Core.Domain.Directory;
+//using Nop.Services.Directory;
+//using Nop.Services.Plugins;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+///
+/// Null implementation of IExchangeRatePluginManager for SANDBOX
+///
+//public class NullExchangeRatePluginManager : IExchangeRatePluginManager
+//{
+// public Task LoadPrimaryPluginAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public bool IsPluginActive(IExchangeRateProvider exchangeRateProvider) => false;
+
+// public Task LoadPluginBySystemNameAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task> LoadAllPluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public bool IsPluginActive(IExchangeRateProvider plugin, List systemNames) => false;
+
+// public Task> LoadActivePluginsAsync(List systemNames, Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task GetPluginLogoUrlAsync(IExchangeRateProvider plugin)
+// => Task.FromResult(string.Empty);
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullExternalAuthenticationService.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullExternalAuthenticationService.cs
new file mode 100644
index 0000000..a51e754
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullExternalAuthenticationService.cs
@@ -0,0 +1,35 @@
+using Microsoft.AspNetCore.Mvc;
+using Nop.Core.Domain.Customers;
+//using Nop.Services.Authentication.External;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+///
+/// Null implementation of IExternalAuthenticationService for SANDBOX
+///
+//public class NullExternalAuthenticationService : IExternalAuthenticationService
+//{
+// public Task AuthenticateAsync(ExternalAuthenticationParameters parameters, string returnUrl = null!)
+// => Task.FromResult(new NotFoundResult());
+
+// public Task GetExternalAuthenticationRecordByIdAsync(int externalAuthenticationRecordId)
+// => Task.FromResult(null);
+
+// public Task> GetCustomerExternalAuthenticationRecordsAsync(Customer customer)
+// => Task.FromResult>(new List());
+
+// public Task DeleteExternalAuthenticationRecordAsync(ExternalAuthenticationRecord externalAuthenticationRecord)
+// => Task.CompletedTask;
+
+// public Task GetExternalAuthenticationRecordByExternalAuthenticationParametersAsync(ExternalAuthenticationParameters parameters)
+// => Task.FromResult(null);
+
+// public Task AssociateExternalAccountWithUserAsync(Customer customer, ExternalAuthenticationParameters parameters)
+// => Task.CompletedTask;
+
+// public Task GetUserByExternalAuthenticationParametersAsync(ExternalAuthenticationParameters parameters)
+// => Task.FromResult(null);
+
+// public Task RemoveAssociationAsync(ExternalAuthenticationParameters parameters)
+// => Task.CompletedTask;
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullMultiFactorAuthenticationPluginManager.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullMultiFactorAuthenticationPluginManager.cs
new file mode 100644
index 0000000..922e4f5
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullMultiFactorAuthenticationPluginManager.cs
@@ -0,0 +1,36 @@
+using Nop.Core.Domain.Customers;
+//using Nop.Services.Authentication.MultiFactor;
+//using Nop.Services.Plugins;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+///
+/// Null implementation of IMultiFactorAuthenticationPluginManager for SANDBOX
+///
+//public class NullMultiFactorAuthenticationPluginManager : IMultiFactorAuthenticationPluginManager
+//{
+// public Task LoadPluginBySystemNameAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task> LoadAllPluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(List systemNames, Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task HasActivePluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult(false);
+
+// public bool IsPluginActive(IMultiFactorAuthenticationMethod plugin) => false;
+
+// public bool IsPluginActive(IMultiFactorAuthenticationMethod plugin, List systemNames) => false;
+
+// public Task IsPluginActiveAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(false);
+
+// public Task GetPluginLogoUrlAsync(IMultiFactorAuthenticationMethod plugin)
+// => Task.FromResult(string.Empty);
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullPaymentPluginManager.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullPaymentPluginManager.cs
new file mode 100644
index 0000000..b17d96e
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullPaymentPluginManager.cs
@@ -0,0 +1,36 @@
+using Nop.Core.Domain.Customers;
+//using Nop.Services.Payments;
+//using Nop.Services.Plugins;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+//public class NullPaymentPluginManager : IPaymentPluginManager
+//{
+// public Task LoadPluginBySystemNameAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task> LoadAllPluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(Customer? customer = null, int storeId = 0, int filterByCountryId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(List systemNames, Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public bool IsPluginActive(IPaymentMethod plugin, List systemNames) => false;
+
+// public bool IsPluginActive(IPaymentMethod plugin) => false;
+
+// public Task IsPluginActiveAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(false);
+
+// public Task GetPluginLogoUrlAsync(IPaymentMethod plugin)
+// => Task.FromResult(string.Empty);
+
+// public Task> GetRestrictedCountryIdsAsync(IPaymentMethod paymentMethod)
+// => Task.FromResult>(new List());
+
+// public Task SaveRestrictedCountriesAsync(IPaymentMethod paymentMethod, IList countryIds)
+// => Task.CompletedTask;
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullPickupPluginManager.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullPickupPluginManager.cs
new file mode 100644
index 0000000..7faa52e
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullPickupPluginManager.cs
@@ -0,0 +1,30 @@
+using Nop.Core.Domain.Customers;
+//using Nop.Services.Plugins;
+//using Nop.Services.Shipping.Pickup;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+//public class NullPickupPluginManager : IPickupPluginManager
+//{
+// public Task LoadPluginBySystemNameAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task> LoadAllPluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(Customer? customer = null, int storeId = 0, string filterByCountryId = null!)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(List systemNames, Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public bool IsPluginActive(IPickupPointProvider plugin, List systemNames) => false;
+
+// public bool IsPluginActive(IPickupPointProvider plugin) => false;
+
+// public Task IsPluginActiveAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(false);
+
+// public Task GetPluginLogoUrlAsync(IPickupPointProvider plugin)
+// => Task.FromResult(string.Empty);
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullSearchPluginManager.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullSearchPluginManager.cs
new file mode 100644
index 0000000..fa29bb1
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullSearchPluginManager.cs
@@ -0,0 +1,33 @@
+using Nop.Core.Domain.Customers;
+//using Nop.Services.Catalog;
+//using Nop.Services.Plugins;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+///
+/// Null implementation of ISearchPluginManager for SANDBOX
+///
+//public class NullSearchPluginManager : ISearchPluginManager
+//{
+// public Task LoadPrimaryPluginAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public bool IsPluginActive(ISearchProvider searchProvider) => false;
+
+// public Task IsPluginActiveAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(false);
+
+// public Task LoadPluginBySystemNameAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task> LoadAllPluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public bool IsPluginActive(ISearchProvider plugin, List systemNames) => false;
+
+// public Task> LoadActivePluginsAsync(List systemNames, Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task GetPluginLogoUrlAsync(ISearchProvider plugin)
+// => Task.FromResult(string.Empty);
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullShippingPluginManager.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullShippingPluginManager.cs
new file mode 100644
index 0000000..ae36f18
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullShippingPluginManager.cs
@@ -0,0 +1,36 @@
+using Nop.Core.Domain.Customers;
+//using Nop.Services.Plugins;
+//using Nop.Services.Shipping;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+//public class NullShippingPluginManager : IShippingPluginManager
+//{
+// public Task LoadPluginBySystemNameAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task> LoadAllPluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(Customer? customer = null, int storeId = 0, string filterByCountryId = null!)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(List systemNames, Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public bool IsPluginActive(IShippingRateComputationMethod plugin, List systemNames) => false;
+
+// public bool IsPluginActive(IShippingRateComputationMethod plugin) => false;
+
+// public Task IsPluginActiveAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(false);
+
+// public Task GetPluginLogoUrlAsync(IShippingRateComputationMethod plugin)
+// => Task.FromResult(string.Empty);
+
+// public Task> GetRestrictedCountryIdsAsync(IShippingRateComputationMethod shippingMethod)
+// => Task.FromResult>(new List());
+
+// public Task SaveRestrictedCountriesAsync(IShippingRateComputationMethod shippingMethod, IList countryIds)
+// => Task.CompletedTask;
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullTaxPluginManager.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullTaxPluginManager.cs
new file mode 100644
index 0000000..af3bd27
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullTaxPluginManager.cs
@@ -0,0 +1,30 @@
+using Nop.Core.Domain.Customers;
+//using Nop.Services.Plugins;
+//using Nop.Services.Tax;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+//public class NullTaxPluginManager : ITaxPluginManager
+//{
+// public Task LoadPrimaryPluginAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task LoadPluginBySystemNameAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task> LoadAllPluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(List systemNames, Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public bool IsPluginActive(ITaxProvider plugin, List systemNames) => false;
+
+// public bool IsPluginActive(ITaxProvider plugin) => false;
+
+// public Task IsPluginActiveAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(false);
+
+// public Task GetPluginLogoUrlAsync(ITaxProvider plugin)
+// => Task.FromResult(string.Empty);
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullWidgetPluginManager.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullWidgetPluginManager.cs
new file mode 100644
index 0000000..f9453a4
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/Services/NullWidgetPluginManager.cs
@@ -0,0 +1,30 @@
+using Nop.Core.Domain.Customers;
+//using Nop.Services.Cms;
+//using Nop.Services.Plugins;
+
+namespace Mango.Sandbox.EndPoints.Services;
+
+//public class NullWidgetPluginManager : IWidgetPluginManager
+//{
+// public Task> LoadActivePluginsAsync(Customer? customer = null, int storeId = 0, string widgetZone = null!)
+// => Task.FromResult>(new List());
+
+// public Task LoadPluginBySystemNameAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(null);
+
+// public Task> LoadAllPluginsAsync(Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public Task> LoadActivePluginsAsync(List systemNames, Customer? customer = null, int storeId = 0)
+// => Task.FromResult>(new List());
+
+// public bool IsPluginActive(IWidgetPlugin plugin, List systemNames) => false;
+
+// public bool IsPluginActive(IWidgetPlugin plugin) => false;
+
+// public Task IsPluginActiveAsync(string systemName, Customer? customer = null, int storeId = 0)
+// => Task.FromResult(false);
+
+// public Task GetPluginLogoUrlAsync(IWidgetPlugin plugin)
+// => Task.FromResult(string.Empty);
+//}
diff --git a/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/TestSignalREndpoint.cs b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/TestSignalREndpoint.cs
new file mode 100644
index 0000000..3659e92
--- /dev/null
+++ b/Tests/Mango.Sandbox/Mango.Sandbox.EndPoints/TestSignalREndpoint.cs
@@ -0,0 +1,679 @@
+using AyCode.Services.SignalRs;
+using FruitBank.Common.Dtos;
+using FruitBank.Common.SignalRs;
+using System.Globalization;
+using System.Text.Json.Serialization;
+
+namespace Mango.Sandbox.EndPoints;
+
+///
+/// Egyszerû teszt SignalR endpoint a DevAdminSignalRHub teszteléséhez.
+/// Ez az endpoint minimális függõséggel rendelkezik, hogy könnyebben diagnosztizálható legyen a SignalR Hub mûködése.
+///
+public class TestSignalREndpoint : ITestSignalREndpointServer
+{
+ #region Primitív Paraméter Handlerek
+
+ ///
+ /// Egyszerû ping metódus - visszaadja a kapott üzenetet és egy timestampet.
+ ///
+ [SignalR(TestSignalRTags.PingTag)]
+ public Task Ping(string message)
+ {
+ return Task.FromResult(new TestPingResponse
+ {
+ Message = message,
+ ReceivedAt = DateTime.UtcNow,
+ ServerInfo = $"Sandbox Server - {Environment.MachineName}"
+ });
+ }
+
+ ///
+ /// Int paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.SingleIntParam)]
+ public Task HandleSingleInt(int value)
+ {
+ return Task.FromResult($"Received: {value}");
+ }
+
+ ///
+ /// Két int paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.TwoIntParams)]
+ public Task HandleTwoInts(int a, int b)
+ {
+ return Task.FromResult(a + b);
+ }
+
+ ///
+ /// Bool paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.BoolParam)]
+ public Task HandleBool(bool loadRelations)
+ {
+ return Task.FromResult(loadRelations);
+ }
+
+ ///
+ /// String paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.StringParam)]
+ public Task HandleString(string text)
+ {
+ return Task.FromResult($"Echo: {text}");
+ }
+
+ ///
+ /// Guid paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.GuidParam)]
+ public Task HandleGuid(Guid id)
+ {
+ return Task.FromResult(id);
+ }
+
+ ///
+ /// Enum paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.EnumParam)]
+ public Task HandleEnum(TestStatus status)
+ {
+ return Task.FromResult(status);
+ }
+
+ ///
+ /// Paraméter nélküli metódus teszt
+ ///
+ [SignalR(TestSignalRTags.NoParams)]
+ public Task HandleNoParams()
+ {
+ return Task.FromResult("OK");
+ }
+
+ ///
+ /// Több típusú paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.MultipleTypesParams)]
+ public Task HandleMultipleTypes(bool flag, string text, int number)
+ {
+ return Task.FromResult($"{flag}-{text}-{number}");
+ }
+
+ ///
+ /// Decimal paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.DecimalParam)]
+ public Task HandleDecimal(decimal value)
+ {
+ return Task.FromResult(value * 2);
+ }
+
+ ///
+ /// DateTime paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.DateTimeParam)]
+ public Task HandleDateTime(DateTime dateTime)
+ {
+ return Task.FromResult(dateTime);
+ }
+
+ ///
+ /// Double paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.DoubleParam)]
+ public Task HandleDouble(double value)
+ {
+ return Task.FromResult(value);
+ }
+
+ ///
+ /// Long paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.LongParam)]
+ public Task HandleLong(long value)
+ {
+ return Task.FromResult(value);
+ }
+
+ #endregion
+
+ #region Komplex Objektum Handlerek
+
+ ///
+ /// Egyszerû echo metódus - visszaadja a kapott objektumot változtatás nélkül.
+ ///
+ [SignalR(TestSignalRTags.EchoTag)]
+ public Task Echo(TestEchoRequest request)
+ {
+ return Task.FromResult(new TestEchoResponse
+ {
+ Id = request?.Id ?? 0,
+ Name = request?.Name ?? "Unknown",
+ Timestamp = DateTime.UtcNow,
+ EchoedSuccessfully = true
+ });
+ }
+
+ ///
+ /// TestOrderItem komplex objektum teszt
+ ///
+ [SignalR(TestSignalRTags.TestOrderItemParam)]
+ public Task HandleTestOrderItem(TestOrderItem item)
+ {
+ return Task.FromResult(new TestOrderItem
+ {
+ Id = item.Id,
+ ProductName = $"Processed: {item.ProductName}",
+ Quantity = item.Quantity * 2,
+ UnitPrice = item.UnitPrice * 2,
+ });
+ }
+
+ ///
+ /// TestOrder komplex objektum teszt (beágyazott objektumokkal)
+ ///
+ [SignalR(TestSignalRTags.TestOrderParam)]
+ public Task HandleTestOrder(TestOrder order)
+ {
+ return Task.FromResult(order);
+ }
+
+ ///
+ /// SharedTag komplex objektum teszt
+ ///
+ [SignalR(TestSignalRTags.SharedTagParam)]
+ public Task HandleSharedTag(SharedTag tag)
+ {
+ return Task.FromResult(tag);
+ }
+
+ #endregion
+
+ #region Kollekció Handlerek
+
+ ///
+ /// Lista visszaadása teszthez.
+ ///
+ [SignalR(TestSignalRTags.GetTestItems)]
+ public Task> GetTestItems()
+ {
+ var items = new List
+ {
+ new() { Id = 1, Name = "Item 1", Value = 100.5m },
+ new() { Id = 2, Name = "Item 2", Value = 200.75m },
+ new() { Id = 3, Name = "Item 3", Value = 300.25m }
+ };
+ return Task.FromResult(items);
+ }
+
+ ///
+ /// Int tömb paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.IntArrayParam)]
+ public Task HandleIntArray(int[] values)
+ {
+ return Task.FromResult(values.Select(x => x * 2).ToArray());
+ }
+
+ ///
+ /// Guid tömb paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.GuidArrayParam)]
+ public Task HandleGuidArray(Guid[] ids)
+ {
+ return Task.FromResult(ids);
+ }
+
+ ///
+ /// String lista paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.StringListParam)]
+ public Task> HandleStringList(List items)
+ {
+ return Task.FromResult(items.Select(x => x.ToUpper()).ToList());
+ }
+
+ ///
+ /// TestOrderItem lista paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.TestOrderItemListParam)]
+ public Task> HandleTestOrderItemList(List items)
+ {
+ return Task.FromResult(items);
+ }
+
+ ///
+ /// Int lista paraméter teszt
+ ///
+ [SignalR(TestSignalRTags.IntListParam)]
+ public Task> HandleIntList(List