Skip to content

Commit

Permalink
SLVS-1392 Delete related bindings on deleting connection (#5894)
Browse files Browse the repository at this point in the history
  • Loading branch information
gabriela-trutan-sonarsource committed Dec 17, 2024
1 parent 9038a39 commit 9a95f71
Show file tree
Hide file tree
Showing 19 changed files with 231 additions and 277 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System.Windows;
using SonarLint.VisualStudio.ConnectedMode.Binding;
using SonarLint.VisualStudio.ConnectedMode.Persistence;
using SonarLint.VisualStudio.Core;
Expand Down Expand Up @@ -297,14 +298,37 @@ public void DeleteBinding_DeletesBindingDirectoryOfBindingFile()
[DataRow(false)]
public void DeleteBinding_ReturnsResultOfDeleteBindingDirectory(bool expectedResult)
{
unintrusiveBindingPathProvider.GetBindingPath(LocalBindingKey).Returns(MockFilePath);
solutionBindingFileLoader.DeleteBindingDirectory(MockFilePath).Returns(expectedResult);
MockDeletingBindingDirectory(LocalBindingKey, expectedResult);

var result = testSubject.DeleteBinding(LocalBindingKey);

result.Should().Be(expectedResult);
}

[TestMethod]
public void DeleteBinding_DirectoryNotDeleted_EventNotTriggered()
{
var eventHandler = Substitute.For<EventHandler<LocalBindingKeyEventArgs>>();
testSubject.BindingDeleted += eventHandler;
MockDeletingBindingDirectory(LocalBindingKey, deleted:false);

testSubject.DeleteBinding(LocalBindingKey);

eventHandler.DidNotReceiveWithAnyArgs().Invoke(default, default);
}

[TestMethod]
public void DeleteBinding_DirectoryDeleted_EventTriggered()
{
var eventHandler = Substitute.For<EventHandler<LocalBindingKeyEventArgs>>();
testSubject.BindingDeleted += eventHandler;
MockDeletingBindingDirectory(LocalBindingKey, deleted: true);

testSubject.DeleteBinding(LocalBindingKey);

eventHandler.Received(1).Invoke(testSubject, Arg.Is<LocalBindingKeyEventArgs>(x => x.LocalBindingKey == LocalBindingKey));
}

private BoundServerProject SetUpBinding(string solution, ServerConnection connection, string bindingConfig)
{
var dto = new BindingJsonModel { ServerConnectionId = connection?.Id };
Expand All @@ -329,4 +353,10 @@ private void SetUpConnections(params ServerConnection[] connections) =>
});

private void SetUpUnintrusiveBindingPathProvider(params string[] bindigFolders) => unintrusiveBindingPathProvider.GetBindingPaths().Returns(bindigFolders);

