Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
15e6946
begin the refactor. need to update wherever we touch sanitizers to en…
scbedd Apr 12, 2024
1f34c08
just save away the progress so that this can be pulled over to anothe…
scbedd Apr 17, 2024
13d6486
making a sanitizer store its id so that we have a consistent value to…
scbedd Apr 17, 2024
b861e01
getting closer. Need to rationalize registration + removal
scbedd Apr 17, 2024
e2304aa
first passing build. now time to discover all the test failures. gon …
scbedd Apr 18, 2024
45332b9
gotta initialize the Sanitizers concurrentDictionary!
scbedd Apr 18, 2024
c9bde0c
all but 3 tests passing
scbedd Apr 18, 2024
2ece462
simplify _register a bit
scbedd Apr 18, 2024
9a16d3e
rid ourselves of csharp9 call
scbedd Apr 18, 2024
8e2a873
committing progress w/ incomplete new routes
scbedd Apr 18, 2024
b92dec5
reference a specific branch for harsha to explore on
scbedd Apr 18, 2024
a1ae82e
remove some usings
scbedd Apr 18, 2024
053ce5e
we are getting a ton closer
scbedd Apr 18, 2024
95cb685
attempt interlocked increment with the automatic id
scbedd Apr 19, 2024
ef69312
further feedback to avoid ever having a problem
scbedd Apr 19, 2024
c77d722
skin out the tests
scbedd Apr 19, 2024
a3d7d17
repair admin test
scbedd Apr 19, 2024
755d0b2
forgot to set content length woops
scbedd Apr 19, 2024
a40939a
now we're properly writing the responses from Admin
scbedd Apr 19, 2024
2dde663
actually fix the first test
scbedd Apr 19, 2024
518066b
we can actually remove sanitizers now
scbedd Apr 19, 2024
d52c08e
we can remove sanitizers by route now. time to test
scbedd Apr 20, 2024
4cb7012
now it is connected top to bottom. tests only remain
scbedd Apr 20, 2024
f6fb3ce
adding the tests and catching a bug in recording-specific sanitizer r…
scbedd Apr 22, 2024
75fcad2
small correction on returned result
scbedd Apr 22, 2024
02cd858
Update tools/test-proxy/Azure.Sdk.Tools.TestProxy/Properties/launchSe…
scbedd Apr 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
adding the tests and catching a bug in recording-specific sanitizer r…
…emoval
  • Loading branch information
scbedd committed Apr 22, 2024
commit f6fb3ce556c21d6e2701846a336d1dc3963932df
206 changes: 190 additions & 16 deletions tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/AdminTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
Expand Down Expand Up @@ -88,6 +89,7 @@ public async void TestAddSanitizersHandlesPopulatedArray()
httpContext.Request.Body = TestHelpers.GenerateStreamRequestBody(requestBody);
httpContext.Request.ContentLength = httpContext.Request.Body.Length;
testRecordingHandler.SanitizerRegistry.ResetSessionSanitizers();
httpContext.Response.Body = new MemoryStream();

var controller = new Admin(testRecordingHandler, _nullLogger)
{
Expand All @@ -101,9 +103,17 @@ public async void TestAddSanitizersHandlesPopulatedArray()
var amendedSessionSanitizers = testRecordingHandler.SanitizerRegistry.GetSanitizers();

Assert.Equal(defaultSessionSanitizers.Count + 2, amendedSessionSanitizers.Count);

Assert.True(amendedSessionSanitizers[defaultSessionSanitizers.Count] is GeneralRegexSanitizer);
Assert.True(amendedSessionSanitizers[defaultSessionSanitizers.Count + 1] is HeaderRegexSanitizer);

httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
var response = await JsonDocument.ParseAsync(httpContext.Response.Body, options: new JsonDocumentOptions() { AllowTrailingCommas = true });
Assert.Equal((int)HttpStatusCode.OK, httpContext.Response.StatusCode);

var prop = response.RootElement.GetProperty("Sanitizers");
var returnedSanitizerIds = TestHelpers.EnumerateArray<string>(prop);

Assert.Equal(2, returnedSanitizerIds.Count);
}

[Fact]
Expand Down Expand Up @@ -929,44 +939,208 @@ public async Task TestAddSanitizerContinuesWithTwoRequiredParams()
}

