Skip to content

Commit

Permalink
When connection of current solution is updated, rebind
Browse files Browse the repository at this point in the history
  • Loading branch information
vnaskos-sonar committed Nov 4, 2024
1 parent c7da258 commit e2a210a
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/

using System.ComponentModel;
using SonarLint.VisualStudio.ConnectedMode.Binding;
using SonarLint.VisualStudio.ConnectedMode.UI;
using SonarLint.VisualStudio.ConnectedMode.UI.Credentials;
using SonarLint.VisualStudio.ConnectedMode.UI.ManageConnections;
Expand All @@ -38,6 +39,8 @@ public class ManageConnectionsViewModelTest
private IServerConnectionsRepositoryAdapter serverConnectionsRepositoryAdapter;
private IThreadHandling threadHandling;
private ILogger logger;
private ISolutionInfoProvider solutionInfoProvider;
private IBindingController bindingController;
private IConnectedModeBindingServices connectedModeBindingServices;
private ISolutionBindingRepository solutionBindingRepository;

Expand Down Expand Up @@ -280,10 +283,11 @@ public void CreateNewConnection_ConnectionWasNotAddedToRepository_DoesNotAddConn
testSubject.ConnectionViewModels.Should().BeEmpty();
serverConnectionsRepositoryAdapter.Received(1).TryAddConnection(connectionToAdd, Arg.Any<ICredentialsModel>());
}

[TestMethod]
public async Task UpdateConnectionCredentialsWithProgressAsync_InitializesDataAndReportsProgress()
public async Task UpdateConnectionCredentialsWithProgressAsync_UpdatesConnectionAndReportsProgress()
{
progressReporterViewModel.ExecuteTaskWithProgressAsync(Arg.Any<TaskToPerformParams<AdapterResponse>>()).Returns(Task.FromResult(new AdapterResponse(true)));
var connectionToUpdate = CreateSonarCloudConnection();

await testSubject.UpdateConnectionCredentialsWithProgressAsync(connectionToUpdate, Substitute.For<ICredentialsModel>());
Expand All @@ -294,7 +298,59 @@ await progressReporterViewModel.Received(1)
x.ProgressStatus == UiResources.UpdatingConnectionCredentialsProgressText &&
x.WarningText == UiResources.UpdatingConnectionCredentialsFailedText));
}


[TestMethod]
public async Task UpdateConnectionCredentialsWithProgressAsync_WhenCurrentSolutionIsBoundToUpdatedConnection_RebindsAndReportsProgress()
{
progressReporterViewModel.ExecuteTaskWithProgressAsync(Arg.Any<TaskToPerformParams<AdapterResponse>>()).Returns(Task.FromResult(new AdapterResponse(true)));
var connectionToUpdate = CreateSonarCloudConnection();
var serverConnectionToUpdate = CreateSonarCloudServerConnection(connectionToUpdate);
var configurationProvider = Substitute.For<IConfigurationProvider>();
configurationProvider.GetConfiguration().Returns(new BindingConfiguration(new BoundServerProject("local", "server", serverConnectionToUpdate), SonarLintMode.Connected, "binding-dir"));
connectedModeServices.ConfigurationProvider.Returns(configurationProvider);

await testSubject.UpdateConnectionCredentialsWithProgressAsync(connectionToUpdate, Substitute.For<ICredentialsModel>());

await progressReporterViewModel.Received(1)
.ExecuteTaskWithProgressAsync(
Arg.Is<TaskToPerformParams<AdapterResponse>>(x =>
x.ProgressStatus == UiResources.RebindingProgressText &&
x.WarningText == UiResources.RebindingFailedText));
}

[TestMethod]
public async Task UpdateConnectionCredentialsWithProgressAsync_WhenCurrentSolutionIsStandalone_DoesNotRebindAndReportsProgress()
{
progressReporterViewModel.ExecuteTaskWithProgressAsync(Arg.Any<TaskToPerformParams<AdapterResponse>>()).Returns(Task.FromResult(new AdapterResponse(true)));
var connectionToUpdate = CreateSonarCloudConnection();
var configurationProvider = Substitute.For<IConfigurationProvider>();
configurationProvider.GetConfiguration().Returns(BindingConfiguration.Standalone);
connectedModeServices.ConfigurationProvider.Returns(configurationProvider);

await testSubject.UpdateConnectionCredentialsWithProgressAsync(connectionToUpdate, Substitute.For<ICredentialsModel>());

await progressReporterViewModel.DidNotReceive()
.ExecuteTaskWithProgressAsync(
Arg.Is<TaskToPerformParams<AdapterResponse>>(x =>
x.ProgressStatus == UiResources.RebindingProgressText &&
x.WarningText == UiResources.RebindingFailedText));
}

[TestMethod]
public async Task UpdateConnectionCredentialsWithProgressAsync_WhenConnectionFailedToUpdate_DoesNotRebindAndReportsProgress()
{
progressReporterViewModel.ExecuteTaskWithProgressAsync(Arg.Any<TaskToPerformParams<AdapterResponse>>()).Returns(Task.FromResult(new AdapterResponse(false)));
var connectionToUpdate = CreateSonarCloudConnection();

await testSubject.UpdateConnectionCredentialsWithProgressAsync(connectionToUpdate, Substitute.For<ICredentialsModel>());

await progressReporterViewModel.DidNotReceive()
.ExecuteTaskWithProgressAsync(
Arg.Is<TaskToPerformParams<AdapterResponse>>(x =>
x.ProgressStatus == UiResources.RebindingProgressText &&
x.WarningText == UiResources.RebindingFailedText));
}

[TestMethod]
public void UpdateConnectionCredentials_UpdatesProvidedConnection()
{
Expand All @@ -316,7 +372,50 @@ public void UpdateConnectionCredentials_ConnectionIsNull_DoesNotUpdateConnection
succeeded.Should().BeFalse();
serverConnectionsRepositoryAdapter.DidNotReceive().TryUpdateCredentials(Arg.Any<Connection>(), Arg.Any<ICredentialsModel>());
}


[TestMethod]
public async Task RebindAsync_WhenServerConnectionCannotBeFound_Fails()
{
connectedModeServices.ServerConnectionsRepositoryAdapter.TryGet(Arg.Any<ConnectionInfo>(), out Arg.Any<ServerConnection>()).Returns(false);
var connectionToUpdate = CreateSonarCloudConnection();

var response = await testSubject.RebindAsync(connectionToUpdate, "serverProjectKey");

await bindingController.DidNotReceiveWithAnyArgs().BindAsync(Arg.Any<BoundServerProject>(), Arg.Any<CancellationToken>());
response.Success.Should().BeFalse();
}

[TestMethod]
public async Task RebindAsync_WhenExceptionThrownDuringBinding_Fails()
{
var connectionToUpdate = CreateSonarCloudConnection();
var serverConnectionToUpdate = CreateSonarCloudServerConnection(connectionToUpdate);
MockTryGetServerConnection(serverConnectionToUpdate);
solutionInfoProvider.GetSolutionNameAsync().Returns(Task.FromResult("mySolution"));
bindingController.BindAsync(Arg.Any<BoundServerProject>(), Arg.Any<CancellationToken>()).Returns(_ => throw new Exception("Failed to bind"));

var response = await testSubject.RebindAsync(connectionToUpdate, "serverProjectKey");

await bindingController.ReceivedWithAnyArgs().BindAsync(Arg.Any<BoundServerProject>(), Arg.Any<CancellationToken>());
response.Success.Should().BeFalse();
}

[TestMethod]
public async Task RebindAsync_WhenBindingSucceeds_Succeed()
{
var connectionToUpdate = CreateSonarCloudConnection();
var serverConnectionToUpdate = CreateSonarCloudServerConnection(connectionToUpdate);
MockTryGetServerConnection(serverConnectionToUpdate);
solutionInfoProvider.GetSolutionNameAsync().Returns(Task.FromResult("mySolution"));

var response = await testSubject.RebindAsync(connectionToUpdate, "serverProjectKey");

await bindingController.Received().BindAsync(Arg.Is<BoundServerProject>(
x => x.LocalBindingKey == "mySolution" && x.ServerConnection == serverConnectionToUpdate),
CancellationToken.None);
response.Success.Should().BeTrue();
}

[TestMethod]
public async Task GetConnectionReferencesWithProgressAsync_CalculatesReferencesAndReportsProgress()
{
Expand Down Expand Up @@ -430,7 +529,7 @@ public void GetConnectionReferences_BindingRepositoryThrowsException_ReturnsEmpt

var response = testSubject.GetConnectionReferences(new ConnectionViewModel(twoConnections.First()));

response.Success.Should().BeFalse();
response.Success.Should().BeFalse();
response.ResponseData.Should().BeEmpty();
logger.Received(1).WriteLine(nameof(testSubject.GetConnectionReferences), exceptionMsg);
}
Expand Down Expand Up @@ -471,6 +570,12 @@ private void MockServices()

solutionBindingRepository = Substitute.For<ISolutionBindingRepository>();
connectedModeBindingServices.SolutionBindingRepository.Returns(solutionBindingRepository);

solutionInfoProvider = Substitute.For<ISolutionInfoProvider>();
connectedModeBindingServices.SolutionInfoProvider.Returns(solutionInfoProvider);

bindingController = Substitute.For<IBindingController>();
connectedModeBindingServices.BindingController.Returns(bindingController);
}

private void MockTryGetConnections(List<Connection> connections)
Expand All @@ -482,6 +587,15 @@ private void MockTryGetConnections(List<Connection> connections)
});
}

private void MockTryGetServerConnection(ServerConnection expectedServerConnection = null)
{
serverConnectionsRepositoryAdapter.TryGet(Arg.Any<ConnectionInfo>(), out _).Returns(callInfo =>
{
callInfo[1] = expectedServerConnection;
return true;
});
}

private static Connection CreateSonarCloudConnection()
{
return new Connection(new ConnectionInfo("mySecondOrg", ConnectionServerType.SonarCloud), false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using System.Collections.ObjectModel;
using SonarLint.VisualStudio.ConnectedMode.UI.Credentials;
using SonarLint.VisualStudio.ConnectedMode.UI.Resources;
using SonarLint.VisualStudio.Core.Binding;
using SonarLint.VisualStudio.Core.WPF;

namespace SonarLint.VisualStudio.ConnectedMode.UI.ManageConnections
Expand Down Expand Up @@ -95,14 +96,29 @@ internal async Task CreateConnectionsWithProgressAsync(Connection connection, IC
UiResources.CreatingConnectionFailedText);
await ProgressReporterViewModel.ExecuteTaskWithProgressAsync(validationParams);
}

internal async Task UpdateConnectionCredentialsWithProgressAsync(Connection connection, ICredentialsModel credentialsModel)
{
var validationParams = new TaskToPerformParams<AdapterResponse>(
async () => await SafeExecuteActionAsync(() => UpdateConnectionCredentials(connection, credentialsModel)),
UiResources.UpdatingConnectionCredentialsProgressText,
UiResources.UpdatingConnectionCredentialsFailedText);
await ProgressReporterViewModel.ExecuteTaskWithProgressAsync(validationParams);
var response = await ProgressReporterViewModel.ExecuteTaskWithProgressAsync(validationParams);

if (!response.Success)
{
return;
}

var boundServerProject = connectedModeServices.ConfigurationProvider.GetConfiguration()?.Project;
if (boundServerProject != null && ConnectionInfo.From(boundServerProject.ServerConnection).Id == connection.Info.Id)
{
var refreshBinding = new TaskToPerformParams<AdapterResponse>(
async () => await RebindAsync(connection, boundServerProject.ServerProjectKey),
UiResources.RebindingProgressText,
UiResources.RebindingFailedText);
await ProgressReporterViewModel.ExecuteTaskWithProgressAsync(refreshBinding);
}
}

internal async Task<AdapterResponse> SafeExecuteActionAsync(Func<bool> funcToExecute)
Expand Down Expand Up @@ -156,10 +172,30 @@ internal bool UpdateConnectionCredentials(Connection connection, ICredentialsMod
{
return false;
}

return connectedModeServices.ServerConnectionsRepositoryAdapter.TryUpdateCredentials(connection, credentialsModel);
}

internal async Task<AdapterResponse> RebindAsync(Connection connection, string serverProjectKey)
{
if (!connectedModeServices.ServerConnectionsRepositoryAdapter.TryGet(connection.Info, out var serverConnection))
{
return new AdapterResponse(false);
}

try
{
var localBindingKey = await connectedModeBindingServices.SolutionInfoProvider.GetSolutionNameAsync();
var boundServerProject = new BoundServerProject(localBindingKey, serverProjectKey, serverConnection);
await connectedModeBindingServices.BindingController.BindAsync(boundServerProject, CancellationToken.None);
return new AdapterResponse(true);
}
catch (Exception)
{
return new AdapterResponse(false);
}
}

internal void AddConnectionViewModel(Connection connection)
{
ConnectionViewModels.Add(new ConnectionViewModel(connection));
Expand Down
18 changes: 18 additions & 0 deletions src/ConnectedMode/UI/Resources/UiResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/ConnectedMode/UI/Resources/UiResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -428,4 +428,10 @@ Please manually add the credentials for the connection and then try again.</valu
<data name="UpdatingConnectionCredentialsFailedText" xml:space="preserve">
<value>Failed to update the connection, please make sure that the credentials are correct.</value>
</data>
<data name="RebindingProgressText" xml:space="preserve">
<value>Refreshing binding...</value>
</data>
<data name="RebindingFailedText" xml:space="preserve">
<value>Failed to refresh binding, please make sure that the credentials are correct.</value>
</data>
</root>

0 comments on commit e2a210a

Please sign in to comment.