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-1418 Extract progress duplication in view models #5631

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions src/ConnectedMode.UnitTests/SlCoreConnectionAdapterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using NSubstitute.ExceptionExtensions;
using SonarLint.VisualStudio.ConnectedMode.UI.Credentials;
using SonarLint.VisualStudio.ConnectedMode.UI.OrganizationSelection;
using SonarLint.VisualStudio.ConnectedMode.UI.Resources;
using SonarLint.VisualStudio.Core;
using SonarLint.VisualStudio.SLCore;
using SonarLint.VisualStudio.SLCore.Common.Models;
Expand Down Expand Up @@ -64,7 +62,7 @@ public async Task ValidateConnectionAsync_SwitchesToBackgroundThread()

await slCoreConnectionAdapter.ValidateConnectionAsync(sonarQubeConnectionInfo, "myToken");

await threadHandlingMock.Received(1).RunOnBackgroundThread(Arg.Any<Func<Task<ValidateConnectionResponse>>>());
await threadHandlingMock.Received(1).RunOnBackgroundThread(Arg.Any<Func<Task<AdapterResponse>>>());
}

[TestMethod]
Expand All @@ -75,8 +73,7 @@ public async Task ValidateConnectionAsync_GettingConnectionConfigurationSLCoreSe
var response = await testSubject.ValidateConnectionAsync(sonarQubeConnectionInfo, "myToken");

logger.Received(1).LogVerbose($"[{nameof(IConnectionConfigurationSLCoreService)}] {SLCoreStrings.ServiceProviderNotInitialized}");
response.success.Should().BeFalse();
response.message.Should().Be(UiResources.ValidatingConnectionFailedText);
response.Success.Should().BeFalse();
}

[TestMethod]
Expand Down Expand Up @@ -135,7 +132,7 @@ public async Task ValidateConnectionAsync_ReturnsResponseFromSlCore(bool success

var response = await testSubject.ValidateConnectionAsync(sonarCloudConnectionInfo, "token");

response.Should().BeEquivalentTo(expectedResponse);
response.Success.Should().Be(success);
}

[TestMethod]
Expand All @@ -148,8 +145,7 @@ public async Task ValidateConnectionAsync_SlCoreValidationThrowsException_Return
var response = await testSubject.ValidateConnectionAsync(sonarCloudConnectionInfo, "token");

logger.Received(1).LogVerbose($"{Resources.ValidateCredentials_Fails}: {exceptionMessage}");
response.success.Should().BeFalse();
response.message.Should().Be(exceptionMessage);
response.Success.Should().BeFalse();
}

[TestMethod]
Expand All @@ -160,7 +156,7 @@ public async Task GetOrganizationsAsync_SwitchesToBackgroundThread()

await slCoreConnectionAdapter.GetOrganizationsAsync(new TokenCredentialsModel("token"));

await threadHandlingMock.Received(1).RunOnBackgroundThread(Arg.Any<Func<Task<AdapterResponse<List<OrganizationDisplay>>>>>());
await threadHandlingMock.Received(1).RunOnBackgroundThread(Arg.Any<Func<Task<AdapterResponseWithData<List<OrganizationDisplay>>>>>());
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using System.IO;
using SonarLint.VisualStudio.ConnectedMode.UI;
using SonarLint.VisualStudio.ConnectedMode.UI.Credentials;
using SonarLint.VisualStudio.ConnectedMode.UI.OrganizationSelection;
using SonarLint.VisualStudio.ConnectedMode.UI.Resources;
using SonarLint.VisualStudio.SLCore.Service.Connection;

Expand Down Expand Up @@ -275,26 +276,26 @@ public void AccountSecurityUrl_ConnectionIsSonarQube_ReturnsSonarQubeUrl()
}