[Fact]
public void RemoveSanitizerErrorsForInvalidId()
public async Task RemoveSanitizerErrorsForInvalidIdOnRecording()
{
// remove sanitizer errors when provided an invalid id
RecordingHandler testRecordingHandler = new RecordingHandler(Directory.GetCurrentDirectory());
var httpContext = new DefaultHttpContext();
await testRecordingHandler.StartPlaybackAsync("Test.RecordEntries/oauth_request_with_variables.json", httpContext.Response);
var recordingId = httpContext.Response.Headers["x-recording-id"];
httpContext.Request.Headers["x-recording-id"] = recordingId;
httpContext.Response.Body = new MemoryStream();
var controller = new Admin(testRecordingHandler, _nullLogger)
{
ControllerContext = new ControllerContext()
{
HttpContext = httpContext
}
};

var testSet = new RemoveSanitizerList() { Sanitizers = new List<string>() { "0" } };

var assertion = await Assert.ThrowsAsync<HttpException>(
async () => await controller.RemoveSanitizers(testSet)
);

Assert.Contains("Unable to remove 1 sanitizer. Detailed list follows: \nThe requested sanitizer for removal \"0\" is not active on recording/playback with id", assertion.Message);
}

[Fact]
public async Task RemoveSanitizerErrorsForInvalidId()
{
RecordingHandler testRecordingHandler = new RecordingHandler(Directory.GetCurrentDirectory());
var httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream();
var controller = new Admin(testRecordingHandler, _nullLogger)
{
ControllerContext = new ControllerContext()
{
HttpContext = httpContext
}
};

var testSet = new RemoveSanitizerList() { Sanitizers = new List<string>() { "AZSDK00-1" } };

var assertion = await Assert.ThrowsAsync<HttpException>(
async () => await controller.RemoveSanitizers(testSet)
);

Assert.Equal("Unable to remove 1 sanitizer. Detailed list follows: \nThe requested sanitizer for removal \"AZSDK00-1\" is not active at the session level.", assertion.Message);
}

[Fact]
public void RemoveSanitizerErrorsForMissingId()
public async Task RemoveSanitizerErrorsForMissingId()
{
// remove sanitizer errors when not provided an id
RecordingHandler testRecordingHandler = new RecordingHandler(Directory.GetCurrentDirectory());
var httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream();
var controller = new Admin(testRecordingHandler, _nullLogger)
{
ControllerContext = new ControllerContext()
{
HttpContext = httpContext
}
};

var testSet = new RemoveSanitizerList() { Sanitizers = new List<string>() {} };

var assertion = await Assert.ThrowsAsync<HttpException>(
async () => await controller.RemoveSanitizers(testSet)
);

Assert.Equal(HttpStatusCode.BadRequest, assertion.StatusCode);
Assert.Equal("At least one sanitizerId for removal must be provided.", assertion.Message);
}

[Fact]
public void RemoveSanitizerSucceedsForExistingSessionSanitizer()
public async Task RemoveSanitizerSucceedsForExistingSessionSanitizer()
{
// start a recording
// remove a session sanitizer
// does that work?
RecordingHandler testRecordingHandler = new RecordingHandler(Directory.GetCurrentDirectory());
var httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream();
var controller = new Admin(testRecordingHandler, _nullLogger)
{
ControllerContext = new ControllerContext()
{
HttpContext = httpContext
}
};

var expectedSanitizerCount = testRecordingHandler.SanitizerRegistry.GetSanitizers().Count;
await controller.RemoveSanitizers(new RemoveSanitizerList() { Sanitizers = new List<string>() { "AZSDK001" } });

httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
var response = await JsonDocument.ParseAsync(httpContext.Response.Body, options: new JsonDocumentOptions() { AllowTrailingCommas = true });
Assert.Equal((int)HttpStatusCode.OK, httpContext.Response.StatusCode);

var prop = response.RootElement.GetProperty("Removed");
var returnedSanitizerIds = TestHelpers.EnumerateArray<string>(prop);

Assert.Single(returnedSanitizerIds);
Assert.Equal("AZSDK001", returnedSanitizerIds.First());

Assert.Equal(expectedSanitizerCount - 1, testRecordingHandler.SanitizerRegistry.GetSanitizers().Count);
}

