diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b8f0e4d..4ff760a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 2.1.811 + dotnet-version: 3.1.426 - name: install altcover run: dotnet tool install --global altcover.visualizer --version 8.6.14 - name: Install dependencies @@ -24,7 +24,7 @@ jobs: - name: Build run: dotnet build --configuration Release --no-restore - name: Test - run: dotnet test TeaUnitTests/ /p:AltCover=true + run: dotnet test DaraUnitTests/ /p:AltCover=true env: CA: ${{ secrets.CA }} - name: CodeCov diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 133af91..0000000 --- a/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: csharp -sudo: true -mono: none -dotnet: 2.2 -dist: xenial -branches: - only: - - master - -install: - - dotnet tool install --global altcover.visualizer - - dotnet restore - - dotnet build - -script: - - dotnet test TeaUnitTests/ /p:AltCover=true - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/DaraUnitTests/DaraCoreTest.cs b/DaraUnitTests/DaraCoreTest.cs new file mode 100644 index 0000000..b1f7589 --- /dev/null +++ b/DaraUnitTests/DaraCoreTest.cs @@ -0,0 +1,870 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Darabonba; +using Darabonba.RetryPolicy; +using Darabonba.Exceptions; +using Xunit; + +namespace DaraUnitTests +{ + public class DaraCoreTest + { + [Fact] + public void TestComposeUrl() + { + DaraRequest daraRequest = new DaraRequest(); + + var url = DaraCore.ComposeUrl(daraRequest); + Assert.Equal("http://", url); + + daraRequest.Headers["host"] = "fake.domain.com"; + url = DaraCore.ComposeUrl(daraRequest); + Assert.Equal("http://fake.domain.com", url); + + daraRequest.Port = 8080; + url = DaraCore.ComposeUrl(daraRequest); + Assert.Equal("http://fake.domain.com:8080", url); + + daraRequest.Pathname = "/index.html"; + url = DaraCore.ComposeUrl(daraRequest); + Assert.Equal("http://fake.domain.com:8080/index.html", url); + + daraRequest.Query["foo"] = ""; + url = DaraCore.ComposeUrl(daraRequest); + Assert.Equal("http://fake.domain.com:8080/index.html?foo=", url); + + daraRequest.Query["foo"] = "bar"; + url = DaraCore.ComposeUrl(daraRequest); + Assert.Equal("http://fake.domain.com:8080/index.html?foo=bar", url); + + daraRequest.Pathname = "/index.html?a=b"; + url = DaraCore.ComposeUrl(daraRequest); + Assert.Equal("http://fake.domain.com:8080/index.html?a=b&foo=bar", url); + + daraRequest.Pathname = "/index.html?a=b&"; + url = DaraCore.ComposeUrl(daraRequest); + Assert.Equal("http://fake.domain.com:8080/index.html?a=b&foo=bar", url); + + daraRequest.Query["fake"] = null; + url = DaraCore.ComposeUrl(daraRequest); + Assert.Equal("http://fake.domain.com:8080/index.html?a=b&foo=bar", url); + + daraRequest.Query["fake"] = "val*"; + url = DaraCore.ComposeUrl(daraRequest); + Assert.Equal("http://fake.domain.com:8080/index.html?a=b&foo=bar&fake=val%2A", url); + } + + [Fact] + public void TestDoAction() + { + DaraRequest daraRequest = new DaraRequest + { + Protocol = "http", + Method = "GET", + Headers = new Dictionary() + }; + daraRequest.Headers["host"] = "www.alibabacloud.com"; + daraRequest.Pathname = "/s/zh"; + daraRequest.Query = new Dictionary + { + { "k", "ecs" } + }; + Dictionary runtime = new Dictionary + { + { "readTimeout", 7000 }, + { "connectTimeout", 7000 }, + { "httpsProxy", "http://www.alibabacloud.com/s/zh?k=ecs" }, + { "ignoreSSL", true } + }; + + DaraResponse daraResponse = DaraCore.DoAction(daraRequest, runtime); + Assert.NotNull(daraResponse); + + daraRequest.Protocol = "https"; + daraResponse = DaraCore.DoAction(daraRequest); + Assert.NotNull(daraResponse); + + + string bodyStr = DaraCore.GetResponseBody(daraResponse); + Assert.NotNull(bodyStr); + + daraRequest.Method = "POST"; + daraRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes("test")); + daraRequest.Headers["content-test"] = "test"; + daraResponse = DaraCore.DoAction(daraRequest, runtime); + Assert.NotNull(daraResponse); + + DaraRequest daraRequest404 = new DaraRequest + { + Protocol = "https", + Method = "GET", + Headers = new Dictionary() + }; + daraRequest404.Headers["host"] = "www.alibabacloud404.com"; + daraRequest404.Pathname = "/s/zh"; + daraRequest404.Query = new Dictionary + { + { "k", "ecs" } + }; + Dictionary runtime404 = new Dictionary + { + { "readTimeout", 7000 }, + { "connectTimeout", 7000 } + }; + Assert.Throws(() => { DaraCore.DoAction(daraRequest404, runtime404); }); + + DaraRequest daraRequestProxy = new DaraRequest(); + + + DaraRequest requestException = new DaraRequest + { + Protocol = "http", + Method = "GET", + Pathname = "/test" + }; + Dictionary runtimeException = new Dictionary(); + requestException.Headers["host"] = "www.aliyun.com"; + DaraResponse responseException = DaraCore.DoAction(requestException, runtimeException); + Assert.NotNull(responseException); + } + + [Fact] + public async Task TestDoActionAsync() + { + DaraRequest daraRequest = new DaraRequest + { + Protocol = "https", + Method = "GET", + Headers = new Dictionary() + }; + daraRequest.Headers["host"] = "www.alibabacloud.com"; + daraRequest.Pathname = "/s/zh"; + daraRequest.Query = new Dictionary + { + { "k", "ecs" } + }; + + DaraResponse daraResponse = await DaraCore.DoActionAsync(daraRequest); + Assert.NotNull(daraResponse); + + Dictionary runtime = new Dictionary + { + { "readTimeout", 4000 }, + { "connectTimeout", 0 } + }; + + daraResponse = await DaraCore.DoActionAsync(daraRequest, runtime); + Assert.NotNull(daraResponse); + + string bodyStr = DaraCore.GetResponseBody(daraResponse); + Assert.NotNull(bodyStr); + + daraRequest.Method = "POST"; + daraRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes("test")); + daraResponse = await DaraCore.DoActionAsync(daraRequest, runtime); + Assert.NotNull(daraResponse); + + DaraRequest daraRequest404 = new DaraRequest + { + Protocol = "https", + Method = "GET", + Headers = new Dictionary() + }; + daraRequest404.Headers["host"] = "www.alibabacloud404.com"; + daraRequest404.Pathname = "/s/zh"; + daraRequest404.Query = new Dictionary + { + { "k", "ecs" } + }; + Dictionary runtime404 = new Dictionary + { + { "readTimeout", 7000 }, + { "connectTimeout", 7000 } + }; + await Assert.ThrowsAsync(async () => { await DaraCore.DoActionAsync(daraRequest404, runtime); }); + + DaraRequest requestException = new DaraRequest + { + Protocol = "http", + Method = "GET", + Pathname = "/test" + }; + Dictionary runtimeException = new Dictionary(); + requestException.Headers["host"] = "www.aliyun.com"; + DaraResponse responseException = await DaraCore.DoActionAsync(requestException, runtimeException); + } + + [Fact] + public void TestConvertHeaders() + { + WebHeaderCollection headers = new WebHeaderCollection + { + { "testKey", "testValue" } + }; + Dictionary dic = DaraCore.ConvertHeaders(headers); + Assert.NotNull(dic); + Assert.True(dic.ContainsKey("testkey")); + Assert.Equal("testValue", dic["testkey"]); + } + + [Fact] + public void TestAllowRetry() + { + long _now = DateTime.Now.Millisecond; + + Assert.True(DaraCore.AllowRetry(null, 0, _now)); + + Assert.False(DaraCore.AllowRetry(null, 3, _now)); + + Dictionary dic = new Dictionary(); + Assert.False(DaraCore.AllowRetry(dic, 3, _now)); + + dic.Add("retryable", true); + dic.Add("maxAttempts", null); + Assert.False(DaraCore.AllowRetry(dic, 3, _now)); + + dic["maxAttempts"] = 5; + Assert.True(DaraCore.AllowRetry(dic, 3, _now)); + + Dictionary dicInt = new Dictionary(); + Assert.False(DaraCore.AllowRetry(dicInt, 3, _now)); + } + + public class AException : DaraException + { + public AException() : base() + { + } + + public AException(IDictionary dict) : base(dict) + { + } + } + + public class BException : DaraException + { + public BException() : base() + { + } + + public BException(IDictionary dict) : base(dict) + { + } + } + + public class CException : DaraException + { + public CException() : base() + { + } + + public CException(IDictionary dict) : base(dict) + { + } + } + + [Fact] + public void TestShouldRetry() + { + var backoffPolicy = new ExponentialBackoffPolicy(2, 60 * 1000); + + var retryCondition1 = new RetryCondition + { + MaxAttempts = 1, + Backoff = backoffPolicy, + Exception = new List { "AException" }, + ErrorCode = new List { "BExceptionCode" } + }; + + var retryCondition2 = new RetryCondition + { + MaxAttempts = 2, + Backoff = backoffPolicy, + Exception = new List { "AException", "CException" } + }; + + var retryCondition3 = new RetryCondition + { + Exception = new List { "BException" } + }; + + var retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 0, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + + Assert.True(DaraCore.ShouldRetry(null, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + + Assert.False(DaraCore.ShouldRetry(null, retryPolicyContext)); + + var retryOptions = new RetryOptions + { + Retryable = false, + RetryCondition = null, + NoRetryCondition = null + }; + + Assert.False(DaraCore.ShouldRetry(retryOptions, retryPolicyContext)); + + retryOptions = new RetryOptions + { + Retryable = true, + RetryCondition = null, + NoRetryCondition = null + }; + + Assert.False(DaraCore.ShouldRetry(retryOptions, retryPolicyContext)); + + retryOptions = new RetryOptions + { + Retryable = true, + RetryCondition = new List { retryCondition1, retryCondition2 }, + NoRetryCondition = new List { retryCondition3 } + }; + + Assert.True(DaraCore.ShouldRetry(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 2, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + + Assert.False(DaraCore.ShouldRetry(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 0, + Exception = new BException + { + Message = "BException", + Code = "BExceptionCode" + } + }; + + Assert.True(DaraCore.ShouldRetry(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new BException + { + Message = "BException", + Code = "BExceptionCode" + } + }; + + Assert.False(DaraCore.ShouldRetry(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new CException + { + Message = "CException", + Code = "CExceptionCode" + } + }; + + Assert.True(DaraCore.ShouldRetry(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 2, + Exception = new CException + { + Message = "CException", + Code = "CExceptionCode" + } + }; + + Assert.True(DaraCore.ShouldRetry(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 3, + Exception = new CException + { + Message = "CException", + Code = "CExceptionCode" + } + }; + + Assert.False(DaraCore.ShouldRetry(retryOptions, retryPolicyContext)); + } + + [Fact] + public void TestGetBackoffTime() + { + Dictionary dic = new Dictionary(); + Assert.Equal(0, DaraCore.GetBackoffTime(dic, 1)); + + dic.Add("policy", null); + Assert.Equal(0, DaraCore.GetBackoffTime(dic, 1)); + + dic["policy"] = string.Empty; + Assert.Equal(0, DaraCore.GetBackoffTime(dic, 1)); + + dic["policy"] = "no"; + Assert.Equal(0, DaraCore.GetBackoffTime(dic, 1)); + + dic["policy"] = "yes"; + Assert.Equal(0, DaraCore.GetBackoffTime(dic, 1)); + + dic.Add("period", null); + Assert.Equal(0, DaraCore.GetBackoffTime(dic, 1)); + + dic["period"] = -1; + Assert.Equal(1, DaraCore.GetBackoffTime(dic, 1)); + + dic["period"] = 1000; + Assert.Equal(1000, DaraCore.GetBackoffTime(dic, 1)); + } + + [Fact] + public void TestGetBackoffDelay() + { + BackoffPolicy backoffPolicy = new ExponentialBackoffPolicy(200, 60 * 1000); + + RetryCondition retryCondition1 = new RetryCondition + { + MaxAttempts = 1, + Backoff = backoffPolicy, + Exception = new List { "AException" }, + ErrorCode = new List { "BExceptionCode" } + }; + + RetryCondition retryCondition2 = new RetryCondition + { + MaxAttempts = 2, + Backoff = backoffPolicy, + Exception = new List { "AException", "CException" } + }; + + RetryCondition retryCondition3 = new RetryCondition + { + Exception = new List { "BException" } + }; + + RetryPolicyContext retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 0, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + + Assert.Equal(0, DaraCore.GetBackoffDelay(null, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + + Assert.Equal(100, DaraCore.GetBackoffDelay(null, retryPolicyContext)); + + RetryOptions retryOptions = new RetryOptions + { + Retryable = false, + RetryCondition = null, + NoRetryCondition = null + }; + + Assert.Equal(100, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryOptions = new RetryOptions + { + Retryable = true, + RetryCondition = new List { retryCondition1, retryCondition2 }, + NoRetryCondition = new List { retryCondition3 } + }; + + Assert.Equal(400, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 2, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + + Assert.Equal(800, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new BException + { + Message = "BException", + Code = "BExceptionCode" + } + }; + + Assert.Equal(400, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new CException + { + Message = "CException", + Code = "CExceptionCode" + } + }; + + Assert.Equal(400, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 2, + Exception = new CException + { + Message = "CException", + Code = "CExceptionCode" + } + }; + Assert.Equal(800, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 3, + Exception = new CException + { + Message = "CException", + Code = "CExceptionCode" + } + }; + Assert.Equal(1600, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + RetryCondition retryCondition4 = new RetryCondition + { + MaxAttempts = 20, + Backoff = backoffPolicy, + Exception = new List { "AException" } + }; + + retryOptions = new RetryOptions + { + RetryCondition = new List { retryCondition4 } + }; + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 10, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + Assert.Equal(60000, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + backoffPolicy = new ExponentialBackoffPolicy(200, 180 * 1000); + + retryCondition4 = new RetryCondition + { + MaxAttempts = 20, + Backoff = backoffPolicy, + Exception = new List { "AException" } + }; + + retryOptions = new RetryOptions + { + Retryable = true, + RetryCondition = new List { retryCondition4 } + }; + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 10, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + + Assert.Equal(120000, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 15, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + Assert.Equal(120000, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryCondition4 = new RetryCondition + { + MaxAttempts = 20, + MaxDelayTimeMillis = 30 * 1000, + Backoff = backoffPolicy, + Exception = new List { "AException" } + }; + + retryOptions = new RetryOptions + { + Retryable = true, + RetryCondition = new List { retryCondition4 } + }; + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 10, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + + Assert.Equal(30000, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 15, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + + Assert.Equal(30000, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryCondition4 = new RetryCondition + { + MaxAttempts = 20, + Backoff = null, + Exception = new List { "AException" } + }; + + retryOptions = new RetryOptions + { + Retryable = true, + RetryCondition = new List { retryCondition4 } + }; + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 10, + Exception = new AException + { + Message = "AException", + Code = "AExceptionCode" + } + }; + Assert.Equal(100, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + } + + + public class ThrottlingException : ResponseException + { + public ThrottlingException() : base() + { + } + } + + [Fact] + public void TestThrottlingBackoffDelay() + { + BackoffPolicy backoffPolicy = new ExponentialBackoffPolicy(200, 60 * 1000); + RetryCondition retryCondition = new RetryCondition + { + MaxAttempts = 1, + Backoff = backoffPolicy, + Exception = new List { "ThrottlingException" } + }; + + RetryPolicyContext retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new ThrottlingException { } + }; + Assert.Equal(100, DaraCore.GetBackoffDelay(null, retryPolicyContext)); + + RetryOptions retryOptions = new RetryOptions + { + Retryable = false, + RetryCondition = new List { retryCondition }, + NoRetryCondition = null + }; + Assert.Equal(100, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryOptions = new RetryOptions + { + Retryable = true, + RetryCondition = new List { retryCondition }, + NoRetryCondition = null + }; + Assert.Equal(100, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new ThrottlingException { + Message = "ThrottlingException" + } + }; + Assert.Equal(400, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new ThrottlingException { + // TODO retryable true + Message = "ThrottlingException", + RetryAfter = 2000L + } + }; + Assert.Equal(2000, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new ThrottlingException { + // TODO retryable true + Message = "ThrottlingException", + RetryAfter = 320 * 1000L + } + }; + Assert.Equal(120000, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryCondition = new RetryCondition + { + MaxAttempts = 1, + Backoff = backoffPolicy, + ErrorCode = new List { "Throttling", "Throttling.User", "Throttling.Api"} + }; + retryOptions = new RetryOptions + { + Retryable = true, + RetryCondition = new List { retryCondition }, + NoRetryCondition = null + }; + Assert.Equal(100, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new ThrottlingException { + // TODO retryable true + Code = "Throttling", + RetryAfter = 2000L + } + }; + Assert.Equal(2000, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new ThrottlingException { + // TODO retryable true + Code = "Throttling.User", + RetryAfter = 2000L + } + }; + Assert.Equal(2000, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + + retryPolicyContext = new RetryPolicyContext + { + RetriesAttempted = 1, + Exception = new ThrottlingException { + // TODO retryable true + Code = "Throttling.Api", + RetryAfter = 2000L + } + }; + Assert.Equal(2000, DaraCore.GetBackoffDelay(retryOptions, retryPolicyContext)); + } + + [Fact] + public void TestSleep() + { + TimeSpan tsBefore = new TimeSpan(DateTime.Now.Ticks); + DaraCore.Sleep(1000); + TimeSpan tsAfter = new TimeSpan(DateTime.Now.Ticks); + TimeSpan tsSubtract = tsBefore.Subtract(tsAfter).Duration(); + Assert.InRange(tsSubtract.TotalMilliseconds, 990, 1100); + } + + [Fact] + public async void TestSleepAsync() + { + TimeSpan tsBefore = new TimeSpan(DateTime.Now.Ticks); + await DaraCore.SleepAsync(1000); + TimeSpan tsAfter = new TimeSpan(DateTime.Now.Ticks); + TimeSpan tsSubtract = tsBefore.Subtract(tsAfter).Duration(); + Assert.InRange(tsSubtract.TotalMilliseconds, 990, 1000000); + } + + [Fact] + public void TestIsRetryable() + { + Exception ex = new Exception(); + Assert.False(DaraCore.IsRetryable(ex)); + + DaraRetryableException webEx = new DaraRetryableException(); + Assert.True(DaraCore.IsRetryable(webEx)); + } + + [Fact] + public void TestBytesReadable() + { + string str = "test"; + Stream stream = DaraCore.BytesReadable(str); + byte[] bytes = new byte[stream.Length]; + stream.Read(bytes, 0, bytes.Length); + string bytesStr = Encoding.UTF8.GetString(bytes); + Assert.Equal("test", bytesStr); + } + + [Fact] + public void Test_PercentEncode() + { + Assert.Null(DaraCore.PercentEncode(null)); + + Assert.Equal("test%3D", DaraCore.PercentEncode("test=")); + } + } +} diff --git a/DaraUnitTests/DaraDateTest.cs b/DaraUnitTests/DaraDateTest.cs new file mode 100644 index 0000000..ba0dac9 --- /dev/null +++ b/DaraUnitTests/DaraDateTest.cs @@ -0,0 +1,74 @@ +using Darabonba; +using Xunit; +using System; + +namespace DaraUnitTests +{ + public class DaraDateTest + { + DaraDate dateLocal = new DaraDate("2023-12-31 00:00:00.916000"); + DaraDate dateUTC = new DaraDate("2023-12-31 00:00:00.916000 +0000"); + + [Fact] + public void Test_TimestampStr() + { + DaraDate date = new DaraDate("1723081751"); + Assert.Equal("2024-08-08 01:49:11.000000 +0000 UTC", date.Date.ToString("yyyy-MM-dd HH:mm:ss.ffffff '+0000 UTC'")); + } + + [Fact] + public void Test_Init_NoTimeZone() + { + Assert.Equal("2023-12-31 00:00:00.916000", dateLocal.Date.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss.ffffff")); + } + + [Fact] + public void Test_Init_WithTimeZone() + { + DateTime expectedDate = DateTimeOffset.Parse("2023-12-31 00:00:00.916000 +0000").UtcDateTime; + Assert.Equal(expectedDate, dateUTC.Date); + } + + [Fact] + public void Test_Format() + { + Assert.Equal("2023-12-31 00:00:00.916", dateUTC.Format("yyyy-MM-dd HH:mm:ss.fff")); + Assert.Equal("2023-12-31 00:00:00", dateUTC.Format("yyyy-MM-dd HH:mm:ss")); + Assert.Equal("2023-12-31T00:00:00Z", dateUTC.Format("yyyy-MM-ddTHH:mm:ssZ")); + } + + [Fact] + public void Test_Unix() + { + Assert.Equal(1703980800, dateUTC.Unix()); + Assert.Equal(1703980800, dateLocal.Unix()); + } + + [Fact] + public void Test_UTC() + { + Assert.Equal("2023-12-31 00:00:00.916000 +0000 UTC", dateUTC.UTC()); + // Local time + Assert.Equal("2023-12-31 00:00:00.916000 +0000 UTC", dateLocal.UTC()); + } + + [Fact] + public void Test_Methods() + { + DaraDate yesterday = dateUTC.Sub("day", 1); + Assert.Equal("2023-12-30 00:00:00.916", yesterday.Format("yyyy-MM-dd HH:mm:ss.fff")); + Assert.Equal(1, dateUTC.Diff("day", yesterday)); + DaraDate tomorrow = dateUTC.Add("day", 1); + Assert.Equal(-1, dateUTC.Diff("day", tomorrow)); + Assert.Equal(2023, dateUTC.Year()); + Assert.Equal(2024, tomorrow.Year()); + Assert.Equal(1, tomorrow.Month()); + Assert.Equal(12, dateUTC.Month()); + Assert.Equal(0, dateUTC.Hour()); + Assert.Equal(0, dateUTC.Minute()); + Assert.Equal(0, dateUTC.Second()); + Assert.Equal(31, dateUTC.DayOfMonth()); + Assert.Equal(7, dateUTC.DayOfWeek()); + } + } +} \ No newline at end of file diff --git a/DaraUnitTests/DaraFileTest.cs b/DaraUnitTests/DaraFileTest.cs new file mode 100644 index 0000000..324a4e9 --- /dev/null +++ b/DaraUnitTests/DaraFileTest.cs @@ -0,0 +1,179 @@ +using System.Text; +using Darabonba; +using Xunit; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace DaraUnitTests +{ + public class DaraFileTest : IAsyncLifetime + { + private DaraFile _file; + private FileInfo _fileInfo; + + private string tempTestFile = Path.GetTempFileName(); + + public async Task InitializeAsync() + { + File.WriteAllText(tempTestFile, "Test For File"); + _file = new DaraFile(tempTestFile); + _fileInfo = new FileInfo(tempTestFile); + } + + public Task DisposeAsync() + { + _file.Close(); + File.Delete(tempTestFile); + return Task.CompletedTask; + } + + [Fact] + public async Task Test_All() + { + TestPath(); + await TestCreateTimeAsync(); + await TestModifyTimeAsync(); + await TestLengthAsync(); + TestExists(); + await TestExistsAsync(); + TestRead(); + await TestReadAsync(); + TestWrite(); + await TestWriteAsync(); + TestCreateWriteStream(); + TestCreateReadStream(); + } + + private void TestPath() + { + Assert.Equal(tempTestFile, _file.Path()); + } + + private async Task TestCreateTimeAsync() + { + var createTime = await _file.CreateTimeAsync(); + Assert.Equal(_fileInfo.CreationTimeUtc.ToString("yyyy-MM-dd HH:mm:ssZ"), createTime.Date.ToString("yyyy-MM-dd HH:mm:ssZ")); + } + + private async Task TestModifyTimeAsync() + { + var modifyTime = await _file.ModifyTimeAsync(); + Assert.Equal(_fileInfo.LastWriteTimeUtc.ToString("yyyy-MM-dd HH:mm:ssZ"), modifyTime.Date.ToString("yyyy-MM-dd HH:mm:ssZ")); + } + + private async Task TestLengthAsync() + { + var length = await _file.LengthAsync(); + Assert.Equal(_fileInfo.Length, length); + string tempTestFile1 = Path.GetTempFileName(); + File.WriteAllText(tempTestFile1, "Hello, World!"); + var newFile = new DaraFile(tempTestFile1); + var newLength = await newFile.LengthAsync(); + Assert.Equal(_fileInfo.Length, newLength); + await newFile.CloseAsync(); + } + + private void TestExists() + { + Assert.True(DaraFile.Exists(tempTestFile)); + Assert.False(DaraFile.Exists("../../../../TeaUnitTests/Fixtures/test1.txt")); + } + + private async Task TestExistsAsync() + { + Assert.True(await DaraFile.ExistsAsync(tempTestFile)); + Assert.False(await DaraFile.ExistsAsync("../../../../TeaUnitTests/Fixtures/test1.txt")); + } + + private void TestRead() + { + byte[] text1 = _file.Read(4); + Assert.Equal("Test", Encoding.UTF8.GetString(text1)); + byte[] text2 = _file.Read(4); + Assert.Equal(" For", Encoding.UTF8.GetString(text2)); + Assert.Equal(8, _file._position); + string tempEmptyFile = Path.GetTempFileName(); + DaraFile emptyFile = new DaraFile(tempEmptyFile); + byte[] empty = emptyFile.Read(10); + Assert.Null(empty); + emptyFile.Close(); + } + + private async Task TestReadAsync() + { + _file._position = 0; + byte[] text1 = await _file.ReadAsync(4); + Assert.Equal("Test", Encoding.UTF8.GetString(text1)); + byte[] text2 = await _file.ReadAsync(4); + Assert.Equal(" For", Encoding.UTF8.GetString(text2)); + Assert.Equal(8, _file._position); + string tempEmptyFile = Path.GetTempFileName(); + DaraFile emptyFile = new DaraFile(tempEmptyFile); + byte[] empty = await emptyFile.ReadAsync(10); + Assert.Null(empty); + await emptyFile.CloseAsync(); + } + + private void TestWrite() + { + var expectedLen = _fileInfo.Length; + _file.Write(Encoding.UTF8.GetBytes(" Test")); + DaraDate modifyTime = _file.ModifyTime(); + int length = _file.Length(); + Assert.Equal(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ssZ"), modifyTime.Date.ToString("yyyy-MM-dd HH:mm:ssZ")); + Assert.Equal(expectedLen + 5, length); + string tempNewFile = Path.GetTempFileName(); + DaraFile newFile = new DaraFile(tempNewFile); + newFile.Write(Encoding.UTF8.GetBytes("Test")); + byte[] text = newFile.Read(4); + Assert.Equal("Test", Encoding.UTF8.GetString(text)); + newFile.Close(); + } + + private async Task TestWriteAsync() + { + var expectedLen = _fileInfo.Length; + await _file.WriteAsync(Encoding.UTF8.GetBytes(" Test")); + DaraDate modifyTime = await _file.ModifyTimeAsync(); + int length = await _file.LengthAsync(); + Assert.Equal(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ssZ"), modifyTime.Date.ToString("yyyy-MM-dd HH:mm:ssZ")); + Assert.Equal(expectedLen + 10, length); + string tempNewFile = Path.GetTempFileName(); + DaraFile newFile = new DaraFile(tempNewFile); + await newFile.WriteAsync(Encoding.UTF8.GetBytes("Test")); + byte[] text = await newFile.ReadAsync(4); + Assert.Equal("Test", Encoding.UTF8.GetString(text)); + await newFile.CloseAsync(); + } + + private void TestCreateWriteStream() + { + string tempWriteFile = Path.GetTempFileName(); + using (FileStream stream = DaraFile.CreateWriteStream(tempWriteFile)) + { + Assert.NotNull(stream); + Assert.True(stream.CanWrite); + byte[] contentBytes = Encoding.UTF8.GetBytes("Test Write"); + stream.Write(contentBytes, 0, contentBytes.Length); + } + string finalContent = File.ReadAllText(tempWriteFile); + Assert.EndsWith("Test Write", finalContent); + } + + private void TestCreateReadStream() + { + string tempReadFile = Path.GetTempFileName(); + File.WriteAllText(tempReadFile, "Test For File"); + using (FileStream stream = DaraFile.CreateReadStream(tempReadFile)) + { + Assert.NotNull(stream); + Assert.True(stream.CanRead); + byte[] buffer = new byte[13]; + int bytesRead = stream.Read(buffer, 0, buffer.Length); + string actualContent = Encoding.UTF8.GetString(buffer, 0, bytesRead); + Assert.Equal("Test For File", actualContent); + } + } + } +} \ No newline at end of file diff --git a/TeaUnitTests/TeaModelTest.cs b/DaraUnitTests/DaraModelTest.cs similarity index 82% rename from TeaUnitTests/TeaModelTest.cs rename to DaraUnitTests/DaraModelTest.cs index 3d34549..99a3564 100644 --- a/TeaUnitTests/TeaModelTest.cs +++ b/DaraUnitTests/DaraModelTest.cs @@ -2,17 +2,16 @@ using System.Collections; using System.Collections.Generic; using System.Text; -using Tea; - -using TeaUnitTests.Models; +using Darabonba; +using DaraUnitTests.Models; using Xunit; -namespace TeaUnitTests +namespace DaraUnitTests { - public class TeaModelTest + public class DaraModelTest { - public class NestedTest : TeaModel + public class NestedTest : DaraModel { [NameInMap("MapNestedMap")] public Dictionary> mapnestedMap { get; set; } @@ -25,17 +24,19 @@ public class NestedTest : TeaModel [Fact] public void TestToMap() { - TeaModel modelNull = null; + DaraModel modelNull = null; Assert.Null(modelNull.ToMap()); - TestRegModel model = new TestRegModel(); - model.RequestId = "requestID"; - model.Items = new List { new TestRegSubModel { RequestId = "sub" }, null }; - model.NextMarker = "next"; - model.testNoAttr = "noAttr"; - model.subModel = new TestRegSubModel(); - model.testListStr = new List { "str" }; - model.bytes = Encoding.UTF8.GetBytes("test"); + TestRegModel model = new TestRegModel + { + RequestId = "requestID", + Items = new List { new TestRegSubModel { RequestId = "sub" }, null }, + NextMarker = "next", + testNoAttr = "noAttr", + subModel = new TestRegSubModel(), + testListStr = new List { "str" }, + bytes = Encoding.UTF8.GetBytes("test") + }; TestRegSubModel dicSubModel = new TestRegSubModel { RequestId = "requestDic" @@ -62,7 +63,7 @@ public void TestToMap() Dictionary dic = model.ToMap(); Assert.NotNull(dic); - var from = TeaModel.ToObject(dic); + var from = DaraModel.ToObject(dic); Assert.Equal("test", from.listIDic[0][0]["test"]); Assert.Equal("requestDic", from.dicNestDic["map"]["subDic"].RequestId); @@ -121,7 +122,7 @@ public void TestToObject() nullValueDic.Add("testNullValueDic", new Dictionary()); dic.Add("Content", nullValueDic); - TestRegModel model = TeaModel.ToObject(dic); + TestRegModel model = DaraModel.ToObject(dic); var testNullValueDic = model.Content["testNullValueDic"] as Dictionary; Assert.True(testNullValueDic.Count==0); Assert.NotNull(model); @@ -141,11 +142,11 @@ public void TestToObject() Assert.Null(model.testNull); Dictionary dicString = null; - Assert.Null(TeaModel.ToObject(dicString)); + Assert.Null(DaraModel.ToObject(dicString)); dicString = new Dictionary(); dicString.Add("requestId", "test"); dicString.Add("count", "1"); - TestDicStringModel stringModel = TeaModel.ToObject(dicString); + TestDicStringModel stringModel = DaraModel.ToObject(dicString); Assert.NotNull(stringModel); Assert.Equal("test", stringModel.RequestId); @@ -153,40 +154,40 @@ public void TestToObject() dicConvert.Add("requestId", dicSub); dicConvert.Add("count", "1"); - stringModel = TeaModel.ToObject(dicConvert); + stringModel = DaraModel.ToObject(dicConvert); Assert.NotNull(stringModel); Assert.Equal("{\"requestId\":\"sub\",\"testInt\":100}", stringModel.RequestId); Assert.Equal(1, stringModel.Count); dicConvert.Remove("requestId"); dicConvert.Add("requestId", dicItems); - stringModel = TeaModel.ToObject(dicConvert); + stringModel = DaraModel.ToObject(dicConvert); Assert.Equal("[{\"requestId\":\"sub\",\"testInt\":100},{\"requestId\":\"subRe\",\"testInt\":500},null]", stringModel.RequestId); dicConvert.Remove("requestId"); string[] array = new string[] { "a", "b" }; dicConvert.Add("requestId", array); - stringModel = TeaModel.ToObject(dicConvert); + stringModel = DaraModel.ToObject(dicConvert); Assert.Equal("[\"a\",\"b\"]", stringModel.RequestId); dicConvert.Remove("requestId"); dicConvert.Add("requestId", 1.1); - stringModel = TeaModel.ToObject(dicConvert); + stringModel = DaraModel.ToObject(dicConvert); Assert.Equal("1.1", stringModel.RequestId); dicConvert.Remove("requestId"); dicConvert.Add("requestId", 11111111111111111111L); - stringModel = TeaModel.ToObject(dicConvert); + stringModel = DaraModel.ToObject(dicConvert); Assert.Equal("11111111111111111111", stringModel.RequestId); dicConvert.Remove("requestId"); dicConvert.Add("requestId", null); - stringModel = TeaModel.ToObject(dicConvert); + stringModel = DaraModel.ToObject(dicConvert); Assert.Null(stringModel.RequestId); dicConvert.Remove("requestId"); dicConvert.Add("requestId", true); - stringModel = TeaModel.ToObject(dicConvert); + stringModel = DaraModel.ToObject(dicConvert); Assert.Equal("True", stringModel.RequestId); } @@ -194,7 +195,7 @@ public void TestToObject() [Fact] public void TestValidator() { - TeaModel modelNull = null; + DaraModel modelNull = null; Assert.Throws(() => { modelNull.Validate(); }); TestRegModel successModel = new TestRegModel(); diff --git a/DaraUnitTests/DaraRequestTest.cs b/DaraUnitTests/DaraRequestTest.cs new file mode 100644 index 0000000..a332b1f --- /dev/null +++ b/DaraUnitTests/DaraRequestTest.cs @@ -0,0 +1,28 @@ +using Darabonba; + +using Xunit; + +namespace DaraUnitTests +{ + public class DaraRequestTest + { + [Fact] + public void TestDaraRequest() + { + DaraRequest daraRequest = new DaraRequest(); + Assert.NotNull(daraRequest); + Assert.NotNull(daraRequest.Headers); + Assert.NotNull(daraRequest.Query); + Assert.Equal("http", daraRequest.Protocol); + daraRequest.Headers = null; + Assert.NotNull(daraRequest.Headers); + Assert.Equal("GET", daraRequest.Method); + + daraRequest.Method = "POST"; + Assert.Equal("POST", daraRequest.Method); + + daraRequest.Query = null; + Assert.NotNull(daraRequest.Query); + } + } +} diff --git a/DaraUnitTests/DaraResponseTest.cs b/DaraUnitTests/DaraResponseTest.cs new file mode 100644 index 0000000..b44e85b --- /dev/null +++ b/DaraUnitTests/DaraResponseTest.cs @@ -0,0 +1,31 @@ +using System.IO; +using System.Net; +using System.Net.Http; +using System.Text; + +using Darabonba; + +using Xunit; + +namespace DaraUnitTests +{ + public class DaraResponseTest + { + [Fact] + public void TestDaraResponse() + { + HttpResponseMessage httpResponseMessage = new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("test"))) + }; + DaraResponse response = new DaraResponse(httpResponseMessage); + Assert.NotNull(response); + Assert.Equal(200, response.StatusCode); + Assert.Equal("", response.StatusMessage); + + DaraResponse daraResponseNull = new DaraResponse(null); + Assert.Null(daraResponseNull.Body); + } + } +} diff --git a/DaraUnitTests/DaraURLTest.cs b/DaraUnitTests/DaraURLTest.cs new file mode 100644 index 0000000..4b50173 --- /dev/null +++ b/DaraUnitTests/DaraURLTest.cs @@ -0,0 +1,49 @@ +using Darabonba; +using Xunit; + +namespace DaraUnitTests +{ + public class DaraURLTest + { + [Fact] + public void Test_Parse() + { + string url = "https://sdk:test@ecs.aliyuncs.com:443/sdk/?api&ok=test#sddd"; + DaraURL ret = DaraURL.Parse(url); + Assert.Equal("/sdk/?api&ok=test", ret.Path()); + Assert.Equal("/sdk/", ret.Pathname()); + Assert.Equal("https", ret.Protocol()); + Assert.Equal("ecs.aliyuncs.com", ret.Hostname()); + Assert.Equal("ecs.aliyuncs.com", ret.Host()); + Assert.Equal("443", ret.Port()); + Assert.Equal("sddd", ret.Hash()); + Assert.Equal("api&ok=test", ret.Search()); + Assert.Equal("https://sdk:test@ecs.aliyuncs.com/sdk/?api&ok=test#sddd", ret.Href()); + Assert.Equal("sdk:test", ret.Auth()); + } + + [Fact] + public void Test_UrlEncode() + { + string result = DaraURL.UrlEncode("https://www.baidu.com/"); + Assert.Equal("https%3A%2F%2Fwww.baidu.com%2F", result); + } + + [Fact] + public void Test_PercentEncode() + { + Assert.Null(DaraURL.PercentEncode(null)); + Assert.Equal("test%3D", DaraURL.PercentEncode("test=")); + + string result = DaraURL.PercentEncode("https://www.bai+*~du.com/"); + Assert.Equal("https%3A%2F%2Fwww.bai%2B%2A~du.com%2F", result); + } + + [Fact] + public void Test_PathEncode() + { + string result = DaraURL.PathEncode("/work_space/DARABONBA/GIT/darabonba-util/ts"); + Assert.Equal("/work_space/DARABONBA/GIT/darabonba-util/ts", result); + } + } +} \ No newline at end of file diff --git a/TeaUnitTests/TeaUnitTests.csproj b/DaraUnitTests/DaraUnitTests.csproj similarity index 82% rename from TeaUnitTests/TeaUnitTests.csproj rename to DaraUnitTests/DaraUnitTests.csproj index e417cdc..8f0bb77 100644 --- a/TeaUnitTests/TeaUnitTests.csproj +++ b/DaraUnitTests/DaraUnitTests.csproj @@ -1,10 +1,10 @@  - netcoreapp2.0 + netcoreapp3.1 false false - TeaUnitTests + DaraUnitTests @@ -23,7 +23,7 @@ - + diff --git a/DaraUnitTests/DaraValidatorTest.cs b/DaraUnitTests/DaraValidatorTest.cs new file mode 100644 index 0000000..c13a812 --- /dev/null +++ b/DaraUnitTests/DaraValidatorTest.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using Darabonba; +using Xunit; + +namespace DaraUnitTests +{ + public class DaraValidatorTest + { + [Fact] + public void TestDaraValidator() + { + DaraValidator daraValidator = new DaraValidator(null, "propertyName"); + daraValidator.ValidateRequired("test"); + daraValidator.ValidateRegex("test"); + Assert.NotNull(daraValidator); + + ValidationAttribute attribute = new ValidationAttribute(); + attribute.Required = false; + daraValidator.Attribute = attribute; + daraValidator.ValidateRequired("test"); + Assert.NotNull(daraValidator); + + attribute.Pattern = ""; + daraValidator.ValidateRegex("test"); + Assert.NotNull(daraValidator); + + attribute.Pattern = "pattern"; + daraValidator.ValidateRegex(null); + Assert.NotNull(daraValidator); + + daraValidator.ValidateRegex("patternTest"); + Assert.NotNull(daraValidator); + + Assert.Equal("propertyName is not match pattern", + Assert.Throws(() => { daraValidator.ValidateRegex("test"); }).Message + ); + + attribute.Required = true; + Assert.Equal("propertyName is required.", + Assert.Throws(() => { daraValidator.ValidateRequired(null); }).Message + ); + + attribute.MaxLength = 3; + daraValidator.ValidateMaxLength("阿里"); + Assert.Equal("propertyName is exceed max-length: 3", + Assert.Throws(() => { daraValidator.ValidateMaxLength("阿里test"); }).Message + ); + + List list = new List{ "1", "2","3","4" }; + daraValidator.ValidateMaxLength("阿里"); + Assert.Equal("propertyName is exceed max-length: 3", + Assert.Throws(() => { daraValidator.ValidateMaxLength(list); }).Message + ); + + attribute.MinLength = 2; + daraValidator.ValidateMinLength("阿里"); + Assert.Equal("propertyName is less than min-length: 2", + Assert.Throws(() => { daraValidator.ValidateMinLength("阿"); }).Message + ); + + attribute.Maximun = 1.5; + daraValidator.ValidateMaximum("1"); + Assert.Equal("propertyName is exceed maximum: 1.5", + Assert.Throws(() => { daraValidator.ValidateMaximum(2); }).Message + ); + + attribute.Minimum = 1; + daraValidator.ValidateMinimum(1.5); + Assert.Equal("propertyName is less than Minimum: 1", + Assert.Throws(() => { daraValidator.ValidateMinimum(-2); }).Message + ); + } + } +} diff --git a/DaraUnitTests/Exceptions/DaraRetryableExceptionTest.cs b/DaraUnitTests/Exceptions/DaraRetryableExceptionTest.cs new file mode 100644 index 0000000..d1d9997 --- /dev/null +++ b/DaraUnitTests/Exceptions/DaraRetryableExceptionTest.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Net; +using System.Net.Http; +using System.Text; +using Darabonba; +using Darabonba.Exceptions; +using Xunit; + +namespace DaraUnitTests.Exceptions +{ + public class DaraRetryableExceptionTest + { + [Fact] + public void TestDaraRetryableException() + { + HttpResponseMessage httpResponseMessage = new HttpResponseMessage(); + httpResponseMessage.StatusCode = HttpStatusCode.OK; + httpResponseMessage.Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + DaraResponse response = new DaraResponse(httpResponseMessage); + DaraRetryableException daraRetryableException = new DaraRetryableException(); + Assert.NotNull(daraRetryableException); + } + } +} diff --git a/TeaUnitTests/TeaExceptionTest.cs b/DaraUnitTests/Exceptions/TeaExceptionTest.cs similarity index 98% rename from TeaUnitTests/TeaExceptionTest.cs rename to DaraUnitTests/Exceptions/TeaExceptionTest.cs index 6e30024..d9c27aa 100644 --- a/TeaUnitTests/TeaExceptionTest.cs +++ b/DaraUnitTests/Exceptions/TeaExceptionTest.cs @@ -1,11 +1,9 @@ using System.Collections.Generic; - using Tea; -using Tea.Utils; - +using Darabonba.Utils; using Xunit; -namespace TeaUnitTests +namespace DaraUnitTests { public class TeaExceptionTest { diff --git a/TeaUnitTests/TeaUnretryableExceptionTest.cs b/DaraUnitTests/Exceptions/TeaUnretryableExceptionTest.cs similarity index 76% rename from TeaUnitTests/TeaUnretryableExceptionTest.cs rename to DaraUnitTests/Exceptions/TeaUnretryableExceptionTest.cs index 37b117a..c7e8939 100644 --- a/TeaUnitTests/TeaUnretryableExceptionTest.cs +++ b/DaraUnitTests/Exceptions/TeaUnretryableExceptionTest.cs @@ -1,20 +1,19 @@ using System; - +using Darabonba; using Tea; - using Xunit; -namespace TeaUnitTests +namespace DaraUnitTests.Exceptions { public class TeaUnretryableExceptionTest { [Fact] - public void TestTeaUnretryableException() + public void TestDaraUnretryableException() { TeaUnretryableException teaUnretryableExceptionEmpty = new TeaUnretryableException(); Assert.NotNull(teaUnretryableExceptionEmpty); - TeaUnretryableException teaUnretryableException = new TeaUnretryableException(new TeaRequest(), new Exception("Exception")); + TeaUnretryableException teaUnretryableException = new TeaUnretryableException(new DaraRequest(), new Exception("Exception")); Assert.NotNull(teaUnretryableException); Assert.Equal(" Retry failed : Exception", teaUnretryableException.Message); Assert.NotNull(teaUnretryableException.LastRequest); diff --git a/DaraUnitTests/Fixtures/test.json b/DaraUnitTests/Fixtures/test.json new file mode 100644 index 0000000..715b02d --- /dev/null +++ b/DaraUnitTests/Fixtures/test.json @@ -0,0 +1 @@ +{"key":"value"} \ No newline at end of file diff --git a/DaraUnitTests/Models/ListAllMyBucketsResult.cs b/DaraUnitTests/Models/ListAllMyBucketsResult.cs new file mode 100644 index 0000000..e200b6e --- /dev/null +++ b/DaraUnitTests/Models/ListAllMyBucketsResult.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using Darabonba; + +namespace DaraUnitTests.Models +{ + public class ListAllMyBucketsResult : DaraModel + { + [NameInMap("Owner")] + public Owner owner { get; set; } + + [NameInMap("Buckets")] + public Buckets buckets { get; set; } + + [NameInMap("listStr")] + public List testStrList { get; set; } + + [NameInMap("Owners")] + public List owners { get; set; } + + public long? TestLong { get; set; } + + public short? TestShort { get; set; } + + public uint? TestUInt { get; set; } + + public ushort? TestUShort { get; set; } + + public ulong? TestULong { get; set; } + + public float? TestFloat { get; set; } + + public double? TestDouble { get; set; } + + public string TestNull { get; set; } + + public string TestString { get; set; } + + public bool? TestBool { get; set; } + + public Dictionary dict { get; set; } + + public List TestListNull { get; set; } + + public class Owner : DaraModel + { + public int? ID { get; set; } + + public string DisplayName { get; set; } + } + + public class Buckets : DaraModel + { + [NameInMap("Bucket")] + public List bucket { get; set; } + + public class Bucket : DaraModel + { + public string CreationDate { get; set; } + + public string ExtranetEndpoint { get; set; } + + public string IntranetEndpoint { get; set; } + + public string Location { get; set; } + + public string Name { get; set; } + + public string StorageClass { get; set; } + } + } + } +} diff --git a/TeaUnitTests/Models/TestDicStringModel.cs b/DaraUnitTests/Models/TestDicStringModel.cs similarity index 79% rename from TeaUnitTests/Models/TestDicStringModel.cs rename to DaraUnitTests/Models/TestDicStringModel.cs index 84f6794..804f7a1 100644 --- a/TeaUnitTests/Models/TestDicStringModel.cs +++ b/DaraUnitTests/Models/TestDicStringModel.cs @@ -1,6 +1,6 @@ -using Tea; +using Darabonba; -namespace TeaUnitTests.Models +namespace DaraUnitTests.Models { public class TestDicStringModel { diff --git a/TeaUnitTests/Models/TestRegModel.cs b/DaraUnitTests/Models/TestRegModel.cs similarity index 91% rename from TeaUnitTests/Models/TestRegModel.cs rename to DaraUnitTests/Models/TestRegModel.cs index 610022e..0260831 100644 --- a/TeaUnitTests/Models/TestRegModel.cs +++ b/DaraUnitTests/Models/TestRegModel.cs @@ -1,11 +1,10 @@ using System.Collections; using System.Collections.Generic; +using Darabonba; -using Tea; - -namespace TeaUnitTests.Models +namespace DaraUnitTests.Models { - public class TestRegModel : TeaModel + public class TestRegModel : DaraModel { [NameInMap("requestId")] [Validation(Pattern = "re", MaxLength = 0, Required = true)] @@ -23,8 +22,8 @@ public class TestRegModel : TeaModel [NameInMap("testListStr")] [Validation(Pattern = "listStr", MaxLength = 0)] - public List testListStr { get; set; } - + public List testListStr { get; set; } + [NameInMap("Content")] [Validation(Required = false)] public Dictionary Content diff --git a/TeaUnitTests/Models/TestRegSubModel.cs b/DaraUnitTests/Models/TestRegSubModel.cs similarity index 69% rename from TeaUnitTests/Models/TestRegSubModel.cs rename to DaraUnitTests/Models/TestRegSubModel.cs index 08e30ad..5ca79b4 100644 --- a/TeaUnitTests/Models/TestRegSubModel.cs +++ b/DaraUnitTests/Models/TestRegSubModel.cs @@ -1,8 +1,8 @@ -using Tea; +using Darabonba; -namespace TeaUnitTests.Models +namespace DaraUnitTests.Models { - public class TestRegSubModel : TeaModel + public class TestRegSubModel : DaraModel { [NameInMap("requestId")] [Validation(Pattern = "r", MaxLength = 0, Required = true)] diff --git a/DaraUnitTests/Models/ToBodyModel.cs b/DaraUnitTests/Models/ToBodyModel.cs new file mode 100644 index 0000000..7ecadb5 --- /dev/null +++ b/DaraUnitTests/Models/ToBodyModel.cs @@ -0,0 +1,11 @@ +using Darabonba; + +namespace DaraUnitTests.Models +{ + public class ToBodyModel : DaraModel + { + [NameInMap("ListAllMyBucketsResult")] + public ListAllMyBucketsResult listAllMyBucketsResult { get; set; } + + } +} diff --git a/DaraUnitTests/RetryPolicy/BackoffPolicyTest.cs b/DaraUnitTests/RetryPolicy/BackoffPolicyTest.cs new file mode 100644 index 0000000..5ca1f87 --- /dev/null +++ b/DaraUnitTests/RetryPolicy/BackoffPolicyTest.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using Darabonba.Exceptions; +using Darabonba.RetryPolicy; +using Xunit; + +namespace DaraUnitTests.RetryPolicy +{ + public class BackoffPolicyTest + { + [Fact] + public void Test_BackoffPolicy() + { + var exception = Assert.Throws(() => + { + BackoffPolicy backoffPolicy = BackoffPolicy.NewBackOffPolicy(new Dictionary + { + { "policy", "Any" } + }); + }); + Assert.Equal("Invalid backoff policy", exception.Message); + + BackoffPolicy backoffPolicy = BackoffPolicy.NewBackOffPolicy(new Dictionary + { + { "policy", "Fixed" }, + { "period", 1000 } + }); + Assert.Equal("FixedBackoffPolicy", backoffPolicy.GetType().Name); + + backoffPolicy = BackoffPolicy.NewBackOffPolicy(new Dictionary + { + { "policy", "Random" }, + { "period", 2 }, + { "cap", 60000L } + }); + Assert.Equal("RandomBackoffPolicy", backoffPolicy.GetType().Name); + + backoffPolicy = BackoffPolicy.NewBackOffPolicy(new Dictionary + { + { "policy", "Exponential" }, + { "period", 2 }, + { "cap", 60000L } + }); + Assert.Equal("ExponentialBackoffPolicy", backoffPolicy.GetType().Name); + + backoffPolicy = BackoffPolicy.NewBackOffPolicy(new Dictionary + { + { "policy", "EqualJitter" }, + { "period", 2 }, + { "cap", 60000L } + }); + Assert.Equal("EqualJitterBackoffPolicy", backoffPolicy.GetType().Name); + + backoffPolicy = BackoffPolicy.NewBackOffPolicy(new Dictionary + { + { "policy", "ExponentialWithEqualJitter" }, + { "period", 2 }, + { "cap", 60000L } + }); + Assert.Equal("EqualJitterBackoffPolicy", backoffPolicy.GetType().Name); + + backoffPolicy = BackoffPolicy.NewBackOffPolicy(new Dictionary + { + { "policy", "FullJitter" }, + { "period", 2 }, + { "cap", 60000L } + }); + Assert.Equal("FullJitterBackoffPolicy", backoffPolicy.GetType().Name); + + backoffPolicy = BackoffPolicy.NewBackOffPolicy(new Dictionary + { + { "policy", "ExponentialWithFullJitter" }, + { "period", 2 }, + { "cap", 60000L } + }); + Assert.Equal("FullJitterBackoffPolicy", backoffPolicy.GetType().Name); + } + } +} \ No newline at end of file diff --git a/DaraUnitTests/Streams/FileFormStreamTest.cs b/DaraUnitTests/Streams/FileFormStreamTest.cs new file mode 100644 index 0000000..73a560c --- /dev/null +++ b/DaraUnitTests/Streams/FileFormStreamTest.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +using Darabonba.Streams; +using Xunit; + +namespace DaraUnitTests.Streams +{ + public class FileFormStreamTest + { + [Fact] + public void Test_FileField() + { + FileField fileField = new FileField(); + fileField.Content = new MemoryStream(); + fileField.ContentType = "contentType"; + fileField.Filename = "fileName"; + Assert.NotNull(fileField); + Assert.Equal("contentType", fileField.ContentType); + Assert.Equal("fileName", fileField.Filename); + Assert.NotNull(fileField.Content); + } + + [Fact] + public void Test_FileFormStream() + { + FileFormStream fileFormStream = new FileFormStream(new Dictionary(), ""); + Assert.True(fileFormStream.CanRead); + Assert.False(fileFormStream.CanSeek); + Assert.False(fileFormStream.CanWrite); + fileFormStream.Position = 1; + Assert.Equal(1, fileFormStream.Position); + fileFormStream.SetLength(2); + Assert.Equal(2, fileFormStream.Length); + Assert.Throws(() => { fileFormStream.Flush(); }); + Assert.Throws(() => { fileFormStream.Seek(0, System.IO.SeekOrigin.Begin); }); + Assert.Throws(() => { fileFormStream.Write(new byte[1024], 0, 1024); }); + } + + [Fact] + public void Test_Read() + { + FileFormStream fileFormStream = new FileFormStream(new Dictionary(), ""); + Assert.Equal(6, fileFormStream.Read(new byte[1024], 0, 1024)); + + FileField fileFieldNoContent = new FileField() + { + Filename = "noContent", + Content = null, + ContentType = "contentType" + }; + MemoryStream content = new MemoryStream(); + byte[] contentBytes = Encoding.UTF8.GetBytes("This is file test. This sentence must be long"); + content.Write(contentBytes, 0, contentBytes.Length); + content.Seek(0, SeekOrigin.Begin); + FileField fileField = new FileField() + { + Filename = "haveContent", + Content = content, + ContentType = "contentType" + }; + + Dictionary dict = new Dictionary(); + dict.Add("key", "value"); + dict.Add("testKey", "testValue"); + dict.Add("haveFile", fileField); + dict.Add("noFile", fileFieldNoContent); + MemoryStream StreamResult = new MemoryStream(); + byte[] bytes = new byte[1024]; + fileFormStream = new FileFormStream(dict, "testBoundary"); + int readNoStreamLength = 0; + while ((readNoStreamLength = fileFormStream.Read(bytes, 0, 1024)) != 0) + { + StreamResult.Write(bytes, 0, readNoStreamLength); + } + StreamResult.Seek(0, SeekOrigin.Begin); + byte[] bytesResult = new byte[StreamResult.Length]; + StreamResult.Read(bytesResult, 0, (int) StreamResult.Length); + string result = Encoding.UTF8.GetString(bytesResult); + Assert.Equal("--testBoundary\r\nContent-Disposition: form-data; name=\"key\"\r\n\r\nvalue\r\n--testBoundary\r\nContent-Disposition: form-data; name=\"testKey\"\r\n\r\ntestValue\r\n--testBoundary\r\nContent-Disposition: form-data; name=\"haveFile\"; filename=\"haveContent\"\r\nContent-Type: contentType\r\n\r\nThis is file test. This sentence must be long\r\n--testBoundary\r\nContent-Disposition: form-data; name=\"noFile\"; filename=\"noContent\"\r\nContent-Type: contentType\r\n\r\n\r\n--testBoundary--\r\n", result); + } + + [Fact] + public async Task Test_ReadAsync() + { + FileFormStream fileFormStream = new FileFormStream(new Dictionary(), ""); + Assert.Equal(6, await fileFormStream.ReadAsync(new byte[1024], 0, 1024)); + + FileField fileFieldNoContent = new FileField() + { + Filename = "noContent", + Content = null, + ContentType = "contentType" + }; + MemoryStream content = new MemoryStream(); + byte[] contentBytes = Encoding.UTF8.GetBytes("This is file test. This sentence must be long"); + content.Write(contentBytes, 0, contentBytes.Length); + content.Seek(0, SeekOrigin.Begin); + FileField fileField = new FileField() + { + Filename = "haveContent", + Content = content, + ContentType = "contentType" + }; + + Dictionary dict = new Dictionary(); + dict.Add("key", "value"); + dict.Add("testKey", "testValue"); + dict.Add("haveFile", fileField); + dict.Add("noFile", fileFieldNoContent); + MemoryStream StreamResult = new MemoryStream(); + byte[] bytes = new byte[1024]; + fileFormStream = new FileFormStream(dict, "testBoundary"); + int readNoStreamLength = 0; + while ((readNoStreamLength = await fileFormStream.ReadAsync(bytes, 0, 1024)) != 0) + { + StreamResult.Write(bytes, 0, readNoStreamLength); + } + StreamResult.Seek(0, SeekOrigin.Begin); + byte[] bytesResult = new byte[StreamResult.Length]; + StreamResult.Read(bytesResult, 0, (int) StreamResult.Length); + string result = Encoding.UTF8.GetString(bytesResult); + Assert.Equal("--testBoundary\r\nContent-Disposition: form-data; name=\"key\"\r\n\r\nvalue\r\n--testBoundary\r\nContent-Disposition: form-data; name=\"testKey\"\r\n\r\ntestValue\r\n--testBoundary\r\nContent-Disposition: form-data; name=\"haveFile\"; filename=\"haveContent\"\r\nContent-Type: contentType\r\n\r\nThis is file test. This sentence must be long\r\n--testBoundary\r\nContent-Disposition: form-data; name=\"noFile\"; filename=\"noContent\"\r\nContent-Type: contentType\r\n\r\n\r\n--testBoundary--\r\n", result); + } + + [Fact] + public void Test_PercentEncode() + { + Assert.Null(FileFormStream.PercentEncode(null)); + Assert.Equal("ab%3Dcd", FileFormStream.PercentEncode("ab=cd")); + } + } +} \ No newline at end of file diff --git a/DaraUnitTests/Streams/StreamUtilTest.cs b/DaraUnitTests/Streams/StreamUtilTest.cs new file mode 100644 index 0000000..099b47a --- /dev/null +++ b/DaraUnitTests/Streams/StreamUtilTest.cs @@ -0,0 +1,565 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using System.Threading; +using Darabonba.Streams; +using Darabonba.Models; +using Xunit; +using System.Net; +using System.Net.Http; +using Newtonsoft.Json; +using System; + +namespace DaraUnitTests.Streams +{ + public class SseServer : IDisposable + { + private readonly HttpListener _httpListener; + private CancellationTokenSource _cancellationTokenSource; + + public SseServer(string uriPrefix) + { + _httpListener = new HttpListener(); + _httpListener.Prefixes.Add(uriPrefix); + } + + public void Start() + { + _cancellationTokenSource = new CancellationTokenSource(); + _httpListener.Start(); + Task.Run(() => HandleIncomingConnections(_cancellationTokenSource.Token)); + } + + private async Task HandleIncomingConnections(CancellationToken cancellationToken) + { + while (!_cancellationTokenSource.IsCancellationRequested) + { + try + { + var context = await _httpListener.GetContextAsync().ConfigureAwait(false); + + if (context.Request.Url?.AbsolutePath == "/sse") + { + HandleSseResponse(context.Response); + } + else if (context.Request.Url?.AbsolutePath == "/sse_with_no_spaces") + { + HandleSseWithNoSpacesResponse(context.Response); + } + else if (context.Request.Url?.AbsolutePath == "/sse_invalid_retry") + { + HandleSseWithInvalidRetryResponse(context.Response); + } + else if (context.Request.Url?.AbsolutePath == "/sse_with_data_divided") + { + HandleSseWithDataDividedResponse(context.Response); + } + } + catch (HttpListenerException) when (cancellationToken.IsCancellationRequested) + { + throw new HttpListenerException(); + } + } + } + + private void HandleSseResponse(HttpListenerResponse response) + { + int count = 0; + Timer timer = null; + timer = new Timer(_ => + { + if (count >= 5) + { + timer.Dispose(); + response.Close(); + return; + } + + byte[] buffer = Encoding.UTF8.GetBytes(string.Format("data: {0}\nevent: flow\nid: sse-test\nretry: 3\n:heartbeat\n\n", JsonConvert.SerializeObject(new { count = count }))); + response.OutputStream.Write(buffer, 0, buffer.Length); + response.OutputStream.Flush(); + count++; + }, null, 0, 100); + } + + private void HandleSseWithNoSpacesResponse(HttpListenerResponse response) + { + int count = 0; + Timer timer = null; + timer = new Timer(_ => + { + if (count >= 5) + { + timer.Dispose(); + response.Close(); + return; + } + + byte[] buffer = Encoding.UTF8.GetBytes(string.Format("data: {0}\nevent:flow\nid:sse-test\nretry:3\n\n", JsonConvert.SerializeObject(new { count = count }))); + response.OutputStream.Write(buffer, 0, buffer.Length); + response.OutputStream.Flush(); + count++; + }, null, 0, 100); + } + + private void HandleSseWithInvalidRetryResponse(HttpListenerResponse response) + { + int count = 0; + Timer timer = null; + timer = new Timer(_ => + { + if (count >= 5) + { + timer.Dispose(); + response.Close(); + return; + } + + byte[] buffer = Encoding.UTF8.GetBytes(string.Format("data: {0}\nevent:flow\nid:sse-test\nretry: abc\n\n", JsonConvert.SerializeObject(new { count = count }))); + response.OutputStream.Write(buffer, 0, buffer.Length); + response.OutputStream.Flush(); + count++; + }, null, 0, 100); + } + + private void HandleSseWithDataDividedResponse(HttpListenerResponse response) + { + int count = 0; + Timer timer = null; + timer = new Timer(_ => + { + if (count >= 5) + { + timer.Dispose(); + response.Close(); + return; + } + + if (count == 1) + { + byte[] buffer = Encoding.UTF8.GetBytes("data:{\"count\":"); + response.OutputStream.Write(buffer, 0, buffer.Length); + response.OutputStream.Flush(); + count++; + return; + } + + if (count == 2) + { + byte[] buffer = Encoding.UTF8.GetBytes(string.Format("{0},\"tag\":\"divided\"}}\nevent:flow\nid:sse-test\nretry:3\n\n", count++)); + response.OutputStream.Write(buffer, 0, buffer.Length); + response.OutputStream.Flush(); + return; + } + + byte[] buffer1 = Encoding.UTF8.GetBytes(string.Format("data: {0}\nevent:flow\nid:sse-test\nretry:3\n\n", JsonConvert.SerializeObject(new { count = count++ }))); + response.OutputStream.Write(buffer1, 0, buffer1.Length); + response.OutputStream.Flush(); + }, null, 0, 100); + } + + public void Stop() + { + _cancellationTokenSource.Cancel(); + _httpListener.Stop(); + _httpListener.Close(); + } + + public void Dispose() + { + Stop(); + ((IDisposable)_httpListener)?.Dispose(); + _cancellationTokenSource?.Dispose(); + } + } + + + public class StreamUtilTest : IAsyncLifetime + { + private SseServer server = new SseServer("http://localhost:8384/"); + + public async Task InitializeAsync() + { + server.Start(); + await Task.Delay(1000); + } + + public Task DisposeAsync() + { + server.Dispose(); + return Task.CompletedTask; + } + + [Fact] + public void Test_ReadAsString() + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes("test"))) + { + Assert.Equal("test", StreamUtil.ReadAsString(stream)); + } + } + + [Fact] + public async void Test_ReadAsStringAsync() + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes("test"))) + { + Assert.Equal("test", await StreamUtil.ReadAsStringAsync(stream)); + } + } + + [Fact] + public void Test_ReadAsJSON() + { + string jsonStr = "{\"arrayObj\":[[{\"itemName\":\"item\",\"itemInt\":1},{\"itemName\":\"item2\",\"itemInt\":2}],[{\"itemName\":\"item3\",\"itemInt\":3}]],\"arrayList\":[[[1,2],[3,4]],[[5,6],[7]],[]],\"listStr\":[1,2,3],\"items\":[{\"total_size\":18,\"partNumber\":1,\"tags\":[{\"aa\":\"11\"}]},{\"total_size\":20,\"partNumber\":2,\"tags\":[{\"aa\":\"22\"}]}],\"next_marker\":\"\",\"test\":{\"total_size\":19,\"partNumber\":1,\"tags\":[{\"aa\":\"11\"}]}}"; + byte[] array = Encoding.UTF8.GetBytes(jsonStr); + using (MemoryStream stream = new MemoryStream(array)) + { + Dictionary dic = (Dictionary)StreamUtil.ReadAsJSON(stream); + Assert.NotNull(dic); + List listResult = (List)dic["items"]; + Dictionary item1 = (Dictionary)listResult[0]; + Assert.Equal(18L, item1["total_size"]); + Assert.Empty((string)dic["next_marker"]); + Assert.Equal(2, ((List)dic["arrayObj"]).Count); + } + + jsonStr = "[{\"itemName\":\"item\",\"itemInt\":1},{\"itemName\":\"item2\",\"itemInt\":2}]"; + array = Encoding.UTF8.GetBytes(jsonStr); + using (MemoryStream stream = new MemoryStream(array)) + { + List listResult = (List)StreamUtil.ReadAsJSON(stream); + Assert.NotNull(listResult); + Dictionary item1 = (Dictionary)listResult[0]; + Assert.Equal("item", item1["itemName"]); + Assert.Equal(1L, item1["itemInt"]); + } + } + + [Fact] + public void Test_Read() + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes("test"))) + { + Assert.NotNull(StreamUtil.Read(stream, 3)); + Assert.Equal(3, StreamUtil.Read(stream, 3).Length); + } + } + + [Fact] + public void Test_Pipe() + { + byte[] inputData = new byte[] {1, 2, 3, 4, 5}; + using (MemoryStream readStream = new MemoryStream(inputData)) + using (MemoryStream writeStream = new MemoryStream()) + { + StreamUtil.Pipe(readStream, writeStream); + byte[] outputData = writeStream.ToArray(); + Assert.Equal(inputData, outputData); + } + byte[] inputData1 = new byte[] { }; + using (MemoryStream readStream1 = new MemoryStream(inputData1)) + using (MemoryStream writeStream1 = new MemoryStream()) + { + StreamUtil.Pipe(readStream1, writeStream1); + byte[] outputData1 = writeStream1.ToArray(); + Assert.Empty(outputData1); + } + } + + [Fact] + public void Test_StreamFor() + { + byte[] data = Encoding.UTF8.GetBytes("test"); + using (MemoryStream stream = new MemoryStream(data)) + { + Stream copy = StreamUtil.StreamFor(stream); + Assert.NotNull(copy); + Assert.True(copy.CanRead); + string str = new StreamReader(copy).ReadToEnd(); + Assert.Equal("test", str); + + string data1 = "test1"; + Stream copy1 = StreamUtil.StreamFor(data1); + string str1 = new StreamReader(copy1).ReadToEnd(); + Assert.Equal("test1", str1); + + int data2 = 111; + Exception ex = Assert.Throws(() => StreamUtil.StreamFor(data2)); + Assert.Equal("data is not Stream or String", ex.Message); + } + } + + [Fact] + public async void Test_ReadAsJSONAsync() + { + string jsonStr = "{\"arrayObj\":[[{\"itemName\":\"item\",\"itemInt\":1},{\"itemName\":\"item2\",\"itemInt\":2}],[{\"itemName\":\"item3\",\"itemInt\":3}]],\"arrayList\":[[[1,2],[3,4]],[[5,6],[7]],[]],\"listStr\":[1,2,3],\"items\":[{\"total_size\":18,\"partNumber\":1,\"tags\":[{\"aa\":\"11\"}]},{\"total_size\":20,\"partNumber\":2,\"tags\":[{\"aa\":\"22\"}]}],\"next_marker\":\"\",\"test\":{\"total_size\":19,\"partNumber\":1,\"tags\":[{\"aa\":\"11\"}]}}"; + byte[] array = Encoding.UTF8.GetBytes(jsonStr); + using (MemoryStream stream = new MemoryStream(array)) + { + Dictionary dic = (Dictionary)await StreamUtil.ReadAsJSONAsync(stream); + Assert.NotNull(dic); + List listResult = (List)dic["items"]; + Dictionary item1 = (Dictionary)listResult[0]; + Assert.Equal(18L, item1["total_size"]); + Assert.Empty((string)dic["next_marker"]); + Assert.Equal(2, ((List)dic["arrayObj"]).Count); + } + + jsonStr = "[{\"itemName\":\"item\",\"itemInt\":1},{\"itemName\":\"item2\",\"itemInt\":2}]"; + array = Encoding.UTF8.GetBytes(jsonStr); + using (MemoryStream stream = new MemoryStream(array)) + { + List listResult = (List)await StreamUtil.ReadAsJSONAsync(stream); + Assert.NotNull(listResult); + Dictionary item1 = (Dictionary)listResult[0]; + Assert.Equal("item", item1["itemName"]); + Assert.Equal(1L, item1["itemInt"]); + } + } + + [Fact] + public void Test_ReadAsBytes() + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes("test"))) + { + Assert.NotNull(StreamUtil.ReadAsBytes(stream)); + } + } + + [Fact] + public async void Test_ReadAsBytesAsync() + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes("test"))) + { + Assert.NotNull(await StreamUtil.ReadAsBytesAsync(stream)); + } + } + + [Fact] + public async Task Test_ReadAsSSEAsync() + { + using (var client = new HttpClient()) + { + var response = await client.GetStreamAsync("http://localhost:8384/sse"); + + var events = new List(); + + await foreach (var sseEvent in StreamUtil.ReadAsSSEAsync(response)) + { + events.Add(sseEvent); + } + + for (int i = 0; i < 5; i++) + { + Assert.Equal(JsonConvert.SerializeObject(new { count = i }), events[i].Data); + Assert.Equal("sse-test", events[i].Id); + Assert.Equal("flow", events[i].Event); + Assert.Equal(3, events[i].Retry); + } + } + } + + [Fact] + public async Task Test_ReadAsSSEAsync_WithNoSpaces() + { + using (var client = new HttpClient()) + { + var response = await client.GetStreamAsync("http://localhost:8384/sse_with_no_spaces"); + + var events = new List(); + + await foreach (var sseEvent in StreamUtil.ReadAsSSEAsync(response)) + { + events.Add(sseEvent); + } + + Assert.Equal(5, events.Count); + + for (int i = 0; i < 5; i++) + { + Assert.Equal(JsonConvert.SerializeObject(new { count = i }), events[i].Data); + Assert.Equal("sse-test", events[i].Id); + Assert.Equal("flow", events[i].Event); + Assert.Equal(3, events[i].Retry); + } + } + } + + [Fact] + public async Task Test_ReadAsSSEAsync_WithInvalidRetry() + { + using (var client = new HttpClient()) + { + var response = await client.GetStreamAsync("http://localhost:8384/sse_invalid_retry"); + + var events = new List(); + + await foreach (var sseEvent in StreamUtil.ReadAsSSEAsync(response)) + { + events.Add(sseEvent); + } + + Assert.Equal(5, events.Count); + + for (int i = 0; i < 5; i++) + { + Assert.Equal(JsonConvert.SerializeObject(new { count = i }), events[i].Data); + Assert.Equal("sse-test", events[i].Id); + Assert.Equal("flow", events[i].Event); + Assert.Null(events[i].Retry); + } + } + } + + [Fact] + public async Task Test_ReadAsSSEAsync_WithDividedData() + { + using (var client = new HttpClient()) + { + var response = await client.GetStreamAsync("http://localhost:8384/sse_with_data_divided"); + + var events = new List(); + + await foreach (var sseEvent in StreamUtil.ReadAsSSEAsync(response)) + { + events.Add(sseEvent); + } + Assert.Equal(4, events.Count); + Assert.Equal(JsonConvert.SerializeObject(new { count = 0 }), events[0].Data); + Assert.Equal("sse-test", events[0].Id); + Assert.Equal("flow", events[0].Event); + Assert.Equal(3, events[0].Retry); + + Assert.Equal(JsonConvert.SerializeObject(new { count = 2, tag = "divided" }), events[1].Data); + Assert.Equal("sse-test", events[1].Id); + Assert.Equal("flow", events[1].Event); + Assert.Equal(3, events[1].Retry); + + Assert.Equal(JsonConvert.SerializeObject(new { count = 3 }), events[2].Data); + Assert.Equal("sse-test", events[2].Id); + Assert.Equal("flow", events[2].Event); + Assert.Equal(3, events[2].Retry); + + Assert.Equal(JsonConvert.SerializeObject(new { count = 4 }), events[3].Data); + Assert.Equal("sse-test", events[3].Id); + Assert.Equal("flow", events[3].Event); + Assert.Equal(3, events[3].Retry); + } + } + + [Fact] + public void Test_ReadAsSSE() + { + using (var client = new HttpClient()) + { + var response = client.GetStreamAsync("http://localhost:8384/sse").Result; + var events = new List(); + + foreach (var sseEvent in StreamUtil.ReadAsSSE(response)) + { + events.Add(sseEvent); + } + + for (int i = 0; i < 5; i++) + { + Assert.Equal(JsonConvert.SerializeObject(new { count = i }), events[i].Data); + Assert.Equal("sse-test", events[i].Id); + Assert.Equal("flow", events[i].Event); + Assert.Equal(3, events[i].Retry); + } + } + } + + [Fact] + public async Task Test_ReadAsSSE_WithNoSpaces() + { + using (var client = new HttpClient()) + { + var response = await client.GetStreamAsync("http://localhost:8384/sse_with_no_spaces"); + + var events = new List(); + + foreach (var sseEvent in StreamUtil.ReadAsSSE(response)) + { + events.Add(sseEvent); + } + + Assert.Equal(5, events.Count); + + for (int i = 0; i < 5; i++) + { + Assert.Equal(JsonConvert.SerializeObject(new { count = i }), events[i].Data); + Assert.Equal("sse-test", events[i].Id); + Assert.Equal("flow", events[i].Event); + Assert.Equal(3, events[i].Retry); + } + } + } + + [Fact] + public async Task Test_ReadAsSSE_WithInvalidRetry() + { + using (var client = new HttpClient()) + { + var response = await client.GetStreamAsync("http://localhost:8384/sse_invalid_retry"); + + var events = new List(); + + foreach (var sseEvent in StreamUtil.ReadAsSSE(response)) + { + events.Add(sseEvent); + } + + Assert.Equal(5, events.Count); + + for (int i = 0; i < 5; i++) + { + Assert.Equal(JsonConvert.SerializeObject(new { count = i }), events[i].Data); + Assert.Equal("sse-test", events[i].Id); + Assert.Equal("flow", events[i].Event); + Assert.Null(events[i].Retry); + } + } + } + + [Fact] + public async Task Test_ReadAsSSE_WithDividedData() + { + using (var client = new HttpClient()) + { + var response = await client.GetStreamAsync("http://localhost:8384/sse_with_data_divided"); + + var events = new List(); + + foreach (var sseEvent in StreamUtil.ReadAsSSE(response)) + { + events.Add(sseEvent); + } + + Assert.Equal(4, events.Count); + Assert.Equal(JsonConvert.SerializeObject(new { count = 0 }), events[0].Data); + Assert.Equal("sse-test", events[0].Id); + Assert.Equal("flow", events[0].Event); + Assert.Equal(3, events[0].Retry); + + Assert.Equal(JsonConvert.SerializeObject(new { count = 2, tag = "divided" }), events[1].Data); + Assert.Equal("sse-test", events[1].Id); + Assert.Equal("flow", events[1].Event); + Assert.Equal(3, events[1].Retry); + + Assert.Equal(JsonConvert.SerializeObject(new { count = 3 }), events[2].Data); + Assert.Equal("sse-test", events[2].Id); + Assert.Equal("flow", events[2].Event); + Assert.Equal(3, events[2].Retry); + + Assert.Equal(JsonConvert.SerializeObject(new { count = 4 }), events[3].Data); + Assert.Equal("sse-test", events[3].Id); + Assert.Equal("flow", events[3].Event); + Assert.Equal(3, events[3].Retry); + } + } + } +} + diff --git a/DaraUnitTests/Utils/BytesUitlTest.cs b/DaraUnitTests/Utils/BytesUitlTest.cs new file mode 100644 index 0000000..d840240 --- /dev/null +++ b/DaraUnitTests/Utils/BytesUitlTest.cs @@ -0,0 +1,29 @@ +using System.Text; +using Xunit; +using Darabonba.Utils; + +namespace DaraUnitTests.Utils +{ + public class DaraBytesUtilTest + { + + [Fact] + public void Test_HexEncode() + { + byte[] test = Encoding.UTF8.GetBytes("test"); + var res = BytesUtil.ToHex(test); + Assert.Equal("74657374", res); + } + + [Fact] + public void Test_From() + { + string data = "test"; + Assert.Equal(new byte[] { 116, 101, 115, 116 }, BytesUtil.From(data, "utf8")); + Assert.Equal(new byte[] { 116, 101, 115, 116 }, BytesUtil.From(data, "ASCII")); + Assert.Equal(new byte[] { 0, 116, 0, 101, 0, 115, 0, 116 }, BytesUtil.From(data, "bigendianunicode")); + Assert.Equal(new byte[] { 116, 0, 101, 0, 115, 0, 116, 0 }, BytesUtil.From(data, "unicode")); + Assert.Equal(new byte[] { 116, 0, 0, 0, 101, 0, 0, 0, 115, 0, 0, 0, 116, 0, 0, 0 }, BytesUtil.From(data, "utf32")); + } + } +} \ No newline at end of file diff --git a/DaraUnitTests/Utils/ConverterUtilTest.cs b/DaraUnitTests/Utils/ConverterUtilTest.cs new file mode 100644 index 0000000..fc8ada9 --- /dev/null +++ b/DaraUnitTests/Utils/ConverterUtilTest.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using Darabonba.Utils; +using DaraUnitTests.Models; +using Xunit; + +namespace DaraUnitTests.Utils +{ + public class TestObject + { + public string name { get; set; } + } + + public class ConverterUtilTests + { + [Fact] + public void TestMergeMap() + { + Dictionary dict1 = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; + Dictionary dict2 = new Dictionary + { + { "key2", "value22" }, + { "key3", "value3" } + }; + Dictionary res = ConverterUtil.Merge(dict1, dict2); + Assert.Equal("value1", res["key1"]); + Assert.Equal("value22", res["key2"]); + Assert.Equal("value3", res["key3"]); + } + + [Fact] + public void TestMergeListMap() + { + Assert.Empty(ConverterUtil.Merge(null)); + + Dictionary dic = new Dictionary(); + Dictionary dicNull = null; + Dictionary dicMerge = new Dictionary(); + TestRegModel model = new TestRegModel(); + + dic.Add("testNull", null); + dic.Add("testExist", "testExist"); + dic.Add("test", "test"); + dicMerge.Add("testMerge", "testMerge"); + dicMerge.Add("testExist", "IsExist"); + Dictionary dicResult = ConverterUtil.Merge(dic, dicNull, dicMerge, null); + Assert.NotNull(dicResult); + Assert.Equal(4, dicResult.Count); + + Dictionary dicModelMerge = ConverterUtil.Merge(dic, dicNull, dicMerge, model); + Assert.NotNull(dicResult); + + Assert.Throws(() => { ConverterUtil.Merge(dic, 1); }); + } + + [Fact] + public void TestStrToLower() + { + Assert.Empty(ConverterUtil.StrToLower(null)); + + Assert.Equal("test", ConverterUtil.StrToLower("TEST")); + } + + [Fact] + public void Test_ParseMethods() + { + Assert.Equal(123, ConverterUtil.ParseInt("123")); + Assert.Equal(123, ConverterUtil.ParseInt("123.0123")); + Assert.Equal(123, ConverterUtil.ParseLong("123")); + Assert.Equal(123, ConverterUtil.ParseLong("123.0123")); + Assert.Equal(123.0123, Math.Round(ConverterUtil.ParseFLoat("123.0123"), 4)); + } + } +} diff --git a/TeaUnitTests/Utils/DictUtilsTest.cs b/DaraUnitTests/Utils/DictUtilsTest.cs similarity index 93% rename from TeaUnitTests/Utils/DictUtilsTest.cs rename to DaraUnitTests/Utils/DictUtilsTest.cs index 849f0b5..ebaa5eb 100644 --- a/TeaUnitTests/Utils/DictUtilsTest.cs +++ b/DaraUnitTests/Utils/DictUtilsTest.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; -using Tea.Utils; +using Darabonba.Utils; using Xunit; -namespace TeaUnitTests.Utils +namespace DaraUnitTests.Utils { public class DictUtilsTest { diff --git a/TeaUnitTests/Utils/ExtensionsTest.cs b/DaraUnitTests/Utils/ExtensionsTest.cs similarity index 87% rename from TeaUnitTests/Utils/ExtensionsTest.cs rename to DaraUnitTests/Utils/ExtensionsTest.cs index 67e787c..21666a3 100644 --- a/TeaUnitTests/Utils/ExtensionsTest.cs +++ b/DaraUnitTests/Utils/ExtensionsTest.cs @@ -1,11 +1,23 @@ using System.Collections.Generic; -using Tea.Utils; +using Darabonba.Utils; using Xunit; -namespace TeaUnitTests.Utils +namespace DaraUnitTests.Utils { public class ExtensionsTest { + [Fact] + public void TestIsNull() + { + string str = null; + byte[] bytes = null; + Assert.True(str.IsNull()); + Assert.True(bytes.IsNull()); + + string nonNullStr = "not null"; + Assert.False(nonNullStr.IsNull()); + } + [Fact] public void TestToSafeString() { diff --git a/DaraUnitTests/Utils/FormUtilTest.cs b/DaraUnitTests/Utils/FormUtilTest.cs new file mode 100644 index 0000000..8d95ad5 --- /dev/null +++ b/DaraUnitTests/Utils/FormUtilTest.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using Darabonba.Utils; +using Xunit; +using Darabonba.Streams; + +namespace DaraUnitTests.Utils +{ + public class FormUtilTest + { + [Fact] + public void Test_ToFormString() + { + Assert.Empty(FormUtil.ToFormString(null)); + Assert.Empty(FormUtil.ToFormString(new Dictionary())); + + Dictionary dict = new Dictionary + { + { "form", "test" }, + { "param", "test" }, + { "testNull", null } + }; + Assert.Equal("form=test¶m=test", FormUtil.ToFormString(dict)); + } + + [Fact] + public void Test_GetBoundary() + { + Assert.Equal(14, FormUtil.GetBoundary().Length); + } + + [Fact] + public void Test_ToFileForm() + { + Stream fileFormStream = FormUtil.ToFileForm(new Dictionary(), "boundary"); + Assert.NotNull(fileFormStream); + + string formStr = GetFormStr(fileFormStream); + Assert.Equal("--boundary--\r\n", formStr); + + Dictionary dict = new Dictionary(); + dict.Add("stringkey", "string"); + fileFormStream = FormUtil.ToFileForm(dict, "boundary"); + formStr = GetFormStr(fileFormStream); + Assert.Equal("--boundary\r\n" + + "Content-Disposition: form-data; name=\"stringkey\"\r\n\r\n" + + "string\r\n" + + "--boundary--\r\n", formStr); + + string path = System.AppDomain.CurrentDomain.BaseDirectory; + FileStream file = File.OpenRead("../../../../DaraUnitTests/Fixtures/test.json"); + FileField fileField = new FileField + { + Filename = "fakefilename", + ContentType = "application/json", + Content = file + }; + dict = new Dictionary + { + { "stringkey", "string" }, + { "filefield", fileField } + }; + fileFormStream = FormUtil.ToFileForm(dict, "boundary"); + formStr = GetFormStr(fileFormStream); + Assert.Equal("--boundary\r\n" + + "Content-Disposition: form-data; name=\"stringkey\"\r\n\r\n" + + "string\r\n" + + "--boundary\r\n" + + "Content-Disposition: form-data; name=\"filefield\"; filename=\"fakefilename\"\r\n" + + "Content-Type: application/json\r\n" + + "\r\n" + + "{\"key\":\"value\"}" + + "\r\n" + + "--boundary--\r\n", formStr); + } + + private string GetFormStr(Stream stream) + { + string formStr = string.Empty; + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0) + { + formStr += Encoding.UTF8.GetString(buffer, 0, bytesRead); + } + + return formStr; + } + } +} \ No newline at end of file diff --git a/TeaUnitTests/Utils/HttpClientUtilsTest.cs b/DaraUnitTests/Utils/HttpClientUtilsTest.cs similarity index 96% rename from TeaUnitTests/Utils/HttpClientUtilsTest.cs rename to DaraUnitTests/Utils/HttpClientUtilsTest.cs index e6e5861..04dd996 100644 --- a/TeaUnitTests/Utils/HttpClientUtilsTest.cs +++ b/DaraUnitTests/Utils/HttpClientUtilsTest.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using System.Text; -using Tea.Utils; +using Darabonba.Utils; using Xunit; -namespace TeaUnitTests.Utils +namespace DaraUnitTests.Utils { public class HttpClientUtilsTest { diff --git a/TeaUnitTests/Utils/HttpUtilsTest.cs b/DaraUnitTests/Utils/HttpUtilsTest.cs similarity index 93% rename from TeaUnitTests/Utils/HttpUtilsTest.cs rename to DaraUnitTests/Utils/HttpUtilsTest.cs index 7634f5c..6c4a720 100644 --- a/TeaUnitTests/Utils/HttpUtilsTest.cs +++ b/DaraUnitTests/Utils/HttpUtilsTest.cs @@ -1,10 +1,10 @@ using System.Net.Http; -using Tea.Utils; +using Darabonba.Utils; using Xunit; -namespace TeaUnitTests.Utils +namespace DaraUnitTests.Utils { public class HttpUtilsTest { diff --git a/DaraUnitTests/Utils/JSONUtilTest.cs b/DaraUnitTests/Utils/JSONUtilTest.cs new file mode 100644 index 0000000..80aa804 --- /dev/null +++ b/DaraUnitTests/Utils/JSONUtilTest.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using Darabonba.Utils; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace DaraUnitTests.Utils +{ + public class JSONUtilTest + { + [Fact] + public void Test_SerializeObject() + { + Dictionary dict = new Dictionary + { { "key", "value" } + }; + string jsonStr = JSONUtil.SerializeObject(dict); + Assert.NotNull(jsonStr); + Assert.NotEmpty(jsonStr); + Assert.Equal("{\"key\":\"value\"}", jsonStr); + Assert.Equal("{}", JSONUtil.SerializeObject(new Dictionary())); + Assert.Equal("test str", JSONUtil.SerializeObject("test str")); + Assert.Equal("1", JSONUtil.SerializeObject(1)); + Assert.Equal("true", JSONUtil.SerializeObject(true)); + Assert.Equal("null", JSONUtil.SerializeObject(null)); + Dictionary unicode = new Dictionary + { { "str", "test&<>://中文" } + }; + Assert.Equal("{\"key\":\"value\",\"map\":{\"str\":\"test&<>://中文\"},\"num\":1}", JSONUtil.SerializeObject( + new Dictionary + { + { "key", "value" }, + { "map", unicode }, + { "num", 1 } + })); + } + + [Fact] + public void TestDeserializeToDic() + { + Assert.Null(JSONUtil.Deserialize(null)); + + string jsonStr = "{\"arrayObj\":[[{\"itemName\":\"item\",\"itemInt\":1},{\"itemName\":\"item2\",\"itemInt\":2}],[{\"itemName\":\"item3\",\"itemInt\":3}]],\"arrayList\":[[[1,2],[3,4]],[[5,6],[7]],[]],\"listStr\":[1,2,3],\"items\":[{\"total_size\":18,\"partNumber\":1,\"tags\":[{\"aa\":\"11\"}]},{\"total_size\":20,\"partNumber\":2,\"tags\":[{\"aa\":\"22\"}]}],\"next_marker\":\"\",\"test\":{\"total_size\":19,\"partNumber\":1,\"tags\":[{\"aa\":\"11\"}]}}"; + JObject jObject = JObject.Parse(jsonStr); + Dictionary dic = (Dictionary) JSONUtil.Deserialize(jObject); + Assert.NotNull(dic); + List listResult = (List) dic["items"]; + Dictionary item1 = (Dictionary) listResult[0]; + Assert.Equal(18L, item1["total_size"]); + Assert.Empty((string) dic["next_marker"]); + Assert.Equal(2, ((List) dic["arrayObj"]).Count); + } + + } +} diff --git a/DaraUnitTests/Utils/ListUtilTest.cs b/DaraUnitTests/Utils/ListUtilTest.cs new file mode 100644 index 0000000..d427872 --- /dev/null +++ b/DaraUnitTests/Utils/ListUtilTest.cs @@ -0,0 +1,58 @@ +using Darabonba.Utils; +using Xunit; +using System.Collections.Generic; + +namespace DaraUnitTests.Utils +{ + public class ListUtilTest + { + + [Fact] + public void TestShift() + { + List array = new List { "a", "b", "c" }; + string first = ListUtil.Shift(array); + Assert.Equal(2, array.Count); + Assert.Equal("a", first); + Assert.Equal("b", array[0]); + } + + [Fact] + public void TestUnshift() + { + List array = new List { "a", "b", "c" }; + ListUtil.Unshift(array, "x"); + Assert.Equal(4, array.Count); + Assert.Equal("x", array[0]); + } + + [Fact] + public void TestPush() + { + List array = new List { "a", "b", "c" }; + ListUtil.Push(array, "x"); + Assert.Equal(4, array.Count); + Assert.Equal("x", array[3]); + } + + [Fact] + public void TestPop() + { + List array = new List { "a", "b", "c" }; + string last = ListUtil.Pop(array); + Assert.Equal(2, array.Count); + Assert.Equal("c", last); + Assert.Equal("b", array[1]); + } + + [Fact] + public void TestConcat() + { + List array1 = new List { "a", "b", "c" }; + List array2 = new List { "d", "e", "f" }; + ListUtil.Concat(array1, array2); + Assert.Equal(6, array1.Count); + Assert.Equal(new List { "a", "b", "c", "d", "e", "f" }, array1); + } + } +} \ No newline at end of file diff --git a/DaraUnitTests/Utils/MathUtilTest.cs b/DaraUnitTests/Utils/MathUtilTest.cs new file mode 100644 index 0000000..3cb1cff --- /dev/null +++ b/DaraUnitTests/Utils/MathUtilTest.cs @@ -0,0 +1,76 @@ +using Darabonba.Utils; +using Xunit; + +namespace DaraUnitTests.Utils +{ + public class MathUtilTest + { + [Fact] + public void TestFloor() + { + float funm = 2.13f; + Assert.Equal(2, MathUtil.Floor(funm)); + double dunm = 2.13d; + Assert.Equal(2, MathUtil.Floor(dunm)); + } + + [Fact] + public void TestRound() + { + float funm = 2.49f; + Assert.Equal(2, MathUtil.Round(funm)); + double dunm = 2.51d; + Assert.Equal(3, MathUtil.Round(dunm)); + } + + [Fact] + public void TestParseInt() + { + float funm = 2.13f; + Assert.Equal(2, MathUtil.ParseInt(funm)); + double dunm = 2.13d; + Assert.Equal(2, MathUtil.ParseInt(dunm)); + } + + [Fact] + public void TestParseLong() + { + float funm = 2.13f; + Assert.Equal(2L, MathUtil.ParseLong(funm)); + double dunm = 2.13d; + Assert.Equal(2L, MathUtil.ParseLong(dunm)); + } + + [Fact] + public void TestParseFloat() + { + int iunm = 2; + Assert.Equal(2f, MathUtil.ParseFloat(iunm)); + float funm = 2.13f; + Assert.Equal(2.13f, MathUtil.ParseFloat(funm)); + double dunm = 2.13d; + Assert.Equal(2.13f, MathUtil.ParseFloat(dunm)); + } + + [Fact] + public void TestMin() + { + int inum = 2; + float fnum = 2.01f; + double dnum = 2.001d; + Assert.Equal(2, MathUtil.Min(inum, fnum)); + Assert.Equal(2.001d, MathUtil.Min(dnum, fnum)); + } + + [Fact] + public void TestMax() + { + int inum = 2; + float fnum = 2.01f; + double dnum = 2.02d; + Assert.Equal(2.01f, MathUtil.Max(inum, fnum)); + Assert.Equal(2.02d, MathUtil.Max(dnum, fnum)); + } + } + +} \ No newline at end of file diff --git a/DaraUnitTests/Utils/StringUtilTest.cs b/DaraUnitTests/Utils/StringUtilTest.cs new file mode 100644 index 0000000..f1ed067 --- /dev/null +++ b/DaraUnitTests/Utils/StringUtilTest.cs @@ -0,0 +1,74 @@ +using System; +using Darabonba.Utils; +using Xunit; + +namespace DaraUnitTests.Utils +{ + public class StringUtilTest + { + [Fact] + public void TestSubString() + { + int? start = null; + int? end = null; + Assert.Throws(() => StringUtil.SubString("test", start, end)); + Assert.Equal("te", StringUtil.SubString("test", 0, 2)); + } + + [Fact] + public void TestToBytes() + { + string data = "test"; + Assert.Equal(new byte[] { 116, 101, 115, 116 }, BytesUtil.From(data, "utf8")); + Assert.Equal(new byte[] { 116, 101, 115, 116 }, BytesUtil.From(data, "ASCII")); + Assert.Equal(new byte[] { 0, 116, 0, 101, 0, 115, 0, 116 }, BytesUtil.From(data, "bigendianunicode")); + Assert.Equal(new byte[] { 116, 0, 101, 0, 115, 0, 116, 0 }, BytesUtil.From(data, "unicode")); + Assert.Equal(new byte[] { 116, 0, 0, 0, 101, 0, 0, 0, 115, 0, 0, 0, 116, 0, 0, 0 }, BytesUtil.From(data, "utf32")); + } + + [Fact] + public void TestReplace() + { + string pattern = "/beijing/"; + string replacement = "chengdu"; + string data = "Beijing city, hangzhou city, beijing city, another city, beijing city"; + string res = StringUtil.Replace(data, replacement, pattern); + Assert.Equal("Beijing city, hangzhou city, chengdu city, another city, beijing city", res); + pattern = "/beijing/g"; + res = StringUtil.Replace(data, replacement, pattern); + Assert.Equal("Beijing city, hangzhou city, chengdu city, another city, chengdu city", res); + pattern = "/beijing/gi"; + res = StringUtil.Replace(data, replacement, pattern); + Assert.Equal("chengdu city, hangzhou city, chengdu city, another city, chengdu city", res); + pattern = "/beijing/i"; + res = StringUtil.Replace(data, replacement, pattern); + Assert.Equal("chengdu city, hangzhou city, beijing city, another city, beijing city", res); + pattern = @"\S+(?=\s*city)"; + res = StringUtil.Replace(data, replacement, pattern); + Assert.Equal("chengdu city, chengdu city, chengdu city, chengdu city, chengdu city", res); + } + + [Fact] + public void TestParse() + { + string numberStr = "1.3433"; + int intRes = StringUtil.ParseInt(numberStr); + Assert.Equal(1, intRes); + numberStr = "2"; + intRes = StringUtil.ParseInt(numberStr); + Assert.Equal(2, intRes); + numberStr = "1,682.80"; + intRes = StringUtil.ParseInt(numberStr); + Assert.Equal(1682, intRes); + numberStr = "1.682.80"; + Assert.Throws(() => StringUtil.ParseInt(numberStr)); + numberStr = "3.1415926"; + float floatRes = StringUtil.ParseFloat(numberStr); + Assert.Equal(3.1415926f, floatRes); + numberStr = "3.1415926"; + long longRes = StringUtil.ParseLong(numberStr); + Assert.Equal(3, longRes); + } + + } +} \ No newline at end of file diff --git a/DaraUnitTests/Utils/XmlUtilTest.cs b/DaraUnitTests/Utils/XmlUtilTest.cs new file mode 100644 index 0000000..bcecd4f --- /dev/null +++ b/DaraUnitTests/Utils/XmlUtilTest.cs @@ -0,0 +1,116 @@ +using System.Collections.Generic; +using Darabonba; +using Darabonba.Utils; +using Xunit; +using DaraUnitTests.Models; +using static DaraUnitTests.Models.ListAllMyBucketsResult; +using static DaraUnitTests.Models.ListAllMyBucketsResult.Buckets; + +namespace DaraUnitTests.Utils +{ + public class XmlUtilTest + { + ToBodyModel model; + public XmlUtilTest() + { + model = new ToBodyModel(); + ListAllMyBucketsResult result = new ListAllMyBucketsResult(); + Buckets buckets = new Buckets(); + buckets.bucket = new List(); + buckets.bucket.Add(new Bucket { CreationDate = "2015-12-17T18:12:43.000Z", ExtranetEndpoint = "oss-cn-shanghai.aliyuncs.com", IntranetEndpoint = "oss-cn-shanghai-internal.aliyuncs.com", Location = "oss-cn-shanghai", Name = "app-base-oss", StorageClass = "Standard" }); + buckets.bucket.Add(new Bucket { CreationDate = "2014-12-25T11:21:04.000Z", ExtranetEndpoint = "oss-cn-hangzhou.aliyuncs.com", IntranetEndpoint = "oss-cn-hangzhou-internal.aliyuncs.com", Location = "oss-cn-hangzhou", Name = "atestleo23", StorageClass = "IA" }); + buckets.bucket.Add(null); + result.buckets = buckets; + Owner owner = new Owner { ID = 512, DisplayName = "51264" }; + result.owner = owner; + model.listAllMyBucketsResult = result; + model.listAllMyBucketsResult.testStrList = new List { "1", "2" }; + model.listAllMyBucketsResult.owners = new List(); + model.listAllMyBucketsResult.owners.Add(owner); + model.listAllMyBucketsResult.TestDouble = 1; + model.listAllMyBucketsResult.TestFloat = 2; + model.listAllMyBucketsResult.TestLong = 3; + model.listAllMyBucketsResult.TestShort = 4; + model.listAllMyBucketsResult.TestUInt = 5; + model.listAllMyBucketsResult.TestULong = 6; + model.listAllMyBucketsResult.TestUShort = 7; + model.listAllMyBucketsResult.TestBool = true; + model.listAllMyBucketsResult.TestNull = null; + model.listAllMyBucketsResult.TestString = "string"; + model.listAllMyBucketsResult.TestListNull = null; + model.listAllMyBucketsResult.dict = new Dictionary { { "key", "value" } }; + } + + [Fact] + public void Test_ToXml() + { + DaraModel modelNull = new DaraModel(); + Assert.Empty(XmlUtil.ToXML(modelNull.ToMap())); + + ToBodyModel model = new ToBodyModel(); + ListAllMyBucketsResult result = new ListAllMyBucketsResult(); + Buckets buckets = new Buckets + { + bucket = new List + { + new Bucket { CreationDate = "2015-12-17T18:12:43.000Z", ExtranetEndpoint = "oss-cn-shanghai.aliyuncs.com", IntranetEndpoint = "oss-cn-shanghai-internal.aliyuncs.com", Location = "oss-cn-shanghai", Name = "app-base-oss", StorageClass = "Standard" }, + new Bucket { CreationDate = "2014-12-25T11:21:04.000Z", ExtranetEndpoint = "oss-cn-hangzhou.aliyuncs.com", IntranetEndpoint = "oss-cn-hangzhou-internal.aliyuncs.com", Location = "oss-cn-hangzhou", Name = "atestleo23", StorageClass = "IA" }, + null + } + }; + result.buckets = buckets; + Owner owner = new Owner { ID = 512, DisplayName = "51264" }; + result.owner = owner; + model.listAllMyBucketsResult = result; + model.listAllMyBucketsResult.testStrList = new List { "1", "2" }; + model.listAllMyBucketsResult.owners = new List + { + owner + }; + model.listAllMyBucketsResult.TestDouble = 1; + model.listAllMyBucketsResult.TestFloat = 2; + model.listAllMyBucketsResult.TestLong = 3; + model.listAllMyBucketsResult.TestShort = 4; + model.listAllMyBucketsResult.TestUInt = 5; + model.listAllMyBucketsResult.TestULong = 6; + model.listAllMyBucketsResult.TestUShort = 7; + model.listAllMyBucketsResult.TestBool = true; + model.listAllMyBucketsResult.TestNull = null; + model.listAllMyBucketsResult.TestListNull = null; + string xmlStr = XmlUtil.ToXML(model.ToMap()); + Assert.NotNull(xmlStr); + + Dictionary xmlBody = (Dictionary)XmlUtil.ParseXml(xmlStr, typeof(ToBodyModel)); + ToBodyModel teaModel = DaraModel.ToObject(xmlBody); + Assert.NotNull(teaModel); + Assert.Equal(1, teaModel.listAllMyBucketsResult.TestDouble); + + string xml = "\n" + + "disNamekey"; + Dictionary map = XmlUtil.ParseXml(xml, null); + Assert.False(map.ContainsKey("xml")); + Assert.Single(map); + Assert.True(((Dictionary)map["body"]).ContainsKey("Contents")); + List list = (List)((Dictionary)map["body"])["Contents"]; + Assert.Equal(2, list.Count); + Assert.Equal("key", ((Dictionary)list[0])["Key"]); + Assert.Equal("disName", ((Dictionary)((Dictionary)list[0])["Owner"])["DisplayName"]); + Assert.Null((Dictionary)list[1]); + } + + [Fact] + public void TestXml() + { + string xmlStr = XmlUtil.SerializeXml(model); + Assert.NotNull(xmlStr); + Assert.NotEmpty(xmlStr); + + Assert.Equal(xmlStr, XmlUtil.SerializeXml(model.ToMap())); + + model.listAllMyBucketsResult.dict = null; + xmlStr = XmlUtil.SerializeXml(model); + Dictionary dict = XmlUtil.DeserializeXml(xmlStr, model.GetType()); + Assert.NotNull(dict); + } + } +} diff --git a/TeaUnitTests/xunit.runner.json b/DaraUnitTests/xunit.runner.json similarity index 100% rename from TeaUnitTests/xunit.runner.json rename to DaraUnitTests/xunit.runner.json diff --git a/Tea/Attributes/NameInMapAttribute.cs b/Darabonba/Attributes/NameInMapAttribute.cs similarity index 91% rename from Tea/Attributes/NameInMapAttribute.cs rename to Darabonba/Attributes/NameInMapAttribute.cs index 43699f1..e7fb851 100644 --- a/Tea/Attributes/NameInMapAttribute.cs +++ b/Darabonba/Attributes/NameInMapAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace Tea +namespace Darabonba { public class NameInMapAttribute : Attribute { diff --git a/Tea/Attributes/ValidationAttribute.cs b/Darabonba/Attributes/ValidationAttribute.cs similarity index 94% rename from Tea/Attributes/ValidationAttribute.cs rename to Darabonba/Attributes/ValidationAttribute.cs index 0673616..3184a43 100644 --- a/Tea/Attributes/ValidationAttribute.cs +++ b/Darabonba/Attributes/ValidationAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace Tea +namespace Darabonba { public class ValidationAttribute : Attribute { diff --git a/Tea/TeaCore.cs b/Darabonba/DaraCore.cs similarity index 57% rename from Tea/TeaCore.cs rename to Darabonba/DaraCore.cs index 45b3c71..3a1046a 100644 --- a/Tea/TeaCore.cs +++ b/Darabonba/DaraCore.cs @@ -10,21 +10,26 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Darabonba.RetryPolicy; +using Darabonba.Exceptions; +using Darabonba.Utils; -using Tea.Utils; - -namespace Tea +namespace Darabonba { - public class TeaCore + public class DaraCore { private static readonly int bufferLength = 1024; + private static readonly int DefaultMaxAttempts = 3; private static List bodyMethod = new List { "POST", "PUT", "PATCH" }; + private static readonly TimeSpan DefaultMinDelay = TimeSpan.FromMilliseconds(100); + private static readonly TimeSpan DefaultMaxDelay = TimeSpan.FromSeconds(120); + - public static string ComposeUrl(TeaRequest request) + public static string ComposeUrl(DaraRequest request) { var urlBuilder = new StringBuilder(""); - urlBuilder.Append(TeaConverter.StrToLower(request.Protocol)).Append("://"); + urlBuilder.Append(ConverterUtil.StrToLower(request.Protocol)).Append("://"); urlBuilder.Append(DictUtils.GetDicValue(request.Headers, "host")); if (request.Port > 0) { @@ -63,12 +68,12 @@ public static string ComposeUrl(TeaRequest request) return urlBuilder.ToString().TrimEnd('?').TrimEnd('&'); } - public static TeaResponse DoAction(TeaRequest request) + public static DaraResponse DoAction(DaraRequest request) { return DoAction(request, new Dictionary()); } - public static TeaResponse DoAction(TeaRequest request, Dictionary runtimeOptions) + public static DaraResponse DoAction(DaraRequest request, Dictionary runtimeOptions) { int timeout; var url = ComposeUrl(request); @@ -77,22 +82,22 @@ public static TeaResponse DoAction(TeaRequest request, Dictionary DoActionAsync(TeaRequest request) + public static async Task DoActionAsync(DaraRequest request) { return await DoActionAsync(request, new Dictionary()); } - public static async Task DoActionAsync(TeaRequest request, Dictionary runtimeOptions) + public static async Task DoActionAsync(DaraRequest request, Dictionary runtimeOptions) { int timeout; var url = ComposeUrl(request); @@ -103,17 +108,17 @@ public static async Task DoActionAsync(TeaRequest request, Dictiona { HttpClient httpClient = HttpClientUtils.GetOrAddHttpClient(request.Protocol, uri.Host, uri.Port, runtimeOptions); HttpResponseMessage response = await httpClient.SendAsync(req, new CancellationTokenSource(timeout).Token); - return new TeaResponse(response); + return new DaraResponse(response); } - catch (System.Threading.Tasks.TaskCanceledException) + catch (TaskCanceledException) { throw new WebException("operation is timeout"); } } - public static string GetResponseBody(TeaResponse response) + public static string GetResponseBody(DaraResponse response) { - using(var ms = new MemoryStream()) + using (var ms = new MemoryStream()) { var buffer = new byte[bufferLength]; var stream = response.Body; @@ -145,7 +150,7 @@ public static Dictionary ConvertHeaders(WebHeaderCollection head var result = new Dictionary(); for (int i = 0; i < headers.Count; i++) { - result.Add(TeaConverter.StrToLower(headers.GetKey(i)), headers.Get(i)); + result.Add(ConverterUtil.StrToLower(headers.GetKey(i)), headers.Get(i)); } return result; } @@ -156,23 +161,24 @@ public static Dictionary ConvertHeaders(HttpResponseHeaders head var enumerator = headers.GetEnumerator(); foreach (var item in headers) { - result.Add(TeaConverter.StrToLower(item.Key), item.Value.First()); + result.Add(ConverterUtil.StrToLower(item.Key), item.Value.First()); } return result; } + [Obsolete("This method is obsolete. Use ShouldRetry instead.")] public static bool AllowRetry(IDictionary dict, int retryTimes, long now) { - if(retryTimes == 0) + if (retryTimes == 0) { return true; } - if(!dict.Get("retryable").ToSafeBool(false)) + if (!dict.Get("retryable").ToSafeBool(false)) { return false; } - + int retry; if (dict == null) { @@ -191,6 +197,66 @@ public static bool AllowRetry(IDictionary dict, int retryTimes, long now) return retry >= retryTimes; } + public static bool ShouldRetry(RetryOptions options, RetryPolicyContext ctx) + { + if (ctx.RetriesAttempted == 0) + { + return true; + } + if (options == null || options.Retryable == null || options.Retryable == false) + { + return false; + } + + if (ctx.Exception is DaraException) + { + var daraException = (DaraException)ctx.Exception; + List noRetryConditions = options.NoRetryCondition; + List retryConditions = options.RetryCondition; + + if (noRetryConditions != null) + { + foreach (var noRetryCondition in noRetryConditions) + { + if (DictUtils.Contains(noRetryCondition.Exception, daraException.Message) || + DictUtils.Contains(noRetryCondition.ErrorCode, daraException.Code)) + { + return false; + } + } + } + if (retryConditions != null) + { + foreach (var retryCondition in retryConditions) + { + if (!DictUtils.Contains(retryCondition.Exception, daraException.Message) && + !DictUtils.Contains(retryCondition.ErrorCode, daraException.Code)) + { + continue; + } + + if (ctx.RetriesAttempted > retryCondition.MaxAttempts) + { + return false; + } + + if (daraException is ResponseException) + { + // TODO retryable是否需要 + // var ResponseException = (ResponseException)daraException; + // if (!ResponseException.Retryable) + // { + // return false; + // } + } + return true; + } + } + } + return false; + } + + [Obsolete("This method is obsolete. Use GetBackoffDelay instead.")] public static int GetBackoffTime(IDictionary Idict, int retryTimes) { int backOffTime = 0; @@ -212,6 +278,50 @@ public static int GetBackoffTime(IDictionary Idict, int retryTimes) return backOffTime; } + public static int GetBackoffDelay(RetryOptions options, RetryPolicyContext ctx) + { + if (ctx.RetriesAttempted == null || ctx.RetriesAttempted == 0) + { + return 0; + } + + if (ctx.Exception is DaraException) + { + var daraException = (DaraException)ctx.Exception; + if (options != null) + { + var retryConditions = options.RetryCondition; + if (retryConditions != null) + { + foreach (var retryCondition in retryConditions) + { + if (!DictUtils.Contains(retryCondition.Exception, daraException.Message) && !DictUtils.Contains(retryCondition.ErrorCode, daraException.Code)) + { + continue; + } + long maxDelay = retryCondition.MaxDelayTimeMillis != null ? (long)retryCondition.MaxDelayTimeMillis : (long)DefaultMaxDelay.TotalMilliseconds; + if (daraException is ResponseException) + { + var ResponseException = (ResponseException)daraException; + if (ResponseException.RetryAfter != null) + { + return (int)Math.Min(ResponseException.RetryAfter.Value, maxDelay); + } + } + if (retryCondition.Backoff == null) + { + return (int)DefaultMinDelay.TotalMilliseconds; + } + var delayTimeMillis = retryCondition.Backoff.GetDelayTime(ctx); + long delayTime = delayTimeMillis != null ? (long)delayTimeMillis : (long)DefaultMinDelay.TotalMilliseconds; + return (int)Math.Min(delayTime, maxDelay); + } + } + } + } + return (int)DefaultMinDelay.TotalMilliseconds; + } + public static void Sleep(int backoffTime) { Thread.Sleep(backoffTime); @@ -227,7 +337,7 @@ await Task.Run(() => public static bool IsRetryable(Exception e) { - return e is TeaRetryableException; + return e is DaraRetryableException; } public static Stream BytesReadable(string str) @@ -243,6 +353,18 @@ public static Stream BytesReadable(byte[] bytes) return stream; } + public static Dictionary ToObject(IDictionary dictionary) + { + var result = new Dictionary(); + + foreach (DictionaryEntry entry in dictionary) + { + result[entry.Key.ToString()] = entry.Value; // 将值存储为 object + } + + return result; + } + internal static string PercentEncode(string value) { if (value == null) @@ -260,14 +382,14 @@ internal static string PercentEncode(string value) } else { - stringBuilder.Append("%").Append(string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int) c)); + stringBuilder.Append("%").Append(string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c)); } } return stringBuilder.ToString(); } - internal static HttpRequestMessage GetRequestMessage(TeaRequest request, Dictionary runtimeOptions, out int timeout) + internal static HttpRequestMessage GetRequestMessage(DaraRequest request, Dictionary runtimeOptions, out int timeout) { var url = ComposeUrl(request); HttpRequestMessage req = new HttpRequestMessage(); diff --git a/Darabonba/DaraDate.cs b/Darabonba/DaraDate.cs new file mode 100644 index 0000000..2563f7b --- /dev/null +++ b/Darabonba/DaraDate.cs @@ -0,0 +1,185 @@ +using System; +using Darabonba.Exceptions; + +namespace Darabonba +{ + public class DaraDate + { + private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + public DateTime Date { get; private set; } + + public DaraDate(DateTime date) + { + Date = date; + } + + public DaraDate(string dateStr) + { + long timestamp; + DateTimeOffset dateTimeOffset; + if (long.TryParse(dateStr, out timestamp)) + { + dateTimeOffset = Jan1st1970.AddSeconds(timestamp); + Date = dateTimeOffset.UtcDateTime; + } + // if no time zone, treat as local time as DateTimeOffset.Parse. + else if (DateTimeOffset.TryParse(dateStr, out dateTimeOffset)) + { + Date = dateTimeOffset.UtcDateTime; + } + else + { + throw new DaraException + { + Message = dateStr + "is not a valid time string." + }; + } + } + + public string Format(string layout) + { + layout = layout.Replace('Y', 'y') + .Replace('D', 'd') + .Replace('h', 'H'); + return Date.ToUniversalTime().ToString(layout); + } + + public long Unix() + { + return (long)(Date.ToUniversalTime() - Jan1st1970).TotalSeconds; + } + + public string UTC() + { + return Date.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss.ffffff '+0000 UTC'"); + } + + public DaraDate Sub(string unit, int amount) + { + DateTime newDate; + switch (unit.ToLowerInvariant()) + { + case "millisecond": + newDate = Date.AddMilliseconds(-amount); + break; + case "second": + newDate = Date.AddSeconds(-amount); + break; + case "minute": + newDate = Date.AddMinutes(-amount); + break; + case "hour": + newDate = Date.AddHours(-amount); + break; + case "day": + newDate = Date.AddDays(-amount); + break; + case "month": + newDate = Date.AddMonths(-amount); + break; + case "year": + newDate = Date.AddYears(-amount); + break; + default: + throw new ArgumentException("Unsupported unit."); + } + return new DaraDate(newDate); + } + + public DaraDate Add(string unit, int amount) + { + DateTime newDate; + switch (unit.ToLowerInvariant()) + { + case "millisecond": + newDate = Date.AddMilliseconds(amount); + break; + case "second": + newDate = Date.AddSeconds(amount); + break; + case "minute": + newDate = Date.AddMinutes(amount); + break; + case "hour": + newDate = Date.AddHours(amount); + break; + case "day": + newDate = Date.AddDays(amount); + break; + case "month": + newDate = Date.AddMonths(amount); + break; + case "year": + newDate = Date.AddYears(amount); + break; + default: + throw new ArgumentException("Unsupported unit."); + } + return new DaraDate(newDate); + } + + public int Diff(string unit, DaraDate diffDate) + { + TimeSpan timeSpan = Date - diffDate.Date; + switch (unit.ToLowerInvariant()) + { + case "millisecond": + return timeSpan.Milliseconds; + case "second": + return timeSpan.Seconds; + case "minute": + return timeSpan.Minutes; + case "hour": + return timeSpan.Hours; + case "day": + return timeSpan.Days; + case "month": + return timeSpan.Days / 30; + case "year": + return timeSpan.Days / 365; + default: + throw new ArgumentException("Unsupported unit."); + } + } + + public int Hour() + { + return Date.Hour; + } + + public int Minute() + { + return Date.Minute; + } + + public int Second() + { + return Date.Second; + } + + public int Month() + { + return Date.Month; + } + + public int Year() + { + return Date.Year; + } + + public int DayOfMonth() + { + return Date.Day; + } + + public int DayOfWeek() + { + if (Date.DayOfWeek == 0) + { + return 7; + } + return (int)Date.DayOfWeek; + } + } +} \ No newline at end of file diff --git a/Darabonba/DaraFile.cs b/Darabonba/DaraFile.cs new file mode 100644 index 0000000..8020e02 --- /dev/null +++ b/Darabonba/DaraFile.cs @@ -0,0 +1,163 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Darabonba +{ + public class DaraFile : IDisposable + { + public readonly string _path; + public FileInfo _fileInfo; + public FileStream _fileStream; + public long _position; + + + public DaraFile(string path) + { + _path = path; + _fileInfo = new FileInfo(path); + _position = 0; + _fileStream = new FileStream(_path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); + } + + public void Dispose() + { + if (_fileStream != null) + { + _fileStream.Dispose(); + } + } + + public string Path() + { + return _path; + } + + private void EnsureFileInfoLoaded() + { + if (!_fileInfo.Exists) + { + _fileInfo = new FileInfo(_path); + } + } + + public DaraDate CreateTime() + { + EnsureFileInfoLoaded(); + return new DaraDate(_fileInfo.CreationTimeUtc); + } + + public async Task CreateTimeAsync() + { + EnsureFileInfoLoaded(); + return new DaraDate(_fileInfo.CreationTimeUtc); + } + + public DaraDate ModifyTime() + { + EnsureFileInfoLoaded(); + return new DaraDate(_fileInfo.LastWriteTimeUtc); + } + + public async Task ModifyTimeAsync() + { + EnsureFileInfoLoaded(); + return new DaraDate(_fileInfo.LastWriteTimeUtc); + } + + public int Length() + { + EnsureFileInfoLoaded(); + return (int)_fileInfo.Length; + } + + public async Task LengthAsync() + { + EnsureFileInfoLoaded(); + return (int)_fileInfo.Length; + } + + public byte[] Read(int size) + { + _fileStream.Seek(_position, SeekOrigin.Begin); + byte[] buffer = new byte[size]; + int bytesRead = _fileStream.Read(buffer, 0, size); + if (bytesRead == 0) + { + return null; + } + _position += bytesRead; + return buffer; + } + + public async Task ReadAsync(int size) + { + _fileStream.Seek(_position, SeekOrigin.Begin); + byte[] buffer = new byte[size]; + int bytesRead = await _fileStream.ReadAsync(buffer, 0, size); + if (bytesRead == 0) + { + return null; + } + + _position += bytesRead; + return buffer; + } + + public void Write(byte[] data) + { + _fileStream.Seek(0, SeekOrigin.End); + _fileStream.Write(data, 0, data.Length); + _fileStream.Flush(); + _fileInfo.Refresh(); + } + + public async Task WriteAsync(byte[] data) + { + _fileStream.Seek(0, SeekOrigin.End); + await _fileStream.WriteAsync(data, 0, data.Length); + await _fileStream.FlushAsync(); + _fileInfo.Refresh(); + } + + + public void Close() + { + if (_fileStream != null) + { + _fileStream.Flush(); + _fileStream.Close(); + _fileStream = null; + } + } + public async Task CloseAsync() + { + if (_fileStream != null) + { + await _fileStream.FlushAsync(); + _fileStream.Close(); + _fileStream = null; + } + } + + public static bool Exists(string path) + { + return File.Exists(path); + } + + public static async Task ExistsAsync(string path) + { + return File.Exists(path); + } + + public static FileStream CreateReadStream(string path) + { + return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None); + } + + public static FileStream CreateWriteStream(string path) + { + return new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.None); + } + } +} \ No newline at end of file diff --git a/Tea/TeaModel.cs b/Darabonba/DaraModel.cs similarity index 94% rename from Tea/TeaModel.cs rename to Darabonba/DaraModel.cs index f7f01a3..4ca1a0c 100644 --- a/Tea/TeaModel.cs +++ b/Darabonba/DaraModel.cs @@ -3,12 +3,12 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; - using Newtonsoft.Json; -namespace Tea +namespace Darabonba { - public class TeaModel + // TODO tea-util中也要同步名称更改 + public class DaraModel { public static T ToObject(IDictionary dict) where T : class, new() { @@ -90,7 +90,7 @@ private static object MapObj(Type propertyType, object value) } return list; } - else if (typeof(TeaModel).IsAssignableFrom(propertyType)) + else if (typeof(DaraModel).IsAssignableFrom(propertyType)) { var v = Activator.CreateInstance(propertyType); Dictionary dicObj = ((IDictionary) value).Keys.Cast().ToDictionary(key => key, key => ((IDictionary) value) [key]); @@ -98,7 +98,7 @@ private static object MapObj(Type propertyType, object value) } else if (typeof(IDictionary).IsAssignableFrom(propertyType)) { - var dic = (IDictionary) value; + var dic = (IDictionary) value; if (dic.Count == 0) { return dic; diff --git a/Tea/TeaModelExtensions.cs b/Darabonba/DaraModelExtensions.cs similarity index 73% rename from Tea/TeaModelExtensions.cs rename to Darabonba/DaraModelExtensions.cs index 7362e0b..191bbfb 100644 --- a/Tea/TeaModelExtensions.cs +++ b/Darabonba/DaraModelExtensions.cs @@ -4,11 +4,11 @@ using System.IO; using System.Reflection; -namespace Tea +namespace Darabonba { - public static class TeaModelExtensions + public static class DaraModelExtensions { - public static Dictionary ToMap(this TeaModel model) + public static Dictionary ToMap(this DaraModel model) { if (model == null) { @@ -22,7 +22,7 @@ public static Dictionary ToMap(this TeaModel model) { PropertyInfo propertyInfo = properties[i]; Type property = propertyInfo.PropertyType; - if(typeof(Stream).IsAssignableFrom(property)) + if (typeof(Stream).IsAssignableFrom(property)) { continue; } @@ -42,8 +42,8 @@ public static object ToMapFactory(Type type, object value) } if (typeof(IList).IsAssignableFrom(type) && !typeof(Array).IsAssignableFrom(type)) { - IList list = (IList) value; - Type listType = type.GetGenericArguments() [0]; + IList list = (IList)value; + Type listType = type.GetGenericArguments()[0]; List resultList = new List(); for (int j = 0; j < list.Count; j++) { @@ -60,7 +60,7 @@ public static object ToMapFactory(Type type, object value) } else if (typeof(IDictionary).IsAssignableFrom(type)) { - IDictionary dic = (IDictionary) value; + IDictionary dic = (IDictionary)value; IDictionary resultDic = new Dictionary(); foreach (DictionaryEntry keypair in dic) { @@ -76,16 +76,16 @@ public static object ToMapFactory(Type type, object value) } return resultDic; } - else if (typeof(TeaModel).IsAssignableFrom(type)) + else if (typeof(DaraModel).IsAssignableFrom(type)) { - TeaModel teaModel = (TeaModel) value; - return teaModel.ToMap(); + DaraModel daraModel = (DaraModel)value; + return daraModel.ToMap(); } return value; } - public static void Validate(this TeaModel model) + public static void Validate(this DaraModel model) { if (model == null) { @@ -99,26 +99,26 @@ public static void Validate(this TeaModel model) Type propertyType = p.PropertyType; object obj = p.GetValue(model); ValidationAttribute attribute = p.GetCustomAttribute(typeof(ValidationAttribute)) as ValidationAttribute; - TeaValidator teaValidator = new TeaValidator(attribute, p.Name); - teaValidator.ValidateRequired(obj); + DaraValidator daraValidator = new DaraValidator(attribute, p.Name); + daraValidator.ValidateRequired(obj); if (obj == null) { continue; } if (typeof(IList).IsAssignableFrom(propertyType) && !typeof(Array).IsAssignableFrom(propertyType)) { - IList list = (IList) obj; + IList list = (IList)obj; //validate list count - teaValidator.ValidateMaxLength(list); - teaValidator.ValidateMinLength(list); + daraValidator.ValidateMaxLength(list); + daraValidator.ValidateMinLength(list); - Type listType = propertyType.GetGenericArguments() [0]; - if (typeof(TeaModel).IsAssignableFrom(listType)) + Type listType = propertyType.GetGenericArguments()[0]; + if (typeof(DaraModel).IsAssignableFrom(listType)) { for (int j = 0; j < list.Count; j++) { - ((TeaModel) list[j]).Validate(); + ((DaraModel)list[j]).Validate(); } } else @@ -126,24 +126,24 @@ public static void Validate(this TeaModel model) for (int j = 0; j < list.Count; j++) { //validate pattern - teaValidator.ValidateRegex(list[j]); + daraValidator.ValidateRegex(list[j]); } } } - else if (typeof(TeaModel).IsAssignableFrom(propertyType)) + else if (typeof(DaraModel).IsAssignableFrom(propertyType)) { - ((TeaModel) obj).Validate(); + ((DaraModel)obj).Validate(); } else { //validate pattern - teaValidator.ValidateRegex(obj); + daraValidator.ValidateRegex(obj); //validate count - teaValidator.ValidateMaxLength(obj); - teaValidator.ValidateMinLength(obj); + daraValidator.ValidateMaxLength(obj); + daraValidator.ValidateMinLength(obj); //validate num - teaValidator.ValidateMaximum(obj); - teaValidator.ValidateMinimum(obj); + daraValidator.ValidateMaximum(obj); + daraValidator.ValidateMinimum(obj); } } } diff --git a/Tea/TeaRequest.cs b/Darabonba/DaraRequest.cs similarity index 83% rename from Tea/TeaRequest.cs rename to Darabonba/DaraRequest.cs index eb1345b..32694f3 100644 --- a/Tea/TeaRequest.cs +++ b/Darabonba/DaraRequest.cs @@ -1,15 +1,15 @@ using System.Collections.Generic; using System.IO; -namespace Tea +namespace Darabonba { - public class TeaRequest + public class DaraRequest { private string _protocol; private string _method; private Dictionary _query; private Dictionary _headers; - public TeaRequest() + public DaraRequest() { Query = new Dictionary(); Headers = new Dictionary(); @@ -92,5 +92,17 @@ public Dictionary Headers } public Stream Body; + + public Dictionary ToMap(bool noStream = false) + { + var map = new Dictionary(); + return map; + } + + public static DaraRequest FromMap(Dictionary map) + { + var model = new DaraRequest(); + return model; + } } } diff --git a/Tea/TeaResponse.cs b/Darabonba/DaraResponse.cs similarity index 54% rename from Tea/TeaResponse.cs rename to Darabonba/DaraResponse.cs index 0d725a4..5aed700 100644 --- a/Tea/TeaResponse.cs +++ b/Darabonba/DaraResponse.cs @@ -2,9 +2,9 @@ using System.IO; using System.Net.Http; -namespace Tea +namespace Darabonba { - public class TeaResponse + public class DaraResponse { private HttpResponseMessage _responseAsync { get; set; } @@ -29,15 +29,29 @@ public Stream Body } } - public TeaResponse(HttpResponseMessage response) + + public DaraResponse(HttpResponseMessage response) { if (response != null) { - StatusCode = (int) response.StatusCode; + StatusCode = (int)response.StatusCode; StatusMessage = ""; - Headers = TeaCore.ConvertHeaders(response.Headers); + Headers = DaraCore.ConvertHeaders(response.Headers); _responseAsync = response; } } + + public Dictionary ToMap(bool noStream = false) + { + var map = new Dictionary(); + return map; + } + + public static DaraResponse FromMap(Dictionary map) + { + HttpResponseMessage httpResponseMessage = new HttpResponseMessage(); + var model = new DaraResponse(httpResponseMessage); + return model; + } } } diff --git a/Darabonba/DaraURL.cs b/Darabonba/DaraURL.cs new file mode 100644 index 0000000..3acd2a9 --- /dev/null +++ b/Darabonba/DaraURL.cs @@ -0,0 +1,119 @@ +using System; +using System.Globalization; +using System.Net; +using System.Text; + +namespace Darabonba +{ + public class DaraURL + { + private Uri _uri; + + public DaraURL(string str) + { + _uri = new Uri(str); + } + + public string Path() + { + return _uri.AbsolutePath + _uri.Query; + } + + public string Pathname() + { + return _uri.AbsolutePath; + } + + public string Protocol() + { + return _uri.Scheme; + } + + public string Hostname() + { + return _uri.Host; + } + + public string Host() + { + return _uri.Authority; + } + + public string Port() + { + return _uri.Port == -1 ? "" : _uri.Port.ToString(); + } + + public string Hash() + { + return string.IsNullOrWhiteSpace(_uri.Fragment) ? "" : _uri.Fragment.TrimStart('#'); + } + + public string Search() + { + return string.IsNullOrWhiteSpace(_uri.Query) ? "" : _uri.Query.TrimStart('?'); + } + + public string Href() + { + return _uri.ToString(); + } + + public string Auth() + { + return _uri.UserInfo; + } + + + internal static DaraURL Parse(string url) + { + return new DaraURL(url); + } + + internal static string UrlEncode(string url) + { + return url != null ? WebUtility.UrlEncode(url) : string.Empty; + } + + internal static string PercentEncode(string value) + { + if (value == null) + { + return null; + } + var stringBuilder = new StringBuilder(); + var text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; + var bytes = Encoding.UTF8.GetBytes(value); + foreach (char c in bytes) + { + if (text.IndexOf(c) >= 0) + { + stringBuilder.Append(c); + } + else + { + stringBuilder.Append("%").Append(string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c)); + } + } + + return stringBuilder.ToString(); + } + + internal static string PathEncode(string path) + { + if (string.IsNullOrEmpty(path) || path == "/") + { + return path; + } + var paths = path.Split('/'); + var stringBuilder = new StringBuilder(); + + foreach (var s in paths) + { + stringBuilder.Append('/').Append(PercentEncode(s)); + } + + return "/" + stringBuilder.ToString().TrimStart('/'); + } + } +} \ No newline at end of file diff --git a/Tea/TeaValidator.cs b/Darabonba/DaraValidator.cs similarity index 94% rename from Tea/TeaValidator.cs rename to Darabonba/DaraValidator.cs index 3fdc84a..f5ab71d 100644 --- a/Tea/TeaValidator.cs +++ b/Darabonba/DaraValidator.cs @@ -3,17 +3,17 @@ using System.Runtime.CompilerServices; using System.Text.RegularExpressions; -[assembly : InternalsVisibleTo("TeaUnitTests")] +[assembly : InternalsVisibleTo("DaraUnitTests")] -namespace Tea +namespace Darabonba { - internal class TeaValidator + internal class DaraValidator { public ValidationAttribute Attribute { get; set; } public string PropertyName { get; set; } - public TeaValidator(ValidationAttribute attribute, string propertyName) + public DaraValidator(ValidationAttribute attribute, string propertyName) { Attribute = attribute; PropertyName = propertyName; diff --git a/Tea/tea.csproj b/Darabonba/Darabonba.csproj similarity index 73% rename from Tea/tea.csproj rename to Darabonba/Darabonba.csproj index 43879c8..74ad329 100644 --- a/Tea/tea.csproj +++ b/Darabonba/Darabonba.csproj @@ -1,19 +1,19 @@  - netstandard2.0;net45 - Tea + netstandard2.0;net45;netstandard2.1;netcoreapp3.1 + Darabonba Library Alibaba Cloud - Alibaba Aliyun Tea Core SDK + Alibaba Aliyun Dara Core SDK ©2009-present Alibaba Cloud https://github.com/aliyun/ https://www.alibabacloud.com/favicon.ico - Alibaba Cloud Tea Core SDK for .NET + Alibaba Cloud Dara Core SDK for .NET false false - Tea + Darabonba 1.1.3 false 5 @@ -27,6 +27,16 @@ NET45 + + NETSTANDARD2_1 + 8 + + + + NETCOREAPP3_1 + 8 + + @@ -52,8 +62,10 @@ + + + - diff --git a/Darabonba/Exceptions/DaraException.cs b/Darabonba/Exceptions/DaraException.cs new file mode 100644 index 0000000..6f0d80c --- /dev/null +++ b/Darabonba/Exceptions/DaraException.cs @@ -0,0 +1,16 @@ +using System.Collections; +using Tea; + +namespace Darabonba.Exceptions +{ + public class DaraException : TeaException + { + public DaraException() : base() + { + } + + public DaraException(IDictionary dict) : base(dict) + { + } + } +} diff --git a/Darabonba/Exceptions/DaraRetryableException.cs b/Darabonba/Exceptions/DaraRetryableException.cs new file mode 100644 index 0000000..46717fd --- /dev/null +++ b/Darabonba/Exceptions/DaraRetryableException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Darabonba.Exceptions +{ + public class DaraRetryableException : Exception + { + public DaraRetryableException() + { + } + } +} diff --git a/Darabonba/Exceptions/ResponseException.cs b/Darabonba/Exceptions/ResponseException.cs new file mode 100644 index 0000000..546e6bb --- /dev/null +++ b/Darabonba/Exceptions/ResponseException.cs @@ -0,0 +1,11 @@ +namespace Darabonba.Exceptions +{ + public class ResponseException : DaraException + { + public long? RetryAfter { get; set; } + + public ResponseException() : base() + { + } + } +} diff --git a/Darabonba/Exceptions/TeaException.cs b/Darabonba/Exceptions/TeaException.cs new file mode 100644 index 0000000..88b5bc2 --- /dev/null +++ b/Darabonba/Exceptions/TeaException.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Darabonba.Utils; + +namespace Tea +{ + public class TeaException : Exception + { + public string Code { get; set; } + public new string Message { get; set; } + public new Dictionary Data { get; set; } + public int StatusCode { get; set; } + public string Description { get; set; } + public Dictionary AccessDeniedDetail { get; set; } + + public Dictionary DataResult + { + get + { + return Data; + } + } + + public TeaException() + { + } + + public TeaException(IDictionary dict) + { + Dictionary dicObj = dict.Keys.Cast().ToDictionary(key => key, key => dict[key]); + Code = DictUtils.GetDicValue(dicObj, "code").ToSafeString(); + Message = DictUtils.GetDicValue(dicObj, "message").ToSafeString(); + Description = DictUtils.GetDicValue(dicObj, "description").ToSafeString(); + object obj = DictUtils.GetDicValue(dicObj, "accessDeniedDetail"); + if (obj != null) + { + if (typeof(IDictionary).IsAssignableFrom(obj.GetType())) + { + IDictionary dicDetail = (IDictionary)obj; + AccessDeniedDetail = dicDetail.Keys.Cast().ToDictionary(key => key, key => dicDetail[key]); + } + } + obj = DictUtils.GetDicValue(dicObj, "data"); + if (obj == null) + { + return; + } + if (typeof(IDictionary).IsAssignableFrom(obj.GetType())) + { + IDictionary dicData = (IDictionary)obj; + Data = dicData.Keys.Cast().ToDictionary(key => key, key => dicData[key]); + if (DictUtils.GetDicValue(Data, "statusCode") != null) + { + StatusCode = int.Parse(DictUtils.GetDicValue(Data, "statusCode").ToSafeString()); + } + return; + } + + Dictionary filedsDict = new Dictionary(); + Type type = obj.GetType(); + PropertyInfo[] properties = type.GetProperties(); + for (int i = 0; i < properties.Length; i++) + { + PropertyInfo p = properties[i]; + filedsDict.Add(p.Name, p.GetValue(obj)); + } + Data = filedsDict; + } + } +} diff --git a/Darabonba/Exceptions/TeaUnretryableException.cs b/Darabonba/Exceptions/TeaUnretryableException.cs new file mode 100644 index 0000000..77cd8ac --- /dev/null +++ b/Darabonba/Exceptions/TeaUnretryableException.cs @@ -0,0 +1,22 @@ +using System; +using Darabonba; + +namespace Tea +{ + public class TeaUnretryableException : Exception + { + private readonly DaraRequest _lastRequest; + + public DaraRequest LastRequest { get { return _lastRequest; } } + + public TeaUnretryableException() : base() + { + + } + + public TeaUnretryableException(DaraRequest lastRequest, Exception innerException) : base(" Retry failed : " + (innerException == null ? "" : innerException.Message), innerException) + { + _lastRequest = lastRequest; + } + } +} diff --git a/Darabonba/Models/ExtendsParameters.cs b/Darabonba/Models/ExtendsParameters.cs new file mode 100644 index 0000000..97d3d5e --- /dev/null +++ b/Darabonba/Models/ExtendsParameters.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; + +namespace Darabonba.Models +{ + public class ExtendsParameters : DaraModel + { + public Dictionary Headers { get; set; } + + public new void Validate() {} + + public new ExtendsParameters Copy() + { + ExtendsParameters copy = FromMap(ToMap()); + return copy; + } + + public new ExtendsParameters CopyWithoutStream() + { + ExtendsParameters copy = FromMap(ToMap(true)); + return copy; + } + + public Dictionary ToMap(bool noStream = false) + { + var map = new Dictionary(); + + if (Headers != null) + { + map["headers"] = Headers; + } + + return map; + } + + public static ExtendsParameters FromMap(IDictionary map) + { + var model = new ExtendsParameters(); + + if (map.ContainsKey("headers")) + { + model.Headers = map["headers"] as Dictionary; + } + + return model; + } + } +} \ No newline at end of file diff --git a/Darabonba/Models/RuntimeOptions.cs b/Darabonba/Models/RuntimeOptions.cs new file mode 100644 index 0000000..20bdfee --- /dev/null +++ b/Darabonba/Models/RuntimeOptions.cs @@ -0,0 +1,211 @@ +using System.Collections.Generic; +using Darabonba; +using Darabonba.RetryPolicy; + +namespace Darabonba.Models +{ + public class RuntimeOptions : DaraModel + { + public bool? Autoretry { get; set; } + public bool? IgnoreSSL { get; set; } + public string Key { get; set; } + public string Cert { get; set; } + public string Ca { get; set; } + public int? MaxAttempts { get; set; } + public string BackoffPolicy { get; set; } + public int? BackoffPeriod { get; set; } + public int? ReadTimeout { get; set; } + public int? ConnectTimeout { get; set; } + public string HttpProxy { get; set; } + public string HttpsProxy { get; set; } + public string NoProxy { get; set; } + public int? MaxIdleConns { get; set; } + public string LocalAddr { get; set; } + public string Socks5Proxy { get; set; } + public string Socks5NetWork { get; set; } + public bool? KeepAlive { get; set; } + public ExtendsParameters ExtendsParameters { get; set; } + + public new void Validate() {} + + public new RuntimeOptions Copy() + { + RuntimeOptions copy = FromMap(ToMap()); + return copy; + } + + public new RuntimeOptions CopyWithoutStream() + { + RuntimeOptions copy = FromMap(ToMap(true)); + return copy; + } + + public Dictionary ToMap(bool noStream = false) + { + var map = new Dictionary(); + + if (Autoretry != null) + { + map["autoretry"] = Autoretry; + } + if (IgnoreSSL != null) + { + map["ignoreSSL"] = IgnoreSSL; + } + if (Key != null) + { + map["key"] = Key; + } + if (Cert != null) + { + map["cert"] = Cert; + } + if (Ca != null) + { + // TODO 是全小写吗 + map["ca"] = Ca; + } + if (MaxAttempts != null) + { + map["max_attempts"] = MaxAttempts; + } + if (BackoffPolicy != null) + { + map["backoff_policy"] = BackoffPolicy; + } + if (BackoffPeriod != null) + { + map["backoff_period"] = BackoffPeriod; + } + if (ReadTimeout != null) + { + map["readTimeout"] = ReadTimeout; + } + if (ConnectTimeout != null) + { + map["connectTimeout"] = ConnectTimeout; + } + if (HttpProxy != null) + { + map["httpProxy"] = HttpProxy; + } + if (HttpsProxy != null) + { + map["httpsProxy"] = HttpsProxy; + } + if (NoProxy != null) + { + map["noProxy"] = NoProxy; + } + if (MaxIdleConns != null) + { + map["maxIdleConns"] = MaxIdleConns; + } + if (LocalAddr != null) + { + map["localAddr"] = LocalAddr; + } + if (Socks5Proxy != null) + { + map["socks5Proxy"] = Socks5Proxy; + } + if (Socks5NetWork != null) + { + map["socks5NetWork"] = Socks5NetWork; + } + if (KeepAlive != null) + { + map["keepAlive"] = KeepAlive; + } + if (ExtendsParameters != null) + { + map["extendsParameters"] = ExtendsParameters != null ? ExtendsParameters.ToMap(noStream) : null; + } + return map; + } + + public static RuntimeOptions FromMap(Dictionary map) + { + var model = new RuntimeOptions(); + + if (map.ContainsKey("autoretry")) + { + model.Autoretry = (bool?)map["autoretry"]; + } + if (map.ContainsKey("ignoreSSL")) + { + model.IgnoreSSL = (bool?)map["ignoreSSL"]; + } + if (map.ContainsKey("key")) + { + model.Key = (string)map["key"]; + } + if (map.ContainsKey("cert")) + { + model.Cert = (string)map["cert"]; + } + if (map.ContainsKey("ca")) + { + model.Ca = (string)map["ca"]; + } + if (map.ContainsKey("max_attempts")) + { + model.MaxAttempts = (int?)map["max_attempts"]; + } + if (map.ContainsKey("backoff_policy")) + { + model.BackoffPolicy = (string)map["backoff_policy"]; + } + if (map.ContainsKey("backoff_period")) + { + model.BackoffPeriod = (int?)map["backoff_period"]; + } + if (map.ContainsKey("readTimeout")) + { + model.ReadTimeout = (int?)map["readTimeout"]; + } + if (map.ContainsKey("connectTimeout")) + { + model.ConnectTimeout = (int?)map["connectTimeout"]; + } + if (map.ContainsKey("httpProxy")) + { + model.HttpProxy = (string)map["httpProxy"]; + } + if (map.ContainsKey("httpsProxy")) + { + model.HttpsProxy = (string)map["httpsProxy"]; + } + if (map.ContainsKey("noProxy")) + { + model.NoProxy = (string)map["noProxy"]; + } + if (map.ContainsKey("maxIdleConns")) + { + model.MaxIdleConns = (int?)map["maxIdleConns"]; + } + if (map.ContainsKey("localAddr")) + { + model.LocalAddr = (string)map["localAddr"]; + } + if (map.ContainsKey("socks5Proxy")) + { + model.Socks5Proxy = (string)map["socks5Proxy"]; + } + if (map.ContainsKey("socks5NetWork")) + { + model.Socks5NetWork = (string)map["socks5NetWork"]; + } + if (map.ContainsKey("keepAlive")) + { + model.KeepAlive = (bool?)map["keepAlive"]; + } + if (map.ContainsKey("extendsParameters")) + { + var temp = (Dictionary)map["extendsParameters"]; + model.ExtendsParameters = ExtendsParameters.FromMap(temp); + } + return model; + } + } +} \ No newline at end of file diff --git a/Darabonba/Models/SSEEvent.cs b/Darabonba/Models/SSEEvent.cs new file mode 100644 index 0000000..ed2bc3d --- /dev/null +++ b/Darabonba/Models/SSEEvent.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using Darabonba; + +namespace Darabonba.Models +{ + public class SSEEvent : DaraModel + { + public string Data { get; set; } + public string Id { get; set; } + public string Event { get; set; } + public int? Retry { get; set; } + + public SSEEvent Copy() + { + SSEEvent copy = FromMap(ToMap()); + return copy; + } + + public SSEEvent CopyWithoutStream() + { + SSEEvent copy = FromMap(ToMap(true)); + return copy; + } + + public Dictionary ToMap(bool noStream = false) + { + var map = new Dictionary(); + if (Data != null) + { + map["data"] = Data; + } + if (Id != null) + { + map["id"] = Id; + } + if (Event != null) + { + map["event"] = Event; + } + if (Retry != null) + { + map["retry"] = Retry; + } + return map; + } + + public SSEEvent FromMap(Dictionary map) + { + var model = new SSEEvent(); + if (map.ContainsKey("data")) + { + model.Data = (string)map["data"]; + } + if (map.ContainsKey("id")) + { + model.Id = (string)map["id"]; + } + if (map.ContainsKey("event")) + { + model.Event = (string)map["event"]; + } + if (map.ContainsKey("retry")) + { + model.Retry = (int?)map["retry"]; + } + return model; + } + } +} \ No newline at end of file diff --git a/Tea/Properties/AssemblyInfo.cs b/Darabonba/Properties/AssemblyInfo.cs similarity index 100% rename from Tea/Properties/AssemblyInfo.cs rename to Darabonba/Properties/AssemblyInfo.cs diff --git a/Darabonba/RetryPolicy/BackoffPolicy.cs b/Darabonba/RetryPolicy/BackoffPolicy.cs new file mode 100644 index 0000000..2baf1d8 --- /dev/null +++ b/Darabonba/RetryPolicy/BackoffPolicy.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using Darabonba.Exceptions; + +namespace Darabonba.RetryPolicy +{ + public interface IBackoffPolicy + { + long? GetDelayTime(RetryPolicyContext ctx); + } + + public abstract class BackoffPolicy : IBackoffPolicy + { + protected string Policy { get; set; } + + public BackoffPolicy(string policy) + { + Policy = policy; + } + + public abstract long? GetDelayTime(RetryPolicyContext ctx); + + public static BackoffPolicy NewBackOffPolicy(Dictionary option) + { + var validPolicy = new List { "Fixed", "Random", "Exponential", "EqualJitter", "ExponentialWithEqualJitter", "FullJitter", "ExponentialWithFullJitter" }; + if (!option.ContainsKey("policy") || option["policy"] == null || !(option["policy"] is string)) + { + throw new DaraException + { + Message = "Invalid backoff policy" + }; + } + else + { + string policy = (string)option["policy"]; + if (!validPolicy.Contains(policy)) + { + throw new DaraException + { + Message = "Invalid backoff policy" + }; + } + if (!option.ContainsKey("period") || option["period"] == null || !(option["period"] is int)) + { + throw new DaraException { Message = "Period must be specified." }; + } + int period = (int)option["period"]; + switch (policy) + { + case "Fixed": + { + return new FixedBackoffPolicy(period); + } + case "Random": + { + var cap = option.ContainsKey("cap") && option["cap"] != null && option["cap"] is long ? (long)option["cap"] : 20000; + return new RandomBackoffPolicy(period, cap); + } + case "Exponential": + { + var cap = option.ContainsKey("cap") && option["cap"] != null && option["cap"] is long ? (long)option["cap"] : 3L * 24 * 60 * 60 * 1000; + return new ExponentialBackoffPolicy(period, cap); + } + case "EqualJitter": + case "ExponentialWithEqualJitter": + { + var cap = option.ContainsKey("cap") && option["cap"] != null && option["cap"] is long ? (long)option["cap"] : 3L * 24 * 60 * 60 * 1000; + return new EqualJitterBackoffPolicy(period, cap); + } + case "FullJitter": + case "ExponentialWithFullJitter": + { + var cap = option.ContainsKey("cap") && option["cap"] != null && option["cap"] is long ? (long)option["cap"] : 3L * 24 * 60 * 60 * 1000; + return new FullJitterBackoffPolicy(period, cap); + } + default: + throw new DaraException + { + Message = "Invalid backoff policy" + }; + } + } + } + } +} \ No newline at end of file diff --git a/Darabonba/RetryPolicy/EqualJitterBackoffPolicy.cs b/Darabonba/RetryPolicy/EqualJitterBackoffPolicy.cs new file mode 100644 index 0000000..9cb139f --- /dev/null +++ b/Darabonba/RetryPolicy/EqualJitterBackoffPolicy.cs @@ -0,0 +1,25 @@ +using System; + +namespace Darabonba.RetryPolicy +{ + public class EqualJitterBackoffPolicy : BackoffPolicy + { + private readonly int? Period; + private readonly long? Cap; + + public EqualJitterBackoffPolicy(int? period, long? cap = 3L * 24 * 60 * 60 * 1000) + : base(null) + { + Period = period; + Cap = cap; + } + + public override long? GetDelayTime(RetryPolicyContext ctx) + { + double ceil = Math.Min((double)Cap, (double)(Math.Pow(2.0, (double)ctx.RetriesAttempted) * Period)); + Random random = new Random(); + double delay = ceil / 2 + random.NextDouble() * (ceil / 2); + return (long?)delay; + } + } +} \ No newline at end of file diff --git a/Darabonba/RetryPolicy/ExponentialBackoffPolicy.cs b/Darabonba/RetryPolicy/ExponentialBackoffPolicy.cs new file mode 100644 index 0000000..3a39590 --- /dev/null +++ b/Darabonba/RetryPolicy/ExponentialBackoffPolicy.cs @@ -0,0 +1,23 @@ +using System; + +namespace Darabonba.RetryPolicy +{ + public class ExponentialBackoffPolicy : BackoffPolicy + { + private readonly int? Period; + public readonly long? Cap; + + public ExponentialBackoffPolicy(int? period, long? cap = 3L * 24 * 60 * 60 * 1000) + : base(null) + { + Period = period; + Cap = cap; + } + + public override long? GetDelayTime(RetryPolicyContext ctx) + { + double potentialTime = Math.Pow(2.0, (double)ctx.RetriesAttempted) * (double)Period; + return (long?)Math.Min((double)Cap, potentialTime); + } + } +} \ No newline at end of file diff --git a/Darabonba/RetryPolicy/FixedBackoffPolicy.cs b/Darabonba/RetryPolicy/FixedBackoffPolicy.cs new file mode 100644 index 0000000..f89888d --- /dev/null +++ b/Darabonba/RetryPolicy/FixedBackoffPolicy.cs @@ -0,0 +1,18 @@ +namespace Darabonba.RetryPolicy +{ + public class FixedBackoffPolicy : BackoffPolicy + { + private readonly int? Period; + + public FixedBackoffPolicy(int? period) + : base(null) + { + Period = period; + } + + public override long? GetDelayTime(RetryPolicyContext ctx) + { + return Period; + } + } +} \ No newline at end of file diff --git a/Darabonba/RetryPolicy/FullJitterBackoffPolicy.cs b/Darabonba/RetryPolicy/FullJitterBackoffPolicy.cs new file mode 100644 index 0000000..40154f7 --- /dev/null +++ b/Darabonba/RetryPolicy/FullJitterBackoffPolicy.cs @@ -0,0 +1,25 @@ +using System; + +namespace Darabonba.RetryPolicy +{ + public class FullJitterBackoffPolicy : BackoffPolicy + { + private readonly int? Period; + private readonly long? Cap; + + public FullJitterBackoffPolicy(int? period, long? cap = 3L * 24 * 60 * 60 * 1000) + : base(null) + { + Period = period; + Cap = cap; + } + + public override long? GetDelayTime(RetryPolicyContext ctx) + { + double ceil = Math.Min((double)Cap, (double)(Math.Pow(2.0, (double)ctx.RetriesAttempted) * Period)); + Random random = new Random(); + double delay = random.NextDouble() * ceil; + return (long?)delay; + } + } +} \ No newline at end of file diff --git a/Darabonba/RetryPolicy/RandomBackoffPolicy.cs b/Darabonba/RetryPolicy/RandomBackoffPolicy.cs new file mode 100644 index 0000000..f23126c --- /dev/null +++ b/Darabonba/RetryPolicy/RandomBackoffPolicy.cs @@ -0,0 +1,24 @@ +using System; + +namespace Darabonba.RetryPolicy +{ + public class RandomBackoffPolicy : BackoffPolicy + { + private readonly int? Period; + private readonly long? Cap; + + public RandomBackoffPolicy(int? period, long? cap = 2000) + : base(null) + { + Period = period; + Cap = cap; + } + + public override long? GetDelayTime(RetryPolicyContext ctx) + { + Random random = new Random(); + double randomTime = random.NextDouble() * Math.Min((double)Cap, (double)(Period * ctx.RetriesAttempted)); + return (long?)randomTime; + } + } +} \ No newline at end of file diff --git a/Darabonba/RetryPolicy/RetryCondition.cs b/Darabonba/RetryPolicy/RetryCondition.cs new file mode 100644 index 0000000..6d28102 --- /dev/null +++ b/Darabonba/RetryPolicy/RetryCondition.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Darabonba.RetryPolicy +{ + public class RetryCondition + { + public int? MaxAttempts { get; set; } + public long? MaxDelayTimeMillis { get; set; } + public BackoffPolicy Backoff { get; set; } + public List Exception { get; set; } + public List ErrorCode { get; set; } + } +} \ No newline at end of file diff --git a/Darabonba/RetryPolicy/RetryOptions.cs b/Darabonba/RetryPolicy/RetryOptions.cs new file mode 100644 index 0000000..6e7e0d7 --- /dev/null +++ b/Darabonba/RetryPolicy/RetryOptions.cs @@ -0,0 +1,23 @@ +using Darabonba; +using System; +using System.Collections.Generic; + +namespace Darabonba.RetryPolicy +{ + public class RetryOptions + { + public bool? Retryable { get; set; } + public List RetryCondition { get; set; } + public List NoRetryCondition { get; set; } + + public Dictionary ToMap(bool noStream = false) + { + return new Dictionary(); + } + + public static RetryOptions FromMap(Dictionary map) + { + return new RetryOptions(); + } + } +} \ No newline at end of file diff --git a/Darabonba/RetryPolicy/RetryPolicyContext.cs b/Darabonba/RetryPolicy/RetryPolicyContext.cs new file mode 100644 index 0000000..221db19 --- /dev/null +++ b/Darabonba/RetryPolicy/RetryPolicyContext.cs @@ -0,0 +1,12 @@ +using System; + +namespace Darabonba.RetryPolicy +{ + public class RetryPolicyContext + { + public int? RetriesAttempted { get; set; } + public DaraRequest Request { get; set; } + public DaraResponse Response { get; set; } + public Exception Exception { get; set; } + } +} \ No newline at end of file diff --git a/Darabonba/Streams/FileFormStream.cs b/Darabonba/Streams/FileFormStream.cs new file mode 100644 index 0000000..1d9245a --- /dev/null +++ b/Darabonba/Streams/FileFormStream.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Darabonba; + +namespace Darabonba.Streams +{ + public class FileField + { + [NameInMap("filename")] + public string Filename { get; set; } + + [NameInMap("contentType")] + public string ContentType { get; set; } + + [NameInMap("content")] + public Stream Content { get; set; } + } + + public class FileFormStream : Stream + { + + private Stream streamingStream { get; set; } + + private Dictionary form { get; set; } + + private string boundary; + + private List keys; + + private int index; + + private bool streaming; + + private long position; + + private long length; + + public FileFormStream(Dictionary form, string boundary) + { + this.form = form; + this.boundary = boundary; + index = 0; + keys = form.Keys.ToList(); + streaming = false; + } + + public override bool CanRead { get { return true; } } + + public override bool CanSeek { get { return false; } } + + public override bool CanWrite { get { return false; } } + + public override long Length { get { return length; } } + + public override long Position + { + get + { + return position; + } + set + { + position = value; + } + } + + public override void Flush() + { + throw new NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (streaming) + { + int bytesRLength; + if (streamingStream != null && (bytesRLength = streamingStream.Read(buffer, 0, buffer.Length)) != 0) + { + return bytesRLength; + } + else + { + streaming = false; + if (streamingStream != null && streamingStream.CanSeek) + { + streamingStream.Seek(0, SeekOrigin.Begin); + } + streamingStream = null; + byte[] bytesFileEnd = Encoding.UTF8.GetBytes("\r\n"); + for (int i = 0; i < bytesFileEnd.Length; i++) + { + buffer[i] = bytesFileEnd[i]; + } + index++; + return bytesFileEnd.Length; + } + } + + if (index < keys.Count) + { + string name = this.keys[this.index]; + object fieldValue = form[name]; + if (fieldValue is FileField) + { + FileField fileField = (FileField) fieldValue; + + streaming = true; + streamingStream = fileField.Content; + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append("--").Append(boundary).Append("\r\n"); + stringBuilder.Append("Content-Disposition: form-data; name=\"").Append(name).Append("\"; filename=\"").Append(fileField.Filename).Append("\"\r\n"); + stringBuilder.Append("Content-Type: ").Append(fileField.ContentType).Append("\r\n\r\n"); + byte[] startBytes = Encoding.UTF8.GetBytes(stringBuilder.ToString()); + for (int i = 0; i < startBytes.Length; i++) + { + buffer[i] = startBytes[i]; + } + return startBytes.Length; + } + else + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append("--").Append(boundary).Append("\r\n"); + stringBuilder.Append("Content-Disposition: form-data; name=\"").Append(name).Append("\"\r\n\r\n"); + stringBuilder.Append(fieldValue.ToString()).Append("\r\n"); + byte[] formBytes = Encoding.UTF8.GetBytes(stringBuilder.ToString()); + for (int i = 0; i < formBytes.Length; i++) + { + buffer[i] = formBytes[i]; + } + index++; + return formBytes.Length; + } + } + else if (index == keys.Count) + { + string endStr = string.Format("--{0}--\r\n", boundary); + byte[] endBytes = Encoding.UTF8.GetBytes(endStr); + for (int i = 0; i < endBytes.Length; i++) + { + buffer[i] = endBytes[i]; + } + index++; + return endBytes.Length; + } + else + { + return 0; + } + } + + public new async Task ReadAsync(byte[] buffer, int offset, int count) + { + if (streaming) + { + int bytesRLength; + if (streamingStream != null && (bytesRLength = await streamingStream.ReadAsync(buffer, 0, buffer.Length)) != 0) + { + return bytesRLength; + } + else + { + streaming = false; + if (streamingStream != null) + { + streamingStream.Flush(); + streamingStream.Close(); + } + streamingStream = null; + byte[] bytesFileEnd = Encoding.UTF8.GetBytes("\r\n"); + for (int i = 0; i < bytesFileEnd.Length; i++) + { + buffer[i] = bytesFileEnd[i]; + } + index++; + return bytesFileEnd.Length; + } + } + + if (index < keys.Count) + { + string name = this.keys[this.index]; + object fieldValue = form[name]; + if (fieldValue is FileField) + { + FileField fileField = (FileField) fieldValue; + + streaming = true; + streamingStream = fileField.Content; + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append("--").Append(boundary).Append("\r\n"); + stringBuilder.Append("Content-Disposition: form-data; name=\"").Append(name).Append("\"; filename=\"").Append(fileField.Filename).Append("\"\r\n"); + stringBuilder.Append("Content-Type: ").Append(fileField.ContentType).Append("\r\n\r\n"); + byte[] startBytes = Encoding.UTF8.GetBytes(stringBuilder.ToString()); + for (int i = 0; i < startBytes.Length; i++) + { + buffer[i] = startBytes[i]; + } + return startBytes.Length; + } + else + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append("--").Append(boundary).Append("\r\n"); + stringBuilder.Append("Content-Disposition: form-data; name=\"").Append(name).Append("\"\r\n\r\n"); + stringBuilder.Append(fieldValue.ToString()).Append("\r\n"); + byte[] formBytes = Encoding.UTF8.GetBytes(stringBuilder.ToString()); + for (int i = 0; i < formBytes.Length; i++) + { + buffer[i] = formBytes[i]; + } + index++; + return formBytes.Length; + } + } + else if (index == keys.Count) + { + string endStr = string.Format("--{0}--\r\n", boundary); + byte[] endBytes = Encoding.UTF8.GetBytes(endStr); + for (int i = 0; i < endBytes.Length; i++) + { + buffer[i] = endBytes[i]; + } + index++; + return endBytes.Length; + } + else + { + return 0; + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + length = value; + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + internal static string PercentEncode(string value) + { + if (value == null) + { + return null; + } + var stringBuilder = new StringBuilder(); + var text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; + var bytes = Encoding.UTF8.GetBytes(value); + foreach (char c in bytes) + { + if (text.IndexOf(c) >= 0) + { + stringBuilder.Append(c); + } + else + { + stringBuilder.Append("%").Append(string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int) c)); + } + } + + return stringBuilder.ToString().Replace("+", "%20") + .Replace("*", "%2A").Replace("%7E", "~"); + } + } +} \ No newline at end of file diff --git a/Darabonba/Streams/StreamUtil.cs b/Darabonba/Streams/StreamUtil.cs new file mode 100644 index 0000000..372aedd --- /dev/null +++ b/Darabonba/Streams/StreamUtil.cs @@ -0,0 +1,275 @@ +using System; +using System.IO; +using System.Text; +using System.Collections.Generic; +using Newtonsoft.Json; +using System.Threading.Tasks; +using Darabonba.Utils; +using Darabonba.Models; + +namespace Darabonba.Streams +{ + public class StreamUtil + { + private const string DATA_PREFIX = "data:"; + private const string EVENT_PREFIX = "event:"; + private const string ID_PREFIX = "id:"; + private const string RETRY_PREFIX = "retry:"; + + public static string ToString(byte[] val) + { + return Encoding.UTF8.GetString(val); + } + + public static object ParseJSON(string val) + { + return JsonConvert.DeserializeObject(val); + } + + public static byte[] Read(Stream stream, int length) + { + byte[] data = new byte[length]; + stream.Read(data, 0, length); + return data; + } + + public static void Pipe(Stream readStream, Stream writeStream) + { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = readStream.Read(buffer, 0, buffer.Length)) > 0) + { + writeStream.Write(buffer, 0, bytesRead); + } + } + + public static Stream StreamFor(object data) + { + if (data is Stream) + { + Stream stream = data as Stream; + if (stream.CanRead) + { + Stream copy = new MemoryStream(); + stream.Position = 0; + stream.CopyTo(copy); + copy.Position = 0; + return copy; + } + throw new Exception("stream is not readable"); + } + if (data is string) + { + string str = data as string; + Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(str)); + return stream; + } + throw new Exception("data is not Stream or String"); + } + + public static byte[] ReadAsBytes(Stream stream) + { + int bufferLength = 4096; + using (var ms = new MemoryStream()) + { + var buffer = new byte[bufferLength]; + + while (true) + { + var length = stream.Read(buffer, 0, bufferLength); + if (length == 0) + { + break; + } + + ms.Write(buffer, 0, length); + } + + ms.Seek(0, SeekOrigin.Begin); + var bytes = new byte[ms.Length]; + ms.Read(bytes, 0, bytes.Length); + + stream.Close(); + stream.Dispose(); + + return bytes; + } + } + + public async static Task ReadAsBytesAsync(Stream stream) + { + int bufferLength = 4096; + using (var ms = new MemoryStream()) + { + var buffer = new byte[bufferLength]; + + while (true) + { + var length = await stream.ReadAsync(buffer, 0, bufferLength); + if (length == 0) + { + break; + } + + await ms.WriteAsync(buffer, 0, length); + } + + ms.Seek(0, SeekOrigin.Begin); + var bytes = new byte[ms.Length]; + await ms.ReadAsync(bytes, 0, bytes.Length); + + stream.Close(); + stream.Dispose(); + + return bytes; + } + } + + public static string ReadAsString(Stream stream) + { + return ToString(ReadAsBytes(stream)); + } + + public static async Task ReadAsStringAsync(Stream stream) + { + return ToString(await ReadAsBytesAsync(stream)); + } + + public static object ReadAsJSON(Stream stream) + { + object jResult = ParseJSON(ReadAsString(stream)); + object result = JSONUtil.Deserialize(jResult); + return result; + } + + public async static Task ReadAsJSONAsync(Stream stream) + { + object jResult = ParseJSON(await ReadAsStringAsync(stream)); + object result = JSONUtil.Deserialize(jResult); + return result; + } + + public class EventResult + { + public List Events { get; set; } + public string Remain { get; set; } + + public EventResult(List events, string remain) + { + Events = events; + Remain = remain; + } + } + + private static EventResult TryGetEvents(string head, string chunk) + { + string all = head + chunk; + var events = new List(); + var start = 0; + for (var i = 0; i < all.Length - 1; i++) + { + // message separated by \n\n + if (all[i] == '\n' && i + 1 < all.Length && all[i + 1] == '\n') + { + var rawEvent = all.Substring(start, i - start).Trim(); + var sseEvent = ParseEvent(rawEvent); + events.Add(sseEvent); + start = i + 2; + i++; + } + } + string remain = all.Substring(start); + return new EventResult(events, remain); + } + + private static SSEEvent ParseEvent(string rawEvent) + { + var sseEvent = new SSEEvent(); + var lines = rawEvent.Split('\n'); + + foreach (var line in lines) + { + if (line.StartsWith(DATA_PREFIX)) + { + sseEvent.Data = line.Substring(DATA_PREFIX.Length).Trim(); + } + else if (line.StartsWith(EVENT_PREFIX)) + { + sseEvent.Event = line.Substring(EVENT_PREFIX.Length).Trim(); + } + else if (line.StartsWith(ID_PREFIX)) + { + sseEvent.Id = line.Substring(ID_PREFIX.Length).Trim(); + } + else if (line.StartsWith(RETRY_PREFIX)) + { + var retryData = line.Substring(RETRY_PREFIX.Length).Trim(); + int retryValue; + if (int.TryParse(retryData, out retryValue)) + { + sseEvent.Retry = retryValue; + } + } + else if (line.StartsWith(":")) + { + // ignore the line + } + } + + return sseEvent; + } + + + public static IEnumerable ReadAsSSE(Stream stream) + { + using (var reader = new StreamReader(stream)) + { + var buffer = new char[4096]; + var rest = string.Empty; + int count; + + while ((count = reader.Read(buffer, 0, buffer.Length)) > 0) + { + var chunk = new string(buffer, 0, count); + + var eventResult = TryGetEvents(rest, chunk); + rest = eventResult.Remain; + + if (eventResult.Events != null && eventResult.Events.Count > 0) + { + foreach (var @event in eventResult.Events) + { + yield return @event; + } + } + } + } + } + + +#if NETSTANDARD2_1 || NETCOREAPP3_1 + public static async IAsyncEnumerable ReadAsSSEAsync(Stream stream) + { + using var reader = new StreamReader(stream); + var buffer = new char[4096]; + var rest = string.Empty; + + int count; + while ((count = await reader.ReadAsync(buffer, 0, buffer.Length)) > 0) + { + var chunk = new string(buffer, 0, count); + + var eventResult = TryGetEvents(rest, chunk); + rest = eventResult.Remain; + if (eventResult.Events != null && eventResult.Events.Count > 0) + { + foreach (var @event in eventResult.Events) + { + yield return @event; + } + } + } + } +#endif + } +} \ No newline at end of file diff --git a/Darabonba/Utils/BytesUtil.cs b/Darabonba/Utils/BytesUtil.cs new file mode 100644 index 0000000..c71e8f1 --- /dev/null +++ b/Darabonba/Utils/BytesUtil.cs @@ -0,0 +1,42 @@ +using System.Text; + +namespace Darabonba.Utils +{ + public class BytesUtil + { + public static byte[] From(string data, string type) + { + string lowerEncoding = type.ToLower(); + switch (lowerEncoding.ToLowerInvariant()) + { + case "ascii": + return Encoding.ASCII.GetBytes(data); + case "bigendianunicode": + return Encoding.BigEndianUnicode.GetBytes(data); + case "unicode": + return Encoding.Unicode.GetBytes(data); + case "utf32": + case "utf-32": + return Encoding.UTF32.GetBytes(data); + case "utf8": + case "utf-8": + return Encoding.UTF8.GetBytes(data); + default: + return Encoding.UTF8.GetBytes(data); + } + } + + public static string ToHex(byte[] raw) + { + if (raw == null) + { + return string.Empty; + } + + StringBuilder result = new StringBuilder(raw.Length * 2); + for (int i = 0; i < raw.Length; i++) + result.Append(raw[i].ToString("x2")); + return result.ToString(); + } + } +} \ No newline at end of file diff --git a/Darabonba/Utils/ConverterUtil.cs b/Darabonba/Utils/ConverterUtil.cs new file mode 100644 index 0000000..db3d0ea --- /dev/null +++ b/Darabonba/Utils/ConverterUtil.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; + +namespace Darabonba.Utils +{ + public class ConverterUtil + { + public static Dictionary Merge(Dictionary dic1, Dictionary dic2) + { + object[] objs = new object[] { dic1, dic2 }; + return Merge(objs); + } + + public static Dictionary Merge(params object[] objs) + { + Dictionary dicResult = new Dictionary(); + if (objs == null) + { + return dicResult; + } + + foreach (object obj in objs) + { + if (obj == null) + { + continue; + } + Dictionary dicObj = new Dictionary(); + Type typeObj = obj.GetType(); + if (typeof(DaraModel).IsAssignableFrom(typeObj)) + { + dicObj = ((DaraModel)obj).ToMap(); + } + else if (obj is Dictionary) + { + dicObj = (Dictionary)obj; + } + else if (obj is Dictionary) + { + Dictionary dicString = (Dictionary)obj; + foreach (var keypair in dicString) + { + dicObj.Add(keypair.Key, keypair.Value); + } + } + else + { + throw new ArgumentException(" inparams only support Dictionary or DaraModel. "); + } + + foreach (var keypair in dicObj) + { + T dicValue = (T)keypair.Value; + if (dicResult.ContainsKey(keypair.Key)) + { + dicResult[keypair.Key] = dicValue; + } + else + { + dicResult.Add(keypair.Key, dicValue); + } + } + } + return dicResult; + } + + public static string StrToLower(string str) + { + if (string.IsNullOrWhiteSpace(str)) + { + return string.Empty; + } + else + { + return str.ToLower(); + } + } + + public static int ParseInt(T data) + { + if (data == null) + { + return 0; // 返回 0 + } + + try + { + return (int)double.Parse(data.ToString()); + } + catch (FormatException) + { + return 0; + } + catch (InvalidCastException) + { + return 0; + } + catch (Exception) + { + return 0; + } + } + + public static long ParseLong(T data) + { + if (data == null) + { + return 0L; // 返回 0 + } + try + { + return (long)double.Parse(data.ToString()); + } + catch (FormatException) + { + return 0L; + } + catch (InvalidCastException) + { + return 0L; + } + catch (Exception) + { + return 0L; + } + + } + + public static float ParseFLoat(T data) + { + if (data == null) + { + return 0.0f; // 返回 0 + } + try + { + return (float)double.Parse(data.ToString()); + } + catch (FormatException) + { + return 0.0f; + } + catch (InvalidCastException) + { + return 0.0f; + } + catch (Exception) + { + return 0.0f; + } + + } + } +} diff --git a/Tea/Utils/DictUtils.cs b/Darabonba/Utils/DictUtils.cs similarity index 57% rename from Tea/Utils/DictUtils.cs rename to Darabonba/Utils/DictUtils.cs index 108dcc7..198f5f0 100644 --- a/Tea/Utils/DictUtils.cs +++ b/Darabonba/Utils/DictUtils.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Tea.Utils +namespace Darabonba.Utils { internal class DictUtils { @@ -21,5 +21,21 @@ internal static string GetDicValue(Dictionary dic, string keyNam } return null; } + + internal static bool Contains(List list, string value) + { + if (list == null) + { + return false; + } + foreach (var item in list) + { + if (value != null && item != null && item == value) + { + return true; + } + } + return false; + } } } diff --git a/Tea/Utils/Extensions.cs b/Darabonba/Utils/Extensions.cs similarity index 92% rename from Tea/Utils/Extensions.cs rename to Darabonba/Utils/Extensions.cs index 0ee6b9d..267f407 100644 --- a/Tea/Utils/Extensions.cs +++ b/Darabonba/Utils/Extensions.cs @@ -2,10 +2,15 @@ using System.Collections; using System.Collections.Generic; -namespace Tea.Utils +namespace Darabonba.Utils { public static class Extensions { + public static bool IsNull(this T data) + { + return data == null; + } + public static string ToSafeString(this object obj, string defaultStr = null) { if (obj == null) diff --git a/Darabonba/Utils/FormUtil.cs b/Darabonba/Utils/FormUtil.cs new file mode 100644 index 0000000..13bf2b7 --- /dev/null +++ b/Darabonba/Utils/FormUtil.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Web; +using Darabonba.Streams; + +namespace Darabonba.Utils +{ + public class FormUtil + { + public static string ToFormString(Dictionary map) + { + if (map == null || map.Count <= 0) + { + return ""; + } + StringBuilder result = new StringBuilder(); + bool first = true; + foreach (var entry in map) + { + if (entry.Value == null) + { + continue; + } + if (first) + { + first = false; + } + else + { + result.Append("&"); + } + result.Append(HttpUtility.UrlEncode(entry.Key, Encoding.UTF8)); + result.Append("="); + result.Append(HttpUtility.UrlEncode(entry.Value.ToSafeString(""), Encoding.UTF8)); + } + return result.ToString(); + } + + public static string GetBoundary() + { + long num = (long)Math.Floor((new Random()).NextDouble() * 100000000000000D); ; + return num.ToString(); + } + + public static Stream ToFileForm(Dictionary form, string boundary) + { + return new FileFormStream(form, boundary); + } + } +} \ No newline at end of file diff --git a/Tea/Utils/HttpClientUtils.cs b/Darabonba/Utils/HttpClientUtils.cs similarity index 97% rename from Tea/Utils/HttpClientUtils.cs rename to Darabonba/Utils/HttpClientUtils.cs index 136b10b..cf758cd 100644 --- a/Tea/Utils/HttpClientUtils.cs +++ b/Darabonba/Utils/HttpClientUtils.cs @@ -9,7 +9,7 @@ using System.Security.Cryptography.X509Certificates; using System.Text; -namespace Tea.Utils +namespace Darabonba.Utils { public static class HttpClientUtils { diff --git a/Tea/Utils/HttpUtils.cs b/Darabonba/Utils/HttpUtils.cs similarity index 97% rename from Tea/Utils/HttpUtils.cs rename to Darabonba/Utils/HttpUtils.cs index 1c0674c..5f3aa23 100644 --- a/Tea/Utils/HttpUtils.cs +++ b/Darabonba/Utils/HttpUtils.cs @@ -1,6 +1,6 @@ using System.Net.Http; -namespace Tea.Utils +namespace Darabonba.Utils { internal static class HttpUtils { diff --git a/Darabonba/Utils/JSONUtil.cs b/Darabonba/Utils/JSONUtil.cs new file mode 100644 index 0000000..9e0f9eb --- /dev/null +++ b/Darabonba/Utils/JSONUtil.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Darabonba.Utils +{ + public static class JSONUtil + { + public static string SerializeObject(object data) + { + if (data is string) + { + return data.ToString(); + } + return JsonConvert.SerializeObject(data); + } + + public static object Deserialize(object obj) + { + if (obj == null) + { + return null; + } + if (obj is JArray) + { + return DeserializeJArray((JArray) obj); + } + else if (obj is JObject) + { + return DeserializeJObject((JObject) obj); + } + else + { + return obj; + } + } + + private static Dictionary DeserializeJObject(JObject obj) + { + Dictionary dic = new Dictionary(); + Dictionary dicJObj = obj.ToObject>(); + foreach (var keypair in dicJObj) + { + dic.Add(keypair.Key, Deserialize(keypair.Value)); + } + return dic; + } + + private static List DeserializeJArray(JArray obj) + { + if (obj.Count == 0) + { + return new List(); + } + + if (obj[0].Type == JTokenType.Object) + { + List dicList = new List(); + List> dicObjList = obj.ToObject>>(); + foreach (Dictionary objItem in dicObjList) + { + Dictionary objDict = new Dictionary(); + foreach (var keypair in objItem) + { + objDict.Add(keypair.Key, Deserialize(keypair.Value)); + } + dicList.Add(objDict); + } + return dicList; + } + else if (obj[0].Type == JTokenType.Array) + { + List dicObjList = obj.ToObject>(); + List dicList = new List(); + foreach (var item in dicObjList) + { + dicList.Add(Deserialize((JArray) item)); + } + + return dicList; + } + else + { + List dicObjList = obj.ToObject>(); + return dicObjList; + } + } + } +} \ No newline at end of file diff --git a/Darabonba/Utils/ListUtil.cs b/Darabonba/Utils/ListUtil.cs new file mode 100644 index 0000000..78dfac2 --- /dev/null +++ b/Darabonba/Utils/ListUtil.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using Darabonba.Exceptions; + +namespace Darabonba.Utils +{ + public class ListUtil + { + public static T Shift(List list) + { + if (list == null || list.Count == 0) + { + // TODO return default 还是抛出错误 + throw new DaraException + { + Message = "array is empty" + }; + } + T first = list[0]; + list.RemoveAt(0); + return first; + } + + public static int Unshift(List array, T data) + { + array.Insert(0, data); + return array.Count; + } + + + public static int Push(List array, T data) + { + array.Add(data); + return array.Count; + } + + public static T Pop(List list) + { + if (list == null || list.Count == 0) + { + // TODO return default 还是抛出错误 + throw new DaraException + { + Message = "array is empty" + }; + } + T last = list[list.Count - 1]; + list.RemoveAt(list.Count - 1); + return last; + } + + public static List Sort(List array, string order) where T : IComparable + { + if (order == "asc") + { + array.Sort(); + } + else if (order == "desc") + { + array.Sort((x, y) => y.CompareTo(x)); + } + return array; + } + + public static List Concat(List array1, List array2) + { + array1.AddRange(array2); + return array1; + } + } +} \ No newline at end of file diff --git a/Darabonba/Utils/MathUtil.cs b/Darabonba/Utils/MathUtil.cs new file mode 100644 index 0000000..46f4aac --- /dev/null +++ b/Darabonba/Utils/MathUtil.cs @@ -0,0 +1,58 @@ +using System; +using Darabonba.Exceptions; + +namespace Darabonba.Utils +{ + public class MathUtil + { + public static int Floor(double? num) + { + return (int)Math.Floor(num.Value); + } + + public static int Round(double? num) + { + return (int)Math.Round(num.Value, MidpointRounding.AwayFromZero); + } + + public static int ParseInt(T data) + { + if (data == null) + { + throw new DaraException + { + Message = "data is null" + }; + } + return (int)double.Parse(data.ToString()); + } + + public static long ParseLong(T data) + { + if (data == null) + { + return 0; + } + return (long)double.Parse(data.ToString()); + } + + public static float ParseFloat(T data) + { + if (data == null) + { + return 0; + } + return (float)double.Parse(data.ToString()); + } + + public static T Min(T num1, T num2) where T : IComparable + { + return num1.CompareTo(num2) <= 0 ? num1 : num2; + } + + public static T Max(T num1, T num2) where T : IComparable + { + return num1.CompareTo(num2) <= 0 ? num2 : num1; + } + } +} \ No newline at end of file diff --git a/Darabonba/Utils/StringUtil.cs b/Darabonba/Utils/StringUtil.cs new file mode 100644 index 0000000..53e9cd0 --- /dev/null +++ b/Darabonba/Utils/StringUtil.cs @@ -0,0 +1,84 @@ +using System; +using System.Text.RegularExpressions; +using System.Globalization; +using Darabonba.Exceptions; + +namespace Darabonba.Utils +{ + public class StringUtil + { + public static string SubString(string str, int? start, int? end) + { + return str.Substring(start.Value, end.Value - start.Value); + } + + public static byte[] ToBytes(string data, string type) + { + return BytesUtil.From(data, type); + } + + // TODO 正常正则表达式是否要规定用户按照C#的正则格式写,C#的正则格式不是/xx/,而是@"xx",如 @"d \w+ \s" + public static string Replace(string data, string replacement, string pattern) + { + string regexPattern = @"\/(.*)\/([gi]*)$"; + Match match = Regex.Match(pattern, regexPattern); + if (match.Success) + { + string patternStr = match.Groups[1].Value; + string flags = match.Groups[2].Value; + if (flags == "g") + { + return Regex.Replace(data, patternStr, replacement, RegexOptions.None); + } + else if (flags == "gi") + { + return Regex.Replace(data, patternStr, replacement, RegexOptions.IgnoreCase); + } + else if (flags == "i") + { + Match matchFirst = Regex.Match(data, patternStr, RegexOptions.IgnoreCase); + if (matchFirst.Success) + { + return data.Remove(matchFirst.Index, matchFirst.Length).Insert(matchFirst.Index, replacement); + } + return data; + } + else if (flags == "") + { + Match matchFirst = Regex.Match(data, patternStr); + if (matchFirst.Success) + { + return data.Remove(matchFirst.Index, matchFirst.Length).Insert(matchFirst.Index, replacement); + } + return data; + } + } + try + { + return Regex.Replace(data, pattern, replacement, RegexOptions.None); + } + catch (Exception e) + { + throw new DaraException + { + Message = "Replace error occured: " + e.Message + }; + } + } + + public static int ParseInt(string data) + { + return (int)double.Parse(data, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.InvariantInfo); + } + + public static long ParseLong(string data) + { + return (long)double.Parse(data, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.InvariantInfo); + } + + public static float ParseFloat(string data) + { + return (float)double.Parse(data, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.InvariantInfo); + } + } +} \ No newline at end of file diff --git a/Darabonba/Utils/XmlUtil.cs b/Darabonba/Utils/XmlUtil.cs new file mode 100644 index 0000000..a50f231 --- /dev/null +++ b/Darabonba/Utils/XmlUtil.cs @@ -0,0 +1,362 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Xml; +using System.Xml.Linq; +using Darabonba; + +namespace Darabonba.Utils +{ + public class XmlUtil + { + public static Dictionary ParseXml(string body, Type response) + { + return DeserializeXml(body, response); + } + + public static string ToXML(Dictionary body) + { + return SerializeXml(body); + } + + internal static Dictionary DeserializeXml(string xmlStr, Type type) + { + Dictionary result = new Dictionary(); + XmlDocument contentXmlDoc = new XmlDocument(); + contentXmlDoc.LoadXml(xmlStr); + + XmlNodeList nodeList = contentXmlDoc.ChildNodes; + for (int i = 0; i < nodeList.Count; i++) + { + XmlNode root = nodeList.Item(i); + if (type != null) + { + PropertyInfo[] properties = type.GetProperties(); + foreach (PropertyInfo p in properties) + { + Type propertyType = p.PropertyType; + NameInMapAttribute attribute = p.GetCustomAttribute(typeof(NameInMapAttribute)) as NameInMapAttribute; + string realName = attribute == null ? p.Name : attribute.Name; + if (root.Name == realName) + { + if (!typeof(DaraModel).IsAssignableFrom(propertyType)) + { + result.Add(realName, root.InnerText); + } + else + { + result.Add(realName, GetDictFromXml(root, propertyType)); + } + } + } + } + else + { + ElementToDict(root, result); + } + } + if (result.ContainsKey("xml") && result["xml"].ToString().Contains("version=\"1.0\"")) + { + result.Remove("xml"); + } + return result; + } + + private static Dictionary GetDictFromXml(XmlNode element, Type type) + { + Dictionary nodeDict = new Dictionary(); + PropertyInfo[] properties = type.GetProperties(); + for (int i = 0; i < properties.Length; i++) + { + PropertyInfo p = properties[i]; + Type propertyType = p.PropertyType; + NameInMapAttribute attribute = p.GetCustomAttribute(typeof(NameInMapAttribute)) as NameInMapAttribute; + string realName = attribute == null ? p.Name : attribute.Name; + XmlNodeList node = element.SelectNodes(realName); + + if (node != null && node.Count > 0) + { + int count = (node[0].OuterXml.Length - node[0].OuterXml.Replace(realName, "").Length) / realName.Length; + if (count > 1) + { + if (typeof(IList).IsAssignableFrom(propertyType)) + { + Type innerPropertyType = propertyType.GetGenericArguments()[0]; + if (typeof(DaraModel).IsAssignableFrom(innerPropertyType)) + { + IList dicList = new List>(); + for (int j = 0; j < node.Count; j++) + { + dicList.Add(GetDictFromXml(node.Item(j), innerPropertyType)); + } + nodeDict.Add(realName, dicList); + } + else + { + var dicList = (IList)Activator.CreateInstance(propertyType); + for (int j = 0; j < node.Count; j++) + { + var value = mapObj(innerPropertyType, node.Item(j).InnerText); + dicList.Add(value); + } + nodeDict.Add(realName, dicList); + } + } + else if (typeof(DaraModel).IsAssignableFrom(propertyType)) + { + nodeDict.Add(realName, GetDictFromXml(node.Item(0), propertyType)); + } + else + { + string value = node.Item(0).InnerText; + nodeDict.Add(realName, mapObj(propertyType, value)); + } + } + } + else + { + nodeDict.Add(realName, null); + } + } + return nodeDict; + } + + private static object mapObj(Type propertyType, string value) + { + if (value == null) + { + return null; + } + else if (propertyType == typeof(int?)) + { + return Convert.ToInt32(value); + } + else if (propertyType == typeof(long?)) + { + return Convert.ToInt64(value); + } + else if (propertyType == typeof(float?)) + { + return Convert.ToSingle(value); + } + else if (propertyType == typeof(double?)) + { + return Convert.ToDouble(value); + } + else if (propertyType == typeof(bool?)) + { + return Convert.ToBoolean(value); + } + else if (propertyType == typeof(short?)) + { + return Convert.ToInt16(value); + } + else if (propertyType == typeof(ushort?)) + { + return Convert.ToUInt16(value); + } + else if (propertyType == typeof(uint?)) + { + return Convert.ToUInt32(value); + } + else if (propertyType == typeof(ulong?)) + { + return Convert.ToUInt64(value); + } + else + { + return Convert.ChangeType(value, propertyType); + } + } + + private static object ElementToDict(XmlNode element, Dictionary nodeDict) + { + XmlNodeList elements = element.ChildNodes; + if (elements.Count == 0 || (elements.Count == 1 && !elements[0].HasChildNodes)) + { + string context = string.IsNullOrEmpty(element.InnerText.Trim()) ? null : element.InnerText.Trim(); + if (nodeDict != null) + { + nodeDict.Add(element.Name, context); + } + return context; + } + else + { + Dictionary subDict = new Dictionary(); + if (nodeDict != null) + { + nodeDict.Add(element.Name, subDict); + } + foreach (XmlNode subNode in elements) + { + if (subDict.ContainsKey(subNode.Name)) + { + object o = subDict[subNode.Name]; + Type type = o.GetType(); + if (typeof(IList).IsAssignableFrom(type)) + { + ((IList)o).Add(ElementToDict(subNode, null)); + } + else if (typeof(IDictionary).IsAssignableFrom(type)) + { + List list = new List(); + Dictionary remove = (Dictionary)subDict[subNode.Name]; + subDict.Remove(subNode.Name); + list.Add(remove); + list.Add(ElementToDict(subNode, null)); + subDict.Add(subNode.Name, list); + } + else + { + List list = new List(); + list.Add(o); + subDict.Remove(subNode.Name); + list.Add(ElementToDict(subNode, null)); + subDict.Add(subNode.Name, list); + } + } + else + { + ElementToDict(subNode, subDict); + } + } + return subDict; + } + } + + internal static string SerializeXml(object obj) + { + Type type = obj.GetType(); + if (typeof(DaraModel).IsAssignableFrom(type)) + { + return SerializeXmlByModel((DaraModel)obj); + } + else if (obj is Dictionary) + { + return SerializeXmlByDict((Dictionary)obj); + } + else + { + return string.Empty; + } + } + + internal static string SerializeXmlByModel(DaraModel obj) + { + Type type = obj.GetType(); + PropertyInfo[] properties = type.GetProperties(); + if (obj == null || properties.Length == 0) + { + return string.Empty; + } + + PropertyInfo propertyInfo = properties[0]; + NameInMapAttribute attribute = propertyInfo.GetCustomAttribute(typeof(NameInMapAttribute)) as NameInMapAttribute; + string realName = attribute == null ? propertyInfo.Name : attribute.Name; + object rootObj = propertyInfo.GetValue(obj); + + XElement element = new XElement(realName); + GetXmlFactory(rootObj, element); + + return element.ToString(); + } + + private static string SerializeXmlByDict(Dictionary dict) + { + if (dict == null || dict.Count == 0) + { + return string.Empty; + } + + string nodeName = dict.Keys.ToList()[0]; + XElement element = new XElement(nodeName); + GetXmlFactory(dict[nodeName], element); + + return element.ToString(); + } + + private static void GetXmlFactory(object obj, XElement element, XElement xParent = null) + { + if (obj == null) + { + return; + } + Type type = obj.GetType(); + + if (typeof(IList).IsAssignableFrom(type)) + { + if (xParent == null) + { + throw new ArgumentException("unsupported nest list."); + } + IList list = (IList)obj; + string nodeName = element.Name.LocalName; + for (int j = 0; j < list.Count; j++) + { + XElement xNode = new XElement(nodeName); + GetXmlFactory(list[j], xNode); + xParent.Add(xNode); + } + return; + } + + if (typeof(DaraModel).IsAssignableFrom(type)) + { + GetXml((DaraModel)obj, element); + + } + else if (typeof(IDictionary).IsAssignableFrom(type)) + { + Dictionary newDict = CastDict((IDictionary)obj) + .ToDictionary(entry => (string)entry.Key, entry => entry.Value); + GetXml(newDict, element); + } + else + { + element.Add(obj); + } + + if (xParent != null) + { + xParent.Add(element); + } + + } + + private static IEnumerable CastDict(IDictionary dictionary) + { + foreach (DictionaryEntry entry in dictionary) + { + yield return entry; + } + } + + private static void GetXml(Dictionary dict, XElement element) + { + foreach (var keypair in dict) + { + XElement xNode = new XElement(keypair.Key); + GetXmlFactory(keypair.Value, xNode, element); + } + } + + private static void GetXml(DaraModel model, XElement element) + { + Type type = model.GetType(); + PropertyInfo[] properties = type.GetProperties(); + for (int i = 0; i < properties.Length; i++) + { + PropertyInfo propertyInfo = properties[i]; + Type property = propertyInfo.PropertyType; + NameInMapAttribute attribute = propertyInfo.GetCustomAttribute(typeof(NameInMapAttribute)) as NameInMapAttribute; + string realName = attribute == null ? propertyInfo.Name : attribute.Name; + XElement node = new XElement(realName); + GetXmlFactory(propertyInfo.GetValue(model), node, element); + } + } + + } +} diff --git a/Tea/TeaConverter.cs b/Tea/TeaConverter.cs deleted file mode 100644 index c895ea8..0000000 --- a/Tea/TeaConverter.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Tea -{ - public class TeaConverter - { - public static Dictionary merge(params object[] objs) - { - Dictionary dicResult = new Dictionary(); - if (objs == null) - { - return dicResult; - } - - foreach (object obj in objs) - { - if (obj == null) - { - continue; - } - Dictionary dicObj = new Dictionary(); - Type typeObj = obj.GetType(); - if (typeof(TeaModel).IsAssignableFrom(typeObj)) - { - dicObj = ((TeaModel) obj).ToMap(); - } - else if (obj is Dictionary) - { - dicObj = (Dictionary) obj; - } - else if (obj is Dictionary) - { - Dictionary dicString = (Dictionary) obj; - foreach (var keypair in dicString) - { - dicObj.Add(keypair.Key, keypair.Value); - } - } - else - { - throw new ArgumentException(" inparams only support Dictionary or TeaModel. "); - } - - foreach (var keypair in dicObj) - { - T dicValue = (T) keypair.Value; - if (dicResult.ContainsKey(keypair.Key)) - { - dicResult[keypair.Key] = dicValue; - } - else - { - dicResult.Add(keypair.Key, dicValue); - } - } - } - return dicResult; - } - - public static string StrToLower(string str) - { - if (string.IsNullOrWhiteSpace(str)) - { - return string.Empty; - } - else - { - return str.ToLower(); - } - } - } -} diff --git a/Tea/TeaException.cs b/Tea/TeaException.cs deleted file mode 100644 index 555999b..0000000 --- a/Tea/TeaException.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -using Tea.Utils; - -namespace Tea -{ - public class TeaException : Exception - { - private string code; - private string message; - private Dictionary data; - private int statusCode; - private string description; - private Dictionary accessDeniedDetail; - - public string Code - { - get - { - return code; - } - } - - public override string Message - { - get - { - return message; - } - } - - public Dictionary DataResult - { - get - { - return data; - } - } - - public int StatusCode - { - get - { - return statusCode; - } - } - - - public string Description - { - get - { - return description; - } - } - - public Dictionary AccessDeniedDetail - { - get - { - return accessDeniedDetail; - } - } - - public TeaException(IDictionary dict) - { - Dictionary dicObj = dict.Keys.Cast().ToDictionary(key => key, key => dict[key]); - code = DictUtils.GetDicValue(dicObj, "code").ToSafeString(); - message = DictUtils.GetDicValue(dicObj, "message").ToSafeString(); - description = DictUtils.GetDicValue(dicObj, "description").ToSafeString(); - object obj = DictUtils.GetDicValue(dicObj, "accessDeniedDetail"); - if (obj != null) - { - if (typeof(IDictionary).IsAssignableFrom(obj.GetType())) - { - IDictionary dicDetail = (IDictionary) obj; - accessDeniedDetail = dicDetail.Keys.Cast().ToDictionary(key => key, key => dicDetail[key]); - } - } - obj = DictUtils.GetDicValue(dicObj, "data"); - if (obj == null) - { - return; - } - if (typeof(IDictionary).IsAssignableFrom(obj.GetType())) - { - IDictionary dicData = (IDictionary) obj; - data = dicData.Keys.Cast().ToDictionary(key => key, key => dicData[key]); - if (DictUtils.GetDicValue(data, "statusCode") != null) - { - statusCode = int.Parse(DictUtils.GetDicValue(data, "statusCode").ToSafeString()); - } - return; - } - - Dictionary filedsDict = new Dictionary(); - Type type = obj.GetType(); - PropertyInfo[] properties = type.GetProperties(); - for (int i = 0; i < properties.Length; i++) - { - PropertyInfo p = properties[i]; - filedsDict.Add(p.Name, p.GetValue(obj)); - } - data = filedsDict; - } - } -} diff --git a/Tea/TeaRetryableException.cs b/Tea/TeaRetryableException.cs deleted file mode 100644 index 7fe3f3e..0000000 --- a/Tea/TeaRetryableException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Tea -{ - public class TeaRetryableException : Exception - { - public TeaRetryableException() - { - } - } -} diff --git a/Tea/TeaUnretryableException.cs b/Tea/TeaUnretryableException.cs deleted file mode 100644 index 0453fcf..0000000 --- a/Tea/TeaUnretryableException.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace Tea -{ - public class TeaUnretryableException : Exception - { - private readonly TeaRequest _lastRequest; - - public TeaRequest LastRequest { get { return _lastRequest; } } - - public TeaUnretryableException() : base() - { - - } - - public TeaUnretryableException(TeaRequest lastRequest, Exception innerException) : base(" Retry failed : " + (innerException == null ? "" : innerException.Message), innerException) - { - _lastRequest = lastRequest; - } - } -} diff --git a/TeaUnitTests/TeaConverterTest.cs b/TeaUnitTests/TeaConverterTest.cs deleted file mode 100644 index bc6462c..0000000 --- a/TeaUnitTests/TeaConverterTest.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; - -using Tea; - -using TeaUnitTests.Models; - -using Xunit; - -namespace TeaUnitTests -{ - public class TestObject - { - public string name { get; set; } - } - - public class TeaConverterTests - { - [Fact] - public void TestMerge() - { - Assert.Empty(TeaConverter.merge(null)); - - Dictionary dic = new Dictionary(); - Dictionary dicNull = null; - Dictionary dicMerge = new Dictionary(); - TestRegModel model = new TestRegModel(); - - dic.Add("testNull", null); - dic.Add("testExist", "testExist"); - dic.Add("test", "test"); - dicMerge.Add("testMerge", "testMerge"); - dicMerge.Add("testExist", "IsExist"); - Dictionary dicResult = TeaConverter.merge(dic, dicNull, dicMerge, null); - Assert.NotNull(dicResult); - Assert.Equal(4, dicResult.Count); - - Dictionary dicModelMerge = TeaConverter.merge(dic, dicNull, dicMerge, model); - Assert.NotNull(dicResult); - - Assert.Throws(() => { TeaConverter.merge(dic, 1); }); - } - - [Fact] - public void TestStrToLower() - { - Assert.Empty(TeaConverter.StrToLower(null)); - - Assert.Equal("test", TeaConverter.StrToLower("TEST")); - } - - } -} diff --git a/TeaUnitTests/TeaCoreTest.cs b/TeaUnitTests/TeaCoreTest.cs deleted file mode 100644 index 6f7d707..0000000 --- a/TeaUnitTests/TeaCoreTest.cs +++ /dev/null @@ -1,289 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; - -using Tea; - -using Xunit; - -namespace TeaUnitTests -{ - public class TeaCoreTest - { - [Fact] - public void TestComposeUrl() - { - TeaRequest teaRequest = new TeaRequest(); - - var url = TeaCore.ComposeUrl(teaRequest); - Assert.Equal("http://", url); - - teaRequest.Headers["host"] = "fake.domain.com"; - url = TeaCore.ComposeUrl(teaRequest); - Assert.Equal("http://fake.domain.com", url); - - teaRequest.Port = 8080; - url = TeaCore.ComposeUrl(teaRequest); - Assert.Equal("http://fake.domain.com:8080", url); - - teaRequest.Pathname = "/index.html"; - url = TeaCore.ComposeUrl(teaRequest); - Assert.Equal("http://fake.domain.com:8080/index.html", url); - - teaRequest.Query["foo"] = ""; - url = TeaCore.ComposeUrl(teaRequest); - Assert.Equal("http://fake.domain.com:8080/index.html?foo=", url); - - teaRequest.Query["foo"] = "bar"; - url = TeaCore.ComposeUrl(teaRequest); - Assert.Equal("http://fake.domain.com:8080/index.html?foo=bar", url); - - teaRequest.Pathname = "/index.html?a=b"; - url = TeaCore.ComposeUrl(teaRequest); - Assert.Equal("http://fake.domain.com:8080/index.html?a=b&foo=bar", url); - - teaRequest.Pathname = "/index.html?a=b&"; - url = TeaCore.ComposeUrl(teaRequest); - Assert.Equal("http://fake.domain.com:8080/index.html?a=b&foo=bar", url); - - teaRequest.Query["fake"] = null; - url = TeaCore.ComposeUrl(teaRequest); - Assert.Equal("http://fake.domain.com:8080/index.html?a=b&foo=bar", url); - - teaRequest.Query["fake"] = "val*"; - url = TeaCore.ComposeUrl(teaRequest); - Assert.Equal("http://fake.domain.com:8080/index.html?a=b&foo=bar&fake=val%2A", url); - } - - [Fact] - public void TestDoAction() - { - TeaRequest teaRequest = new TeaRequest(); - teaRequest.Protocol = "http"; - teaRequest.Method = "GET"; - teaRequest.Headers = new Dictionary(); - teaRequest.Headers["host"] = "www.alibabacloud.com"; - teaRequest.Pathname = "/s/zh"; - teaRequest.Query = new Dictionary(); - teaRequest.Query.Add("k", "ecs"); - Dictionary runtime = new Dictionary(); - runtime.Add("readTimeout", 7000); - runtime.Add("connectTimeout", 7000); - runtime.Add("httpsProxy", "http://www.alibabacloud.com/s/zh?k=ecs"); - runtime.Add("ignoreSSL", true); - - TeaResponse teaResponse = TeaCore.DoAction(teaRequest, runtime); - Assert.NotNull(teaResponse); - - teaRequest.Protocol = "https"; - teaResponse = TeaCore.DoAction(teaRequest); - Assert.NotNull(teaResponse); - - - string bodyStr = TeaCore.GetResponseBody(teaResponse); - Assert.NotNull(bodyStr); - - teaRequest.Method = "POST"; - teaRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes("test")); - teaRequest.Headers["content-test"] = "test"; - teaResponse = TeaCore.DoAction(teaRequest, runtime); - Assert.NotNull(teaResponse); - - TeaRequest teaRequest404 = new TeaRequest(); - teaRequest404.Protocol = "https"; - teaRequest404.Method = "GET"; - teaRequest404.Headers = new Dictionary(); - teaRequest404.Headers["host"] = "www.alibabacloud404.com"; - teaRequest404.Pathname = "/s/zh"; - teaRequest404.Query = new Dictionary(); - teaRequest404.Query.Add("k", "ecs"); - Dictionary runtime404 = new Dictionary(); - runtime404.Add("readTimeout", 7000); - runtime404.Add("connectTimeout", 7000); - Assert.Throws(() => { TeaCore.DoAction(teaRequest404, runtime404); }); - - TeaRequest teaRequestProxy = new TeaRequest(); - - - TeaRequest requestException = new TeaRequest - { - Protocol = "http", - Method = "GET", - Pathname = "/test" - }; - Dictionary runtimeException = new Dictionary(); - requestException.Headers["host"] = "www.aliyun.com"; - TeaResponse responseException = TeaCore.DoAction(requestException, runtimeException); - Assert.NotNull(responseException); - } - - [Fact] - public async Task TestDoActionAsync() - { - TeaRequest teaRequest = new TeaRequest(); - teaRequest.Protocol = "https"; - teaRequest.Method = "GET"; - teaRequest.Headers = new Dictionary(); - teaRequest.Headers["host"] = "www.alibabacloud.com"; - teaRequest.Pathname = "/s/zh"; - teaRequest.Query = new Dictionary(); - teaRequest.Query.Add("k", "ecs"); - - TeaResponse teaResponse = await TeaCore.DoActionAsync(teaRequest); - Assert.NotNull(teaResponse); - - Dictionary runtime = new Dictionary(); - runtime.Add("readTimeout", 4000); - runtime.Add("connectTimeout", 0); - - teaResponse = await TeaCore.DoActionAsync(teaRequest, runtime); - Assert.NotNull(teaResponse); - - string bodyStr = TeaCore.GetResponseBody(teaResponse); - Assert.NotNull(bodyStr); - - teaRequest.Method = "POST"; - teaRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes("test")); - teaResponse = await TeaCore.DoActionAsync(teaRequest, runtime); - Assert.NotNull(teaResponse); - - TeaRequest teaRequest404 = new TeaRequest(); - teaRequest404.Protocol = "https"; - teaRequest404.Method = "GET"; - teaRequest404.Headers = new Dictionary(); - teaRequest404.Headers["host"] = "www.alibabacloud404.com"; - teaRequest404.Pathname = "/s/zh"; - teaRequest404.Query = new Dictionary(); - teaRequest404.Query.Add("k", "ecs"); - Dictionary runtime404 = new Dictionary(); - runtime404.Add("readTimeout", 7000); - runtime404.Add("connectTimeout", 7000); - await Assert.ThrowsAsync(async() => { await TeaCore.DoActionAsync(teaRequest404, runtime); }); - - TeaRequest requestException = new TeaRequest - { - Protocol = "http", - Method = "GET", - Pathname = "/test" - }; - Dictionary runtimeException = new Dictionary(); - requestException.Headers["host"] = "www.aliyun.com"; - TeaResponse responseException = await TeaCore.DoActionAsync(requestException, runtimeException); - } - - [Fact] - public void TestConvertHeaders() - { - WebHeaderCollection headers = new WebHeaderCollection(); - headers.Add("testKey", "testValue"); - Dictionary dic = TeaCore.ConvertHeaders(headers); - Assert.NotNull(dic); - Assert.True(dic.ContainsKey("testkey")); - Assert.Equal("testValue", dic["testkey"]); - } - - [Fact] - public void TestAllowRetry() - { - long _now = System.DateTime.Now.Millisecond; - - Assert.True(TeaCore.AllowRetry(null, 0, _now)); - - Assert.False(TeaCore.AllowRetry(null, 3, _now)); - - Dictionary dic = new Dictionary(); - Assert.False(TeaCore.AllowRetry(dic, 3, _now)); - - dic.Add("retryable", true); - dic.Add("maxAttempts", null); - Assert.False(TeaCore.AllowRetry(dic, 3, _now)); - - dic["maxAttempts"] = 5; - Assert.True(TeaCore.AllowRetry(dic, 3, _now)); - - Dictionary dicInt = new Dictionary(); - Assert.False(TeaCore.AllowRetry(dicInt, 3, _now)); - } - - [Fact] - public void TestGetBackoffTime() - { - Dictionary dic = new Dictionary(); - Assert.Equal(0, TeaCore.GetBackoffTime(dic, 1)); - - dic.Add("policy", null); - Assert.Equal(0, TeaCore.GetBackoffTime(dic, 1)); - - dic["policy"] = string.Empty; - Assert.Equal(0, TeaCore.GetBackoffTime(dic, 1)); - - dic["policy"] = "no"; - Assert.Equal(0, TeaCore.GetBackoffTime(dic, 1)); - - dic["policy"] = "yes"; - Assert.Equal(0, TeaCore.GetBackoffTime(dic, 1)); - - dic.Add("period", null); - Assert.Equal(0, TeaCore.GetBackoffTime(dic, 1)); - - dic["period"] = -1; - Assert.Equal(1, TeaCore.GetBackoffTime(dic, 1)); - - dic["period"] = 1000; - Assert.Equal(1000, TeaCore.GetBackoffTime(dic, 1)); - } - - [Fact] - public void TestSleep() - { - TimeSpan tsBefore = new TimeSpan(DateTime.Now.Ticks); - TeaCore.Sleep(1000); - TimeSpan tsAfter = new TimeSpan(DateTime.Now.Ticks); - TimeSpan tsSubtract = tsBefore.Subtract(tsAfter).Duration(); - Assert.InRange(tsSubtract.TotalMilliseconds, 990, 1100); - } - - [Fact] - public async void TestSleepAsync() - { - TimeSpan tsBefore = new TimeSpan(DateTime.Now.Ticks); - await TeaCore.SleepAsync(1000); - TimeSpan tsAfter = new TimeSpan(DateTime.Now.Ticks); - TimeSpan tsSubtract = tsBefore.Subtract(tsAfter).Duration(); - Assert.InRange(tsSubtract.TotalMilliseconds, 990, 1000000); - } - - [Fact] - public void TestIsRetryable() - { - Exception ex = new Exception(); - Assert.False(TeaCore.IsRetryable(ex)); - - TeaRetryableException webEx = new TeaRetryableException(); - Assert.True(TeaCore.IsRetryable(webEx)); - } - - [Fact] - public void TestBytesReadable() - { - string str = "test"; - Stream stream = TeaCore.BytesReadable(str); - byte[] bytes = new byte[stream.Length]; - stream.Read(bytes, 0, bytes.Length); - string bytesStr = Encoding.UTF8.GetString(bytes); - Assert.Equal("test", bytesStr); - } - - [Fact] - public void Test_PercentEncode() - { - Assert.Null(TeaCore.PercentEncode(null)); - - Assert.Equal("test%3D", TeaCore.PercentEncode("test=")); - } - } -} diff --git a/TeaUnitTests/TeaRequestTest.cs b/TeaUnitTests/TeaRequestTest.cs deleted file mode 100644 index bee6cb6..0000000 --- a/TeaUnitTests/TeaRequestTest.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Tea; - -using Xunit; - -namespace TeaUnitTests -{ - public class TeaRequestTest - { - [Fact] - public void TestTeaRequest() - { - TeaRequest teaRequest = new TeaRequest(); - Assert.NotNull(teaRequest); - Assert.NotNull(teaRequest.Headers); - Assert.NotNull(teaRequest.Query); - Assert.Equal("http", teaRequest.Protocol); - teaRequest.Headers = null; - Assert.NotNull(teaRequest.Headers); - Assert.Equal("GET", teaRequest.Method); - - teaRequest.Method = "POST"; - Assert.Equal("POST", teaRequest.Method); - - teaRequest.Query = null; - Assert.NotNull(teaRequest.Query); - } - } -} diff --git a/TeaUnitTests/TeaResponseTest.cs b/TeaUnitTests/TeaResponseTest.cs deleted file mode 100644 index 6cf57a5..0000000 --- a/TeaUnitTests/TeaResponseTest.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.IO; -using System.Net; -using System.Net.Http; -using System.Text; - -using Tea; - -using Xunit; - -namespace TeaUnitTests -{ - public class TeaResponseTest - { - [Fact] - public void TestTeaResponse() - { - HttpResponseMessage httpResponseMessage = new HttpResponseMessage(); - httpResponseMessage.StatusCode = HttpStatusCode.OK; - httpResponseMessage.Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("test"))); - TeaResponse response = new TeaResponse(httpResponseMessage); - Assert.NotNull(response); - Assert.Equal(200, response.StatusCode); - Assert.Equal("", response.StatusMessage); - - TeaResponse teaResponseNull = new TeaResponse(null); - Assert.Null(teaResponseNull.Body); - } - } -} diff --git a/TeaUnitTests/TeaRetryableExceptionTest.cs b/TeaUnitTests/TeaRetryableExceptionTest.cs deleted file mode 100644 index 538feba..0000000 --- a/TeaUnitTests/TeaRetryableExceptionTest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.IO; -using System.Net; -using System.Net.Http; -using System.Text; - -using Tea; - -using Xunit; - -namespace TeaUnitTests -{ - public class TeaRetryableExceptionTest - { - [Fact] - public void TestTeaRetryableException() - { - HttpResponseMessage httpResponseMessage = new HttpResponseMessage(); - httpResponseMessage.StatusCode = HttpStatusCode.OK; - httpResponseMessage.Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("test"))); - TeaResponse response = new TeaResponse(httpResponseMessage); - TeaRetryableException teaRetryableException = new TeaRetryableException(); - Assert.NotNull(teaRetryableException); - } - } -} diff --git a/TeaUnitTests/TeaValidatorTest.cs b/TeaUnitTests/TeaValidatorTest.cs deleted file mode 100644 index 7306c38..0000000 --- a/TeaUnitTests/TeaValidatorTest.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using Tea; - -using Xunit; - -namespace TeaUnitTests -{ - public class TeaValidatorTest - { - [Fact] - public void TestTeaValidator() - { - TeaValidator teaValidator = new TeaValidator(null, "propertyName"); - teaValidator.ValidateRequired("test"); - teaValidator.ValidateRegex("test"); - Assert.NotNull(teaValidator); - - ValidationAttribute attribute = new ValidationAttribute(); - attribute.Required = false; - teaValidator.Attribute = attribute; - teaValidator.ValidateRequired("test"); - Assert.NotNull(teaValidator); - - attribute.Pattern = ""; - teaValidator.ValidateRegex("test"); - Assert.NotNull(teaValidator); - - attribute.Pattern = "pattern"; - teaValidator.ValidateRegex(null); - Assert.NotNull(teaValidator); - - teaValidator.ValidateRegex("patternTest"); - Assert.NotNull(teaValidator); - - Assert.Equal("propertyName is not match pattern", - Assert.Throws(() => { teaValidator.ValidateRegex("test"); }).Message - ); - - attribute.Required = true; - Assert.Equal("propertyName is required.", - Assert.Throws(() => { teaValidator.ValidateRequired(null); }).Message - ); - - attribute.MaxLength = 3; - teaValidator.ValidateMaxLength("阿里"); - Assert.Equal("propertyName is exceed max-length: 3", - Assert.Throws(() => { teaValidator.ValidateMaxLength("阿里test"); }).Message - ); - - List list = new List{ "1", "2","3","4" }; - teaValidator.ValidateMaxLength("阿里"); - Assert.Equal("propertyName is exceed max-length: 3", - Assert.Throws(() => { teaValidator.ValidateMaxLength(list); }).Message - ); - - attribute.MinLength = 2; - teaValidator.ValidateMinLength("阿里"); - Assert.Equal("propertyName is less than min-length: 2", - Assert.Throws(() => { teaValidator.ValidateMinLength("阿"); }).Message - ); - - attribute.Maximun = 1.5; - teaValidator.ValidateMaximum("1"); - Assert.Equal("propertyName is exceed maximum: 1.5", - Assert.Throws(() => { teaValidator.ValidateMaximum(2); }).Message - ); - - attribute.Minimum = 1; - teaValidator.ValidateMinimum(1.5); - Assert.Equal("propertyName is less than Minimum: 1", - Assert.Throws(() => { teaValidator.ValidateMinimum(-2); }).Message - ); - } - } -} diff --git a/appveyor.yml b/appveyor.yml index 7c69097..33b6389 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,7 +16,7 @@ build_script: - cmd: dotnet build test_script: # Dotnet Test - - dotnet test TeaUnitTests/ /p:AltCover=true + - dotnet test DaraUnitTests/ /p:AltCover=true on_finish: - ps: | $env:PATH = 'C:\msys64\usr\bin;' + $env:PATH diff --git a/codecov.yml b/codecov.yml index f596fa0..1b183bf 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,2 @@ ignore: - - "TeaUnitTests/" # TeaUnitTests + - "DaraUnitTests/" # DaraUnitTests diff --git a/tea-csharp.sln b/tea-csharp.sln index 637f9ff..83104d5 100644 --- a/tea-csharp.sln +++ b/tea-csharp.sln @@ -2,9 +2,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29306.81 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "tea", "Tea\tea.csproj", "{BA7831AD-0335-437E-8F8F-EDB62B874D22}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Darabonba", "Darabonba\Darabonba.csproj", "{BA7831AD-0335-437E-8F8F-EDB62B874D22}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeaUnitTests", "TeaUnitTests\TeaUnitTests.csproj", "{9BC6DAB8-9222-4856-AE71-05E0AB1B8B61}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DaraUnitTests", "DaraUnitTests\DaraUnitTests.csproj", "{9BC6DAB8-9222-4856-AE71-05E0AB1B8B61}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution