Skip to content

Commit

Permalink
feat: upload exceptions to s3 (#62)
Browse files Browse the repository at this point in the history
* feat: upload exceptions to s3

* chore: fix broken tests

* fix: use utf8 for callstack.txt file

* feat: added support for notes field

* fix: notes default should be string.Empty
  • Loading branch information
bobbyg603 authored Jan 21, 2023
1 parent 1f8f5bd commit c4b86c1
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 128 deletions.
111 changes: 31 additions & 80 deletions BugSplatDotNetStandard.Test/Api/CrashPostClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
Expand Down Expand Up @@ -35,125 +36,75 @@ public void CrashPostClient_Constructor_ShouldThrowIfS3ClientFactoryIsNullOrEmpt
}

[Test]
public void CrashPostClient_PostException_ShouldCallPostAsyncWithUriAndMultipartFormDataContent()
public async Task CrashPostClient_PostException_ShouldReturn200()
{
var expectedUri = $"https://{database}.bugsplat.com/post/dotnetstandard/";
var expectedUri = $"https://{database}.bugsplat.com/api/getCrashUploadUrl?database={database}&appName={application}&appVersion={version}";
var stackTrace = CreateStackTrace();
var bugsplat = new BugSplat(database, application, version);
bugsplat.Description = "dangit bobby";
bugsplat.Email = "[email protected]";
bugsplat.IpAddress = "192.168.0.1";
bugsplat.Key = "en-US";
bugsplat.User = "@bobbyg603";
var expectedFormDataParams = new List<string>() {
"name=database", database,
"name=appName", application,
"name=appVersion", version,
"name=description", bugsplat.Description,
"name=email", bugsplat.Email,
"name=appKey", bugsplat.Key,
"name=user", bugsplat.User,
"name=callstack", stackTrace,
"name=crashTypeId", $"{(int)bugsplat.ExceptionType}"
};
var mockHttp = CreateMockHttpClientForAuthenticateSuccess();
var getCrashUrl = "https://fake.url.com";
var mockHttp = CreateMockHttpClientForExceptionPost(getCrashUrl);
var httpClient = new HttpClient(mockHttp.Object);
var httpClientFactory = new FakeHttpClientFactory(httpClient);
var mockS3ClientFactory = FakeS3ClientFactory.CreateMockS3ClientFactory();

var sut = new CrashPostClient(httpClientFactory, S3ClientFactory.Default);
var sut = new CrashPostClient(httpClientFactory, mockS3ClientFactory);

var postResult = sut.PostException(
var postResult = await sut.PostException(
database,
application,
version,
stackTrace,
bugsplat
);

mockHttp.Protected().Verify(
"SendAsync",
Times.Exactly(1),
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Post
&& req.RequestUri.ToString().Equals(expectedUri)
&& ContainsValues(
req.Content.ReadAsStringAsync().Result,
expectedFormDataParams
)
),
ItExpr.IsAny<CancellationToken>()
);
Assert.AreEqual(HttpStatusCode.OK, postResult.StatusCode);
}

