mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-06-13 14:38:43 +00:00
RPC test
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
using Esiur.Data;
|
||||
using RPC.EsiurTest;
|
||||
using Esiur.Tests.RPC.EsiurServer;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
@@ -8,7 +8,7 @@ using System.Linq;
|
||||
|
||||
|
||||
#nullable enable
|
||||
namespace RPC.Client.Tests;
|
||||
namespace Esiur.Tests.RPC.Client;
|
||||
|
||||
public static class DocGenerator
|
||||
{
|
||||
@@ -1,19 +1,9 @@
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Resource;
|
||||
using RPC.EsiurTest;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Esiur.Tests.RPC.EsiurServer;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Data;
|
||||
|
||||
namespace RPC.Client.Tests.Docs
|
||||
namespace Esiur.Tests.RPC.Client
|
||||
{
|
||||
public static class EsiurTest
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using Echo.Model.Grpc;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Tests.RPC.Client.Grpc;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Net.Client;
|
||||
using System;
|
||||
@@ -8,7 +8,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace RPC.Client.Tests.Docs;
|
||||
namespace Esiur.Tests.RPC.Client;
|
||||
|
||||
public class GrpcTest
|
||||
{
|
||||
@@ -26,7 +26,7 @@ public class GrpcTest
|
||||
Console.WriteLine($"\n== Grpc @ {address} ==");
|
||||
|
||||
using var channel = GrpcChannel.ForAddress(address);
|
||||
var service = new Echo.Model.Grpc.EchoService.EchoServiceClient(channel);
|
||||
var service = new Client.Grpc.EchoService.EchoServiceClient(channel);
|
||||
|
||||
|
||||
Thread.Sleep(3000);
|
||||
@@ -1,4 +1,4 @@
|
||||
using RPC.EsiurTest;
|
||||
using Esiur.Tests.RPC.EsiurServer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@@ -6,7 +6,7 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace RPC.Client.Tests.Docs;
|
||||
namespace Esiur.Tests.RPC.Client;
|
||||
|
||||
|
||||
public class JsonTest
|
||||
@@ -1,10 +0,0 @@
|
||||
using System;
|
||||
namespace Esiur
|
||||
{
|
||||
public static class Generated
|
||||
{
|
||||
public static Type[] Resources { get; } = new Type[] { typeof(RPC.EsiurTest.Service), typeof(RPC.EsiurTest.TestObject) };
|
||||
public static Type[] Records { get; } = new Type[] { typeof(RPC.EsiurTest.BusinessDocument), typeof(RPC.EsiurTest.Attachment), typeof(RPC.EsiurTest.Party), typeof(RPC.EsiurTest.Address), typeof(RPC.EsiurTest.DocumentHeader), typeof(RPC.EsiurTest.LineItem), typeof(RPC.EsiurTest.Variant), typeof(RPC.EsiurTest.Payment) };
|
||||
public static Type[] Enums { get; } = new Type[] { typeof(RPC.EsiurTest.Currency), typeof(RPC.EsiurTest.DocType), typeof(RPC.EsiurTest.Kind), typeof(RPC.EsiurTest.LineType), typeof(RPC.EsiurTest.PaymentMethod) };
|
||||
}
|
||||
}
|
||||
+16
-14
@@ -3,28 +3,29 @@ using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
namespace RPC.EsiurTest
|
||||
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("0f8f447ee993847189b2c1ad6f83931a")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.Address", "")]
|
||||
[Export]
|
||||
public class Address : IRecord
|
||||
{
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string City { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string Country { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string Line1 { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
public string? Line2 { get; set; }
|
||||
[Annotation("", "String")]
|
||||
public string Line2 { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
public string? PostalCode { get; set; }
|
||||
[Annotation("", "String")]
|
||||
public string PostalCode { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string Region { get; set; }
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
@@ -41,9 +42,9 @@ namespace RPC.EsiurTest
|
||||
return true;
|
||||
}
|
||||
|
||||
public SharedModel.Address ToShared()
|
||||
public Client.SharedModel.Address ToShared()
|
||||
{
|
||||
return new SharedModel.Address()
|
||||
return new Client.SharedModel.Address()
|
||||
{
|
||||
City = City,
|
||||
Country = Country,
|
||||
@@ -55,9 +56,9 @@ namespace RPC.EsiurTest
|
||||
};
|
||||
}
|
||||
|
||||
public Echo.Model.Grpc.Address ToGrpc()
|
||||
public Esiur.Tests.RPC.Client.Grpc.Address ToGrpc()
|
||||
{
|
||||
return new Echo.Model.Grpc.Address()
|
||||
return new Esiur.Tests.RPC.Client.Grpc.Address()
|
||||
{
|
||||
City = City,
|
||||
Country = Country,
|
||||
@@ -80,5 +81,6 @@ namespace RPC.EsiurTest
|
||||
Region = Region,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+12
-11
@@ -1,24 +1,25 @@
|
||||
using System;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
using Esiur.Resource;
|
||||
using Google.Protobuf;
|
||||
namespace RPC.EsiurTest
|
||||
using System;
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("4befaa686f038a2885268fca4cbf3c2c")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.Attachment", "")]
|
||||
[Export]
|
||||
public class Attachment : IRecord
|
||||
{
|
||||
[Annotation("Byte[]")]
|
||||
[Annotation("", "Byte[]")]
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string MimeType { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string Name { get; set; }
|
||||
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
var other = obj as Attachment;
|
||||
@@ -29,9 +30,9 @@ namespace RPC.EsiurTest
|
||||
return true;
|
||||
}
|
||||
|
||||
public SharedModel.Attachment ToShared()
|
||||
public Client.SharedModel.Attachment ToShared()
|
||||
{
|
||||
return new SharedModel.Attachment()
|
||||
return new Client.SharedModel.Attachment()
|
||||
{
|
||||
Data = Data,
|
||||
MimeType = MimeType,
|
||||
@@ -39,9 +40,9 @@ namespace RPC.EsiurTest
|
||||
};
|
||||
}
|
||||
|
||||
public Echo.Model.Grpc.Attachment ToGrpc()
|
||||
public Esiur.Tests.RPC.Client.Grpc.Attachment ToGrpc()
|
||||
{
|
||||
return new Echo.Model.Grpc.Attachment()
|
||||
return new Esiur.Tests.RPC.Client.Grpc.Attachment()
|
||||
{
|
||||
Data = ByteString.CopyFrom(Data),
|
||||
MimeType = MimeType,
|
||||
+23
-26
@@ -3,34 +3,32 @@ using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.Collections;
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("9a34d22890e787b48133a2a61ac84ad8")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.BusinessDocument", "")]
|
||||
[Export]
|
||||
public class BusinessDocument : IRecord
|
||||
{
|
||||
[Annotation("Attachment[]")]
|
||||
public RPC.EsiurTest.Attachment[] Attachments { get; set; }
|
||||
[Annotation("", "Attachment[]")]
|
||||
public Esiur.Tests.RPC.EsiurServer.Attachment[] Attachments { get; set; }
|
||||
|
||||
[Annotation("Party")]
|
||||
public RPC.EsiurTest.Party Buyer { get; set; }
|
||||
[Annotation("", "Party")]
|
||||
public Esiur.Tests.RPC.EsiurServer.Party Buyer { get; set; }
|
||||
|
||||
[Annotation("DocumentHeader")]
|
||||
public RPC.EsiurTest.DocumentHeader Header { get; set; }
|
||||
[Annotation("", "DocumentHeader")]
|
||||
public Esiur.Tests.RPC.EsiurServer.DocumentHeader Header { get; set; }
|
||||
|
||||
[Annotation("LineItem[]")]
|
||||
public RPC.EsiurTest.LineItem[] Items { get; set; }
|
||||
[Annotation("", "LineItem[]")]
|
||||
public Esiur.Tests.RPC.EsiurServer.LineItem[] Items { get; set; }
|
||||
|
||||
[Annotation("Payment[]")]
|
||||
public RPC.EsiurTest.Payment[] Payments { get; set; }
|
||||
[Annotation("", "Payment[]")]
|
||||
public Esiur.Tests.RPC.EsiurServer.Payment[] Payments { get; set; }
|
||||
|
||||
[Annotation("Int32[]")]
|
||||
[Annotation("", "Int32[]")]
|
||||
public int[] RiskScores { get; set; }
|
||||
|
||||
[Annotation("Party")]
|
||||
public RPC.EsiurTest.Party Seller { get; set; }
|
||||
[Annotation("", "Party")]
|
||||
public Esiur.Tests.RPC.EsiurServer.Party Seller { get; set; }
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
@@ -68,15 +66,15 @@ namespace RPC.EsiurTest
|
||||
}
|
||||
|
||||
|
||||
public SharedModel.BusinessDocument ToShared()
|
||||
public Client.SharedModel.BusinessDocument ToShared()
|
||||
{
|
||||
return new SharedModel.BusinessDocument()
|
||||
return new Client.SharedModel.BusinessDocument()
|
||||
{
|
||||
Attachments = Attachments?.Select(x=>x.ToShared()).ToArray() ?? null,
|
||||
Attachments = Attachments?.Select(x => x.ToShared()).ToArray() ?? null,
|
||||
Buyer = Buyer.ToShared(),
|
||||
Header = Header.ToShared(),
|
||||
Items = Items.Select(x=>x.ToShared()).ToArray(),
|
||||
Payments = Payments.Select(x=>x.ToShared()).ToArray(),
|
||||
Items = Items.Select(x => x.ToShared()).ToArray(),
|
||||
Payments = Payments.Select(x => x.ToShared()).ToArray(),
|
||||
RiskScores = RiskScores,
|
||||
Seller = Seller.ToShared(),
|
||||
};
|
||||
@@ -96,7 +94,7 @@ namespace RPC.EsiurTest
|
||||
rt.Seller = Seller.ToThrift();
|
||||
|
||||
if (Attachments != null)
|
||||
rt.Attachments = Attachments.Select(x=>x.ToThrift()).ToList();
|
||||
rt.Attachments = Attachments.Select(x => x.ToThrift()).ToList();
|
||||
|
||||
if (RiskScores != null)
|
||||
rt.RiskScores = RiskScores.ToList();
|
||||
@@ -110,10 +108,10 @@ namespace RPC.EsiurTest
|
||||
return rt;
|
||||
}
|
||||
|
||||
public Echo.Model.Grpc.BusinessDocument ToGrpc()
|
||||
public Esiur.Tests.RPC.Client.Grpc.BusinessDocument ToGrpc()
|
||||
{
|
||||
|
||||
var rt = new Echo.Model.Grpc.BusinessDocument()
|
||||
var rt = new Esiur.Tests.RPC.Client.Grpc.BusinessDocument()
|
||||
{
|
||||
Header = Header.ToGrpc(),
|
||||
Buyer = Buyer.ToGrpc(),
|
||||
@@ -140,6 +138,5 @@ namespace RPC.EsiurTest
|
||||
return rt;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -3,9 +3,9 @@ using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("c44e42333dfd8d3485bb2a79fd7a9f6f")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.Currency", "")]
|
||||
[Export]
|
||||
public enum Currency
|
||||
{
|
||||
+2
-2
@@ -3,9 +3,9 @@ using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("6ded4eca74c8886a85a74e082770be4b")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.DocType", "")]
|
||||
[Export]
|
||||
public enum DocType
|
||||
{
|
||||
+26
-26
@@ -4,49 +4,49 @@ using Esiur.Protocol;
|
||||
using Esiur.Resource;
|
||||
using Google.Protobuf;
|
||||
using System;
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("4631164f62d489e68ffab70e20b421f2")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.DocumentHeader", "")]
|
||||
[Export]
|
||||
public class DocumentHeader : IRecord
|
||||
{
|
||||
[Annotation("DateTime")]
|
||||
[Annotation("", "DateTime")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[Annotation("Currency")]
|
||||
public RPC.EsiurTest.Currency Currency { get; set; }
|
||||
[Annotation("", "Currency")]
|
||||
public Esiur.Tests.RPC.EsiurServer.Currency Currency { get; set; }
|
||||
|
||||
[Annotation("Byte[]")]
|
||||
[Annotation("", "Byte[]")]
|
||||
public byte[] DocId { get; set; }
|
||||
|
||||
[Annotation("Dictionary`2")]
|
||||
public Map<string, RPC.EsiurTest.Variant> Meta { get; set; }
|
||||
[Annotation("", "Dictionary`2")]
|
||||
public Map<string, Esiur.Tests.RPC.EsiurServer.Variant> Meta { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
public string? Notes { get; set; }
|
||||
[Annotation("", "String")]
|
||||
public string Notes { get; set; }
|
||||
|
||||
[Annotation("DocType")]
|
||||
public RPC.EsiurTest.DocType Type { get; set; }
|
||||
[Annotation("", "DocType")]
|
||||
public Esiur.Tests.RPC.EsiurServer.DocType Type { get; set; }
|
||||
|
||||
[Annotation("Nullable`1?")]
|
||||
[Annotation("", "Nullable`1?")]
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
[Annotation("Int32")]
|
||||
[Annotation("", "Int32")]
|
||||
public int Version { get; set; }
|
||||
|
||||
|
||||
public SharedModel.DocumentHeader ToShared()
|
||||
public Client.SharedModel.DocumentHeader ToShared()
|
||||
{
|
||||
return new SharedModel.DocumentHeader()
|
||||
return new Client.SharedModel.DocumentHeader()
|
||||
{
|
||||
CreatedAt = CreatedAt,
|
||||
DocId = DocId,
|
||||
Meta = Meta.ToDictionary(x=>x.Key, v=>v.Value.ToShared()),
|
||||
Meta = Meta.ToDictionary(x => x.Key, v => v.Value.ToShared()),
|
||||
Notes = Notes,
|
||||
Currency = Enum.Parse<SharedModel.Currency>(Currency.ToString(), true),
|
||||
Currency = Enum.Parse<Client.SharedModel.Currency>(Currency.ToString(), true),
|
||||
UpdatedAt = UpdatedAt,
|
||||
Version = Version,
|
||||
Type = Enum.Parse<SharedModel.DocType>(Type.ToString(), true)
|
||||
Type = Enum.Parse<Client.SharedModel.DocType>(Type.ToString(), true)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,11 +55,11 @@ namespace RPC.EsiurTest
|
||||
var rt = new Echo.ThriftModel.DocumentHeader()
|
||||
{
|
||||
DocId = DocId,
|
||||
CreatedAt = CreatedAt.Ticks,
|
||||
Currency = Enum.Parse< Echo.ThriftModel.Currency>(Currency.ToString(), true),
|
||||
Type = Enum.Parse< Echo.ThriftModel.DocType>(Type.ToString(), true),
|
||||
CreatedAt = CreatedAt.Ticks,
|
||||
Currency = Enum.Parse<Echo.ThriftModel.Currency>(Currency.ToString(), true),
|
||||
Type = Enum.Parse<Echo.ThriftModel.DocType>(Type.ToString(), true),
|
||||
Version = Version,
|
||||
Meta = Meta.ToDictionary(x=>x.Key, x=>x.Value.ToThrift())
|
||||
Meta = Meta.ToDictionary(x => x.Key, x => x.Value.ToThrift())
|
||||
};
|
||||
|
||||
if (UpdatedAt != null)
|
||||
@@ -71,13 +71,13 @@ namespace RPC.EsiurTest
|
||||
return rt;
|
||||
}
|
||||
|
||||
public Echo.Model.Grpc.DocumentHeader ToGrpc()
|
||||
public Esiur.Tests.RPC.Client.Grpc.DocumentHeader ToGrpc()
|
||||
{
|
||||
var hdr = new Echo.Model.Grpc.DocumentHeader();
|
||||
var hdr = new Esiur.Tests.RPC.Client.Grpc.DocumentHeader();
|
||||
|
||||
hdr.DocId = ByteString.CopyFrom(DocId);
|
||||
hdr.CreatedAt = CreatedAt.Ticks;
|
||||
hdr.Currency = Enum.Parse<Echo.Model.Grpc.Currency>(Currency.ToString(), true);
|
||||
hdr.Currency = Enum.Parse<Esiur.Tests.RPC.Client.Grpc.Currency>(Currency.ToString(), true);
|
||||
hdr.Version = Version;
|
||||
hdr.Notes = Notes;
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@ using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("32ae8265068382608399b7e427be37db")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.Kind", "")]
|
||||
[Export]
|
||||
public enum Kind
|
||||
{
|
||||
+20
-20
@@ -3,46 +3,46 @@ using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("142f42b0e1a78c098f35fa935cde22c1")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.LineItem", "")]
|
||||
[Export]
|
||||
public class LineItem : IRecord
|
||||
{
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[Annotation("Nullable`1?")]
|
||||
[Annotation("", "Nullable`1?")]
|
||||
public double? Discount { get; set; }
|
||||
|
||||
[Annotation("Map`2")]
|
||||
public Map<string, RPC.EsiurTest.Variant> Ext { get; set; }
|
||||
[Annotation("", "Map`2")]
|
||||
public Map<string, Esiur.Tests.RPC.EsiurServer.Variant> Ext { get; set; }
|
||||
|
||||
[Annotation("Int32")]
|
||||
[Annotation("", "Int32")]
|
||||
public int LineNo { get; set; }
|
||||
|
||||
[Annotation("Double")]
|
||||
[Annotation("", "Double")]
|
||||
public double Qty { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string QtyUnit { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string SKU { get; set; }
|
||||
|
||||
[Annotation("LineType")]
|
||||
public RPC.EsiurTest.LineType Type { get; set; }
|
||||
[Annotation("", "LineType")]
|
||||
public Esiur.Tests.RPC.EsiurServer.LineType Type { get; set; }
|
||||
|
||||
[Annotation("Double")]
|
||||
[Annotation("", "Double")]
|
||||
public double UnitPrice { get; set; }
|
||||
|
||||
[Annotation("Nullable`1?")]
|
||||
[Annotation("", "Nullable`1?")]
|
||||
public double? VatRate { get; set; }
|
||||
|
||||
|
||||
public SharedModel.LineItem ToShared()
|
||||
public Client.SharedModel.LineItem ToShared()
|
||||
{
|
||||
return new SharedModel.LineItem()
|
||||
return new Client.SharedModel.LineItem()
|
||||
{
|
||||
Description = Description,
|
||||
Discount = Discount,
|
||||
@@ -51,7 +51,7 @@ namespace RPC.EsiurTest
|
||||
Qty = Qty,
|
||||
QtyUnit = QtyUnit,
|
||||
SKU = SKU,
|
||||
Type = Enum.Parse<SharedModel.LineType>(Type.ToString(), true),
|
||||
Type = Enum.Parse<Client.SharedModel.LineType>(Type.ToString(), true),
|
||||
UnitPrice = UnitPrice,
|
||||
VatRate = VatRate
|
||||
};
|
||||
@@ -80,9 +80,9 @@ namespace RPC.EsiurTest
|
||||
return rt;
|
||||
}
|
||||
|
||||
public Echo.Model.Grpc.LineItem ToGrpc()
|
||||
public Esiur.Tests.RPC.Client.Grpc.LineItem ToGrpc()
|
||||
{
|
||||
var rt = new Echo.Model.Grpc.LineItem()
|
||||
var rt = new Esiur.Tests.RPC.Client.Grpc.LineItem()
|
||||
{
|
||||
Description = Description,
|
||||
Discount = Discount ?? 0,
|
||||
@@ -91,7 +91,7 @@ namespace RPC.EsiurTest
|
||||
UnitPrice = UnitPrice,
|
||||
QtyUnit = QtyUnit,
|
||||
Sku = SKU,
|
||||
Type = Enum.Parse<Echo.Model.Grpc.LineType>(Type.ToString(), true),
|
||||
Type = Enum.Parse<Esiur.Tests.RPC.Client.Grpc.LineType>(Type.ToString(), true),
|
||||
VatRate = VatRate ?? 0,
|
||||
};
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@ using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("7e474e8826e288f28bddddf69782c580")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.LineType", "")]
|
||||
[Export]
|
||||
public enum LineType
|
||||
{
|
||||
+14
-14
@@ -3,36 +3,36 @@ using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("44fff9c7bd9b86f580bf479a64cb84af")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.Party", "")]
|
||||
[Export]
|
||||
public class Party : IRecord
|
||||
{
|
||||
[Annotation("Address")]
|
||||
public RPC.EsiurTest.Address Address { get; set; }
|
||||
[Annotation("", "Address")]
|
||||
public Esiur.Tests.RPC.EsiurServer.Address Address { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Annotation("UInt64")]
|
||||
[Annotation("", "UInt64")]
|
||||
public ulong Id { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string Phone { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string PreferredLanguage { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string TaxId { get; set; }
|
||||
|
||||
public SharedModel.Party ToShared()
|
||||
public Client.SharedModel.Party ToShared()
|
||||
{
|
||||
return new SharedModel.Party()
|
||||
return new Client.SharedModel.Party()
|
||||
{
|
||||
Address = Address.ToShared(),
|
||||
Email = Email,
|
||||
@@ -58,9 +58,9 @@ namespace RPC.EsiurTest
|
||||
};
|
||||
}
|
||||
|
||||
public Echo.Model.Grpc.Party ToGrpc()
|
||||
public Esiur.Tests.RPC.Client.Grpc.Party ToGrpc()
|
||||
{
|
||||
return new Echo.Model.Grpc.Party()
|
||||
return new Esiur.Tests.RPC.Client.Grpc.Party()
|
||||
{
|
||||
Address = Address.ToGrpc(),
|
||||
Email = Email,
|
||||
+19
-19
@@ -3,38 +3,38 @@ using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("f172196340298b8586fed434c72bc158")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.Payment", "")]
|
||||
[Export]
|
||||
public class Payment : IRecord
|
||||
{
|
||||
[Annotation("Double")]
|
||||
[Annotation("", "Double")]
|
||||
public double Amount { get; set; }
|
||||
|
||||
[Annotation("Currency")]
|
||||
public RPC.EsiurTest.Currency Currency { get; set; }
|
||||
[Annotation("", "Currency")]
|
||||
public Esiur.Tests.RPC.EsiurServer.Currency Currency { get; set; }
|
||||
|
||||
[Annotation("Nullable`1?")]
|
||||
[Annotation("", "Nullable`1?")]
|
||||
public double? Fee { get; set; }
|
||||
|
||||
[Annotation("PaymentMethod")]
|
||||
public RPC.EsiurTest.PaymentMethod Method { get; set; }
|
||||
[Annotation("", "PaymentMethod")]
|
||||
public Esiur.Tests.RPC.EsiurServer.PaymentMethod Method { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string Reference { get; set; }
|
||||
|
||||
[Annotation("DateTime")]
|
||||
[Annotation("", "DateTime")]
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
|
||||
public SharedModel.Payment ToShared()
|
||||
public Client.SharedModel.Payment ToShared()
|
||||
{
|
||||
return new SharedModel.Payment()
|
||||
return new Client.SharedModel.Payment()
|
||||
{
|
||||
Amount = Amount,
|
||||
Currency = Enum.Parse<SharedModel.Currency>(Currency.ToString(), true),
|
||||
Method = Enum.Parse<SharedModel.PaymentMethod>(Method.ToString(), true),
|
||||
Currency = Enum.Parse<Client.SharedModel.Currency>(Currency.ToString(), true),
|
||||
Method = Enum.Parse<Client.SharedModel.PaymentMethod>(Method.ToString(), true),
|
||||
Reference = Reference,
|
||||
Timestamp = Timestamp,
|
||||
Fee = Fee,
|
||||
@@ -43,7 +43,7 @@ namespace RPC.EsiurTest
|
||||
|
||||
public Echo.ThriftModel.Payment ToThrift()
|
||||
{
|
||||
var rt= new Echo.ThriftModel.Payment()
|
||||
var rt = new Echo.ThriftModel.Payment()
|
||||
{
|
||||
Amount = Amount,
|
||||
Currency = Enum.Parse<Echo.ThriftModel.Currency>(Currency.ToString(), true),
|
||||
@@ -58,14 +58,14 @@ namespace RPC.EsiurTest
|
||||
return rt;
|
||||
}
|
||||
|
||||
public Echo.Model.Grpc.Payment ToGrpc()
|
||||
public Esiur.Tests.RPC.Client.Grpc.Payment ToGrpc()
|
||||
{
|
||||
return new Echo.Model.Grpc.Payment()
|
||||
return new Esiur.Tests.RPC.Client.Grpc.Payment()
|
||||
{
|
||||
Amount = Amount,
|
||||
Currency = Enum.Parse<Echo.Model.Grpc.Currency>(Currency.ToString(), true),
|
||||
Currency = Enum.Parse<Esiur.Tests.RPC.Client.Grpc.Currency>(Currency.ToString(), true),
|
||||
Fee = Fee ?? 0,
|
||||
Method = Enum.Parse<Echo.Model.Grpc.PaymentMethod>(Method.ToString(), true),
|
||||
Method = Enum.Parse<Esiur.Tests.RPC.Client.Grpc.PaymentMethod>(Method.ToString(), true),
|
||||
Reference = Reference,
|
||||
Timestamp = Timestamp.Ticks,
|
||||
};
|
||||
+2
-3
@@ -3,9 +3,9 @@ using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("fadfe3764f808d7e839fef5275490dd7")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.PaymentMethod", "")]
|
||||
[Export]
|
||||
public enum PaymentMethod
|
||||
{
|
||||
@@ -14,6 +14,5 @@ namespace RPC.EsiurTest
|
||||
Crypto = 3,
|
||||
Other = 4,
|
||||
Wire = 2
|
||||
|
||||
}
|
||||
}
|
||||
+38
-39
@@ -4,15 +4,14 @@ using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
#nullable enable
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
//{ab8e681b-61d9-8fb7-8c63-bbded15457e1}
|
||||
[TypeId("f7e00be8881d88d68a8a2dc2d3a4b3d1")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.Service", "")]
|
||||
public class Service : EpResource
|
||||
{
|
||||
public Service(EpConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) { }
|
||||
public Service() { }
|
||||
[Annotation("([Int32] count,[Int32] size,[Int32] delay) -> AsyncReply`1")]
|
||||
[Annotation("", "([Int32] count,[Int32] size,[Int32] delay) -> AsyncReply`1")]
|
||||
[Export]
|
||||
public AsyncReply<byte[]> ChunkTest(int count, int size, int delay)
|
||||
{
|
||||
@@ -24,7 +23,7 @@ namespace RPC.EsiurTest
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([Byte[]] payload) -> Byte[]")]
|
||||
[Annotation("", "([Byte[]] payload) -> Byte[]")]
|
||||
[Export]
|
||||
public AsyncReply<byte[]> EchoBytes(byte[] payload)
|
||||
{
|
||||
@@ -36,31 +35,31 @@ namespace RPC.EsiurTest
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([BusinessDocument[]] payload) -> BusinessDocument[]")]
|
||||
[Annotation("", "([BusinessDocument[]] payload) -> BusinessDocument[]")]
|
||||
[Export]
|
||||
public AsyncReply<RPC.EsiurTest.BusinessDocument[]> EchoDocuments(RPC.EsiurTest.BusinessDocument[] payload)
|
||||
public AsyncReply<Esiur.Tests.RPC.EsiurServer.BusinessDocument[]> EchoDocuments(Esiur.Tests.RPC.EsiurServer.BusinessDocument[] payload)
|
||||
{
|
||||
var args = new Map<byte, object>() { [0] = payload };
|
||||
var rt = new AsyncReply<RPC.EsiurTest.BusinessDocument[]>();
|
||||
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.BusinessDocument[]>();
|
||||
_Invoke(2, args)
|
||||
.Then(x => rt.Trigger((RPC.EsiurTest.BusinessDocument[])x))
|
||||
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.BusinessDocument[])x))
|
||||
.Error(x => rt.TriggerError(x))
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([DocType[]] payload) -> DocType[]")]
|
||||
[Annotation("", "([DocType[]] payload) -> DocType[]")]
|
||||
[Export]
|
||||
public AsyncReply<RPC.EsiurTest.DocType[]> EchoEnumArray(RPC.EsiurTest.DocType[] payload)
|
||||
public AsyncReply<Esiur.Tests.RPC.EsiurServer.DocType[]> EchoEnumArray(Esiur.Tests.RPC.EsiurServer.DocType[] payload)
|
||||
{
|
||||
var args = new Map<byte, object>() { [0] = payload };
|
||||
var rt = new AsyncReply<RPC.EsiurTest.DocType[]>();
|
||||
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.DocType[]>();
|
||||
_Invoke(3, args)
|
||||
.Then(x => rt.Trigger((RPC.EsiurTest.DocType[])x))
|
||||
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.DocType[])x))
|
||||
.Error(x => rt.TriggerError(x))
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([Int32[]] payload) -> Int32[]")]
|
||||
[Annotation("", "([Int32[]] payload) -> Int32[]")]
|
||||
[Export]
|
||||
public AsyncReply<int[]> EchoIntArray(int[] payload)
|
||||
{
|
||||
@@ -72,19 +71,19 @@ namespace RPC.EsiurTest
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([Map`2] payload) -> Map`2")]
|
||||
[Annotation("", "([Map`2] payload) -> Map`2")]
|
||||
[Export]
|
||||
public AsyncReply<Map<string, RPC.EsiurTest.BusinessDocument>> EchoMap(Map<string, RPC.EsiurTest.BusinessDocument> payload)
|
||||
public AsyncReply<Map<string, Esiur.Tests.RPC.EsiurServer.BusinessDocument>> EchoMap(Map<string, Esiur.Tests.RPC.EsiurServer.BusinessDocument> payload)
|
||||
{
|
||||
var args = new Map<byte, object>() { [0] = payload };
|
||||
var rt = new AsyncReply<Map<string, RPC.EsiurTest.BusinessDocument>>();
|
||||
var rt = new AsyncReply<Map<string, Esiur.Tests.RPC.EsiurServer.BusinessDocument>>();
|
||||
_Invoke(5, args)
|
||||
.Then(x => rt.Trigger((Map<string, RPC.EsiurTest.BusinessDocument>)x))
|
||||
.Then(x => rt.Trigger((Map<string, Esiur.Tests.RPC.EsiurServer.BusinessDocument>)x))
|
||||
.Error(x => rt.TriggerError(x))
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([String[]] payload) -> String[]")]
|
||||
[Annotation("", "([String[]] payload) -> String[]")]
|
||||
[Export]
|
||||
public AsyncReply<string[]> EchoStringArray(string[] payload)
|
||||
{
|
||||
@@ -96,7 +95,7 @@ namespace RPC.EsiurTest
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([Int32] count,[Int32] size,[Int32] delay) -> Void")]
|
||||
[Annotation("", "([Int32] count,[Int32] size,[Int32] delay) -> Void")]
|
||||
[Export]
|
||||
public AsyncReply<object> EventTest(int count, int size, int delay)
|
||||
{
|
||||
@@ -108,7 +107,7 @@ namespace RPC.EsiurTest
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([Int32] count,[Int32] size,[Int32] delay) -> Void")]
|
||||
[Annotation("", "([Int32] count,[Int32] size,[Int32] delay) -> Void")]
|
||||
[Export]
|
||||
public AsyncReply<object> PropertyChangeTest(int count, int size, int delay)
|
||||
{
|
||||
@@ -120,62 +119,62 @@ namespace RPC.EsiurTest
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([Int32] interval,[Int32] count,[Double] localProbability,[Double] remoteProbability,[String] remoteHostLink) -> AsyncReply`1")]
|
||||
[Annotation("", "([Int32] interval,[Int32] count,[Double] localProbability,[Double] remoteProbability,[String] remoteHostLink) -> AsyncReply`1")]
|
||||
[Export]
|
||||
public AsyncReply<RPC.EsiurTest.TestObject> StartUpdates(int interval, int count, double localProbability, double remoteProbability, string remoteHostLink)
|
||||
public AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject> StartUpdates(int interval, int count, double localProbability, double remoteProbability, string remoteHostLink)
|
||||
{
|
||||
var args = new Map<byte, object>() { [0] = interval, [1] = count, [2] = localProbability, [3] = remoteProbability, [4] = remoteHostLink };
|
||||
var rt = new AsyncReply<RPC.EsiurTest.TestObject>();
|
||||
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject>();
|
||||
_Invoke(9, args)
|
||||
.Then(x => rt.Trigger((RPC.EsiurTest.TestObject)x))
|
||||
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.TestObject)x))
|
||||
.Error(x => rt.TriggerError(x))
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([Int32] interval,[Int32] count,[Double] localProbability) -> AsyncReply`1")]
|
||||
[Annotation("", "([Int32] interval,[Int32] count,[Double] localProbability) -> AsyncReply`1")]
|
||||
[Export]
|
||||
public AsyncReply<RPC.EsiurTest.TestObject> StartUpdatesLocal(int interval, int count, double localProbability)
|
||||
public AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject> StartUpdatesLocal(int interval, int count, double localProbability)
|
||||
{
|
||||
var args = new Map<byte, object>() { [0] = interval, [1] = count, [2] = localProbability };
|
||||
var rt = new AsyncReply<RPC.EsiurTest.TestObject>();
|
||||
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject>();
|
||||
_Invoke(10, args)
|
||||
.Then(x => rt.Trigger((RPC.EsiurTest.TestObject)x))
|
||||
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.TestObject)x))
|
||||
.Error(x => rt.TriggerError(x))
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([Int32] interval,[Int32] count,[Double] remoteProbability,[String] remoteNode,[String] remoteLink) -> AsyncReply`1")]
|
||||
[Annotation("", "([Int32] interval,[Int32] count,[Double] remoteProbability,[String] remoteNode,[String] remoteLink) -> AsyncReply`1")]
|
||||
[Export]
|
||||
public AsyncReply<RPC.EsiurTest.TestObject> StartUpdatesMirror(int interval, int count, double remoteProbability, string remoteNode, string remoteLink)
|
||||
public AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject> StartUpdatesMirror(int interval, int count, double remoteProbability, string remoteNode, string remoteLink)
|
||||
{
|
||||
var args = new Map<byte, object>() { [0] = interval, [1] = count, [2] = remoteProbability, [3] = remoteNode, [4] = remoteLink };
|
||||
var rt = new AsyncReply<RPC.EsiurTest.TestObject>();
|
||||
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject>();
|
||||
_Invoke(11, args)
|
||||
.Then(x => rt.Trigger((RPC.EsiurTest.TestObject)x))
|
||||
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.TestObject)x))
|
||||
.Error(x => rt.TriggerError(x))
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("([Int32] interval,[Int32] count,[Double] remoteProbability,[String] remoteLink) -> AsyncReply`1")]
|
||||
[Annotation("", "([Int32] interval,[Int32] count,[Double] remoteProbability,[String] remoteLink) -> AsyncReply`1")]
|
||||
[Export]
|
||||
public AsyncReply<RPC.EsiurTest.TestObject> StartUpdatesRemote(int interval, int count, double remoteProbability, string remoteLink)
|
||||
public AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject> StartUpdatesRemote(int interval, int count, double remoteProbability, string remoteLink)
|
||||
{
|
||||
var args = new Map<byte, object>() { [0] = interval, [1] = count, [2] = remoteProbability, [3] = remoteLink };
|
||||
var rt = new AsyncReply<RPC.EsiurTest.TestObject>();
|
||||
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject>();
|
||||
_Invoke(12, args)
|
||||
.Then(x => rt.Trigger((RPC.EsiurTest.TestObject)x))
|
||||
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.TestObject)x))
|
||||
.Error(x => rt.TriggerError(x))
|
||||
.Chunk(x => rt.TriggerChunk(x));
|
||||
return rt;
|
||||
}
|
||||
[Annotation("Byte[]")]
|
||||
[Annotation("", "Byte[]")]
|
||||
[Export]
|
||||
public byte[] MessageToChange
|
||||
{
|
||||
get => (byte[])_properties[0];
|
||||
set => SetResourceProperty(0, value);
|
||||
}
|
||||
[Annotation("Object")]
|
||||
[Annotation("", "Object")]
|
||||
[Export]
|
||||
public object TestProperty
|
||||
{
|
||||
+8
-8
@@ -4,32 +4,32 @@ using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
#nullable enable
|
||||
namespace RPC.EsiurTest
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("d90d3558e2b18d9a8f45707372ddf2c3")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.TestObject", "")]
|
||||
public class TestObject : EpResource
|
||||
{
|
||||
public TestObject(EpConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) { }
|
||||
public TestObject() { }
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
[Export]
|
||||
public string Name
|
||||
{
|
||||
get => (string)properties[0];
|
||||
get => (string)_properties[0];
|
||||
set => SetResourceProperty(0, value);
|
||||
}
|
||||
[Annotation("Int32")]
|
||||
[Annotation("", "Int32")]
|
||||
[Export]
|
||||
public int Size
|
||||
{
|
||||
get => (int)properties[1];
|
||||
get => (int)_properties[1];
|
||||
set => SetResourceProperty(1, value);
|
||||
}
|
||||
[Annotation("Object")]
|
||||
[Annotation("", "Object")]
|
||||
[Export]
|
||||
public object Value
|
||||
{
|
||||
get => (object)properties[2];
|
||||
get => (object)_properties[2];
|
||||
set => SetResourceProperty(2, value);
|
||||
}
|
||||
|
||||
+22
-21
@@ -1,46 +1,47 @@
|
||||
using System;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
using Esiur.Resource;
|
||||
using Google.Protobuf;
|
||||
|
||||
namespace RPC.EsiurTest
|
||||
using System;
|
||||
namespace Esiur.Tests.RPC.EsiurServer
|
||||
{
|
||||
[TypeId("91ed22c5c53e846181f799dc76ddd93c")]
|
||||
[Remote("Esiur.Tests.RPC.EsiurServer.Variant", "")]
|
||||
[Export]
|
||||
public class Variant : IRecord
|
||||
{
|
||||
[Annotation("Nullable`1?")]
|
||||
[Annotation("", "Nullable`1?")]
|
||||
public bool? Bool { get; set; }
|
||||
|
||||
[Annotation("Byte[]")]
|
||||
[Annotation("", "Byte[]")]
|
||||
public byte[] Bytes { get; set; }
|
||||
|
||||
[Annotation("Nullable`1?")]
|
||||
[Annotation("", "Nullable`1?")]
|
||||
public DateTime? Dt { get; set; }
|
||||
|
||||
[Annotation("Nullable`1?")]
|
||||
[Annotation("", "Nullable`1?")]
|
||||
public double? F64 { get; set; }
|
||||
|
||||
[Annotation("Byte[]")]
|
||||
[Annotation("", "Byte[]")]
|
||||
public byte[] Guid { get; set; }
|
||||
|
||||
[Annotation("Nullable`1?")]
|
||||
[Annotation("", "Nullable`1?")]
|
||||
public long? I64 { get; set; }
|
||||
|
||||
[Annotation("String")]
|
||||
[Annotation("", "String")]
|
||||
public string Str { get; set; }
|
||||
|
||||
[Annotation("Kind")]
|
||||
public RPC.EsiurTest.Kind Tag { get; set; }
|
||||
[Annotation("", "Kind")]
|
||||
public Esiur.Tests.RPC.EsiurServer.Kind Tag { get; set; }
|
||||
|
||||
[Annotation("Nullable`1?")]
|
||||
[Annotation("", "Nullable`1?")]
|
||||
public ulong? U64 { get; set; }
|
||||
|
||||
public RPC.SharedModel.Variant ToShared()
|
||||
|
||||
|
||||
public Client.SharedModel.Variant ToShared()
|
||||
{
|
||||
return new SharedModel.Variant()
|
||||
return new Client.SharedModel.Variant()
|
||||
{
|
||||
|
||||
Bool = Bool,
|
||||
@@ -50,14 +51,14 @@ namespace RPC.EsiurTest
|
||||
Guid = Guid,
|
||||
I64 = I64,
|
||||
Str = Str,
|
||||
Tag = Enum.Parse<SharedModel.Kind>(Tag.ToString(), true),
|
||||
Tag = Enum.Parse<Client.SharedModel.Kind>(Tag.ToString(), true),
|
||||
U64 = U64
|
||||
};
|
||||
}
|
||||
|
||||
public Echo.Model.Grpc.Variant ToGrpc()
|
||||
public Client.Grpc.Variant ToGrpc()
|
||||
{
|
||||
return new Echo.Model.Grpc.Variant()
|
||||
return new Client.Grpc.Variant()
|
||||
{
|
||||
BoolVal = Bool ?? false,
|
||||
BytesVal = ByteString.CopyFrom(Bytes ?? new byte[0]),
|
||||
@@ -66,7 +67,7 @@ namespace RPC.EsiurTest
|
||||
GuidVal = ByteString.CopyFrom(Guid ?? new byte[0]),
|
||||
I64Val = I64 ?? 0,
|
||||
StrVal = Str,
|
||||
Tag = Enum.Parse<Echo.Model.Grpc.Kind>(Tag.ToString(), true),
|
||||
Tag = Enum.Parse<Client.Grpc.Kind>(Tag.ToString(), true),
|
||||
U64Val = U64 ?? 0,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
namespace Esiur {
|
||||
public static class Generated {
|
||||
public static Type[] Resources {get;} = new Type[] { typeof(Esiur.Tests.RPC.EsiurServer.Service),typeof(Esiur.Tests.RPC.EsiurServer.TestObject) };
|
||||
public static Type[] Records { get; } = new Type[] { typeof(Esiur.Tests.RPC.EsiurServer.BusinessDocument),typeof(Esiur.Tests.RPC.EsiurServer.Attachment),typeof(Esiur.Tests.RPC.EsiurServer.Party),typeof(Esiur.Tests.RPC.EsiurServer.Address),typeof(Esiur.Tests.RPC.EsiurServer.DocumentHeader),typeof(Esiur.Tests.RPC.EsiurServer.LineItem),typeof(Esiur.Tests.RPC.EsiurServer.Variant),typeof(Esiur.Tests.RPC.EsiurServer.Payment) };
|
||||
public static Type[] Enums { get; } = new Type[] { typeof(Esiur.Tests.RPC.EsiurServer.Currency),typeof(Esiur.Tests.RPC.EsiurServer.DocType),typeof(Esiur.Tests.RPC.EsiurServer.Kind),typeof(Esiur.Tests.RPC.EsiurServer.LineType),typeof(Esiur.Tests.RPC.EsiurServer.PaymentMethod) };
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RPC.SharedModel
|
||||
namespace Esiur.Tests.RPC.Client.SharedModel
|
||||
{
|
||||
// ====================== Enums ======================
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
package gpmodel;
|
||||
|
||||
option csharp_namespace = "Echo.Model.Grpc";
|
||||
option csharp_namespace = "Esiur.Tests.RPC.Client.Grpc";
|
||||
|
||||
// ========================= Enums =========================
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using Microsoft.Diagnostics.Tracing;
|
||||
using Microsoft.Diagnostics.Tracing.Parsers;
|
||||
using Microsoft.Diagnostics.Tracing.Session;
|
||||
|
||||
namespace RPC.Client.Tests;
|
||||
namespace Esiur.Tests.RPC.Client;
|
||||
|
||||
public class PerProcessNetMonitor : IDisposable
|
||||
{
|
||||
@@ -1,13 +1,10 @@
|
||||
using MQTTnet;
|
||||
using RPC.Client.Tests;
|
||||
using RPC.Client.Tests.Docs;
|
||||
using RPC.Client.Tests.Events;
|
||||
using Esiur.Tests.RPC.Client;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static RPC.Client.Tests.Events.Websockets;
|
||||
|
||||
|
||||
var results = new Dictionary<string, List<TestResults>>();
|
||||
@@ -22,7 +19,7 @@ for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var seed = 1000 + (i * 1000);
|
||||
|
||||
var docsWorkloads = new Dictionary<string, RPC.EsiurTest.BusinessDocument[]>();// RPC.Client.Tests.DocGenerator.BuildWorkloads(seed);
|
||||
var docsWorkloads = new Dictionary<string, Esiur.Tests.RPC.EsiurServer.BusinessDocument[]>();// RPC.Client.Tests.DocGenerator.BuildWorkloads(seed);
|
||||
var dataWorkLoads = Shared.BuildBytesWorkLoads(seed);
|
||||
var intWorkloads = Shared.BuildIntWorkloads(seed);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RPC.Client.Tests
|
||||
namespace Esiur.Tests.RPC.Client
|
||||
{
|
||||
internal class Shared
|
||||
{
|
||||
@@ -13,7 +13,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RPC.Client.Tests.Docs
|
||||
namespace Esiur.Tests.RPC.Client
|
||||
{
|
||||
public static class SignalRTest
|
||||
{
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace RPC.Client.Tests
|
||||
namespace Esiur.Tests.RPC.Client
|
||||
{
|
||||
public class TestResults
|
||||
{
|
||||
@@ -1,114 +0,0 @@
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Resource;
|
||||
using RPC.EsiurTest;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace RPC.Client.Tests.Events
|
||||
{
|
||||
public class EsiurEventsTest
|
||||
{
|
||||
|
||||
public enum TestType
|
||||
{
|
||||
Chunk,
|
||||
Event,
|
||||
Property
|
||||
}
|
||||
|
||||
public static async Task DoTest(TestType type, string address, int count, int size, int delay)
|
||||
{
|
||||
var rt = new TestResults();
|
||||
//using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
|
||||
//mon.Start();
|
||||
|
||||
|
||||
var service = await Warehouse.Default.Get<Service>(address);
|
||||
var sock = service.ResourceConnection.Socket as TcpSocket;
|
||||
|
||||
|
||||
//await Task.Delay(3000);
|
||||
|
||||
//var (tx, rx, ctx, crx) = mon.GetDiff(0, 0);
|
||||
|
||||
if (type == TestType.Event)
|
||||
service.EventTest(count, size, delay);
|
||||
else if (type == TestType.Property)
|
||||
service.PropertyChangeTest(count, size, delay);
|
||||
else if (type == TestType.Chunk)
|
||||
service.ChunkTest(count, size, delay);
|
||||
|
||||
var crx = sock.BytesReceived;
|
||||
var ctx = sock.BytesSent;
|
||||
|
||||
Console.WriteLine($"Handshake {ctx}/{crx}");
|
||||
|
||||
|
||||
await Task.Delay(7000 + (count * size));
|
||||
|
||||
crx = sock.BytesReceived - crx;
|
||||
ctx = sock.BytesSent - ctx;
|
||||
|
||||
//(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
|
||||
//Console.WriteLine($"Results {ctx}/{crx} Total: {tx}/{rx}");
|
||||
Console.WriteLine($"Results {ctx}/{crx}");
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static async Task DoEventTest(string address, int count, int size, int delay)
|
||||
{
|
||||
var rt = new TestResults();
|
||||
using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
|
||||
mon.Start();
|
||||
|
||||
|
||||
var service = await Warehouse.Default.Get<Service>(address);
|
||||
|
||||
await service.EventTest(count, size, delay);
|
||||
|
||||
await Task.Delay(3000);
|
||||
|
||||
var (tx, rx, ctx, crx) = mon.GetDiff(0, 0);
|
||||
|
||||
Console.WriteLine($"Handshake {ctx}/{crx}");
|
||||
|
||||
|
||||
|
||||
await Task.Delay(7000 + (count * delay));
|
||||
|
||||
(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
|
||||
Console.WriteLine($"Results {ctx}/{crx} Total: {tx}/{rx}");
|
||||
|
||||
}
|
||||
|
||||
public static async Task DoPropertyTest(string address, int count, int size, int delay)
|
||||
{
|
||||
var rt = new TestResults();
|
||||
using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
|
||||
mon.Start();
|
||||
|
||||
|
||||
var service = await Warehouse.Default.Get<Service>(address);
|
||||
|
||||
await Task.Delay(3000);
|
||||
|
||||
var (tx, rx, ctx, crx) = mon.GetDiff(0, 0);
|
||||
|
||||
Console.WriteLine($"Handshake {ctx}/{crx}");
|
||||
|
||||
await service.PropertyChangeTest(count, size, delay);
|
||||
|
||||
|
||||
await Task.Delay(3000 + (size * delay));
|
||||
|
||||
(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
|
||||
Console.WriteLine($"Results {ctx}/{crx} Total: {tx}/{rx}");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
// Benchmark MQTT client helper
|
||||
// Usage: call BenchmarkClient.RunAsync("localhost", 1883, "welcome/topic/100/1024/0");
|
||||
// This class is not an executable by itself to avoid duplicate entry points in the project.
|
||||
|
||||
using MQTTnet;
|
||||
using MQTTnet.Protocol;
|
||||
using RPC.Client.Tests;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public static class MQTTTest
|
||||
{
|
||||
public static async Task DoTest(string brokerHost = "localhost", int brokerPort = 1883, string topic = "test/topic/100/100/100", CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
|
||||
mon.Start();
|
||||
|
||||
var factory = new MqttClientFactory();
|
||||
using var mqttClient = factory.CreateMqttClient();
|
||||
|
||||
var options = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer(brokerHost, brokerPort)
|
||||
.WithCleanSession()
|
||||
.Build();
|
||||
|
||||
long receivedMessages = 0;
|
||||
long receivedBytes = 0;
|
||||
DateTime? firstReceivedAt = null;
|
||||
DateTime? lastReceivedAt = null;
|
||||
|
||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
mqttClient.ApplicationMessageReceivedAsync += (e =>
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
Interlocked.Add(ref receivedBytes, e.ApplicationMessage?.Payload.Length ?? 0);
|
||||
|
||||
if (firstReceivedAt == null)
|
||||
{
|
||||
firstReceivedAt = now;
|
||||
}
|
||||
|
||||
lastReceivedAt = now;
|
||||
|
||||
// If expected count encoded in topic, check completion
|
||||
var segments = topic.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (segments.Length >= 5 && int.TryParse(segments[2], out var expected) && expected > 0)
|
||||
{
|
||||
if (Interlocked.Read(ref receivedMessages) >= expected)
|
||||
{
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
mqttClient.ConnectedAsync += (async e =>
|
||||
{
|
||||
Console.WriteLine($"Connected to {brokerHost}:{brokerPort}");
|
||||
await mqttClient.SubscribeAsync(new MqttClientSubscribeOptionsBuilder()
|
||||
.WithTopicFilter(f => { f.WithTopic(topic).WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce); })
|
||||
.Build());
|
||||
|
||||
Console.WriteLine($"Subscribed to topic '{topic}'");
|
||||
});
|
||||
|
||||
|
||||
|
||||
//mqttClient.DisconnectedAsync += (e => Console.WriteLine("Disconnected from broker"));
|
||||
|
||||
await mqttClient.ConnectAsync(options, cancellationToken);
|
||||
|
||||
await Task.Delay(1000);
|
||||
var (tx, rx, ctx, crx) = mon.GetDiff(0, 0);
|
||||
Console.WriteLine($"Handshake {ctx}/{crx}");
|
||||
|
||||
// If no expected count, wait until cancelled
|
||||
var segmentsCheck = topic.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
Task waitTask;
|
||||
if (segmentsCheck.Length >= 5 && int.TryParse(segmentsCheck[2], out var expectedCount) && expectedCount > 0)
|
||||
{
|
||||
// wait for expected messages or cancellation
|
||||
waitTask = Task.WhenAny(tcs.Task, Task.Run(() => { var ct = new CancellationTokenSource(); cancellationToken.Register(ct.Cancel); return Task.Delay(Timeout.Infinite, ct.Token); })).Unwrap();
|
||||
await waitTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
// wait until cancellation
|
||||
try
|
||||
{
|
||||
await Task.Delay(Timeout.Infinite, cancellationToken);
|
||||
}
|
||||
catch (TaskCanceledException) { }
|
||||
}
|
||||
|
||||
// compute results
|
||||
var totalMsgs = Interlocked.Read(ref receivedMessages);
|
||||
var totalBytes = Interlocked.Read(ref receivedBytes);
|
||||
var duration = (lastReceivedAt.HasValue && firstReceivedAt.HasValue) ? (lastReceivedAt.Value - firstReceivedAt.Value) : TimeSpan.Zero;
|
||||
|
||||
Console.WriteLine("--- Benchmark results ---");
|
||||
Console.WriteLine($"Messages received: {totalMsgs}");
|
||||
Console.WriteLine($"Total bytes: {totalBytes}");
|
||||
Console.WriteLine($"Duration (first->last): {duration.TotalSeconds:F3} s");
|
||||
Console.WriteLine($"Messages/sec: {(duration.TotalSeconds > 0 ? (totalMsgs / duration.TotalSeconds) : 0):F3}");
|
||||
Console.WriteLine($"Bytes/sec: {(duration.TotalSeconds > 0 ? (totalBytes / duration.TotalSeconds) : 0):F3}");
|
||||
|
||||
|
||||
await Task.Delay(2000);
|
||||
(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
|
||||
|
||||
Console.WriteLine($"Results {ctx}/{crx} Total: {tx}/{rx}");
|
||||
|
||||
try
|
||||
{
|
||||
await mqttClient.DisconnectAsync();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RPC.Client.Tests.Events;
|
||||
|
||||
|
||||
|
||||
// Simple SSE client helper for benchmarking the /sse endpoint.
|
||||
// Usage:
|
||||
// var opts = new SseOptions { MessagesPerBurst = 10, MessageSize = 512, IntervalMs = 10, BurstIntervalMs = 1000 };
|
||||
// await SseClient.ConnectAsync("http://localhost:5000/sse", opts, CancellationToken.None,
|
||||
// onMessage: data => Console.WriteLine("msg:" + data.Length), onOpen: () => Console.WriteLine("open"), onError: ex => Console.WriteLine(ex));
|
||||
public static class SseClient
|
||||
{
|
||||
public static async Task DoTest(string baseUrl, SseOptions options, CancellationToken ct,
|
||||
Action<string>? onMessage = null, Action? onOpen = null, Action<Exception>? onError = null)
|
||||
{
|
||||
|
||||
Console.WriteLine("Starting SSE client..." );
|
||||
|
||||
options ??= new SseOptions();
|
||||
|
||||
var url = BuildUrl(baseUrl, options);
|
||||
|
||||
using var http = new HttpClient() { Timeout = Timeout.InfiniteTimeSpan };
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
req.Headers.Accept.Clear();
|
||||
req.Headers.Accept.ParseAdd("text/event-stream");
|
||||
|
||||
using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
|
||||
mon.Start();
|
||||
long tx = 0, rx = 0, ctx = 0, crx = 0;// = mon.GetDiff(0, 0);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
using var resp = await http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
(tx, rx, ctx, crx) = mon.GetDiff(0, 0);
|
||||
Console.WriteLine($"Handshake {ctx}/{crx}");
|
||||
|
||||
onOpen?.Invoke();
|
||||
|
||||
using var stream = await resp.Content.ReadAsStreamAsync(ct).ConfigureAwait(false);
|
||||
using var reader = new System.IO.StreamReader(stream, Encoding.UTF8);
|
||||
|
||||
var dataBuilder = new StringBuilder();
|
||||
|
||||
|
||||
|
||||
long totalRxBytes = 0;
|
||||
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
var readTask = reader.ReadLineAsync();
|
||||
var completed = await Task.WhenAny(readTask, Task.Delay(Timeout.InfiniteTimeSpan, ct)).ConfigureAwait(false);
|
||||
if (completed != readTask)
|
||||
break; // cancelled
|
||||
|
||||
var line = await readTask!; // safe because completed
|
||||
if (line is null)
|
||||
break; // stream ended
|
||||
|
||||
if (line.Length == 0)
|
||||
{
|
||||
// dispatch event
|
||||
if (dataBuilder.Length > 0)
|
||||
{
|
||||
// Remove trailing newline added while parsing
|
||||
if (dataBuilder.Length > 0 && dataBuilder[dataBuilder.Length - 1] == '\n')
|
||||
dataBuilder.Length--;
|
||||
|
||||
var data = dataBuilder.ToString();
|
||||
onMessage?.Invoke(data);
|
||||
dataBuilder.Clear();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// comments start with ':' -> ignore
|
||||
if (line[0] == ':')
|
||||
continue;
|
||||
|
||||
// data: lines (may be multi-line)
|
||||
if (line.StartsWith("data:"))
|
||||
{
|
||||
var payload = line.Length > 5 ? line.Substring(5) : string.Empty;
|
||||
// If the payload starts with a single space per SSE spec, trim one leading space
|
||||
if (payload.Length > 0 && payload[0] == ' ') payload = payload.Substring(1);
|
||||
dataBuilder.Append(payload);
|
||||
dataBuilder.Append('\n');
|
||||
}
|
||||
// other fields (event:, id:, retry:) are ignored by this simple client
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// cancellation requested by caller
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
onError?.Invoke(ex);
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
|
||||
|
||||
Console.WriteLine($"Results {ctx}/{crx} Total: {tx}/{rx}");
|
||||
|
||||
}
|
||||
|
||||
static string BuildUrl(string baseUrl, SseOptions opts)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(baseUrl);
|
||||
var sep = baseUrl.Contains('?') ? '&' : '?';
|
||||
sb.Append(sep);
|
||||
sb.Append("messagesPerBurst=").Append(opts.MessagesPerBurst);
|
||||
sb.Append("&messageSize=").Append(opts.MessageSize);
|
||||
sb.Append("&intervalMs=").Append(opts.IntervalMs);
|
||||
sb.Append("&burstIntervalMs=").Append(opts.BurstIntervalMs);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class SseOptions
|
||||
{
|
||||
public int MessagesPerBurst { get; set; } = 1;
|
||||
public int MessageSize { get; set; } = 100;
|
||||
public int IntervalMs { get; set; } = 0;
|
||||
public int BurstIntervalMs { get; set; } = 1000;
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace RPC.Client.Tests.Events
|
||||
{
|
||||
public class Websockets
|
||||
{
|
||||
|
||||
public record BenchmarkResult(int? Requested, int Received, long TotalBytes, long DurationMs, double MessagesPerSec, double BytesPerSec);
|
||||
|
||||
public static class WSClient
|
||||
{
|
||||
public static async Task<BenchmarkResult> DoTest(
|
||||
string url,
|
||||
int? count = 100,
|
||||
int size = 128,
|
||||
int intervalMs = 1000,
|
||||
bool binary = true,
|
||||
bool useQuery = true,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
Console.WriteLine("Starting Websockets benchmark...");
|
||||
// prepare connect url
|
||||
var connectUrl = url;
|
||||
if (useQuery)
|
||||
{
|
||||
var qs = new QueryBuilder();
|
||||
if (count.HasValue) qs.Add("count", count.Value.ToString());
|
||||
qs.Add("size", size.ToString());
|
||||
qs.Add("intervalMs", intervalMs.ToString());
|
||||
qs.Add("binary", binary.ToString().ToLowerInvariant());
|
||||
connectUrl += (url.Contains('?') ? "&" : "?") + qs.ToString();
|
||||
}
|
||||
await Task.Delay(2000);
|
||||
|
||||
using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
|
||||
mon.Start();
|
||||
|
||||
using var client = new ClientWebSocket();
|
||||
await client.ConnectAsync(new Uri(connectUrl), cancellationToken);
|
||||
|
||||
// if not using query, send init JSON
|
||||
if (!useQuery)
|
||||
{
|
||||
var init = JsonSerializer.Serialize(new { count, size, intervalMs, binary });
|
||||
var initBytes = Encoding.UTF8.GetBytes(init);
|
||||
await client.SendAsync(new ArraySegment<byte>(initBytes), WebSocketMessageType.Text, true, cancellationToken);
|
||||
}
|
||||
|
||||
var bufferSize = Math.Max(8192, size + 4096);
|
||||
var buffer = new byte[bufferSize];
|
||||
|
||||
int received = 0;
|
||||
long totalBytes = 0;
|
||||
long firstMs = 0;
|
||||
long lastMs = 0;
|
||||
|
||||
await Task.Delay(2000);
|
||||
|
||||
|
||||
var (tx, rx, ctx, crx) = mon.GetDiff(0, 0);
|
||||
|
||||
Console.WriteLine($"Handshake {ctx}/{crx}");
|
||||
|
||||
long totalRxBytes = 0;
|
||||
|
||||
try
|
||||
{
|
||||
while (client.State == WebSocketState.Open && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (count.HasValue && received >= count.Value) break;
|
||||
|
||||
// Receive a full message (handle fragmentation)
|
||||
int messageBytes = 0;
|
||||
WebSocketReceiveResult result;
|
||||
do
|
||||
{
|
||||
result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
|
||||
|
||||
//(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
|
||||
//totalRxBytes += crx;
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close) goto EndReceive;
|
||||
messageBytes += result.Count;
|
||||
|
||||
} while (!result.EndOfMessage);
|
||||
|
||||
var now = DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond;
|
||||
if (received == 0) firstMs = now;
|
||||
lastMs = now;
|
||||
|
||||
received++;
|
||||
totalBytes += messageBytes;
|
||||
|
||||
if (count.HasValue && received >= count.Value) break;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { /* cancelled by caller */ }
|
||||
catch (WebSocketException) { /* network error */ }
|
||||
|
||||
EndReceive:
|
||||
if (client.State == WebSocketState.Open || client.State == WebSocketState.CloseReceived)
|
||||
{
|
||||
try { await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client closing", CancellationToken.None); } catch { }
|
||||
}
|
||||
|
||||
var durationMs = (firstMs != 0 && lastMs >= firstMs) ? (lastMs - firstMs) : 0;
|
||||
var msgsPerSec = durationMs > 0 ? (received * 1000.0) / durationMs : (double)received;
|
||||
var bytesPerSec = durationMs > 0 ? (totalBytes * 1000.0) / durationMs : (double)totalBytes;
|
||||
|
||||
|
||||
Console.WriteLine("Total RX bytes (monitor): " + totalRxBytes);
|
||||
|
||||
await Task.Delay(2000);
|
||||
|
||||
(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
|
||||
|
||||
Console.WriteLine($"Total Mon {tx}/{rx} {ctx}/{crx} {received}");
|
||||
|
||||
mon.Stop();
|
||||
return new BenchmarkResult(count, received, totalBytes, durationMs, msgsPerSec, bytesPerSec);
|
||||
}
|
||||
|
||||
// minimal QueryString builder to avoid extra deps
|
||||
private class QueryBuilder
|
||||
{
|
||||
private readonly StringBuilder _sb = new();
|
||||
private bool _first = true;
|
||||
public void Add(string name, string value)
|
||||
{
|
||||
if (!_first) _sb.Append('&');
|
||||
_first = false;
|
||||
_sb.Append(Uri.EscapeDataString(name));
|
||||
_sb.Append('=');
|
||||
_sb.Append(Uri.EscapeDataString(value));
|
||||
}
|
||||
public override string ToString() => _sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace RPC.Client.Tests.Queue;
|
||||
|
||||
using Esiur.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
public static class EsiurQueueEval
|
||||
{
|
||||
|
||||
public static class EvalPrinter
|
||||
{
|
||||
public static void Print(EsiurQueueEval.EvalResult r)
|
||||
{
|
||||
Console.WriteLine("=== Evaluation Result ===");
|
||||
Console.WriteLine($"α (HasResource probability): {r.Alpha:F3}");
|
||||
Console.WriteLine($"λ̂ (effective arrival rate): {r.LambdaEventsPerSecond:F2} events/s");
|
||||
Console.WriteLine();
|
||||
|
||||
PrintLatencyTable(r.Latency);
|
||||
|
||||
Console.WriteLine();
|
||||
PrintValidation(r.Validation);
|
||||
|
||||
if (r.FlushSizeStats != null)
|
||||
{
|
||||
Console.WriteLine();
|
||||
PrintStats("Flush size (≤ window)", r.FlushSizeStats);
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintLatencyTable(EsiurQueueEval.LatencyDecomposition l)
|
||||
{
|
||||
Console.WriteLine("Latency Decomposition (ms)");
|
||||
Console.WriteLine("-----------------------------------------------");
|
||||
Console.WriteLine($"{"Metric",-16} {"Mean",8} {"P50",8} {"P95",8} {"P99",8} {"Max",8}");
|
||||
Console.WriteLine("-----------------------------------------------");
|
||||
|
||||
PrintRow("Readiness R", l.ReadinessMs);
|
||||
PrintRow("HOL Δ", l.HolMs);
|
||||
PrintRow("End-to-End D", l.EndToEndMs);
|
||||
|
||||
Console.WriteLine("-----------------------------------------------");
|
||||
}
|
||||
|
||||
private static void PrintValidation(EsiurQueueEval.ModelValidation v)
|
||||
{
|
||||
Console.WriteLine("Resequencing Model Validation");
|
||||
Console.WriteLine("-----------------------------------------------");
|
||||
Console.WriteLine("Absolute error |d − d̂| (ms)");
|
||||
PrintStats("Error", v.AbsErrorMs);
|
||||
|
||||
if (v.MaxNegativeSlackMs > 0)
|
||||
{
|
||||
Console.WriteLine($"Max negative slack (delivered earlier than model): {v.MaxNegativeSlackMs:F4} ms");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("No negative slack observed (model is conservative).");
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintRow(string name, EsiurQueueEval.Stats s)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"{name,-16} " +
|
||||
$"{s.Mean,8:F2} " +
|
||||
$"{s.P50,8:F2} " +
|
||||
$"{s.P95,8:F2} " +
|
||||
$"{s.P99,8:F2} " +
|
||||
$"{s.Max,8:F2}"
|
||||
);
|
||||
}
|
||||
|
||||
private static void PrintStats(string name, EsiurQueueEval.Stats s)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"{name,-20} " +
|
||||
$"mean={s.Mean,8:F02} " +
|
||||
$"p50={s.P50,8:F02} " +
|
||||
$"p95={s.P95,8:F02} " +
|
||||
$"p99={s.P99,8:F02} " +
|
||||
$"max={s.Max,8:F02}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record Stats(double Mean, double P50, double P95, double P99, double Max);
|
||||
|
||||
public sealed record LatencyDecomposition(Stats ReadinessMs, Stats HolMs, Stats EndToEndMs);
|
||||
|
||||
public sealed record ModelValidation(
|
||||
Stats AbsErrorMs,
|
||||
double MaxNegativeSlackMs, // worst case where Delivered < predicted (if happens)
|
||||
int Count);
|
||||
|
||||
public sealed record EvalResult(
|
||||
double Alpha,
|
||||
double LambdaEventsPerSecond,
|
||||
double MuEventsPerSecond, // <-- NEW
|
||||
LatencyDecomposition Latency,
|
||||
ModelValidation Validation,
|
||||
Stats QueueLength,
|
||||
Stats? FlushSizeStats);
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates Esiur fork-join readiness + in-order resequencing using in-memory items.
|
||||
/// Assumes items refer to a single ordered stream (per resource queue).
|
||||
/// </summary>
|
||||
public static EvalResult Evaluate<T>(
|
||||
IReadOnlyList<AsyncQueueItem<T>> items,
|
||||
double flushWindowMs = 0.5,
|
||||
bool computeFlush = true)
|
||||
{
|
||||
if (items == null) throw new ArgumentNullException(nameof(items));
|
||||
if (items.Count == 0) throw new ArgumentException("items is empty.");
|
||||
|
||||
// Ensure deterministic order: prefer Sequence, then Arrival timestamp
|
||||
var ordered = items
|
||||
.OrderBy(x => x.Sequence)
|
||||
.ThenBy(x => x.Arrival)
|
||||
.ToArray();
|
||||
|
||||
int n = ordered.Length;
|
||||
|
||||
// Latency components in milliseconds
|
||||
var readiness = new double[n]; // R = r-a
|
||||
var hol = new double[n]; // Δ = d-r
|
||||
var endToEnd = new double[n]; // D = d-a
|
||||
|
||||
int resCount = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
var e = ordered[i];
|
||||
|
||||
double Rms = (e.Ready - e.Arrival).TotalMilliseconds;
|
||||
double Hms = (e.Delivered - e.Ready).TotalMilliseconds;
|
||||
double Dms = (e.Delivered - e.Arrival).TotalMilliseconds;
|
||||
|
||||
// Robustness against logging placement or clock issues
|
||||
if (Rms < 0) Rms = 0;
|
||||
if (Hms < 0) Hms = 0;
|
||||
if (Dms < 0) Dms = 0;
|
||||
|
||||
readiness[i] = Rms;
|
||||
hol[i] = Hms;
|
||||
endToEnd[i] = Dms;
|
||||
|
||||
if (e.HasResource) resCount++;
|
||||
}
|
||||
|
||||
// α = P(HasResource)
|
||||
double alpha = (double)resCount / n;
|
||||
|
||||
|
||||
// Effective arrival rate λ̂ from arrival timeline
|
||||
double lambda = EstimateLambda(ordered);
|
||||
|
||||
// Effective departure / readiness rate μ̂ from delivery timeline
|
||||
double mu = EstimateMu(ordered);
|
||||
|
||||
var latency = new LatencyDecomposition(
|
||||
ReadinessMs: ComputeStats(readiness),
|
||||
HolMs: ComputeStats(hol),
|
||||
EndToEndMs: ComputeStats(endToEnd));
|
||||
|
||||
// --- Resequencing validation: d_hat_i = max(r_i, d_hat_{i-1}) ---
|
||||
// Use DateTime ticks for exactness, then convert to ms.
|
||||
var absErrMs = new double[n];
|
||||
long prevPredictedTicks = long.MinValue;
|
||||
double maxNegativeSlackMs = 0;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
long ri = ordered[i].Ready.Ticks;
|
||||
|
||||
long predicted = (i == 0)
|
||||
? ri
|
||||
: Math.Max(ri, prevPredictedTicks);
|
||||
|
||||
prevPredictedTicks = predicted;
|
||||
|
||||
long observed = ordered[i].Delivered.Ticks;
|
||||
long errTicks = Math.Abs(observed - predicted);
|
||||
absErrMs[i] = TicksToMs(errTicks);
|
||||
|
||||
// If observed delivery occurs earlier than model predicts (shouldn't, but track it)
|
||||
long slackTicks = observed - predicted; // negative => earlier than predicted
|
||||
if (slackTicks < 0)
|
||||
{
|
||||
double slackMs = TicksToMs(-slackTicks);
|
||||
if (slackMs > maxNegativeSlackMs) maxNegativeSlackMs = slackMs;
|
||||
}
|
||||
}
|
||||
|
||||
var validation = new ModelValidation(
|
||||
AbsErrorMs: ComputeStats(absErrMs),
|
||||
MaxNegativeSlackMs: maxNegativeSlackMs,
|
||||
Count: n);
|
||||
|
||||
// --- Flush sizes (optional): consecutive deliveries within window ---
|
||||
//Stats? flushStats = null;
|
||||
//if (computeFlush)
|
||||
//{
|
||||
// var flushSizes = ComputeFlushSizes(ordered, flushWindowMs);
|
||||
// flushStats = ComputeStats(flushSizes.Select(x => (double)x).ToArray());
|
||||
//}
|
||||
|
||||
var queueLength = ComputeStats(ordered.Select(x => (double)x.NotificationsCountWaitingInTheQueueAtEnqueueing).ToArray());
|
||||
|
||||
var flushStats = ComputeStats(ordered.GroupBy(x => x.FlushId).Select(x => (double)x.First().BatchSize).ToArray());
|
||||
|
||||
return new EvalResult(alpha, lambda, mu, latency, validation, queueLength, flushStats);
|
||||
}
|
||||
|
||||
// ---------------- Helpers ----------------
|
||||
|
||||
private static double EstimateLambda<T>(AsyncQueueItem<T>[] ordered)
|
||||
{
|
||||
if (ordered.Length < 2) return 0;
|
||||
|
||||
DateTime first = ordered[0].Arrival;
|
||||
DateTime last = ordered[^1].Arrival;
|
||||
double seconds = (last - first).TotalSeconds;
|
||||
if (seconds <= 0) return 0;
|
||||
|
||||
// N-1 arrivals over observed interval
|
||||
return (ordered.Length - 1) / seconds;
|
||||
}
|
||||
|
||||
private static double TicksToMs(long ticks) => ticks / 10_000.0; // 1 tick = 100ns
|
||||
|
||||
private static int[] ComputeFlushSizes<T>(AsyncQueueItem<T>[] ordered, double windowMs)
|
||||
{
|
||||
var sizes = new List<int>(ordered.Length);
|
||||
long windowTicks = (long)(windowMs * 10_000.0);
|
||||
|
||||
int i = 0;
|
||||
while (i < ordered.Length)
|
||||
{
|
||||
int j = i + 1;
|
||||
long baseD = ordered[i].Delivered.Ticks;
|
||||
|
||||
while (j < ordered.Length)
|
||||
{
|
||||
long dj = ordered[j].Delivered.Ticks;
|
||||
if (Math.Abs(dj - baseD) <= windowTicks) j++;
|
||||
else break;
|
||||
}
|
||||
|
||||
sizes.Add(j - i);
|
||||
i = j;
|
||||
}
|
||||
|
||||
return sizes.ToArray();
|
||||
}
|
||||
|
||||
private static Stats ComputeStats(double[] values)
|
||||
{
|
||||
if (values.Length == 0) return new Stats(0, 0, 0, 0, 0);
|
||||
|
||||
double mean = values.Average();
|
||||
double max = values.Max();
|
||||
|
||||
var sorted = (double[])values.Clone();
|
||||
Array.Sort(sorted);
|
||||
|
||||
return new Stats(
|
||||
Mean: mean,
|
||||
P50: QuantileSorted(sorted, 0.50),
|
||||
P95: QuantileSorted(sorted, 0.95),
|
||||
P99: QuantileSorted(sorted, 0.99),
|
||||
Max: max);
|
||||
}
|
||||
|
||||
// Linear interpolation quantile
|
||||
private static double QuantileSorted(double[] sorted, double q)
|
||||
{
|
||||
if (sorted.Length == 1) return sorted[0];
|
||||
|
||||
double pos = (sorted.Length - 1) * q;
|
||||
int lo = (int)Math.Floor(pos);
|
||||
int hi = (int)Math.Ceiling(pos);
|
||||
if (lo == hi) return sorted[lo];
|
||||
|
||||
double frac = pos - lo;
|
||||
return sorted[lo] + (sorted[hi] - sorted[lo]) * frac;
|
||||
}
|
||||
|
||||
// Compute the element-wise average of a sequence of EvalResult
|
||||
public static EvalResult Average(IEnumerable<EvalResult> results)
|
||||
{
|
||||
if (results == null) throw new ArgumentNullException(nameof(results));
|
||||
var arr = results.ToArray();
|
||||
if (arr.Length == 0) throw new ArgumentException("results is empty.", nameof(results));
|
||||
|
||||
double avgAlpha = arr.Average(r => r.Alpha);
|
||||
double avgLambda = arr.Average(r => r.LambdaEventsPerSecond);
|
||||
double avgMu = arr.Average(r => r.MuEventsPerSecond);
|
||||
|
||||
Stats avgReadiness = AverageStats(arr.Select(r => r.Latency.ReadinessMs));
|
||||
Stats avgHol = AverageStats(arr.Select(r => r.Latency.HolMs));
|
||||
Stats avgE2E = AverageStats(arr.Select(r => r.Latency.EndToEndMs));
|
||||
|
||||
var avgLatency = new LatencyDecomposition(avgReadiness, avgHol, avgE2E);
|
||||
|
||||
Stats avgAbsError = AverageStats(arr.Select(r => r.Validation.AbsErrorMs));
|
||||
double worstNegativeSlack = arr.Max(r => r.Validation.MaxNegativeSlackMs);
|
||||
int totalCount = arr.Sum(r => r.Validation.Count);
|
||||
|
||||
var avgValidation = new ModelValidation(avgAbsError, worstNegativeSlack, totalCount);
|
||||
|
||||
Stats? avgFlush = null;
|
||||
var flushStatsSeq = arr.Select(r => r.FlushSizeStats).Where(s => s != null).Select(s => s!).ToArray();
|
||||
if (flushStatsSeq.Length > 0) avgFlush = AverageStats(flushStatsSeq);
|
||||
|
||||
|
||||
var avgQueue = AverageStats(arr.Select(x => x.QueueLength));
|
||||
|
||||
return new EvalResult(avgAlpha, avgLambda, avgMu, avgLatency, avgValidation, avgQueue, avgFlush);
|
||||
}
|
||||
|
||||
private static double EstimateMu<T>(AsyncQueueItem<T>[] ordered)
|
||||
{
|
||||
if (ordered.Length < 2) return 0;
|
||||
|
||||
DateTime first = ordered[0].Delivered;
|
||||
DateTime last = ordered[^1].Delivered;
|
||||
double seconds = (last - first).TotalSeconds;
|
||||
if (seconds <= 0) return 0;
|
||||
|
||||
// N-1 completions over observed interval
|
||||
return (ordered.Length - 1) / seconds;
|
||||
}
|
||||
|
||||
private static Stats AverageStats(IEnumerable<Stats> seq)
|
||||
{
|
||||
var arr = seq.ToArray();
|
||||
if (arr.Length == 0) return new Stats(0, 0, 0, 0, 0);
|
||||
return new Stats(
|
||||
Mean: arr.Average(s => s.Mean),
|
||||
P50: arr.Average(s => s.P50),
|
||||
P95: arr.Average(s => s.P95),
|
||||
P99: arr.Average(s => s.P99),
|
||||
Max: arr.Average(s => s.Max));
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace RPC.Client.Tests.Docs;
|
||||
namespace Esiur.Tests.RPC.Client;
|
||||
|
||||
public class ThriftTest
|
||||
{
|
||||
@@ -25,7 +25,7 @@ public class ThriftTest
|
||||
using var socket = new Thrift.Transport.Client.TSocketTransport(host, port, new Thrift.TConfiguration());
|
||||
//await socket.OpenAsync(new CancellationToken());
|
||||
var proto = new Thrift.Protocol.TBinaryProtocol(socket);
|
||||
var service = new Echo.ThriftModel.EchoService.Client(proto);
|
||||
var service = new EchoService.Client(proto);
|
||||
|
||||
|
||||
Thread.Sleep(3000);
|
||||
Reference in New Issue
Block a user