[TestMethod]
public async Task ValidateConnectionAsync_TokenIsProvided_ShouldValidateConnectionWithToken()
public async Task AdapterValidateConnectionAsync_TokenIsProvided_ShouldValidateConnectionWithToken()
{
MockAdapterValidateConnectionAsync();
testSubject.SelectedAuthenticationType = UiResources.AuthenticationTypeOptionToken;
testSubject.Token = "dummyToken";

await testSubject.ValidateConnectionAsync();
await testSubject.AdapterValidateConnectionAsync();

await slCoreConnectionAdapter.Received(1).ValidateConnectionAsync(testSubject.ConnectionInfo, testSubject.Token);
}

[TestMethod]
public async Task ValidateConnectionAsync_CredentialsAreProvided_ShouldValidateConnectionWithToken()
public async Task AdapterValidateConnectionAsync_CredentialsAreProvided_ShouldValidateConnectionWithToken()
{
MockAdapterValidateConnectionAsync();
testSubject.SelectedAuthenticationType = UiResources.AuthenticationTypeOptionCredentials;
testSubject.Username = "username";
testSubject.Password = "password";

await testSubject.ValidateConnectionAsync();
await testSubject.AdapterValidateConnectionAsync();

await slCoreConnectionAdapter.Received(1).ValidateConnectionAsync(testSubject.ConnectionInfo, testSubject.Username, testSubject.Password);
}
Expand All @@ -304,74 +305,24 @@ public async Task ValidateConnectionAsync_CredentialsAreProvided_ShouldValidateC
[DataRow(false)]
public async Task ValidateConnectionAsync_ReturnsResponseFromSlCore(bool success)
{
MockAdapterValidateConnectionAsync(success);
progressReporterViewModel.ExecuteTaskWithProgressAsync(Arg.Any<TaskToPerformParams<AdapterResponse>>()).Returns(new AdapterResponse(success));

var response = await testSubject.ValidateConnectionAsync();

response.Should().Be(success);
}

[TestMethod]
public async Task ValidateConnectionAsync_UpdatesProgress()
{
MockAdapterValidateConnectionAsync();

await testSubject.ValidateConnectionAsync();

Received.InOrder(() =>
{
progressReporterViewModel.ProgressStatus = UiResources.ValidatingConnectionProgressText;
slCoreConnectionAdapter.ValidateConnectionAsync(Arg.Any<ConnectionInfo>(), Arg.Any<string>());
progressReporterViewModel.ProgressStatus = null;
});
}

[TestMethod]
public async Task ValidateConnectionAsync_AdapterValidationThrowsException_SetsProgressToNull()
{
testSubject.ProgressReporterViewModel.ProgressStatus.Returns(UiResources.ValidatingConnectionProgressText);

await RunAdapterValidationThrowingException();

testSubject.ProgressReporterViewModel.Received(1).ProgressStatus = null;
}

[TestMethod]
public async Task ValidateConnectionAsync_AdapterValidationFails_UpdatesWarning()
{
var warning = "wrong credentials";
MockAdapterValidateConnectionAsync(success:false, message: warning);

await testSubject.ValidateConnectionAsync();

progressReporterViewModel.Received(1).Warning = warning;
}

[TestMethod]
public async Task ValidateConnectionAsync_AdapterValidationSucceeds_DoesNotUpdateWarning()
public async Task ValidateConnectionAsync_ReturnsResponseFromSlCore()
{
var warning = "correct credentials";
MockAdapterValidateConnectionAsync(success: true, message: warning);

await testSubject.ValidateConnectionAsync();

progressReporterViewModel.DidNotReceive().Warning = warning;
}

[TestMethod]
public async Task ValidateConnectionAsync_ResetsPreviousWarningBeforeValidation()
{
var warning = "correct credentials";
MockAdapterValidateConnectionAsync(success: false, message: warning);

await testSubject.ValidateConnectionAsync();

Received.InOrder(() =>
{
progressReporterViewModel.Warning = null;
slCoreConnectionAdapter.ValidateConnectionAsync(Arg.Any<ConnectionInfo>(), Arg.Any<string>());
progressReporterViewModel.Warning = warning;
});
await progressReporterViewModel.Received(1)
.ExecuteTaskWithProgressAsync(Arg.Is<TaskToPerformParams<AdapterResponse>>(x =>
x.TaskToPerform == testSubject.AdapterValidateConnectionAsync &&
x.ProgressStatus == UiResources.ValidatingConnectionProgressText &&
x.WarningText == UiResources.ValidatingConnectionFailedText &&
x.AfterProgressUpdated == testSubject.AfterProgressStatusUpdated));
}