[Fact]
public void RemoveSanitizerSucceedsForAddedRecordingSanitizer()
public async Task RemoveSanitizerSucceedsForAddedRecordingSanitizer()
{
// start a recording
// add two sanitizers to it
// remove one
// total should be session + the one remainig
RecordingHandler testRecordingHandler = new RecordingHandler(Directory.GetCurrentDirectory());
testRecordingHandler.SanitizerRegistry.Clear();
var httpContext = new DefaultHttpContext();
await testRecordingHandler.StartPlaybackAsync("Test.RecordEntries/oauth_request_with_variables.json", httpContext.Response);
var recordingId = httpContext.Response.Headers["x-recording-id"];

// use returned recordingId to register new sanitizers
httpContext.Request.Headers["x-recording-id"] = recordingId;
httpContext.Response.Body = new MemoryStream();
var controller = new Admin(testRecordingHandler, _nullLogger)
{
ControllerContext = new ControllerContext()
{
HttpContext = httpContext
}
};
var session = testRecordingHandler.GetActiveSession(recordingId);

var forRemoval = testRecordingHandler.RegisterSanitizer(new HeaderRegexSanitizer("Content-Type"), recordingId);
testRecordingHandler.RegisterSanitizer(new HeaderRegexSanitizer("Connection"), recordingId);
await controller.RemoveSanitizers(new RemoveSanitizerList() { Sanitizers = new List<string>() { forRemoval } });

var activeRecordingSanitizers = testRecordingHandler.SanitizerRegistry.GetSanitizers(session);

Assert.Single(activeRecordingSanitizers);
var privateSetting = (string)typeof(HeaderRegexSanitizer).GetField("_targetKey", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(activeRecordingSanitizers.First());
Assert.Equal("Connection", privateSetting);
}

[Fact]
public void GetSanitizersReturnsSessionSanitizers()
public async void GetSanitizersReturnsSessionSanitizers()
{
RecordingHandler testRecordingHandler = new RecordingHandler(Directory.GetCurrentDirectory());
testRecordingHandler.RegisterSanitizer(new HeaderRegexSanitizer("Connection"));

var httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream();

var controller = new Admin(testRecordingHandler, _nullLogger)
{
ControllerContext = new ControllerContext()
{
HttpContext = httpContext
}
};

await controller.GetSanitizers();

httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
var response = await JsonDocument.ParseAsync(httpContext.Response.Body, options: new JsonDocumentOptions() { AllowTrailingCommas = true });
Assert.Equal((int)HttpStatusCode.OK, httpContext.Response.StatusCode);

var prop = response.RootElement.GetProperty("Sanitizers");
List<string> foundIds = new List<string>();

foreach(var i in prop.EnumerateArray())
{
foundIds.Add(i.GetProperty("Id").GetRawText());
}

Assert.Equal(testRecordingHandler.SanitizerRegistry.DefaultSanitizerList.Count + 1, foundIds.Count);
}

[Fact]
public void GetSanitizersReturnsRecordingSanitizers()
public async Task GetSanitizersReturnsRecordingSanitizers()
{
RecordingHandler testRecordingHandler = new RecordingHandler(Directory.GetCurrentDirectory());
var httpContext = new DefaultHttpContext();
testRecordingHandler.SanitizerRegistry.Clear();
await testRecordingHandler.StartPlaybackAsync("Test.RecordEntries/oauth_request_with_variables.json", httpContext.Response);
var recordingId = httpContext.Response.Headers["x-recording-id"];
httpContext.Request.Headers["x-recording-id"] = recordingId;
httpContext.Response.Body = new MemoryStream();
var controller = new Admin(testRecordingHandler, _nullLogger)
{
ControllerContext = new ControllerContext()
{
HttpContext = httpContext
}
};

testRecordingHandler.RegisterSanitizer(new HeaderRegexSanitizer("Connection"), recordingId);
await controller.GetSanitizers();

httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
var response = await JsonDocument.ParseAsync(httpContext.Response.Body, options: new JsonDocumentOptions() { AllowTrailingCommas = true });
Assert.Equal((int)HttpStatusCode.OK, httpContext.Response.StatusCode);

var prop = response.RootElement.GetProperty("Sanitizers");
List<string> foundIds = new List<string>();

foreach(var i in prop.EnumerateArray())
{
foundIds.Add(i.GetProperty("Id").GetRawText());
}

Assert.Single(foundIds);
}

}
Expand Down
19 changes: 19 additions & 0 deletions tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/TestHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -516,5 +516,24 @@ public static bool CheckExistenceOfTag(Assets assets, string workingDirectory)
CommandResult result = GitHandler.Run($"ls-remote {cloneUrl} --tags {assets.Tag}", workingDirectory);
return result.StdOut.Trim().Length > 0;
}

