Skip to content

Commit

Permalink
[FSSDK-9538] bug: Fix last modified formatting (#361)
Browse files Browse the repository at this point in the history
* Fix non-RFC1123 formatting

* Update tests to ensure expected format

* Add more accurate assert

* Lint fixes

(cherry picked from commit 962bd68)
  • Loading branch information
mikechu-optimizely committed Jul 25, 2023
1 parent 97b240a commit 378525d
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 45 deletions.
89 changes: 47 additions & 42 deletions OptimizelySDK.Tests/ConfigTest/HttpProjectConfigManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,17 @@
* limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
using OptimizelySDK.Config;
using OptimizelySDK.Logger;
using OptimizelySDK.Tests.NotificationTests;
using OptimizelySDK.Tests.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace OptimizelySDK.Tests.DatafileManagement_Tests
{
Expand All @@ -39,6 +37,11 @@ public class HttpProjectConfigManagerTest
private Mock<TestNotificationCallbacks> NotificationCallbackMock =
new Mock<TestNotificationCallbacks>();

private const string ExpectedRfc1123DateTime = "Thu, 03 Nov 2022 16:00:00 GMT";

private readonly DateTime _pastLastModified =
new DateTimeOffset(new DateTime(2022, 11, 3, 16, 0, 0, DateTimeKind.Utc)).UtcDateTime;

[SetUp]
public void Setup()
{
Expand All @@ -54,7 +57,7 @@ public void Setup()
public void TestHttpConfigManagerRetrieveProjectConfigByURL()
{
var t = MockSendAsync(TestData.Datafile);
HttpProjectConfigManager httpManager = new HttpProjectConfigManager.Builder()
var httpManager = new HttpProjectConfigManager.Builder()
.WithUrl("https://cdn.optimizely.com/datafiles/QBw9gFM8oTn7ogY9ANCC1z.json")
.WithLogger(LoggerMock.Object)
.WithPollingInterval(TimeSpan.FromMilliseconds(1000))
Expand All @@ -67,7 +70,7 @@ public void TestHttpConfigManagerRetrieveProjectConfigByURL()
t.Wait(1000);

HttpClientMock.Verify(_ => _.SendAsync(
It.Is<System.Net.Http.HttpRequestMessage>(requestMessage =>
It.Is<HttpRequestMessage>(requestMessage =>
requestMessage.RequestUri.ToString() ==
"https://cdn.optimizely.com/datafiles/QBw9gFM8oTn7ogY9ANCC1z.json"
)));
Expand All @@ -77,9 +80,9 @@ public void TestHttpConfigManagerRetrieveProjectConfigByURL()
[Test]
public void TestHttpConfigManagerWithInvalidStatus()
{
var t = MockSendAsync(statusCode: HttpStatusCode.Forbidden);
MockSendAsync(statusCode: HttpStatusCode.Forbidden);

HttpProjectConfigManager httpManager = new HttpProjectConfigManager.Builder()
var httpManager = new HttpProjectConfigManager.Builder()
.WithUrl("https://cdn.optimizely.com/datafiles/QBw9gFM8oTn7ogY9ANCC1z.json")
.WithLogger(LoggerMock.Object)
.WithPollingInterval(TimeSpan.FromMilliseconds(1000))
Expand All @@ -102,24 +105,29 @@ public void TestSettingIfModifiedSinceInRequestHeader()
statusCode: HttpStatusCode.NotModified,
responseContentHeaders: new Dictionary<string, string>
{
{
"Last-Modified", new DateTime(2050, 10, 10).ToString("R")
},
{ "Last-Modified", _pastLastModified.ToString("r") },
}
);

var httpManager = new HttpProjectConfigManager.Builder()
.WithDatafile(string.Empty)
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
.WithLogger(LoggerMock.Object)
.WithPollingInterval(TimeSpan.FromMilliseconds(1000))
.WithBlockingTimeoutPeriod(TimeSpan.FromMilliseconds(2000))
.WithStartByDefault()
.Build(defer: true);
httpManager.LastModifiedSince = new DateTime(2020, 4, 4).ToString("R");
httpManager.LastModifiedSince = _pastLastModified.ToString("r");
t.Wait(3000);

HttpClientMock.Verify(_ => _.SendAsync(
It.Is<HttpRequestMessage>(requestMessage =>
requestMessage.Headers.IfModifiedSince.HasValue &&
requestMessage.Headers.IfModifiedSince.Value.UtcDateTime.ToString("r") ==
ExpectedRfc1123DateTime
)), Times.Once);
LoggerMock.Verify(
_ => _.Log(LogLevel.DEBUG, "Set If-Modified-Since in request header."),
_ => _.Log(LogLevel.DEBUG,
$"Set If-Modified-Since in request header: {ExpectedRfc1123DateTime}"),
Times.AtLeastOnce);

httpManager.Dispose();
Expand All @@ -129,24 +137,24 @@ public void TestSettingIfModifiedSinceInRequestHeader()
public void TestSettingLastModifiedFromResponseHeader()
{
MockSendAsync(
datafile: TestData.Datafile,
statusCode: HttpStatusCode.OK,
responseContentHeaders: new Dictionary<string, string>
{
{
"Last-Modified", new DateTime(2050, 10, 10).ToString("R")
},
{ "Last-Modified", _pastLastModified.ToString("r") },
}
);
var httpManager = new HttpProjectConfigManager.Builder()
.WithUrl("https://cdn.optimizely.com/datafiles/QBw9gFM8oTn7ogY9ANCC1z.json")
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
.WithLogger(LoggerMock.Object)
.WithPollingInterval(TimeSpan.FromMilliseconds(1000))
.WithBlockingTimeoutPeriod(TimeSpan.FromMilliseconds(500))
.WithStartByDefault()
.Build();

LoggerMock.Verify(
_ => _.Log(LogLevel.DEBUG, "Set LastModifiedSince from response header."),
_ => _.Log(LogLevel.DEBUG,
$"Set LastModifiedSince from response header: {ExpectedRfc1123DateTime}"),
Times.AtLeastOnce);

httpManager.Dispose();
Expand All @@ -157,16 +165,15 @@ public void TestHttpClientHandler()
{
var httpConfigHandler = HttpProjectConfigManager.HttpClient.GetHttpClientHandler();
Assert.IsTrue(httpConfigHandler.AutomaticDecompression ==
(System.Net.DecompressionMethods.Deflate |
System.Net.DecompressionMethods.GZip));
(DecompressionMethods.Deflate | DecompressionMethods.GZip));
}

[Test]
public void TestHttpConfigManagerRetrieveProjectConfigGivenEmptyFormatUseDefaultFormat()
{
var t = MockSendAsync(TestData.Datafile);

HttpProjectConfigManager httpManager = new HttpProjectConfigManager.Builder()
var httpManager = new HttpProjectConfigManager.Builder()
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
.WithFormat("")
.WithLogger(LoggerMock.Object)
Expand All @@ -179,7 +186,7 @@ public void TestHttpConfigManagerRetrieveProjectConfigGivenEmptyFormatUseDefault
// Time is given here to avoid hanging-up in any worst case.
t.Wait(1000);
HttpClientMock.Verify(_ => _.SendAsync(
It.Is<System.Net.Http.HttpRequestMessage>(requestMessage =>
It.Is<HttpRequestMessage>(requestMessage =>
requestMessage.RequestUri.ToString() ==
"https://cdn.optimizely.com/datafiles/QBw9gFM8oTn7ogY9ANCC1z.json"
)));
Expand All @@ -191,7 +198,7 @@ public void TestHttpConfigManagerRetrieveProjectConfigBySDKKey()
{
var t = MockSendAsync(TestData.Datafile);

HttpProjectConfigManager httpManager = new HttpProjectConfigManager.Builder()
var httpManager = new HttpProjectConfigManager.Builder()
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
.WithLogger(LoggerMock.Object)
.WithPollingInterval(TimeSpan.FromMilliseconds(1000))
Expand All @@ -201,7 +208,7 @@ public void TestHttpConfigManagerRetrieveProjectConfigBySDKKey()

t.Wait(1000);
HttpClientMock.Verify(_ => _.SendAsync(
It.Is<System.Net.Http.HttpRequestMessage>(requestMessage =>
It.Is<HttpRequestMessage>(requestMessage =>
requestMessage.RequestUri.ToString() ==
"https://cdn.optimizely.com/datafiles/QBw9gFM8oTn7ogY9ANCC1z.json"
)));
Expand All @@ -214,8 +221,7 @@ public void TestHttpConfigManagerRetrieveProjectConfigByFormat()
{
var t = MockSendAsync(TestData.Datafile);

HttpProjectConfigManager httpManager = new HttpProjectConfigManager.Builder()
.WithSdkKey("10192104166")
var httpManager = new HttpProjectConfigManager.Builder().WithSdkKey("10192104166")
.WithFormat("https://cdn.optimizely.com/json/{0}.json")
.WithLogger(LoggerMock.Object)
.WithPollingInterval(TimeSpan.FromMilliseconds(1000))
Expand All @@ -225,7 +231,7 @@ public void TestHttpConfigManagerRetrieveProjectConfigByFormat()

t.Wait(1000);
HttpClientMock.Verify(_ => _.SendAsync(
It.Is<System.Net.Http.HttpRequestMessage>(requestMessage =>
It.Is<HttpRequestMessage>(requestMessage =>
requestMessage.RequestUri.ToString() ==
"https://cdn.optimizely.com/json/10192104166.json"
)));
Expand All @@ -242,8 +248,7 @@ public void TestHttpProjectConfigManagerDoesntRaiseExceptionForDefaultErrorHandl
{
var t = MockSendAsync(TestData.Datafile);

HttpProjectConfigManager httpManager = new HttpProjectConfigManager.Builder()
.WithSdkKey("10192104166")
var httpManager = new HttpProjectConfigManager.Builder().WithSdkKey("10192104166")
.WithFormat("https://cdn.optimizely.com/json/{0}.json")
.WithLogger(LoggerMock.Object)
.WithPollingInterval(TimeSpan.FromMilliseconds(1000))
Expand All @@ -253,7 +258,7 @@ public void TestHttpProjectConfigManagerDoesntRaiseExceptionForDefaultErrorHandl

t.Wait(1000);
HttpClientMock.Verify(_ => _.SendAsync(
It.Is<System.Net.Http.HttpRequestMessage>(requestMessage =>
It.Is<HttpRequestMessage>(requestMessage =>
requestMessage.RequestUri.ToString() ==
"https://cdn.optimizely.com/json/10192104166.json"
)));
Expand All @@ -272,7 +277,7 @@ public void TestOnReadyPromiseResolvedImmediatelyWhenDatafileIsProvided()
var t = MockSendAsync(TestData.SimpleABExperimentsDatafile,
TimeSpan.FromMilliseconds(100));

HttpProjectConfigManager httpManager = new HttpProjectConfigManager.Builder()
var httpManager = new HttpProjectConfigManager.Builder()
// Revision - 15
.WithSdkKey("10192104166")
.WithDatafile(TestData.Datafile)
Expand Down Expand Up @@ -302,7 +307,7 @@ public void TestOnReadyPromiseWaitsForProjectConfigRetrievalWhenDatafileIsNotPro
var t = MockSendAsync(TestData.SimpleABExperimentsDatafile,
TimeSpan.FromMilliseconds(1000));

HttpProjectConfigManager httpManager = new HttpProjectConfigManager.Builder()
var httpManager = new HttpProjectConfigManager.Builder()
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
.WithLogger(LoggerMock.Object)
.WithPollingInterval(TimeSpan.FromSeconds(2))
Expand Down Expand Up @@ -350,7 +355,7 @@ public void TestHttpConfigManagerDoesNotWaitForTheConfigWhenDeferIsTrue()
[Test]
public void TestHttpConfigManagerSendConfigUpdateNotificationWhenProjectConfigGetsUpdated()
{
var t = MockSendAsync(TestData.Datafile);
MockSendAsync(TestData.Datafile);

var httpManager = new HttpProjectConfigManager.Builder()
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
Expand All @@ -373,7 +378,7 @@ public void TestHttpConfigManagerSendConfigUpdateNotificationWhenProjectConfigGe
[Test]
public void TestHttpConfigManagerDoesNotSendConfigUpdateNotificationWhenDatafileIsProvided()
{
var t = MockSendAsync(TestData.Datafile, TimeSpan.FromMilliseconds(100));
MockSendAsync(TestData.Datafile, TimeSpan.FromMilliseconds(100));

var httpManager = new HttpProjectConfigManager.Builder()
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
Expand Down Expand Up @@ -533,7 +538,7 @@ public void TestAuthUrlWhenTokenProvided()
t.Wait(2000);

HttpClientMock.Verify(_ => _.SendAsync(
It.Is<System.Net.Http.HttpRequestMessage>(requestMessage =>
It.Is<HttpRequestMessage>(requestMessage =>
requestMessage.RequestUri.ToString() ==
"https://config.optimizely.com/datafiles/auth/QBw9gFM8oTn7ogY9ANCC1z.json"
)));
Expand All @@ -554,7 +559,7 @@ public void TestDefaultUrlWhenTokenNotProvided()
// it's to wait if SendAsync is not triggered.
t.Wait(2000);
HttpClientMock.Verify(_ => _.SendAsync(
It.Is<System.Net.Http.HttpRequestMessage>(requestMessage =>
It.Is<HttpRequestMessage>(requestMessage =>
requestMessage.RequestUri.ToString() ==
"https://cdn.optimizely.com/datafiles/QBw9gFM8oTn7ogY9ANCC1z.json"
)));
Expand All @@ -577,7 +582,7 @@ public void TestAuthenticationHeaderWhenTokenProvided()
t.Wait(2000);

HttpClientMock.Verify(_ => _.SendAsync(
It.Is<System.Net.Http.HttpRequestMessage>(requestMessage =>
It.Is<HttpRequestMessage>(requestMessage =>
requestMessage.Headers.Authorization.ToString() == "Bearer datafile1"
)));
httpManager.Dispose();
Expand All @@ -597,7 +602,7 @@ public void TestFormatUrlHigherPriorityThanDefaultUrl()
// it's to wait if SendAsync is not triggered.
t.Wait(2000);
HttpClientMock.Verify(_ => _.SendAsync(
It.Is<System.Net.Http.HttpRequestMessage>(requestMessage =>
It.Is<HttpRequestMessage>(requestMessage =>
requestMessage.RequestUri.ToString() ==
"http://customformat/QBw9gFM8oTn7ogY9ANCC1z.json"
)));
Expand Down
8 changes: 5 additions & 3 deletions OptimizelySDK/Config/HttpProjectConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ private string GetRemoteDatafileResponse()
if (!string.IsNullOrEmpty(LastModifiedSince))
{
request.Headers.Add("If-Modified-Since", LastModifiedSince);
Logger.Log(LogLevel.DEBUG, $"Set If-Modified-Since in request header.");
Logger.Log(LogLevel.DEBUG,
$"Set If-Modified-Since in request header: {LastModifiedSince}");
}

if (!string.IsNullOrEmpty(DatafileAccessToken))
Expand Down Expand Up @@ -138,8 +139,9 @@ private string GetRemoteDatafileResponse()
// Update Last-Modified header if provided.
if (result.Content.Headers.LastModified.HasValue)
{
LastModifiedSince = result.Content.Headers.LastModified.ToString();
Logger.Log(LogLevel.DEBUG, $"Set LastModifiedSince from response header.");
LastModifiedSince = result.Content.Headers.LastModified?.UtcDateTime.ToString("r");
Logger.Log(LogLevel.DEBUG,
$"Set LastModifiedSince from response header: {LastModifiedSince}");
}

var content = result.Content.ReadAsStringAsync();
Expand Down

0 comments on commit 378525d

Please sign in to comment.