[TestMethod]
Expand All @@ -381,7 +332,7 @@ public void UpdateProgressStatus_RaisesEvents()
testSubject.PropertyChanged += eventHandler;
eventHandler.ReceivedCalls().Should().BeEmpty();

testSubject.UpdateProgressStatus(null);
testSubject.AfterProgressStatusUpdated();

eventHandler.Received().Invoke(testSubject, Arg.Is<PropertyChangedEventArgs>(x => x.PropertyName == nameof(testSubject.IsConfirmationEnabled)));
}
Expand Down Expand Up @@ -415,26 +366,12 @@ public void GetCredentialsModel_SelectedAuthenticationTypeIsCredentials_ReturnsM
((UsernamePasswordModel)credentialsModel).Password.Should().Be(testSubject.Password);
}

private void MockAdapterValidateConnectionAsync(bool success = true, string message = null)
private void MockAdapterValidateConnectionAsync(bool success = true)
{
slCoreConnectionAdapter.ValidateConnectionAsync(Arg.Any<ConnectionInfo>(), Arg.Any<string>())
.Returns(new ValidateConnectionResponse(success, message));
.Returns(new AdapterResponse(success));
slCoreConnectionAdapter.ValidateConnectionAsync(Arg.Any<ConnectionInfo>(), Arg.Any<string>(), Arg.Any<string>())
.Returns(new ValidateConnectionResponse(success, message));
}

private async Task RunAdapterValidationThrowingException()
{
slCoreConnectionAdapter.When(x => x.ValidateConnectionAsync(Arg.Any<ConnectionInfo>(), Arg.Any<string>()))
.Do(x => throw new Exception("testing"));
try
{
await testSubject.ValidateConnectionAsync();
}
catch (Exception)
{
// this is only for testing purposes
}
.Returns(new AdapterResponse(success));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,119 +164,53 @@ public void AddOrganization_AddsToList()
}

[TestMethod]
public async Task LoadOrganizationsAsync_UpdatesProgress()
public async Task LoadOrganizationsAsync_AddsOrganization()
{
MockAdapterLoadOrganizationsAsync();

await testSubject.LoadOrganizationsAsync();

Received.InOrder(() =>
{
progressReporterViewModel.ProgressStatus = UiResources.LoadingOrganizationsProgressText;
slCoreConnectionAdapter.GetOrganizationsAsync(Arg.Any<ICredentialsModel>());
progressReporterViewModel.ProgressStatus = null;
});
}

[TestMethod]
public async Task LoadOrganizationsAsync_AdapterThrowsException_SetsProgressToNull()
{
testSubject.ProgressReporterViewModel.ProgressStatus.Returns(UiResources.LoadingOrganizationsProgressText);

await ExecuteLoadOrganizationsAsyncThrowingException();

testSubject.ProgressReporterViewModel.Received(1).ProgressStatus = null;
await progressReporterViewModel.Received(1)
.ExecuteTaskWithProgressAsync(
Arg.Is<TaskToPerformParams<AdapterResponseWithData<List<OrganizationDisplay>>>>(x =>
x.TaskToPerform == testSubject.AdapterLoadOrganizationsAsync &&
x.ProgressStatus == UiResources.LoadingOrganizationsProgressText &&
x.WarningText == UiResources.LoadingOrganizationsFailedText &&
x.AfterSuccess == testSubject.UpdateOrganizations));
}

