Description
openedon Jul 18, 2024
Hi there, I'd like to add a suggestion that the CerbosClient use an interface, maybe something like ICerbosClient, with the public methods that it uses, like CheckResources/CheckResourcesAsync , PlanResources/PlanResourcesAsync so that it can more easily be Mocked in a unit test.
i.e.
var cerbosClient = new Mock<ICerbosClient>();
cerbosClientMock.Setup(x => x.CheckResourcesAsync(It.IsAny<CheckResourcesRequest>()))
.ReturnsAsync(checkResourcesResponse);
However, this is possible today if developers write their own C# interface and class that uses the CerbosClient internally.
Similarly, to help unit testing, it would be nice to have public access to the ResourceEntries property of the class CheckResourcesRequest (Cerbos.Sdk.Builder) type, to validate the request has the correct form and attributes when the request is made. Currently these are all private.
For example,
// Application code creating request to send to Cerbos
var request = CheckResourcesRequest.NewInstance()
.WithIncludeMeta(true)
.WithPrincipal(
Principal.NewInstance("1", "APPLICATION")
.WithPolicyVersion("default"))
.WithResourceEntries(
ResourceEntry.NewInstance("todoItem", "123")
.WithPolicyVersion("default")
.WithAttribute("isTodoItemComplete", AttributeValue.BoolValue(todoItemComplete))
.WithAttribute("isTodoItemOwnedByUser", AttributeValue.BoolValue(todoItemOwnedByUser))
.WithActions("delete"));
Test code snippet testing application code above:
// Arrange
// Create resultEntry
var resultEntry = new CheckResourcesResponse.Types.ResultEntry(new Cerbos.Api.V1.Response.CheckResourcesResponse.Types.ResultEntry()
{
Resource = new Cerbos.Api.V1.Response.CheckResourcesResponse.Types.ResultEntry.Types.Resource()
{
Id = "1"
},
Actions = { { "delete", Cerbos.Api.V1.Effect.Effect.Allow } }
});
// Create CheckResourcesResponse with the resultEntry
var checkResourcesResponse = new CheckResourcesResponse(new Cerbos.Api.V1.Response.CheckResourcesResponse()
{
Results = { resultEntry.Raw }
});
var capturedRequest = new List<CheckResourcesRequest>();
// ICerbosClient Interface doesn't exist yet, but when it does this should be possible
var cerbosClientMock = new Mock<ICerbosClient>();
cerbosClientMock.Setup(x => x.CheckResourcesRequest(It.IsAny<CheckResourcesRequest>()))
.Callback<CheckResourcesRequest>(req => capturedRequest.Add(req))
.ReturnsAsync(checkResourcesResponse);
// Act
// Call the method that triggers CheckResourcesRequest
// Assert
Assert.Single(capturedRequest); // Ensure a request was captured
var request = capturedRequest.First();
// Inspect the captured request to verify it contains the expected attributes
// Code below is not possible since ResourceEntries is private.
Assert.True(request.ResourceEntries.Any(re =>
re.Attributes.Any(attr => attr.Key == "isTodoItemComplete" && attr.Value.BoolValue) &&
re.Attributes.Any(attr => attr.Key == "isTodoItemOwnedByUser" && attr.Value.BoolValue)));
I hope that helps! Thank you!