How do you replace one syntax token with multiple ones? - roslyn

I've written an analyzer to force a library to use GetAwaiter().GetResult() instead of blocking a thread with .Result / .Wait()
After several hours and attempts to find a way to generate a syntaxtree which covers all my test cases i attempted to use the ReplaceToken method.
SyntaxNodeExtensions.cs
public static TRoot ReplaceToken<TRoot>(this TRoot root, SyntaxToken tokenInList, IEnumerable<SyntaxToken> newTokens) where TRoot : SyntaxNode
{
return (TRoot) root.ReplaceTokenInListCore(tokenInList, newTokens);
}
seemed like the perfect method - however i never got it working using this method.
I ended up doing it this way (which to me, seems rather lazy):
My working codefix
private async Task<Document> ReplaceWithGetAwaiterGetResultAsync(Document document, IdentifierNameSyntax declaration, CancellationToken cancellationToken)
{
var source = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
if(declaration.Identifier.ValueText == "Result")
return document.WithText(source.Replace(declaration.Span, "GetAwaiter().GetResult()"));
if(declaration.Identifier.ValueText == "Wait")
return document.WithText(source.Replace(new TextSpan(declaration.SpanStart, 6), "GetAwaiter().GetResult()"));
return document;
}
Does anyone know a better way of how i could have turned something like
Task.Run(() => 42).Result
into
Task.Run(() => 42).GetAwaiter.GetResult()
?
E.g. a version of this:
var tokens = SyntaxFactory.ParseTokens("GetAwaiter().GetResult()");
var root = await document.GetSyntaxRootAsync(cancellationToken);
var replaced = root.ReplaceToken(declaration.Identifier, tokens);
return document.WithSyntaxRoot(replaced.WithAdditionalAnnotations());
which does not crash.

The problem you faced when implementing your solution was that you tried to replace tokens. The access of the Result Property is however not a Token but a SyntaxNode.
The structure of your code consists of a MemberAccessExpression (Task.Run().Result) containing a MethodInvocationExpression (Task.Run()).
What you're trying to achieve is to remove the MemberAccessExpression, create a new MethodInvocationExpression (invocating GetAwaiter()) and insert the original InvocationExpression into your new Expression in order to call GetAwaiter() on the original expression.
Then you need to create another Incovation Expression to call GetResult().
To do this you need to create a new node like this and replace the old node using the SyntaxRoot:
private async Task<Document> ReplaceWithAwaiter(Document document,
IdentifierNameSyntax nameSyntax, CancellationToken cancellationToken)
{
var memberAccess = nameSyntax.Ancestors().OfType<MemberAccessExpressionSyntax>()
.First();
// Create .GetAwaiter()
var invocationOfAwaiter = SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
memberAccess.Expression, SyntaxFactory.IdentifierName("GetAwaiter")));
// Create .GetResult()
var invocationOfGetResult = SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
invocationOfAwaiter, SyntaxFactory.IdentifierName("GetResult")));
// Replace .Result by .GetAwaiter().GetResult()
var syntaxRoot = await document.GetSyntaxRootAsync();
syntaxRoot = syntaxRoot.ReplaceNode(memberAccess, invocationOfGetResult);
return document.WithSyntaxRoot(syntaxRoot);
}
For .Wait() the code is a bit different, but the same principle applies. In this case you want to replace an InvocationExpression instead of a MemberAccessExpression however.
private async Task<Document> ReplaceWithAwaiterForWait(Document document,
IdentifierNameSyntax nameSyntax, CancellationToken cancellationToken)
{
// Get the Invocation Task.Run().Wait()
var invocationOfWait = nameSyntax.Ancestors().OfType<InvocationExpressionSyntax>()
.First();
// Get the Access for Task.Run().Wait
var memberAccessOfWait = (MemberAccessExpressionSyntax)invocationOfWait.Expression;
// Get the Invocation Task.Run()
var invocationOfTaskRun = memberAccessOfWait.Expression;
// Create new Expressions
var invocationOfAwaiter = SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
invocationOfTaskRun, SyntaxFactory.IdentifierName("GetAwaiter")));
var invocationOfGetResult = SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
invocationOfAwaiter, SyntaxFactory.IdentifierName("GetResult")));
var syntaxRoot = await document.GetSyntaxRootAsync();
// Replace the old expression
syntaxRoot = syntaxRoot.ReplaceNode(invocationOfWait, invocationOfGetResult);
return document.WithSyntaxRoot(syntaxRoot);
}
If you don't wont to work with the generation of individual nodes you could also just use SyntaxFactory.ParseExpression() to generate a new expression and replace the old one:
private async Task<Document> ReplaceWithAwaiterWithParse(Document document,
IdentifierNameSyntax nameSyntax, CancellationToken cancellationToken)
{
var invocationOfWait = nameSyntax.Ancestors().OfType<InvocationExpressionSyntax>()
.First();
var memberAccessOfWait = (MemberAccessExpressionSyntax)invocationOfWait.Expression;
var invocationOfBoth = SyntaxFactory.ParseExpression(
memberAccessOfWait.Expression.ToFullString()
+ ".GetAwaiter().GetResult()");
var syntaxRoot = await document.GetSyntaxRootAsync();
syntaxRoot = syntaxRoot.ReplaceNode(invocationOfWait, invocationOfBoth);
return document.WithSyntaxRoot(syntaxRoot);
}
While this solution is a little shorter and more concise you would not be working on a string instead of the SyntaxTree which could create all kinds of issues. Furthermore the performance is a bit worse than using the SyntaxFactory to create individual objects. However you do not have to create every part of the invocation yourself.

