Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SLVS-1392 Delete related bindings on deleting connection #5894

Merged
merged 8 commits into from
Dec 16, 2024
Merged
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,79 @@ 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);
});
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 +680,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
Loading