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

MMF-3869: [Visual Studio] Connected mode setup UI helps users discover the value of Sonar solution and connected mode #5708

Merged
merged 66 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
d1ff008
SLVS-1327 Add dummy window and menu item (#5579)
georgii-borovinskikh-sonarsource Aug 7, 2024
10d3d2d
SLVS-1337 Move ViewModelBase in Core (#5582)
gabriela-trutan-sonarsource Aug 7, 2024
2a3e03b
SLVS-1330 Create the server selection window for the new connected mo…
gabriela-trutan-sonarsource Aug 9, 2024
88ef5d6
SLVS-1329 Create Credentials dialog for new connected mode (#5588)
gabriela-trutan-sonarsource Aug 12, 2024
8c1245b
SLVS-1339 Create connections window (#5590)
gabriela-trutan-sonarsource Aug 12, 2024
cbdab98
SLVS-1342 Show warning for non-https connection (#5591)
gabriela-trutan-sonarsource Aug 12, 2024
b4464a7
SLVS-1331 Create Organization Selection dialog (#5589)
georgii-borovinskikh-sonarsource Aug 12, 2024
1a6acd3
SLVS-1333 Create Connection Info component for displaying connection …
gabriela-trutan-sonarsource Aug 13, 2024
5dc82ed
SLVS-1332 Create Project Selection window (#5587)
vnaskos-sonar Aug 13, 2024
8731338
SLVS-1351 Make ConnectionInfo component inline-friendly (#5595)
georgii-borovinskikh-sonarsource Aug 14, 2024
6f2540b
SLVS-1341 Add connection deletion confirmation dialog (#5594)
georgii-borovinskikh-sonarsource Aug 14, 2024
0b29638
SLVS-1343 Clean up name contractions (#5593)
gabriela-trutan-sonarsource Aug 14, 2024
2e210d5
SLVS-1348 Integrate ConnectionInfo component with Project Selection w…
georgii-borovinskikh-sonarsource Aug 15, 2024
b1e0466
SLVS-1344 Unify styling for project and organization list (#5598)
georgii-borovinskikh-sonarsource Aug 15, 2024
3c09bf2
SLVS-1345 Connect Manage Connections window and related dialogs (#5600)
gabriela-trutan-sonarsource Aug 15, 2024
d3de286
SLVS-1349 Create project binding window (#5596)
gabriela-trutan-sonarsource Aug 15, 2024
7a08eaf
SLVS-1359 Improve usability experience (#5601)
gabriela-trutan-sonarsource Aug 15, 2024
f84d495
SLVS-1346 Remove window used for development (#5603)
gabriela-trutan-sonarsource Aug 16, 2024
ffe9141
SLVS-1365 Add button theming to ConnectedMode UI Styles (#5607)
georgii-borovinskikh-sonarsource Aug 16, 2024
8c47b91
SLVS-1352 Refactor ConnectionInfo.Connection (#5602)
georgii-borovinskikh-sonarsource Aug 19, 2024
3b24763
SLVS-1363 Disable Use Shared Binding when no config exists (#5608)
gabriela-trutan-sonarsource Aug 19, 2024
dc8b0ea
SLVS-1360 Make UI pretty (#5606)
gabriela-trutan-sonarsource Aug 19, 2024
76e6fe3
SLVS-1353 Display more project info when deleting connection (#5609)
gabriela-trutan-sonarsource Aug 19, 2024
3b8e35d
SLVS-1372 Add SLCore ValidateConnection method to ConnectionRpcServic…
gabriela-trutan-sonarsource Aug 20, 2024
4154636
SLVS-1371 Add listUserOrganizations to ConnectionRpcService (#5611)
gabriela-trutan-sonarsource Aug 21, 2024
15ffe4a
SLVS-1409 Add fuzzySearchProjects & getAllProjects to ConnectionRpcSe…
vnaskos-sonar Aug 21, 2024
71afd46
SLVS-1397 Add ServerConnection model (#5614)
georgii-borovinskikh-sonarsource Aug 21, 2024
0930f38
SLVS-1398 Add IServerConnectionRepository interface (#5621)
georgii-borovinskikh-sonarsource Aug 22, 2024
86832d6
SLVS-1374 Validate connection in CredentialsDialog (#5616)
gabriela-trutan-sonarsource Aug 22, 2024
0031e23
SLVS-1413 Show connection validation status in UI (#5618)
gabriela-trutan-sonarsource Aug 23, 2024
aa7607b
SLVS-1401 Add new BoundProject model (#5624)
georgii-borovinskikh-sonarsource Aug 23, 2024
3fd08bf
SLVS-1394 Load organizations (#5629)
gabriela-trutan-sonarsource Aug 26, 2024
2150dd5
SLVS-1405 Integrate new binding with ActiveSolutionBoundTracker (#5632)
georgii-borovinskikh-sonarsource Aug 27, 2024
43c6a1b
SLVS-1373 Validate credentials in OrganizationSelectionDialog (#5634)
gabriela-trutan-sonarsource Aug 27, 2024
fcc5585
SLVS-1410 Split IObsoleteConfigurationProvider from IConfigurationPro…
georgii-borovinskikh-sonarsource Aug 27, 2024
98cea84
SLVS-1411 Use BoundServerProject model in IConfigurationProvider (#5637)
georgii-borovinskikh-sonarsource Aug 28, 2024
524a032
SLVS-1426 Add generic JsonSerializer (#5640)
gabriela-trutan-sonarsource Aug 28, 2024
a031c6e
SLVS-1422 Split ISolutionBindingRepository and ILegacySolutionBinding…
georgii-borovinskikh-sonarsource Aug 28, 2024
a995066
SLVS-1427 Add generic json file handler (#5641)
gabriela-trutan-sonarsource Aug 28, 2024
8619648
SLVS-1406 Add binding changed trigger to ActiveSolutionBoundTracker (…
georgii-borovinskikh-sonarsource Aug 30, 2024
acbc463
SLVS-1376 Binding Dialog: list connections (#5649)
gabriela-trutan-sonarsource Aug 30, 2024
dfb3413
SLVS-1431 Add server connection json model and mapper (#5658)
gabriela-trutan-sonarsource Sep 3, 2024
0fc33ac
SLVS-1432 Support removing credentials from store (#5659)
gabriela-trutan-sonarsource Sep 3, 2024
4af1c03
SLVS-1407 Integrate BoundServerProject with IBindingProcess (#5655)
georgii-borovinskikh-sonarsource Sep 3, 2024
07efbac
SLVS-1433 Add credentials uri to the ServerConnection model (#5660)
gabriela-trutan-sonarsource Sep 3, 2024
98de897
SLVS-1434 Extend JsonFileHandler (#5661)
gabriela-trutan-sonarsource Sep 3, 2024
3a7d4d8
SLVS-1399 Implement IServerConnectionRepository (#5663)
gabriela-trutan-sonarsource Sep 12, 2024
5102ca1
SLVS-1446 Add BindingDto model & serialization (#5688)
georgii-borovinskikh-sonarsource Sep 16, 2024
282fdeb
SLVS-1393 Delete Connection Dialog: User should be able to delete a c…
gabriela-trutan-sonarsource Sep 17, 2024
3ce5162
SLVS-1424 Integrate ISolutionBindingRepository with BoundServerProjec…
georgii-borovinskikh-sonarsource Sep 17, 2024
e0086c5
SLVS-1386 Manage Connections Dialog: User should be able to create a …
gabriela-trutan-sonarsource Sep 17, 2024
2097547
SLVS-1445 Use SecureString for token and password (#5687)
gabriela-trutan-sonarsource Sep 17, 2024
aa4c1ad
SLVS-1381 Binding Dialog: Show current binding status (#5689)
vnaskos-sonar Sep 17, 2024
485f027
SLVS-1388 Project Selection Dialog: User should be able to view list …
gabriela-trutan-sonarsource Sep 18, 2024
56e554e
SLVS-1400 Add migration mechanism to separate connections from bindin…
gabriela-trutan-sonarsource Sep 19, 2024
a173289
SLVS-1378 Binding Dialog: Trigger binding (#5695)
vnaskos-sonar Sep 19, 2024
c76da97
SLVS-1379 Binding Dialog: Implement Use Shared Binding Configuration …
gabriela-trutan-sonarsource Sep 24, 2024
9c754e2
SLVS-1455 Make Id of type URL (#5698)
vnaskos-sonar Sep 25, 2024
ca5fbca
SLVS-1396 Migration wizard: Integrate with the new binding and connec…
gabriela-trutan-sonarsource Sep 25, 2024
3b6f543
SLVS-1448 Prevent deleting connection when in use (#5701)
gabriela-trutan-sonarsource Sep 25, 2024
02cc2db
SLVS-1461 Remove controls from TeamExplorer window (#5702)
gabriela-trutan-sonarsource Sep 25, 2024
6f97e1d
SLVS-1452 Hide features not supported initially (#5703)
vnaskos-sonar Sep 25, 2024
d8c2f9d
SLVS-1469 Fix typo in json model (#5707)
gabriela-trutan-sonarsource Sep 26, 2024
8cc6611
SLVS-1466 Add back warning for the deprecated credentials (#5705)
gabriela-trutan-sonarsource Sep 26, 2024
4766c20
SLVS-1467 Fix icons not visible on dark theme (#5706)
gabriela-trutan-sonarsource Sep 26, 2024
3d9f3dc
SLVS-1465 Use shared binding: show message when credentials do not ex…
gabriela-trutan-sonarsource Sep 26, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using SonarLint.VisualStudio.ConnectedMode.QualityProfiles;
using SonarLint.VisualStudio.Core;
using SonarLint.VisualStudio.Core.Analysis;
using SonarLint.VisualStudio.Core.Binding;
using SonarLint.VisualStudio.TestInfrastructure;
using SonarQube.Client;
using SonarQube.Client.Models;
Expand All @@ -45,7 +46,7 @@ public void MefCtor_CheckIsExported()
[TestMethod]
public void Create_ReturnsProcessImpl()
{
var bindingArgs = new BindCommandArgs("proj key", "proj name", new ConnectionInformation(new Uri("http://localhost")));
var bindingArgs = new BindCommandArgs(new BoundServerProject("any", "any", new ServerConnection.SonarCloud("any")));

var testSubject = CreateTestSubject();

Expand Down
69 changes: 10 additions & 59 deletions src/ConnectedMode.UnitTests/Binding/BindingProcessImplTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class BindingProcessImplTests
[TestMethod]
public void Ctor_ArgChecks()
{
var bindingArgs = CreateBindCommandArgs(connection: new ConnectionInformation(new Uri("http://server")));
var bindingArgs = CreateBindCommandArgs();
var exclusionSettingsStorage = Mock.Of<IExclusionSettingsStorage>();
var qpDownloader = Mock.Of<IQualityProfileDownloader>();
var sonarQubeService = Mock.Of<ISonarQubeService>();
Expand Down Expand Up @@ -150,9 +150,7 @@ public async Task DownloadQualityProfile_CreatesBoundProjectAndCallsQPDownloader
var qpDownloader = new Mock<IQualityProfileDownloader>();
var progress = Mock.Of<IProgress<FixedStepsProgress>>();

var connectionInfo = new ConnectionInformation(new Uri("http://theServer"));
connectionInfo.Organization = new SonarQubeOrganization("the org key", "the org name");
var bindingArgs = CreateBindCommandArgs("the project key", "the project name", connectionInfo);
var bindingArgs = CreateBindCommandArgs("the project key", "http://theServer");

var testSubject = CreateTestSubject(bindingArgs,
qpDownloader: qpDownloader.Object);
Expand All @@ -162,61 +160,15 @@ public async Task DownloadQualityProfile_CreatesBoundProjectAndCallsQPDownloader

result.Should().BeTrue();

qpDownloader.Verify(x => x.UpdateAsync(It.IsAny<BoundSonarQubeProject>(), progress, It.IsAny<CancellationToken>()),
qpDownloader.Verify(x => x.UpdateAsync(It.IsAny<BoundServerProject>(), progress, It.IsAny<CancellationToken>()),
Times.Once);

var actualProject = (BoundSonarQubeProject)qpDownloader.Invocations[0].Arguments[0];
var actualProject = (BoundServerProject)qpDownloader.Invocations[0].Arguments[0];

// Check the bound project was correctly constructed from the BindCommandArgs
actualProject.Should().NotBeNull();
actualProject.ServerUri.Should().Be("http://theServer");
actualProject.ProjectKey.Should().Be("the project key");
actualProject.ProjectName.Should().Be("the project name");
actualProject.Organization.Key.Should().Be("the org key");
actualProject.Organization.Name.Should().Be("the org name");
}

[TestMethod]
[DataRow("the user name", null)]
[DataRow("the user name", "a password")]
[DataRow(null, null)]
[DataRow(null, "should be ignored")]
public async Task DownloadQualityProfile_HandlesBoundProjectCredentialsCorrectly(string userName, string rawPassword)
{
var qpDownloader = new Mock<IQualityProfileDownloader>();
var password = rawPassword == null ? null : rawPassword.ToSecureString();

var connectionInfo = new ConnectionInformation(new Uri("http://any"), userName, password);
var bindingArgs = CreateBindCommandArgs(connection: connectionInfo);

var testSubject = CreateTestSubject(bindingArgs,
qpDownloader: qpDownloader.Object);

// Act
var result = await testSubject.DownloadQualityProfileAsync(Mock.Of<IProgress<FixedStepsProgress>>(), CancellationToken.None);

result.Should().BeTrue();

qpDownloader.Verify(x => x.UpdateAsync(It.IsAny<BoundSonarQubeProject>(),
It.IsAny<IProgress<FixedStepsProgress>>(),
It.IsAny<CancellationToken>()),
Times.Once);

var actualProject = (BoundSonarQubeProject)qpDownloader.Invocations[0].Arguments[0];

// Check the credentials were handled correctly
if (userName == null)
{
actualProject.Credentials.Should().BeNull();
}
else
{
actualProject.Credentials.Should().BeOfType<BasicAuthCredentials>();
var actualCreds = (BasicAuthCredentials)actualProject.Credentials;

actualCreds.UserName.Should().Be(userName);
CheckIsExpectedPassword(rawPassword, actualCreds.Password);
}
actualProject.ServerConnection.ServerUri.Should().Be("http://theServer");
actualProject.ServerProjectKey.Should().Be("the project key");
}

[TestMethod]
Expand All @@ -225,7 +177,7 @@ public async Task DownloadQualityProfile_HandlesInvalidOperationException()
var qpDownloader = new Mock<IQualityProfileDownloader>();
qpDownloader
.Setup(x =>
x.UpdateAsync(It.IsAny<BoundSonarQubeProject>(),
x.UpdateAsync(It.IsAny<BoundServerProject>(),
It.IsAny<IProgress<FixedStepsProgress>>(),
It.IsAny<CancellationToken>()))
.Throws(new InvalidOperationException());
Expand All @@ -238,7 +190,7 @@ public async Task DownloadQualityProfile_HandlesInvalidOperationException()
await testSubject.DownloadQualityProfileAsync(Mock.Of<IProgress<FixedStepsProgress>>(), CancellationToken.None);

result.Should().BeFalse();
qpDownloader.Verify(x => x.UpdateAsync(It.IsAny<BoundSonarQubeProject>(),
qpDownloader.Verify(x => x.UpdateAsync(It.IsAny<BoundServerProject>(),
It.IsAny<IProgress<FixedStepsProgress>>(),
It.IsAny<CancellationToken>()),
Times.Once);
Expand Down Expand Up @@ -267,10 +219,9 @@ private BindingProcessImpl CreateTestSubject(BindCommandArgs bindingArgs = null,
logger);
}

private BindCommandArgs CreateBindCommandArgs(string projectKey = "key", string projectName = "name", ConnectionInformation connection = null)
private BindCommandArgs CreateBindCommandArgs(string projectKey = "key", string serverUri = "http://any")
{
connection = connection ?? new ConnectionInformation(new Uri("http://connected"));
return new BindCommandArgs(projectKey, projectName, connection);
return new BindCommandArgs(new BoundServerProject("any", projectKey, new ServerConnection.SonarQube(new Uri(serverUri))));
}

private static void CheckIsExpectedPassword(string expectedRawPassword, SecureString actualPassword)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ public async Task GetConfig_ReturnsCorrectAdditionalFile()
public async Task GetConfig_HasActiveInactiveAndUnsupportedRules_ReturnsValidBindingConfig()
{
// Arrange
const string expectedProjectName = "my project";
const string expectedServerUrl = "http://myhost:123/";

var properties = new SonarQubeProperty[]
Expand All @@ -207,7 +206,7 @@ public async Task GetConfig_HasActiveInactiveAndUnsupportedRules_ReturnsValidBin
InactiveRuleWithUnsupportedSeverity
};

var builder = new TestEnvironmentBuilder(validQualityProfile, Language.CSharp, expectedProjectName, expectedServerUrl)
var builder = new TestEnvironmentBuilder(validQualityProfile, Language.CSharp, expectedServerUrl)
{
ActiveRulesResponse = activeRules,
InactiveRulesResponse = inactiveRules,
Expand Down Expand Up @@ -278,17 +277,14 @@ private class TestEnvironmentBuilder

private readonly SonarQubeQualityProfile profile;
private readonly Language language;
private readonly string projectName;
private readonly string serverUrl;

private const string ExpectedProjectKey = "fixed.project.key";

public TestEnvironmentBuilder(SonarQubeQualityProfile profile, Language language,
string projectName = "any", string serverUrl = "http://any")
public TestEnvironmentBuilder(SonarQubeQualityProfile profile, Language language, string serverUrl = "http://any")
{
this.profile = profile;
this.language = language;
this.projectName = projectName;
this.serverUrl = serverUrl;

Logger = new TestLogger();
Expand Down Expand Up @@ -342,8 +338,10 @@ public CSharpVBBindingConfigProvider CreateTestSubject()
CapturedRulesPassedToGlobalConfigGenerator = rules;
});

BindingConfiguration = new BindingConfiguration(new BoundSonarQubeProject(new Uri(serverUrl), ExpectedProjectKey, projectName),
SonarLintMode.Connected, "c:\\test\\");
BindingConfiguration = new BindingConfiguration(
new BoundServerProject("solution", ExpectedProjectKey, new ServerConnection.SonarQube(new Uri(serverUrl))),
SonarLintMode.Connected,
"c:\\test\\");

var sonarProperties = PropertiesResponse.ToDictionary(x => x.Key, y => y.Value);
sonarLintConfigGeneratorMock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ public DummyProvider(IBindingConfig configToReturn = null, params Language[] sup

#region IBindingConfigProvider implementation

public Task<IBindingConfig> GetConfigurationAsync(SonarQubeQualityProfile qualityProfile, Language language, BindingConfiguration bindingConfiguration, CancellationToken cancellationToken)
public Task<IBindingConfig> GetConfigurationAsync(SonarQubeQualityProfile qualityProfile, Language language,
BindingConfiguration bindingConfiguration, CancellationToken cancellationToken)
{
return Task.FromResult(ConfigToReturn);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public async Task GetRules_Success()
serviceMock.Setup(x => x.GetRulesAsync(true, It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(() => rules);

var bindingConfiguration = new BindingConfiguration(new BoundSonarQubeProject { ProjectKey = "test" }, SonarLintMode.Connected, "c:\\");
var bindingConfiguration = new BindingConfiguration(new BoundServerProject("any", "any", new ServerConnection.SonarCloud("any")), SonarLintMode.Connected, "c:\\");

var testSubject = CreateTestSubject(serviceMock, testLogger);

Expand Down Expand Up @@ -176,7 +176,7 @@ public async Task GetRules_NoData_EmptyResultReturned()
serviceMock.Setup(x => x.GetRulesAsync(It.IsAny<bool>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(() => new List<SonarQubeRule>());

var bindingConfiguration = new BindingConfiguration(new BoundSonarQubeProject { ProjectKey = "test" }, SonarLintMode.Connected, "c:\\");
var bindingConfiguration = new BindingConfiguration(new BoundServerProject("any", "any", new ServerConnection.SonarCloud("any")), SonarLintMode.Connected, "c:\\");

var testSubject = CreateTestSubject(serviceMock, testLogger);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,81 +18,118 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System;
using System.Collections.Generic;
using System.Threading;
using System.Security;
using SonarLint.VisualStudio.ConnectedMode.Binding;
using SonarLint.VisualStudio.ConnectedMode.Persistence;
using SonarLint.VisualStudio.Core;
using SonarLint.VisualStudio.Core.Binding;
using SonarLint.VisualStudio.TestInfrastructure;
using SonarQube.Client;
using SonarQube.Client.Helpers;
using SonarQube.Client.Models;
using Task = System.Threading.Tasks.Task;

namespace SonarLint.VisualStudio.ConnectedMode.Binding.UnitTests
namespace SonarLint.VisualStudio.ConnectedMode.UnitTests.Binding;

[TestClass]
public class UnintrusiveBindingControllerTests
{
[TestClass]
public class UnintrusiveBindingControllerTests
{
private static readonly BoundSonarQubeProject AnyBoundProject = new BoundSonarQubeProject(new Uri("http://localhost:9000"), "any-key", "any-name");
private static readonly CancellationToken ACancellationToken = CancellationToken.None;
private static readonly BasicAuthCredentials ValidToken = new ("TOKEN", new SecureString());
private static readonly BoundServerProject AnyBoundProject = new ("any", "any", new ServerConnection.SonarCloud("any", credentials: ValidToken));

[TestMethod]
public void MefCtor_CheckTypeIsNonShared()
=> MefTestHelpers.CheckIsNonSharedMefComponent<UnintrusiveBindingController>();
[TestMethod]
public void MefCtor_CheckTypeIsNonShared()
=> MefTestHelpers.CheckIsNonSharedMefComponent<UnintrusiveBindingController>();

[TestMethod]
public void MefCtor_CheckIsExported()
{
MefTestHelpers.CheckTypeCanBeImported<UnintrusiveBindingController, IUnintrusiveBindingController>(
MefTestHelpers.CreateExport<IBindingProcessFactory>());
}
[TestMethod]
public void MefCtor_IUnintrusiveBindingController_CheckIsExported()
{
MefTestHelpers.CheckTypeCanBeImported<UnintrusiveBindingController, IUnintrusiveBindingController>(
MefTestHelpers.CreateExport<IBindingProcessFactory>(),
MefTestHelpers.CreateExport<ISonarQubeService>(),
MefTestHelpers.CreateExport<IActiveSolutionChangedHandler>());
}

[TestMethod]
public void MefCtor_IBindingController_CheckIsExported()
{
MefTestHelpers.CheckTypeCanBeImported<UnintrusiveBindingController, IBindingController>(
MefTestHelpers.CreateExport<IBindingProcessFactory>(),
MefTestHelpers.CreateExport<ISonarQubeService>(),
MefTestHelpers.CreateExport<IActiveSolutionChangedHandler>());
}

[TestMethod]
public async Task BindAsnyc_GetsBindingProcessFromFactory()
{
var bindingProcessFactory = CreateBindingProcessFactory();
[TestMethod]
public async Task BindAsync_EstablishesConnection()
{
var sonarQubeService = Substitute.For<ISonarQubeService>();
var projectToBind = new BoundServerProject(
"local-key",
"server-key",
new ServerConnection.SonarCloud("organization", credentials: ValidToken));
var testSubject = CreateTestSubject(sonarQubeService: sonarQubeService);

await testSubject.BindAsync(projectToBind, ACancellationToken);

await sonarQubeService
.Received()
.ConnectAsync(
Arg.Is<ConnectionInformation>(x => x.ServerUri.Equals("https://sonarcloud.io/")
&& x.UserName.Equals(ValidToken.UserName)
&& string.IsNullOrEmpty(x.Password.ToUnsecureString())),
ACancellationToken);
}

[TestMethod]
public async Task BindAsync_NotifiesBindingChanged()
{
var activeSolutionChangedHandler = Substitute.For<IActiveSolutionChangedHandler>();
var testSubject = CreateTestSubject(activeSolutionChangedHandler: activeSolutionChangedHandler);

await testSubject.BindAsync(AnyBoundProject, ACancellationToken);

activeSolutionChangedHandler
.Received(1)
.HandleBindingChange(false);
}

var testSubject = CreateTestSubject(bindingProcessFactory: bindingProcessFactory.Object);
await testSubject.BindAsync(AnyBoundProject, null, CancellationToken.None);
[TestMethod]
public async Task BindAsync_CallsBindingProcessInOrder()
{
var cancellationToken = CancellationToken.None;
var bindingProcess = Substitute.For<IBindingProcess>();
var bindingProcessFactory = CreateBindingProcessFactory(bindingProcess);
var testSubject = CreateTestSubject(bindingProcessFactory);

var args = bindingProcessFactory.Invocations[0].Arguments[0] as BindCommandArgs;
await testSubject.BindAsync(AnyBoundProject, null, cancellationToken);

args.ProjectName.Should().Be(AnyBoundProject.ProjectName);
args.ProjectKey.Should().Be(AnyBoundProject.ProjectKey);
args.Connection.ServerUri.Should().Be(AnyBoundProject.ServerUri);

bindingProcessFactory.Verify(x => x.Create(It.IsAny<BindCommandArgs>()), Times.Once);
}

[TestMethod]
public async Task BindAsnyc_CallsBindingProcessInOrder()
{
var calls = new List<string>();
var cancellationToken = CancellationToken.None;

var bindingProcess = new Mock<IBindingProcess>();
bindingProcess.Setup(x => x.DownloadQualityProfileAsync(null, cancellationToken)).Callback(() => calls.Add("DownloadQualityProfiles"));
bindingProcess.Setup(x => x.SaveServerExclusionsAsync(cancellationToken)).Callback(() => calls.Add("SaveServerExclusionsAsync"));

var testSubject = CreateTestSubject(bindingProcessFactory: CreateBindingProcessFactory(bindingProcess.Object).Object);
await testSubject.BindAsync(AnyBoundProject, null, cancellationToken);

calls.Should().ContainInOrder("DownloadQualityProfiles", "SaveServerExclusionsAsync");
}

private UnintrusiveBindingController CreateTestSubject(IBindingProcessFactory bindingProcessFactory = null)
Received.InOrder(() =>
{
bindingProcessFactory ??= CreateBindingProcessFactory().Object;

var testSubject = new UnintrusiveBindingController(bindingProcessFactory);
bindingProcessFactory.Create(Arg.Is<BindCommandArgs>(b => b.ProjectToBind == AnyBoundProject));
bindingProcess.DownloadQualityProfileAsync(null, cancellationToken);
bindingProcess.SaveServerExclusionsAsync(cancellationToken);
});
}

private UnintrusiveBindingController CreateTestSubject(IBindingProcessFactory bindingProcessFactory = null,
ISonarQubeService sonarQubeService = null,
IActiveSolutionChangedHandler activeSolutionChangedHandler = null)
{
var testSubject = new UnintrusiveBindingController(bindingProcessFactory ?? CreateBindingProcessFactory(),
sonarQubeService ?? Substitute.For<ISonarQubeService>(),
activeSolutionChangedHandler ?? Substitute.For<IActiveSolutionChangedHandler>());

return testSubject;
}
return testSubject;
}

private Mock<IBindingProcessFactory> CreateBindingProcessFactory(IBindingProcess bindingProcess = null)
{
bindingProcess ??= Mock.Of<IBindingProcess>();
private static IBindingProcessFactory CreateBindingProcessFactory(IBindingProcess bindingProcess = null)
{
bindingProcess ??= Substitute.For<IBindingProcess>();

var bindingProcessFactory = new Mock<IBindingProcessFactory>();
bindingProcessFactory.Setup(x => x.Create(It.IsAny<BindCommandArgs>())).Returns(bindingProcess);
var bindingProcessFactory = Substitute.For<IBindingProcessFactory>();
bindingProcessFactory.Create(Arg.Any<BindCommandArgs>()).Returns(bindingProcess);

return bindingProcessFactory;
}
return bindingProcessFactory;
}
}
Loading
Loading