private void MockDeletingBindingDirectory(string localBindingKey, bool deleted)
{
unintrusiveBindingPathProvider.GetBindingPath(localBindingKey).Returns(MockFilePath);
solutionBindingFileLoader.DeleteBindingDirectory(MockFilePath).Returns(deleted);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class DeleteConnectionDialogViewModelTests
[TestMethod]
public void Ctor_SetsProperties()
{
var projectsToUnbind = Substitute.For<IReadOnlyList<ConnectedModeProject>>();
var projectsToUnbind = Substitute.For<IReadOnlyList<string>>();
var connectionInfo = new ConnectionInfo(default, default);
var testSubject = new DeleteConnectionDialogViewModel(projectsToUnbind, connectionInfo);

Expand All @@ -42,7 +42,7 @@ public void Ctor_SetsProperties()
[DataTestMethod]
public void DisplayProjectList_MultipleProjectsToUnbind_ReturnsTrue()
{
var projects = new[] { new ConnectedModeProject(new ServerProject("proj key", "proj name"), new SolutionInfoModel("my sol", SolutionType.Folder)) };
var projects = new[] { "proj key", "my sol" };
var testSubject = new DeleteConnectionDialogViewModel(projects, new ConnectionInfo(default, default));

testSubject.DisplayProjectList.Should().BeTrue();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class ManageConnectionsViewModelTest
private IBindingController bindingController;
private IConnectedModeBindingServices connectedModeBindingServices;
private ISolutionBindingRepository solutionBindingRepository;
private const string LocalBindingKey1 = "solution name 1";
private const string LocalBindingKey2 = "solution name 2";

[TestInitialize]
public void TestInitialize()
Expand Down Expand Up @@ -81,7 +83,7 @@ public void InitializeConnectionViewModels_InitializesConnectionsCorrectly()
[TestMethod]
public async Task RemoveConnectionWithProgressAsync_InitializesDataAndReportsProgress()
{
await testSubject.RemoveConnectionWithProgressAsync(new ConnectionViewModel(new Connection(new ConnectionInfo("myOrg", ConnectionServerType.SonarCloud))));
await testSubject.RemoveConnectionWithProgressAsync([], new ConnectionViewModel(new Connection(new ConnectionInfo("myOrg", ConnectionServerType.SonarCloud))));

await progressReporterViewModel.Received(1)
.ExecuteTaskWithProgressAsync(
Expand All @@ -99,7 +101,7 @@ public void RemoveConnectionViewModel_ReturnsStatusFromSlCore(bool expectedStatu
var connectionToRemove = testSubject.ConnectionViewModels[0];
serverConnectionsRepositoryAdapter.TryRemoveConnection(connectionToRemove.Connection.Info).Returns(expectedStatus);

var succeeded = testSubject.RemoveConnectionViewModel(connectionToRemove);
var succeeded = testSubject.RemoveConnectionViewModel([], connectionToRemove);

succeeded.Should().Be(expectedStatus);
serverConnectionsRepositoryAdapter.Received(1).TryRemoveConnection(connectionToRemove.Connection.Info);
Expand All @@ -112,7 +114,7 @@ public void RemoveConnection_ConnectionWasRemoved_RemovesProvidedConnectionViewM
var connectionToRemove = testSubject.ConnectionViewModels[0];
serverConnectionsRepositoryAdapter.TryRemoveConnection(connectionToRemove.Connection.Info).Returns(true);

testSubject.RemoveConnectionViewModel(connectionToRemove);
testSubject.RemoveConnectionViewModel([], connectionToRemove);

testSubject.ConnectionViewModels.Count.Should().Be(twoConnections.Count - 1);
testSubject.ConnectionViewModels.Should().NotContain(connectionToRemove);
Expand All @@ -125,7 +127,7 @@ public void RemoveConnectionViewModel_ConnectionWasNotRemoved_DoesNotRemoveProvi
var connectionToRemove = testSubject.ConnectionViewModels[0];
serverConnectionsRepositoryAdapter.TryRemoveConnection(connectionToRemove.Connection.Info).Returns(false);

testSubject.RemoveConnectionViewModel(connectionToRemove);
testSubject.RemoveConnectionViewModel([], connectionToRemove);

testSubject.ConnectionViewModels.Count.Should().Be(twoConnections.Count);
testSubject.ConnectionViewModels.Should().Contain(connectionToRemove);
Expand All @@ -139,7 +141,7 @@ public void RemoveConnection_ConnectionWasRemoved_RaisesEvents()
var eventHandler = Substitute.For<PropertyChangedEventHandler>();
testSubject.PropertyChanged += eventHandler;

testSubject.RemoveConnectionViewModel(testSubject.ConnectionViewModels[0]);
testSubject.RemoveConnectionViewModel([], testSubject.ConnectionViewModels[0]);

eventHandler.Received().Invoke(testSubject, Arg.Is<PropertyChangedEventArgs>(x => x.PropertyName == nameof(testSubject.NoConnectionExists)));
}
Expand All @@ -152,11 +154,80 @@ public void RemoveConnectionViewModel_ConnectionWasNotRemoved_DoesNotRaiseEvents
var eventHandler = Substitute.For<PropertyChangedEventHandler>();
testSubject.PropertyChanged += eventHandler;

testSubject.RemoveConnectionViewModel(testSubject.ConnectionViewModels[0]);
testSubject.RemoveConnectionViewModel([], testSubject.ConnectionViewModels[0]);

eventHandler.DidNotReceive().Invoke(testSubject, Arg.Any<PropertyChangedEventArgs>());
}

[TestMethod]
public void RemoveConnectionViewModel_TwoBindingsExistForConnection_RemovesBindingsAndThenConnection()
{
InitializeTwoConnections();
MockDeleteBinding(LocalBindingKey1, true);
MockDeleteBinding(LocalBindingKey2, true);

testSubject.RemoveConnectionViewModel([LocalBindingKey1, LocalBindingKey2], testSubject.ConnectionViewModels[0]);

Received.InOrder(() =>
{
solutionBindingRepository.DeleteBinding(LocalBindingKey1);
solutionBindingRepository.DeleteBinding(LocalBindingKey2);
serverConnectionsRepositoryAdapter.TryRemoveConnection(testSubject.ConnectionViewModels[0].Connection.Info);
});
}

[TestMethod]
public void RemoveConnectionViewModel_TwoBindingsExistForConnection_DeletingOneBindingFails_DoesNotRemoveConnection()
{
InitializeTwoConnections();
MockDeleteBinding(LocalBindingKey1, true);
MockDeleteBinding(LocalBindingKey2, false);

testSubject.RemoveConnectionViewModel([LocalBindingKey1, LocalBindingKey2], testSubject.ConnectionViewModels[0]);

Received.InOrder(() =>
{
solutionBindingRepository.DeleteBinding(LocalBindingKey1);
solutionBindingRepository.DeleteBinding(LocalBindingKey2);
logger.WriteLine(UiResources.DeleteConnection_DeleteBindingFails, LocalBindingKey2);
});
serverConnectionsRepositoryAdapter.DidNotReceive().TryRemoveConnection(testSubject.ConnectionViewModels[0].Connection.Info);
}

[TestMethod]
public void RemoveConnectionViewModel_TwoBindingsExistForConnection_OneBindingIsForCurrentSolution_CallsUnbind()
{
InitializeTwoConnections();
InitializeCurrentSolution(LocalBindingKey2);
MockDeleteBinding(LocalBindingKey1, true);
MockUnbind(LocalBindingKey2, true);

testSubject.RemoveConnectionViewModel([LocalBindingKey1, LocalBindingKey2], testSubject.ConnectionViewModels[0]);

Received.InOrder(() =>
{
solutionBindingRepository.DeleteBinding(LocalBindingKey1);
bindingController.Unbind(LocalBindingKey2);
serverConnectionsRepositoryAdapter.TryRemoveConnection(testSubject.ConnectionViewModels[0].Connection.Info);
});
solutionBindingRepository.DidNotReceive().DeleteBinding(LocalBindingKey2);
}

[TestMethod]
public void RemoveConnectionViewModel_TwoBindingsExistForConnection_OneBindingIsForCurrentSolution_UnbindFails_DoesNotRemoveConnection()
{
InitializeTwoConnections();
InitializeCurrentSolution(LocalBindingKey2);
MockDeleteBinding(LocalBindingKey1, true);
MockUnbind(LocalBindingKey2, false);

testSubject.RemoveConnectionViewModel([LocalBindingKey1, LocalBindingKey2], testSubject.ConnectionViewModels[0]);

solutionBindingRepository.Received(1).DeleteBinding(LocalBindingKey1);
bindingController.Received(1).Unbind(LocalBindingKey2);
serverConnectionsRepositoryAdapter.DidNotReceive().TryRemoveConnection(testSubject.ConnectionViewModels[0].Connection.Info);
}

[TestMethod]
public async Task SafeExecuteActionAsync_LoadsConnectionsOnUIThread()
{
Expand Down Expand Up @@ -610,4 +681,10 @@ private static ServerConnection.SonarQube CreateSonarQubeServerConnection(Connec
{
return new ServerConnection.SonarQube(new Uri(sonarQube.Info.Id));
}

private void MockDeleteBinding(string localBindingKey, bool success) => connectedModeBindingServices.SolutionBindingRepository.DeleteBinding(localBindingKey).Returns(success);

private void MockUnbind(string localBindingKey, bool success) => connectedModeBindingServices.BindingController.Unbind(localBindingKey).Returns(success);

private void InitializeCurrentSolution(string solutionName) => connectedModeBindingServices.SolutionInfoProvider.GetSolutionName().Returns(solutionName);
}
3 changes: 0 additions & 3 deletions src/ConnectedMode/ConnectedMode.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@

<ItemGroup>
<Page Include="Migration\Wizard\MigrationWizardWindow.xaml" />
<Page Include="UI\DeleteConnection\PreventDeleteConnectionDialog.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UI\ProgressAndErrorReporterComponent.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
Expand Down
8 changes: 7 additions & 1 deletion src/ConnectedMode/Persistence/SolutionBindingRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,16 @@ public bool Write(string configFilePath, BoundServerProject binding)
public bool DeleteBinding(string localBindingKey)
{
var bindingPath = unintrusiveBindingPathProvider.GetBindingPath(localBindingKey);
return solutionBindingFileLoader.DeleteBindingDirectory(bindingPath);
if (!solutionBindingFileLoader.DeleteBindingDirectory(bindingPath))
{
return false;
}
BindingDeleted?.Invoke(this, new LocalBindingKeyEventArgs(localBindingKey));
return true;
}

public event EventHandler BindingUpdated;
public event EventHandler<LocalBindingKeyEventArgs> BindingDeleted;

public IEnumerable<BoundServerProject> List()
{
Expand Down
12 changes: 0 additions & 12 deletions src/ConnectedMode/UI/DeleteConnection/DeleteConnectionDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,6 @@
Margin="0, 10"
ItemContainerStyle="{StaticResource NoSelectionListBoxItemStyle}"
ItemsSource="{Binding ProjectsToUnbind}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Path=SolutionInfo.SolutionType, StringFormat='{}{0}'}" />
<Run Text="{Binding Path=SolutionInfo.Name, StringFormat='{}&quot;{0}&quot;'}"
FontWeight="DemiBold" />
<Run Text="{x:Static res:UiResources.BoundToLabel}" />
<Run Text="{Binding Path=ServerProject.Name, StringFormat='{}&quot;{0}&quot;'}"
FontWeight="DemiBold" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ui:WarningMessage Grid.Row="2"
WarningText="{x:Static res:UiResources.DeleteConnectionBindingsWarningText}" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public partial class DeleteConnectionDialog : Window
{
public DeleteConnectionDialogViewModel ViewModel { get; }

public DeleteConnectionDialog(IReadOnlyList<ConnectedModeProject> projectsToUnbind, ConnectionInfo connectionInfo)
public DeleteConnectionDialog(IReadOnlyList<string> projectsToUnbind, ConnectionInfo connectionInfo)
{
ViewModel = new DeleteConnectionDialogViewModel(projectsToUnbind, connectionInfo);
InitializeComponent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ namespace SonarLint.VisualStudio.ConnectedMode.UI.DeleteConnection;

public class DeleteConnectionDialogViewModel : ViewModelBase
{
public DeleteConnectionDialogViewModel(IReadOnlyList<ConnectedModeProject> projectsToUnbind, ConnectionInfo connectionInfo)
public DeleteConnectionDialogViewModel(IReadOnlyList<string> projectsToUnbind, ConnectionInfo connectionInfo)
{
ProjectsToUnbind = projectsToUnbind;
ConnectionInfo = connectionInfo;
}

public IReadOnlyList<ConnectedModeProject> ProjectsToUnbind { get; }
public IReadOnlyList<string> ProjectsToUnbind { get; }
public ConnectionInfo ConnectionInfo { get; }

public bool DisplayProjectList => ProjectsToUnbind is not null && ProjectsToUnbind.Count > 0;
Expand Down
Loading

0 comments on commit 9a95f71

Please sign in to comment.