[Test]
public void CrashPostClient_PostException_ShouldCallPostAsyncWithUriAndMultipartFormDataContentOverrides()
public async Task CrashPostClient_PostMinidump_ShouldReturn200()
{
var stackTrace = CreateStackTrace();
var expectedUri = $"https://{database}.bugsplat.com/api/getCrashUploadUrl?database={database}&appName={application}&appVersion={version}";
var bugsplat = new BugSplat(database, application, version);
var overrideOptions = new ExceptionPostOptions();
overrideOptions.Description = "dangit bobby";
overrideOptions.Email = "[email protected]";
overrideOptions.IpAddress = "192.168.0.1";
overrideOptions.Key = "en-US";
overrideOptions.User = "@bobbyg603";
overrideOptions.ExceptionType = BugSplat.ExceptionTypeId.Unity;
var expectedFormDataParams = new List<string>() {
"name=database", database,
"name=appName", application,
"name=appVersion", version,
"name=description", overrideOptions.Description,
"name=email", overrideOptions.Email,
"name=appKey", overrideOptions.Key,
"name=user", overrideOptions.User,
"name=callstack", stackTrace,
"name=crashTypeId", $"{(int)overrideOptions.ExceptionType}"
};
var mockHttp = CreateMockHttpClientForAuthenticateSuccess();
var getCrashUrl = "https://fake.url.com";
var mockHttp = CreateMockHttpClientForExceptionPost(getCrashUrl);
var httpClient = new HttpClient(mockHttp.Object);
var httpClientFactory = new FakeHttpClientFactory(httpClient);
var mockS3ClientFactory = FakeS3ClientFactory.CreateMockS3ClientFactory();

var sut = new CrashPostClient(httpClientFactory, S3ClientFactory.Default);
var sut = new CrashPostClient(httpClientFactory, mockS3ClientFactory);

var postResult = sut.PostException(
var postResult = await sut.PostMinidump(
database,
application,
version,
stackTrace,
bugsplat,
overrideOptions
new FileInfo("Files/minidump.dmp"),
bugsplat
);

mockHttp.Protected().Verify(
"SendAsync",
Times.Exactly(1),
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Post
&& ContainsValues(
req.Content.ReadAsStringAsync().Result,
expectedFormDataParams
)
),
ItExpr.IsAny<CancellationToken>()
);
Assert.AreEqual(HttpStatusCode.OK, postResult.StatusCode);
}

private Mock<HttpMessageHandler> CreateMockHttpClientForAuthenticateSuccess()
private Mock<HttpMessageHandler> CreateMockHttpClientForExceptionPost(string crashUploadUrl)
{
var response = new HttpResponseMessage();
response.Content = new StringContent("");
response.StatusCode = HttpStatusCode.OK;
var getCrashUploadUrlResponse = new HttpResponseMessage();
getCrashUploadUrlResponse.StatusCode = HttpStatusCode.OK;
getCrashUploadUrlResponse.Content = new StringContent($"{{ \"url\": \"{crashUploadUrl}\" }}");

var commitCrashUploadUrlReponse = new HttpResponseMessage();
commitCrashUploadUrlReponse.StatusCode = HttpStatusCode.OK;

var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
.SetupSequence<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(response)
.Verifiable();
.ReturnsAsync(getCrashUploadUrlResponse)
.ReturnsAsync(commitCrashUploadUrlReponse);

return handlerMock;
}
}
}
}
19 changes: 3 additions & 16 deletions BugSplatDotNetStandard.Test/Api/VersionsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void VersionsClient_UploadSymbolsFile_ShouldDeleteZipFileAfterUpload()
var zipFileFullName = "test.zip";
var symbolFileInfo = new FileInfo("Files/myConsoleCrasher.pdb");
var mockApiClient = CreateMockBugSplatApiClient();
var mockS3ClientFactory = CreateMockS3ClientFactory();
var mockS3ClientFactory = FakeS3ClientFactory.CreateMockS3ClientFactory();
var mockZipUtils = CreateMockZipUtils(zipFileFullName);
var sut = new VersionsClient(mockApiClient, mockS3ClientFactory);
sut.ZipUtils = mockZipUtils;
Expand All @@ -106,19 +106,6 @@ public void VersionsClient_UploadSymbolsFile_ShouldDeleteZipFileAfterUpload()
Assert.False(File.Exists(zipFileFullName));
}

private IS3ClientFactory CreateMockS3ClientFactory()
{
var s3Response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
};
var mockS3Client = new Mock<IS3Client>();
mockS3Client
.Setup(s => s.UploadFileStreamToPresignedURL(It.IsAny<Uri>(), It.IsAny<Stream>()))
.Returns(() => Task.Factory.StartNew(() => s3Response));
return new FakeS3ClientFactory(mockS3Client.Object);
}

private IBugSplatApiClient CreateMockBugSplatApiClient()
{
var presignedUrlResponse = new HttpResponseMessage()
Expand All @@ -131,7 +118,7 @@ private IBugSplatApiClient CreateMockBugSplatApiClient()
.Returns(true);
mockApiClient
.Setup(c => c.PostAsync(It.IsAny<string>(), It.IsAny<HttpContent>()))
.Returns(() => Task.Factory.StartNew(() => presignedUrlResponse));
.ReturnsAsync(presignedUrlResponse);
return mockApiClient.Object;
}