public static List<T> EnumerateArray<T>(JsonElement element)
{
List<T> values = new List<T>();

if (element.ValueKind.ToString() != "Array")
{
throw new Exception("This test helper is intended for array members only");
}
else
{
foreach(var item in element.EnumerateArray())
{
values.Add(item.Deserialize<T>());
}
}

return values;
}
}
}
30 changes: 23 additions & 7 deletions tools/test-proxy/Azure.Sdk.Tools.TestProxy/Admin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,24 @@ public async Task AddTransform()
}

[HttpPost]
public void RemoveSanitizers([FromBody]RemoveSanitizerList sanitizerList)
public async Task RemoveSanitizers([FromBody]RemoveSanitizerList sanitizerList)
{
DebugLogger.LogAdminRequestDetails(_logger, Request);
var recordingId = RecordingHandler.GetHeader(Request, "x-recording-id", allowNulls: true);

var removedSanitizers = new List<string>();
var exceptionsList = new List<string>();

if (sanitizerList.Sanitizers.Count == 0)
{
throw new HttpException(HttpStatusCode.BadRequest, "At least one sanitizerId for removal must be provided.");
}

foreach(var sanitizerId in sanitizerList.Sanitizers) {
try
{
_recordingHandler.UnregisterSanitizer(sanitizerId, recordingId);
var removedId = _recordingHandler.UnregisterSanitizer(sanitizerId, recordingId);
removedSanitizers.Add(sanitizerId);
}
catch (HttpException ex) {
exceptionsList.Add(ex.Message);
Expand All @@ -86,14 +92,22 @@ public void RemoveSanitizers([FromBody]RemoveSanitizerList sanitizerList)

if (exceptionsList.Count > 0)
{
var varExceptionMessage = $"Unable to remove up {exceptionsList.Count} sanitizer{(exceptionsList.Count == 1 ? 's' : string.Empty)}. Detailed list follows: \n"
var varExceptionMessage = $"Unable to remove {exceptionsList.Count} sanitizer{(exceptionsList.Count > 1 ? 's' : string.Empty)}. Detailed list follows: \n"
+ string.Join("\n", exceptionsList);
throw new HttpException(HttpStatusCode.BadRequest, varExceptionMessage);
}
else
{
var json = JsonSerializer.Serialize(new { Removed = removedSanitizers });
Response.ContentType = "application/json";
Response.ContentLength = json.Length;

await Response.WriteAsync(json);
}
}

[HttpPost]
public async Task Sanitizers()
[HttpGet]
public async Task GetSanitizers()
{
DebugLogger.LogAdminRequestDetails(_logger, Request);
var recordingId = RecordingHandler.GetHeader(Request, "x-recording-id", allowNulls: true);
Expand Down Expand Up @@ -165,12 +179,14 @@ public async Task AddSanitizers()
{
if (recordingId != null)
{
registeredSanitizers.Append(_recordingHandler.RegisterSanitizer(sanitizer, recordingId));
var registeredId = _recordingHandler.RegisterSanitizer(sanitizer, recordingId);
registeredSanitizers.Add(registeredId);
Response.Headers.Add("x-recording-id", recordingId);
}
else
{
registeredSanitizers.Append(_recordingHandler.RegisterSanitizer(sanitizer));
var registeredId = _recordingHandler.RegisterSanitizer(sanitizer);
registeredSanitizers.Add(registeredId);
}
}
var json = JsonSerializer.Serialize(new { Sanitizers = registeredSanitizers });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading;
using Azure.Sdk.Tools.TestProxy.Common.Exceptions;
using Azure.Sdk.Tools.TestProxy.Sanitizers;
Expand Down Expand Up @@ -147,7 +148,7 @@ public string Register(RecordedTestSanitizer sanitizer)
SessionSanitizers.Add(strCurrent);
return strCurrent;
}
return string.Empty;
throw new HttpException(System.Net.HttpStatusCode.InternalServerError, $"Unable to register global sanitizer id \"{strCurrent}\" with value '{JsonSerializer.Serialize(sanitizer)}'");
}

/// <summary>
Expand Down Expand Up @@ -186,7 +187,7 @@ public string Unregister(string sanitizerId, ModifiableRecordSession session)
{
if (session.AppliedSanitizers.Contains(sanitizerId))
{
SessionSanitizers.Remove(sanitizerId);
session.AppliedSanitizers.Remove(sanitizerId);
return sanitizerId;
}

Expand Down