[TestMethod]
public async Task LoadOrganizationsAsync_AdapterReturnsFailedResponse_UpdatesWarning()
{
MockAdapterLoadOrganizationsAsync(success: false);

await testSubject.LoadOrganizationsAsync();

progressReporterViewModel.Received(1).Warning = UiResources.LoadingOrganizationsFailedText;
}

[TestMethod]
public async Task LoadOrganizationsAsync_AdapterSucceeds_DoesNotUpdateWarning()
{
MockAdapterLoadOrganizationsAsync(success: true);

await testSubject.LoadOrganizationsAsync();

progressReporterViewModel.DidNotReceive().Warning = UiResources.LoadingOrganizationsFailedText;
}

[TestMethod]
public async Task LoadOrganizationsAsync_ResetsPreviousWarningBeforeCallingAdapter()
{
MockAdapterLoadOrganizationsAsync(success: false);

await testSubject.LoadOrganizationsAsync();

Received.InOrder(() =>
{
progressReporterViewModel.Warning = null;
slCoreConnectionAdapter.GetOrganizationsAsync(Arg.Any<ICredentialsModel>());
progressReporterViewModel.Warning = UiResources.LoadingOrganizationsFailedText;
});
}

[TestMethod]
public async Task LoadOrganizationsAsync_AdapterSucceeds_AddsOrganization()
public void UpdateOrganizations_AddsOrganization()
{
var loadedOrganizations = new List<OrganizationDisplay> { new("key", "name") };
MockAdapterLoadOrganizationsAsync(success: true, organizations: loadedOrganizations);
var response = new AdapterResponseWithData<List<OrganizationDisplay>>(true, loadedOrganizations);

await testSubject.LoadOrganizationsAsync();
testSubject.UpdateOrganizations(response);

testSubject.Organizations.Should().BeEquivalentTo(loadedOrganizations);
}

[TestMethod]
public async Task LoadOrganizationsAsync_AdapterSucceeds_ClearsPreviousOrganizations()
public void UpdateOrganizations_ClearsPreviousOrganizations()
{
testSubject.Organizations.Add(new("key", "name"));
var loadedOrganizations = new List<OrganizationDisplay> { new("new_key", "new_name") };
MockAdapterLoadOrganizationsAsync(success: true, organizations: loadedOrganizations);
var response = new AdapterResponseWithData<List<OrganizationDisplay>>(true, loadedOrganizations);

await testSubject.LoadOrganizationsAsync();
testSubject.UpdateOrganizations(response);

testSubject.Organizations.Should().BeEquivalentTo(loadedOrganizations);
}

[TestMethod]
public async Task LoadOrganizationsAsync_AdapterSucceeds_RaisesEvents()
public void UpdateOrganizations_RaisesEvents()
{
MockAdapterLoadOrganizationsAsync(success: true);
var eventHandler = Substitute.For<PropertyChangedEventHandler>();
testSubject.PropertyChanged += eventHandler;
eventHandler.ReceivedCalls().Should().BeEmpty();
var response = new AdapterResponseWithData<List<OrganizationDisplay>>(true, []);

await testSubject.LoadOrganizationsAsync();
testSubject.UpdateOrganizations(response);

eventHandler.Received().Invoke(testSubject,
Arg.Is<PropertyChangedEventArgs>(x => x.PropertyName == nameof(testSubject.NoOrganizationExists)));
}

private void MockAdapterLoadOrganizationsAsync(bool success = true, List<OrganizationDisplay> organizations = null)
{
slCoreConnectionAdapter.GetOrganizationsAsync(Arg.Any<ICredentialsModel>())
.Returns(new AdapterResponse<List<OrganizationDisplay>>(success, organizations ?? []));
}

private async Task ExecuteLoadOrganizationsAsyncThrowingException()
{
slCoreConnectionAdapter.When(x => x.GetOrganizationsAsync(Arg.Any<ICredentialsModel>()))
.Do(x => throw new Exception("testing"));
try
{
await testSubject.LoadOrganizationsAsync();
}
catch (Exception)
{
// this is only for testing purposes
}
}
}
Loading
Loading