Expand All @@ -146,7 +133,7 @@ private IZipUtils CreateMockZipUtils(string zipFileFullName)
var mockZipUtils = new Mock<IZipUtils>();
mockZipUtils
.Setup(z => z.CreateZipFileFullName(It.IsAny<string>()))
.Returns(() => zipFileFullName);
.Returns(zipFileFullName);
mockZipUtils
.Setup(z => z.CreateZipFile(It.IsAny<string>(), It.IsAny<IEnumerable<FileInfo>>()))
.Returns(zipFileInfo);
Expand Down
12 changes: 9 additions & 3 deletions BugSplatDotNetStandard.Test/BugSplat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,15 @@ public void BugSplat_Post_ShouldPostExceptionToBugSplat()
sut.Email = "[email protected] - overridden";
sut.User = "Default - overridden";
sut.Key = "Default - overridden";
sut.Notes = "Default - overridden";
var options = new ExceptionPostOptions()
{
ExceptionType = BugSplat.ExceptionTypeId.DotNetStandard,
Description = "BugSplat rocks!",
Email = "[email protected]",
User = "Fred",
Key = "the key!"
Key = "the key!",
Notes = "the notes!"
};
options.Attachments.Add(new FileInfo("Files/attachment.txt"));
var response = sut.Post(ex, options).Result;
Expand All @@ -115,13 +117,15 @@ public void BugSplat_Post_ShouldPostMinidumpToBugSplat()
sut.Email = "[email protected] - overridden";
sut.User = "Default - overridden";
sut.Key = "Default - overridden";
sut.Notes = "Default - overridden";
var options = new MinidumpPostOptions()
{
MinidumpType = BugSplat.MinidumpTypeId.UnityNativeWindows,
Description = "BugSplat rocks!",
Email = "[email protected]",
User = "Fred",
Key = "the key!"
Key = "the key!",
Notes = "the notes!"
};
options.Attachments.Add(new FileInfo("Files/attachment.txt"));

Expand Down Expand Up @@ -151,14 +155,16 @@ public void BugSplat_Post_ShouldPostStackTraceToBugSplat()
sut.Email = "[email protected] - overridden";
sut.User = "Default - overridden";
sut.Key = "Default - overridden";
sut.Notes = "Default - overridden";
var stackTrace = CreateStackTrace();
var options = new ExceptionPostOptions()
{
ExceptionType = BugSplat.ExceptionTypeId.UnityLegacy,
Description = "BugSplat rocks!",
Email = "[email protected]",
User = "Fred",
Key = "the key!"
Key = "the key!",
Notes = "the notes!"
};
options.Attachments.Add(new FileInfo("Files/attachment.txt"));
var response = sut.Post(stackTrace, options).Result;
Expand Down
28 changes: 28 additions & 0 deletions BugSplatDotNetStandard.Test/Helpers/FakeS3ClientFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using BugSplatDotNetStandard.Http;
using Moq;

namespace Tests
{
Expand All @@ -14,5 +19,28 @@ public IS3Client CreateClient()
{
return this.client;
}

public static IS3ClientFactory CreateMockS3ClientFactory()
{
var s3UploadFileStreamResponse = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
};
var s3UploadFileBytesResponse = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
};
s3UploadFileBytesResponse.Headers.Add("ETag", "\"test\"");

var mockS3Client = new Mock<IS3Client>();
mockS3Client
.Setup(s => s.UploadFileStreamToPresignedURL(It.IsAny<Uri>(), It.IsAny<Stream>()))
.ReturnsAsync(s3UploadFileStreamResponse);
mockS3Client
.Setup(s => s.UploadFileBytesToPresignedURL(It.IsAny<Uri>(), It.IsAny<byte[]>()))
.ReturnsAsync(s3UploadFileBytesResponse);

return new FakeS3ClientFactory(mockS3Client.Object);
}
}
}
Loading

0 comments on commit c4b86c1

Please sign in to comment.