Related

NSubstitute.Exceptions.CouldNotSetReturnDueToTypeMismatchException : Can not return value of type Task`1 for IDisposable.Dispose (expected type Void)

I have code that I want to unit test by substituting and return the list of users:
var users = await _context.MyCollection.FindAsync(filter).Result.ToListAsync();
Here is my unit test using nsubstitute:
// Arrange
var user = new MyUsers{ Id = "1" };
var cursorSub = Substitute.For<IAsyncCursor<MyCollection>>();
cursorSub.MoveNext().Returns(true);
cursorSub.Current.Returns(MyUsers);
var filter = Substitute.For<FilterDefinition<MyCollection>>();
_context.MyCollection.FindAsync(filter).Returns(cursorSub);
// Act
await MyMethod.ExecuteAsync(useCaseRequest);
When I debug:
The substitution for _context.MyCollection.FindAsync(filter) actually works because it returns the cursorSub in the example. However, when it get to the part of the code where it gets the result ".Result.ToListAsync();" the lists becomes empty.
Can you help me substitute my code?

Has anyone got a guide on how to upgrade from PowerBi Embeded v2 to v3? Or a tutorial for v3?

This appears to be a nightmare, sure its easy to upgrade the nuget package to 3.11 I think the latest is, but then nothing at all compiles. So you fix the compile errors, and then it doesn't work. I'm getting an error when it tries to create the PowerBI client.
Getting the token and also creating the client appears to be totally different to v2.
This is my code:
public PowerBiConfig GetPowerBiConfig(string reportId)
{
var result = new PowerBiConfig();
try
{
if (!Guid.TryParse(reportId, out var _))
{
result.ErrorMessage = $"Invalid report guid: {reportId}";
return result;
}
var credential = new UserPasswordCredential(_powerBiProMasterUsername, _powerBiProMasterPassword);
var authenticationContext = new AuthenticationContext(AuthorityUrl);
// Taken from https://stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously
var authenticationResult = authenticationContext.AcquireTokenAsync(ResourceUrl, dataArchiverSettings.PowerBiApplicationId, credential).GetAwaiter().GetResult();
if (authenticationResult == null)
{
result.ErrorMessage = "Authentication Failed.";
return result;
}
var tokenCredentials = new TokenCredentials(authenticationResult.AccessToken, "Bearer");
using (var client = new PowerBIClient(new Uri(ApiUrl), tokenCredentials))
{
var report = client.Reports.GetReportInGroup(dataArchiverSettings.PowerBiWorkspaceId, reportId);
if (report == null)
{
result.ErrorMessage = $"No report with the ID {reportId} was found in the workspace.";
return result;
}
var datasets = client.Datasets.GetDatasetById(dataArchiverSettings.PowerBiWorkspaceId, report.DatasetId);
result.IsEffectiveIdentityRequired = datasets.IsEffectiveIdentityRequired;
result.IsEffectiveIdentityRolesRequired = datasets.IsEffectiveIdentityRolesRequired;
GenerateTokenRequest tokenRequest;
if (datasets.IsEffectiveIdentityRequired == true)
{
var username = UserHelper.GetCurrentUser();
var roles = _userService.GetRolesForUser(username);
tokenRequest = new GenerateTokenRequest(accessLevel: "view",
identities: new List<EffectiveIdentity>
{
new EffectiveIdentity(username: username,
roles: new List<string> (roles.Select(x=> x.RoleName)),
datasets: new List<string> {datasets.Id})
});
}
else
{
tokenRequest = new GenerateTokenRequest(accessLevel: "view");
}
var tokenResponse =
client.Reports.GenerateTokenInGroup(dataArchiverSettings.PowerBiWorkspaceId, report.Id,
tokenRequest);
if (tokenResponse == null)
{
result.ErrorMessage = "Failed to generate embed token.";
return result;
}
// Generate Embed Configuration.
result.EmbedToken = tokenResponse;
result.EmbedUrl = report.EmbedUrl;
result.Id = report.Id.ToString();
result.WorkloadResourceName = dataArchiverSettings.PowerBiWorkloadResourceName.Trim();
}
}
catch (HttpOperationException exc)
{
result.ErrorMessage =
$"Status: {exc.Response.StatusCode} ({(int)exc.Response.StatusCode})\r\n" +
$"Response: {exc.Response.Content}\r\n" +
$"RequestId: {exc.Response.Headers["RequestId"].FirstOrDefault()}";
}
catch (Exception exc)
{
result.ErrorMessage = exc.ToString();
}
return result;
}
The closest to "upgrade guide" is the announcement in Power BI blog. It looks like your code is using v2 (e.g. reportId is string, while in v3 it should be Guid).
Here is a brief summary of the changes:
What you should know about v3
Here are the key changes with this version update:
Namespaces renaming:
Microsoft.PowerBI.Api.V2 was changed to Microsoft.PowerBI.Api
Microsoft.PowerBI.Api.Extensions.V2 was changed to Microsoft.PowerBI.Api.Extensions
Microsoft.PowerBI.Api.V1 namespace was removed.
SetAllConnections and SetAllConnectionsInGroup operations are deprecated and marked as obsolete. You should use UpdateDatasources or UpdateParameters APIs instead.
PowerBI artifacts IDs typing was changed* from string to Guid, we recommend to work with Guid when possible.
*Dataset ID is an exception and it’s typing will remain string.
ODataResponse[List[Object]] types was changed to Objects, thus returning an objects collection on responses. For example, a response of ODataResponse[List[Report]] type will now return Reports collection as the return type.
New credentials classes allow easier build of credentialDetails. The new classes include: BasicCredentials, WindowsCredentials, OAuth2Credentials, and more.
Read Configure credentials article to learn more.
New encryption helper classes for easier encryption when creating CredentialDetails.
For example, using AsymmetricKeyEncryptor class with a gateway public key:
GatewayPublicKey publicKey = new GatewayPublicKey
{
Exponent = "...",
Modulus = "..."
};
CredentialsBase credentials = new BasicCredentials("<USER>", "<PASSWORD>");
var credentialsEncryptor = new AsymmetricKeyEncryptor(publicKey);
var credentialDetails = new CredentialDetails(credentials, PrivacyLevel.None, EncryptedConnection.Encrypted, credentialsEncryptor);
Read Configure credentials article to learn more.
Consistency on field names.
For example, reportKey, datasetKey, dashboardKey and tileKey was changed to reportId, datasetId, dashboardId and tileId.
Consistency on operations names.
For example, use GetDataset instead of GetDatasetById. The effected opertation names are imports, datasets, gateways and datasources.
Use enum class instead of string for enumerated types.
For example, In the generateTokenRequest, we recommend to use TokenAccessLevel.View, and not explicitly use “view” as value.
Required fields was marked – some fields was changed to required fields are not nullable anymore.
Examples
Change in Get Reports call if WorkspaceId is a string:
var reports = await client.Reports.GetReportsInGroupAsync(WorkspaceId);
var reports = await client.Reports.GetReportsInGroupAsync(new Guid( WorkspaceId ) );
Change in response handling if a string is expected:
report = reports.Value.FirstOrDefault(r => r.Id.Equals(ReportId, StringComparison.InvariantCultureIgnoreCase));
report = reports.Value.FirstOrDefault(r => r.Id .ToString() .Equals(ReportId, StringComparison.InvariantCultureIgnoreCase));
Change in Generate token:
var tokenResponse = await client.Reports.GenerateTokenInGroupAsync(WorkspaceId, report.Id, generateTokenRequestParameters);
var tokenResponse = await client.Reports.GenerateTokenInGroupAsync( new Guid( WorkspaceId ), report.Id, generateTokenRequestParameters);
Change in Generate token response handling if a string is expected:
m_embedConfig.Id = report.Id;
m_embedConfig.Id = report.Id .ToString() ;
Required fields are not nullable, i.e. Expiration is not nullable and the Value property should be removed:
var minutesToExpiration = EmbedToken.Expiration .Value – DateTime.UtcNow;
var minutesToExpiration = EmbedToken.Expiration – DateTime.UtcNow;
Consistency on operations names, i.e. use GetDataset instead of GetDatasetById:
var datasets = await client.Datasets.GetDataset ById InGroupAsync(WorkspaceId, report.DatasetId);
var datasets = await client.Datasets.GetDatasetInGroupAsync(new Guid(WorkspaceId), report.DatasetId);

XUnit: How to assert exception thrown by the controller in ASP.NET Core

I have a method inside the controller, which does some checks when the check is not true, it will throw an error. How I will catch that error thrown by the API with the specific message?
I`m confused about how to make those types of assertions.
This is my code so far, but I didn't manage to catch the error thrown by the controller. What I'm doing wrong?
[Fact]
public async Task AppendEmailBase64Dto_InvalidBase64_ReturnBadRequest()
{
// Arrange
var emailControllerMocks = new EmailControllerMocks();
var mockLogger = emailControllerMocks.MockLogger();
var mockMapper = emailControllerMocks.MockMapper();
var mockEmsWorkUnit = emailControllerMocks.MockEmsWorkUnit();
var mockAzureBlob = emailControllerMocks.MockAzureBlobAndGetTemplate();
// Setup
var userRequestTemplateString = File.ReadAllText(#".\EmailController\UserRequestTemplate.txt");
mockAzureBlob.Setup(blob => blob.GetHtmlBlob(It.IsAny<string>(), It.IsAny<Uri>()))
.ReturnsAsync(userRequestTemplateString);
// Act
var emailController = new Controllers.ApiV10.EmailController(mockLogger.Object, mockMapper.Object, mockEmsWorkUnit.Object, mockAzureBlob.Object);
var jsonString = File.ReadAllText(#".\EmailController\TemplateBase64Invalid.json");
var testEmailBase64Dto = GeneralHelpers.Deserialize<EmailBase64Dto>(jsonString);
var badRequestResult = await emailController.AppendEmailBase64Dto(testEmailBase64Dto);
//var result = badRequestResult.Value as ObjectResult;
//var errorMessage = badRequestResult.Value as List<string>;
//var statusCode = badRequestResult.StatusCode;
// Assert
//Assert.Equal("The provided base64 template is not a valid base64 string", errorMessage[0]);
//Assert.Equal(400, statusCode);
}
Use Assert.ThrowsAsync (the asynchronous counterpart of Assert.Throws) to assert that an exception of a particular type is thrown.
In your case you'll want something like:
var ex = await Assert.ThrowsAsync<HttpException>(async () => await emailController.AppendEmailBase64Dto(testEmailBase64Dto));
Obviously adjust 'HttpException' to whatever type of exception you're expecting.
You're not going to be able to check the return type in the same test (because AppendEmailBase64Dto will not return when an exception is thrown), so you'll need to test the exception in a separate test.

RhinoMocks - Unable to cast type foo to type bar when using non static Repository

I am trying out RhinoMocks (only just started, please be gentle!) and am confused as to why if I use this setup:
var mockRepos = new MockRepository();
var mockServiceProvider = mockRepos.DynamicMock<IServiceProvider>(null);
var mockContext = mockRepos.DynamicMock<IPluginExecutionContext>(null);
mockServiceProvider.Expect(x => x.GetService(typeof(IPluginExecutionContext))).Return(mockContext);
var someSampleClass = new SomeClassOrOther(mockServiceProvider);
At some point in SomeClassOrOther, the method GetService is called
var context = (Microsoft.Xrm.Sdk.IPluginExecutionContext)serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
and causes the exception:
Unable to cast object of type 'Castle.Proxies.ObjectProxyd0bf4b879a6341bbba3478cf1189d621' to type 'Microsoft.Xrm.Sdk.IPluginExecutionContext'.
However if I use:
var mockServiceProvider = MockRepository.GenerateMock<IServiceProvider>(null);
var mockContext = MockRepository.GenerateMock<IPluginExecutionContext>(null);
mockServiceProvider.Expect(x => x.GetService(typeof(IPluginExecutionContext))).Return(mockContext);
i.e. the static MockRepository.GenerateMock and it executes with no errors.
Where am I going wrong here?
I don't know why you are getting this very exception. However, when using the "old" non-static syntax, you get mocks in "Record" state. Before using them, you need to set them in Replay mode:
var mockRepos = new MockRepository();
var mockServiceProvider = mockRepos.DynamicMock<IServiceProvider>();
var mockContext = mockRepos.DynamicMock<IPluginExecutionContext>();
mockRepos.ReplayAll();
mockServiceProvider
.Stub(x => x.GetService(typeof(IPluginExecutionContext)))
.Return(mockContext);
var someSampleClass = new SomeClassOrOther(mockServiceProvider);
You are mixing new AAA syntax and old Record/Replay syntax. When you are using Expect extension method and new AAA syntax, you don't need to operate on mocks repository and change its mode manually(Record/Replay). Create mocks with new static methods (which create mocks in replay mode) and verify expectations later:
// Arrange
var mockServiceProvider = MockRepository.GenerateMock<IServiceProvider>();
var mockContext = MockRepository.GenerateMock<IPluginExecutionContext>();
mockServiceProvider.Expect(x => x.GetService(typeof(IPluginExecutionContext)))
.Return(mockContext);
var someSampleClass = new SomeClassOrOther(mockServiceProvider);
// Act
// Assert
mockServiceProvider.VerifyAllExpectations();
Here is old Record/Replay syntax for comparison:
var mockRepos = new MockRepository();
var mockServiceProvider = mockRepos.DynamicMock<IServiceProvider>();
var mockContext = mockRepos.DynamicMock<IPluginExecutionContext>();
Expect.Call(mockServiceProvider.GetService(typeof(IPluginExecutionContext)))
.Return(mockContext);
mockRepos.ReplayAll();
var someSampleClass = new SomeClassOrOther(mockServiceProvider);
// exercise your SUT
mockRepos.VerifyAll();

MVC 3 Unit Test - Get Actual Response Data

All,
I'm developing and unit testing an interactive voice application using ASP.NET MVC 3 whose controllers return Views containing VoiceXML. I'd like to create unit tests that capture the actual VoiceXML output so I can schema-validate it.
My reading and testing have taken me to Scott H's FakeHttpContext that uses Moq, as well as several responses here. Everything compiles correctly, and I'm trying to do something like the following:
[TestMethod]
public void WelcomeTest1()
{
EmergencyController controller = new EmergencyController();
controller.ControllerContext = new ControllerContext(MvcMockHelpers.FakeHttpContext("~/Emergency/Welcome"), new RouteData(), controller);
ViewResult result = (controller.Welcome()) as ViewResult;
.
.
Assert.IsTrue(controller.ControllerContext.HttpContext.Response.OutputStream.Length > 0);
// assert schema validation on the output here
}
However, stepping through this, I can see that the Welcome view being called, but I'm looking for something in the Response.Output and not finding anything. The mock is set up as follows, in hope that setting CallBase to true would actually write something out. I found some code that I added to the FakeHttpContext constructor that supposedly invokes a StringWriter, but to no avail:
public static HttpContextBase FakeHttpContext()
{
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>() { CallBase = true };
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);
response.Setup(r => r.OutputStream).Returns(new MemoryStream());
response.Setup(r => r.Headers).Returns(new NameValueCollection());
var writer = new StringWriter();
var wr = new SimpleWorkerRequest("", "", "", "", writer);
HttpContext.Current = new HttpContext(wr);
return context.Object;
}
I'm sure I'm missing something obvious, but I'm stumped right now.
Thanks
Jim Stanley
Blackboard Connect
The result doesn't get populated in the ViewResult. In other words, the view isn't rendered by you calling return View() in your controller, rather mvc takes the viewresult and renders it at a later time in the request-sycle. What you need to do is to create a render-function for ViewResults, like this one:
using System;
using System.IO;
using System.Web.Mvc;
namespace CoPrice.Helpers
{
public static class ViewRendrer
{
public static string ToHtml(this ViewResult result, Controller controller)
{
controller.ViewData.Model = result.Model;
try
{
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, result.ViewName);
ViewContext context = new ViewContext(controller.ControllerContext, viewResult.View, result.ViewData, result.TempData, sw);
viewResult.View.Render(context, sw);
return sw.GetStringBuilder().ToString();
}
}
catch (Exception e)
{
return e.ToString();
}
}
}
}
Then you can do result.ToHtml(controller) to get the actual data (this works for RazorViews only I think, though I'm not sure, that's what I've used it for at least).