Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 7481136

Browse files
authored
Merge pull request #482 from github/fixes/477-crash-pr-empty-body
Fix crasher when creating a PR with nothing in the body
2 parents 78ac77f + bdd9b97 commit 7481136

File tree

9 files changed

+121
-16
lines changed

9 files changed

+121
-16
lines changed

src/GitHub.App/Services/PullRequestService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@
44

55
namespace GitHub.Services
66
{
7+
[NullGuard.NullGuard(NullGuard.ValidationFlags.None)]
78
[Export(typeof(IPullRequestService))]
89
[PartCreationPolicy(CreationPolicy.Shared)]
910
public class PullRequestService : IPullRequestService
1011
{
1112
public IObservable<IPullRequestModel> CreatePullRequest(IRepositoryHost host, ISimpleRepositoryModel repository, string title, string body, IBranch source, IBranch target)
1213
{
14+
Extensions.Guard.ArgumentNotNull(host, nameof(host));
15+
Extensions.Guard.ArgumentNotNull(repository, nameof(repository));
16+
Extensions.Guard.ArgumentNotNull(title, nameof(title));
17+
Extensions.Guard.ArgumentNotNull(body, nameof(body));
18+
Extensions.Guard.ArgumentNotNull(source, nameof(source));
19+
Extensions.Guard.ArgumentNotNull(target, nameof(target));
20+
1321
return host.ModelService.CreatePullRequest(repository, title, body, source, target);
1422
}
1523
}

src/GitHub.App/ViewModels/PullRequestCreationViewModel.cs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System.Reactive;
1818
using System.Diagnostics.CodeAnalysis;
1919
using Octokit;
20+
using NLog;
2021

2122
namespace GitHub.ViewModels
2223
{
@@ -25,6 +26,8 @@ namespace GitHub.ViewModels
2526
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
2627
public class PullRequestCreationViewModel : BaseViewModel, IPullRequestCreationViewModel
2728
{
29+
static readonly Logger log = LogManager.GetCurrentClassLogger();
30+
2831
readonly IRepositoryHost repositoryHost;
2932
readonly ISimpleRepositoryModel activeRepo;
3033
readonly Subject<Unit> initializationComplete = new Subject<Unit>();
@@ -36,7 +39,7 @@ public class PullRequestCreationViewModel : BaseViewModel, IPullRequestCreationV
3639
IPullRequestService service, INotificationService notifications)
3740
: this(connectionRepositoryHostMap.CurrentRepositoryHost, teservice.ActiveRepo, service, notifications)
3841
{}
39-
42+
4043
public PullRequestCreationViewModel(IRepositoryHost repositoryHost, ISimpleRepositoryModel activeRepo,
4144
IPullRequestService service, INotificationService notifications)
4245
{
@@ -78,18 +81,18 @@ public PullRequestCreationViewModel(IRepositoryHost repositoryHost, ISimpleRepos
7881
.Subscribe(x => notifications.ShowError(BranchValidator.ValidationResult.Message));
7982

8083
createPullRequest = ReactiveCommand.CreateAsyncObservable(whenAnyValidationResultChanges,
81-
_ => service.CreatePullRequest(repositoryHost, activeRepo, PRTitle, Description, SourceBranch, TargetBranch)
82-
);
83-
createPullRequest.ThrownExceptions.Subscribe(ex =>
84-
{
85-
if (!ex.IsCriticalException())
86-
{
87-
//TODO:Will need a uniform solution to HTTP exception message handling
88-
var apiException = ex as ApiValidationException;
89-
var error = apiException?.ApiError?.Errors?.FirstOrDefault();
90-
notifications.ShowError(error?.Message ?? ex.Message);
91-
}
92-
});
84+
_ => service
85+
.CreatePullRequest(repositoryHost, activeRepo, PRTitle, Description ?? String.Empty, SourceBranch, TargetBranch)
86+
.Catch<IPullRequestModel, Exception>(ex =>
87+
{
88+
log.Error(ex);
89+
90+
//TODO:Will need a uniform solution to HTTP exception message handling
91+
var apiException = ex as ApiValidationException;
92+
var error = apiException?.ApiError?.Errors?.FirstOrDefault();
93+
notifications.ShowError(error?.Message ?? ex.Message);
94+
return Observable.Empty<IPullRequestModel>();
95+
}));
9396
}
9497

9598
public override void Initialize([AllowNull] ViewWithData data)

src/UnitTests/GitHub.App/Models/RepositoryModelTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ public class PathConstructorTests : TempFileBaseClass
6060
public void NoRemoteUrl()
6161
{
6262
var provider = Substitutes.ServiceProvider;
63-
Services.PackageServiceProvider = provider;
6463
var gitservice = provider.GetGitService();
6564
var repo = Substitute.For<IRepository>();
6665
var path = Directory.CreateSubdirectory("repo-name");
@@ -73,7 +72,6 @@ public void NoRemoteUrl()
7372
public void WithRemoteUrl()
7473
{
7574
var provider = Substitutes.ServiceProvider;
76-
Services.PackageServiceProvider = provider;
7775
var gitservice = provider.GetGitService();
7876
var repo = Substitute.For<IRepository>();
7977
var path = Directory.CreateSubdirectory("repo-name");
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System.Reactive.Linq;
2+
using System.Threading.Tasks;
3+
using NSubstitute;
4+
using Xunit;
5+
using UnitTests;
6+
using GitHub.Models;
7+
using System;
8+
using GitHub.Services;
9+
10+
public class PullRequestServiceTests : TestBaseClass
11+
{
12+
[Fact]
13+
public async Task CreatePullRequestAllArgsMandatory()
14+
{
15+
var serviceProvider = Substitutes.ServiceProvider;
16+
var service = new PullRequestService();
17+
18+
IRepositoryHost host = null;
19+
ISimpleRepositoryModel repository = null;
20+
string title = null;
21+
string body = null;
22+
IBranch source = null;
23+
IBranch target = null;
24+
25+
Assert.Throws<ArgumentNullException>(() => service.CreatePullRequest(host, repository, title, body, source, target));
26+
27+
host = serviceProvider.GetRepositoryHosts().GitHubHost;
28+
Assert.Throws<ArgumentNullException>(() => service.CreatePullRequest(host, repository, title, body, source, target));
29+
30+
repository = new SimpleRepositoryModel("name", new GitHub.Primitives.UriString("http://github.com/github/stuff"));
31+
Assert.Throws<ArgumentNullException>(() => service.CreatePullRequest(host, repository, title, body, source, target));
32+
33+
title = "a title";
34+
Assert.Throws<ArgumentNullException>(() => service.CreatePullRequest(host, repository, title, body, source, target));
35+
36+
body = "a body";
37+
Assert.Throws<ArgumentNullException>(() => service.CreatePullRequest(host, repository, title, body, source, target));
38+
39+
source = new BranchModel() { Name = "source" };
40+
Assert.Throws<ArgumentNullException>(() => service.CreatePullRequest(host, repository, title, body, source, target));
41+
42+
target = new BranchModel() { Name = "target" };
43+
var pr = await service.CreatePullRequest(host, repository, title, body, source, target);
44+
45+
Assert.NotNull(pr);
46+
}
47+
48+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Reactive.Linq;
2+
using System.Threading.Tasks;
3+
using NSubstitute;
4+
using Xunit;
5+
using UnitTests;
6+
using GitHub.Models;
7+
using System;
8+
using GitHub.Services;
9+
using GitHub.ViewModels;
10+
11+
public class PullRequestCreationViewModelTests : TempFileBaseClass
12+
{
13+
[Fact]
14+
public async Task NullDescriptionBecomesEmptyBody()
15+
{
16+
var serviceProvider = Substitutes.ServiceProvider;
17+
var service = new PullRequestService();
18+
var notifications = Substitute.For<INotificationService>();
19+
20+
var host = serviceProvider.GetRepositoryHosts().GitHubHost;
21+
var ms = Substitute.For<IModelService>();
22+
host.ModelService.Returns(ms);
23+
24+
var repository = new SimpleRepositoryModel("name", new GitHub.Primitives.UriString("http://github.com/github/stuff"));
25+
var title = "a title";
26+
27+
var vm = new PullRequestCreationViewModel(host, repository, service, notifications);
28+
vm.SourceBranch = new BranchModel() { Name = "source" };
29+
vm.TargetBranch = new BranchModel() { Name = "target" };
30+
vm.PRTitle = title;
31+
32+
await vm.CreatePullRequest.ExecuteAsync();
33+
var unused = ms.Received().CreatePullRequest(repository, vm.PRTitle, String.Empty, vm.SourceBranch, vm.TargetBranch);
34+
}
35+
36+
}

src/UnitTests/GitHub.Exports/SimpleRepositoryModelTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ public class SimpleRepositoryModelTests : TempFileBaseClass
1313
void SetupRepository(string sha)
1414
{
1515
var provider = Substitutes.ServiceProvider;
16-
Services.PackageServiceProvider = provider;
1716
var gitservice = provider.GetGitService();
1817
var repo = Substitute.For<IRepository>();
1918
gitservice.GetRepo(Args.String).Returns(repo);

src/UnitTests/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Reflection;
22
using System.Runtime.CompilerServices;
33
using System.Runtime.InteropServices;
4+
using Xunit;
45

56
// General Information about an assembly is controlled through the following
67
// set of attributes. Change these attribute values to modify the information
@@ -34,3 +35,4 @@
3435
// [assembly: AssemblyVersion("1.0.*")]
3536
[assembly: AssemblyVersion("1.0.0.0")]
3637
[assembly: AssemblyFileVersion("1.0.0.0")]
38+
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)]

src/UnitTests/Substitutes.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using GitHub.Authentication;
33
using GitHub.Models;
44
using GitHub.Services;
5+
using GitHub.VisualStudio;
56
using Microsoft.VisualStudio.ComponentModelHost;
67
using NSubstitute;
78
using Rothko;
@@ -65,6 +66,7 @@ public static IOperatingSystem OperatingSystem
6566
public static IConnectionManager ConnectionManager { get { return Substitute.For<IConnectionManager>(); } }
6667
public static ITwoFactorChallengeHandler TwoFactorChallengeHandler { get { return Substitute.For<ITwoFactorChallengeHandler>(); } }
6768
public static IGistPublishService GistPublishService { get { return Substitute.For<IGistPublishService>(); } }
69+
public static IPullRequestService PullRequestService { get { return Substitute.For<IPullRequestService>(); } }
6870

6971
/// <summary>
7072
/// This returns a service provider with everything mocked except for
@@ -106,6 +108,7 @@ public static IServiceProvider GetServiceProvider(
106108
cc.ComposeExportedValue(gitservice);
107109
((IComponentModel)cm).DefaultExportProvider.Returns(cc);
108110
ret.GetService(typeof(SComponentModel)).Returns(cm);
111+
Services.PackageServiceProvider = ret;
109112

110113
var os = OperatingSystem;
111114
var vs = IVSServices;
@@ -126,6 +129,7 @@ public static IServiceProvider GetServiceProvider(
126129
ret.GetService(typeof(IAvatarProvider)).Returns(avatarProvider);
127130
ret.GetService(typeof(ITwoFactorChallengeHandler)).Returns(TwoFactorChallengeHandler);
128131
ret.GetService(typeof(IGistPublishService)).Returns(GistPublishService);
132+
ret.GetService(typeof(IPullRequestService)).Returns(PullRequestService);
129133
return ret;
130134
}
131135

@@ -197,5 +201,10 @@ public static IGistPublishService GetGistPublishService(this IServiceProvider pr
197201
{
198202
return provider.GetService(typeof(IGistPublishService)) as IGistPublishService;
199203
}
204+
205+
public static IPullRequestService GetPullRequestsService(this IServiceProvider provider)
206+
{
207+
return provider.GetService(typeof(IPullRequestService)) as IPullRequestService;
208+
}
200209
}
201210
}

src/UnitTests/UnitTests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,13 @@
166166
<Compile Include="GitHub.App\Models\RepositoryModelTests.cs" />
167167
<Compile Include="GitHub.App\Services\AvatarProviderTests.cs" />
168168
<Compile Include="GitHub.App\Services\GitClientTests.cs" />
169+
<Compile Include="GitHub.App\Services\PullRequestServiceTests.cs" />
169170
<Compile Include="GitHub.App\Services\RepositoryCloneServiceTests.cs" />
170171
<Compile Include="GitHub.App\Services\RepositoryCreationServiceTests.cs" />
171172
<Compile Include="GitHub.App\ViewModels\GistCreationViewModelTests.cs" />
172173
<Compile Include="GitHub.App\ViewModels\LoginControlViewModelTests.cs" />
173174
<Compile Include="GitHub.App\ViewModels\LoginToGitHubViewModelTests.cs" />
175+
<Compile Include="GitHub.App\ViewModels\PullRequestCreationViewModelTests.cs" />
174176
<Compile Include="GitHub.App\ViewModels\PullRequestListViewModelTests.cs" />
175177
<Compile Include="GitHub.App\ViewModels\RepositoryCloneViewModelTests.cs" />
176178
<Compile Include="GitHub.App\ViewModels\RepositoryCreationViewModelTests.cs" />

0 commit comments

Comments
 (0)