Skip to content

Commit 026d08f

Browse files
feat: ODP REST API for Sending ODP Events (#315)
* WIP REST API Manager * Add docs * Fix GraphQL tests * WIP Fixing unit tests... * Completed unit tests * Clean up usings; add missing Copyright notices * Add 'QL' to DotSettings; Disable Enum InconsistentNaming * Code review changes * Code review changes * Copyright notice & static * Code review changes * More Pull Request code changes
1 parent 678c6fd commit 026d08f

17 files changed

+871
-611
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2022 Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
using Moq;
18+
using Moq.Protected;
19+
using System;
20+
using System.Net;
21+
using System.Net.Http;
22+
using System.Threading;
23+
using System.Threading.Tasks;
24+
25+
namespace OptimizelySDK.Tests.OdpTests
26+
{
27+
/// <summary>
28+
/// Shared utility methods used for stubbing HttpClient instances
29+
/// </summary>
30+
public static class HttpClientTestUtil
31+
{
32+
/// <summary>
33+
/// Create an HttpClient instance that returns an expected response
34+
/// </summary>
35+
/// <param name="statusCode">Http Status Code to return</param>
36+
/// <param name="content">Message body content to return</param>
37+
/// <returns>HttpClient instance that will return desired HttpResponseMessage</returns>
38+
public static HttpClient MakeHttpClient(HttpStatusCode statusCode,
39+
string content = default
40+
)
41+
{
42+
var response = new HttpResponseMessage(statusCode);
43+
44+
if (!string.IsNullOrWhiteSpace(content))
45+
{
46+
response.Content = new StringContent(content);
47+
}
48+
49+
var mockedHandler = new Mock<HttpMessageHandler>();
50+
mockedHandler.Protected().Setup<Task<HttpResponseMessage>>(
51+
"SendAsync",
52+
ItExpr.IsAny<HttpRequestMessage>(),
53+
ItExpr.IsAny<CancellationToken>()).
54+
ReturnsAsync(response);
55+
return new HttpClient(mockedHandler.Object);
56+
}
57+
58+
/// <summary>
59+
/// Create an HttpClient instance that will timeout for SendAsync calls
60+
/// </summary>
61+
/// <returns>HttpClient instance that throw TimeoutException</returns>
62+
public static HttpClient MakeHttpClientWithTimeout()
63+
{
64+
var mockedHandler = new Mock<HttpMessageHandler>();
65+
mockedHandler.Protected().Setup<Task<HttpResponseMessage>>(
66+
"SendAsync",
67+
ItExpr.IsAny<HttpRequestMessage>(),
68+
ItExpr.IsAny<CancellationToken>()).
69+
Throws<TimeoutException>();
70+
return new HttpClient(mockedHandler.Object);
71+
}
72+
}
73+
}

OptimizelySDK.Tests/OdpTests/LruCacheTest.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
using OptimizelySDK.Odp;
1919
using System;
2020
using System.Collections.Generic;
21-
using System.Linq;
2221
using System.Threading;
2322

2423
namespace OptimizelySDK.Tests.OdpTests
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright 2022 Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
using Moq;
18+
using NUnit.Framework;
19+
using OptimizelySDK.ErrorHandler;
20+
using OptimizelySDK.Logger;
21+
using OptimizelySDK.Odp;
22+
using OptimizelySDK.Odp.Entity;
23+
using System.Collections.Generic;
24+
using System.Net;
25+
26+
namespace OptimizelySDK.Tests.OdpTests
27+
{
28+
[TestFixture]
29+
public class OdpEventApiManagerTest
30+
{
31+
private const string VALID_ODP_PUBLIC_KEY = "a-valid-odp-public-key";
32+
private const string ODP_REST_API_HOST = "https://api.example.com";
33+
34+
private readonly List<OdpEvent> _odpEvents = new List<OdpEvent>();
35+
36+
private Mock<IErrorHandler> _mockErrorHandler;
37+
private Mock<ILogger> _mockLogger;
38+
39+
[TestFixtureSetUp]
40+
public void Setup()
41+
{
42+
_mockErrorHandler = new Mock<IErrorHandler>();
43+
_mockLogger = new Mock<ILogger>();
44+
_mockLogger.Setup(i => i.Log(It.IsAny<LogLevel>(), It.IsAny<string>()));
45+
46+
_odpEvents.Add(new OdpEvent("t1", "a1",
47+
new Dictionary<string, string>
48+
{
49+
{
50+
"id-key-1", "id-value-1"
51+
},
52+
},
53+
new Dictionary<string, object>
54+
{
55+
{
56+
"key11", "value-1"
57+
},
58+
{
59+
"key12", true
60+
},
61+
{
62+
"key13", 3.5
63+
},
64+
{
65+
"key14", null
66+
},
67+
}
68+
));
69+
_odpEvents.Add(new OdpEvent("t2", "a2",
70+
new Dictionary<string, string>
71+
{
72+
{
73+
"id-key-2", "id-value-2"
74+
},
75+
}, new Dictionary<string, object>
76+
{
77+
{
78+
"key2", "value-2"
79+
},
80+
}
81+
));
82+
}
83+
84+
[Test]
85+
public void ShouldSendEventsSuccessfullyAndNotSuggestRetry()
86+
{
87+
var httpClient = HttpClientTestUtil.MakeHttpClient(HttpStatusCode.OK);
88+
var manger =
89+
new OdpEventApiManager(_mockLogger.Object, _mockErrorHandler.Object, httpClient);
90+
91+
var shouldRetry = manger.SendEvents(VALID_ODP_PUBLIC_KEY, ODP_REST_API_HOST,
92+
_odpEvents);
93+
94+
Assert.IsFalse(shouldRetry);
95+
}
96+
97+
[Test]
98+
public void ShouldNotSuggestRetryFor400HttpResponse()
99+
{
100+
var httpClient = HttpClientTestUtil.MakeHttpClient(HttpStatusCode.BadRequest);
101+
var manger =
102+
new OdpEventApiManager(_mockLogger.Object, _mockErrorHandler.Object, httpClient);
103+
104+
var shouldRetry = manger.SendEvents(VALID_ODP_PUBLIC_KEY, ODP_REST_API_HOST,
105+
_odpEvents);
106+
107+
Assert.IsFalse(shouldRetry);
108+
}
109+
110+
[Test]
111+
public void ShouldSuggestRetryFor500HttpResponse()
112+
{
113+
var httpClient = HttpClientTestUtil.MakeHttpClient(HttpStatusCode.InternalServerError);
114+
var manger =
115+
new OdpEventApiManager(_mockLogger.Object, _mockErrorHandler.Object, httpClient);
116+
117+
var shouldRetry = manger.SendEvents(VALID_ODP_PUBLIC_KEY, ODP_REST_API_HOST,
118+
_odpEvents);
119+
120+
Assert.IsTrue(shouldRetry);
121+
}
122+
123+
[Test]
124+
public void ShouldSuggestRetryForNetworkTimeout() {
125+
var httpClient = HttpClientTestUtil.MakeHttpClientWithTimeout();
126+
var manger =
127+
new OdpEventApiManager(_mockLogger.Object, _mockErrorHandler.Object, httpClient);
128+
129+
var shouldRetry = manger.SendEvents(VALID_ODP_PUBLIC_KEY, ODP_REST_API_HOST,
130+
_odpEvents);
131+
132+
Assert.IsTrue(shouldRetry);}
133+
}
134+
}

0 commit comments

Comments
 (0)