Skip to content

Add extension methods to ControllerBase for TwiML #45

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

Merged
merged 1 commit into from
Mar 10, 2022
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Twilio.AspNet.Core.MinimalApi;
Expand All @@ -12,11 +11,11 @@ namespace Twilio.AspNet.Core.UnitTests;
public class MinimalApiTwiMLResultTests
{
[Fact]
public Task TestTwimlResultWritesVoiceResponseToResponseBody() =>
public Task TwimlResult_Should_Write_VoiceResponse_To_ResponseBody() =>
ValidateTwimlResultWritesToResponseBody(GetVoiceResponse());

[Fact]
public Task TestTwimlResultWritesMessagingResponseToResponseBody() =>
public Task TwimlResult_Should_Write_MessagingResponse_To_ResponseBody() =>
ValidateTwimlResultWritesToResponseBody(GetMessagingResponse());

private static async Task ValidateTwimlResultWritesToResponseBody(TwiML.TwiML twiMlResponse)
Expand Down
239 changes: 119 additions & 120 deletions src/Twilio.AspNet.Core.UnitTests/RequestValidationHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,171 +9,170 @@
using Twilio.AspNet.Core;
using Xunit;

namespace Twilio.AspNet.Core.UnitTests
namespace Twilio.AspNet.Core.UnitTests;

public class ContextMocks
{
public class ContextMocks
public Moq.Mock<HttpContext> HttpContext { get; set; }
public Moq.Mock<HttpRequest> Request { get; set; }

public ContextMocks(bool isLocal, FormCollection? form = null, bool isProxied = false) : this("", isLocal, form, isProxied)
{
public Moq.Mock<HttpContext> HttpContext { get; set; }
public Moq.Mock<HttpRequest> Request { get; set; }
}

public ContextMocks(bool isLocal, FormCollection? form = null, bool isProxied = false) : this("", isLocal, form, isProxied)
public ContextMocks(string urlOverride, bool isLocal, FormCollection? form = null, bool isProxied = false)
{
var headers = new HeaderDictionary();
headers.Add("X-Twilio-Signature", CalculateSignature(urlOverride, form));
if (isProxied)
{
headers.Add("X-Forwarded-For", "1.1.1.1");
}

public ContextMocks(string urlOverride, bool isLocal, FormCollection? form = null, bool isProxied = false)
{
var headers = new HeaderDictionary();
headers.Add("X-Twilio-Signature", CalculateSignature(urlOverride, form));
if (isProxied)
{
headers.Add("X-Forwarded-For", "1.1.1.1");
}
var connectionInfo = new Moq.Mock<ConnectionInfo>();
connectionInfo.Setup(x => x.RemoteIpAddress).Returns(isLocal ? IPAddress.Loopback : IPAddress.Parse("1.1.1.1"));

var connectionInfo = new Moq.Mock<ConnectionInfo>();
connectionInfo.Setup(x => x.RemoteIpAddress).Returns(isLocal ? IPAddress.Loopback : IPAddress.Parse("1.1.1.1"));
HttpContext = new Moq.Mock<HttpContext>();
Request = new Moq.Mock<HttpRequest>();
HttpContext.Setup(x => x.Request).Returns(Request.Object);
HttpContext.Setup(x => x.Connection).Returns(connectionInfo.Object);
Request.Setup(x => x.Headers).Returns(headers);
Request.Setup(x => x.HttpContext).Returns(HttpContext.Object);

HttpContext = new Moq.Mock<HttpContext>();
Request = new Moq.Mock<HttpRequest>();
HttpContext.Setup(x => x.Request).Returns(Request.Object);
HttpContext.Setup(x => x.Connection).Returns(connectionInfo.Object);
Request.Setup(x => x.Headers).Returns(headers);
Request.Setup(x => x.HttpContext).Returns(HttpContext.Object);
var uri = new Uri(ContextMocks.fakeUrl);
Request.Setup(x => x.QueryString).Returns(new QueryString(uri.Query));
Request.Setup(x => x.Scheme).Returns(uri.Scheme);
Request.Setup(x => x.Host).Returns(new HostString(uri.Host));
Request.Setup(x => x.Path).Returns(new PathString(uri.AbsolutePath));

var uri = new Uri(ContextMocks.fakeUrl);
Request.Setup(x => x.QueryString).Returns(new QueryString(uri.Query));
Request.Setup(x => x.Scheme).Returns(uri.Scheme);
Request.Setup(x => x.Host).Returns(new HostString(uri.Host));
Request.Setup(x => x.Path).Returns(new PathString(uri.AbsolutePath));

if (form != null)
{
Request.Setup(x => x.Method).Returns("POST");
Request.Setup(x => x.Form).Returns(form);
}
if (form != null)
{
Request.Setup(x => x.Method).Returns("POST");
Request.Setup(x => x.Form).Returns(form);
}
}

public static string fakeUrl = "https://api.example.com/webhook";
public static string fakeAuthToken = "thisisafakeauthtoken";

private string CalculateSignature(string urlOverride, FormCollection? form)
{
var value = new StringBuilder();
value.Append(string.IsNullOrEmpty(urlOverride) ? ContextMocks.fakeUrl : urlOverride);
public static string fakeUrl = "https://api.example.com/webhook";
public static string fakeAuthToken = "thisisafakeauthtoken";

if (form != null)
private string CalculateSignature(string urlOverride, FormCollection? form)
{
var value = new StringBuilder();
value.Append(string.IsNullOrEmpty(urlOverride) ? ContextMocks.fakeUrl : urlOverride);

if (form != null)
{
var sortedKeys = form.Keys.OrderBy(k => k, StringComparer.Ordinal).ToList();
foreach (var key in sortedKeys)
{
var sortedKeys = form.Keys.OrderBy(k => k, StringComparer.Ordinal).ToList();
foreach (var key in sortedKeys)
{
value.Append(key);
value.Append(form[key]);
}
value.Append(key);
value.Append(form[key]);
}
}

var sha1 = new HMACSHA1(Encoding.UTF8.GetBytes(ContextMocks.fakeAuthToken));
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(value.ToString()));
var sha1 = new HMACSHA1(Encoding.UTF8.GetBytes(ContextMocks.fakeAuthToken));
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(value.ToString()));

return Convert.ToBase64String(hash);
}
return Convert.ToBase64String(hash);
}
}

public class RequestValidationHelperTests
public class RequestValidationHelperTests
{
[Fact]
public void TestLocal()
{
[Fact]
public void TestLocal()
{
var validator = new RequestValidationHelper();
var validator = new RequestValidationHelper();

var fakeContext = (new ContextMocks(true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, "bad-token", true);
var fakeContext = (new ContextMocks(true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, "bad-token", true);

Assert.True(result);
}
Assert.True(result);
}

[Fact]
public void TestNoLocalDueToProxy()
{
var validator = new RequestValidationHelper();
[Fact]
public void TestNoLocalDueToProxy()
{
var validator = new RequestValidationHelper();

var fakeContext = (new ContextMocks(true, isProxied: true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, "bad-token", true);
var fakeContext = (new ContextMocks(true, isProxied: true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, "bad-token", true);

Assert.False(result);
}
Assert.False(result);
}

[Fact]
public void TestNoLocal()
{
var validator = new RequestValidationHelper();
[Fact]
public void TestNoLocal()
{
var validator = new RequestValidationHelper();

var fakeContext = (new ContextMocks(true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, "bad-token", false);
var fakeContext = (new ContextMocks(true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, "bad-token", false);

Assert.False(result);
}
Assert.False(result);
}

[Fact]
public void TestNoForm()
{
var validator = new RequestValidationHelper();
[Fact]
public void TestNoForm()
{
var validator = new RequestValidationHelper();

var fakeContext = (new ContextMocks(true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, ContextMocks.fakeAuthToken, false);
var fakeContext = (new ContextMocks(true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, ContextMocks.fakeAuthToken, false);

Assert.True(result);
}
Assert.True(result);
}

[Fact]
public void TestBadForm()
{
var validator = new RequestValidationHelper();
[Fact]
public void TestBadForm()
{
var validator = new RequestValidationHelper();

var contextMocks = new ContextMocks(true);
var fakeContext = contextMocks.HttpContext.Object;
contextMocks.Request.Setup(x => x.Method).Returns("POST");
contextMocks.Request.Setup(x => x.Form).Throws(new Exception("poof!"));
var contextMocks = new ContextMocks(true);
var fakeContext = contextMocks.HttpContext.Object;
contextMocks.Request.Setup(x => x.Method).Returns("POST");
contextMocks.Request.Setup(x => x.Form).Throws(new Exception("poof!"));

var result = validator.IsValidRequest(fakeContext, ContextMocks.fakeAuthToken, false);
var result = validator.IsValidRequest(fakeContext, ContextMocks.fakeAuthToken, false);

Assert.True(result);
}
Assert.True(result);
}

[Fact]
public void TestUrlOverrideFail()
{
var validator = new RequestValidationHelper();
[Fact]
public void TestUrlOverrideFail()
{
var validator = new RequestValidationHelper();

var fakeContext = (new ContextMocks(true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, ContextMocks.fakeAuthToken, "https://example.com/", false);
var fakeContext = (new ContextMocks(true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, ContextMocks.fakeAuthToken, "https://example.com/", false);

Assert.False(result);
}
Assert.False(result);
}


[Fact]
public void TestUrlOverride()
{
var validator = new RequestValidationHelper();
[Fact]
public void TestUrlOverride()
{
var validator = new RequestValidationHelper();

var fakeContext = (new ContextMocks("https://example.com/", true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, ContextMocks.fakeAuthToken, "https://example.com/", false);
var fakeContext = (new ContextMocks("https://example.com/", true)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, ContextMocks.fakeAuthToken, "https://example.com/", false);

Assert.True(result);
}
Assert.True(result);
}

[Fact]
public void TestForm()
{
var validator = new RequestValidationHelper();
[Fact]
public void TestForm()
{
var validator = new RequestValidationHelper();

var form = new FormCollection(new Dictionary<string, StringValues>() {
var form = new FormCollection(new Dictionary<string, StringValues>() {
{"key1", "value1"},
{"key2", "value2"}
});
var fakeContext = (new ContextMocks(true, form)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, ContextMocks.fakeAuthToken, false);
var fakeContext = (new ContextMocks(true, form)).HttpContext.Object;
var result = validator.IsValidRequest(fakeContext, ContextMocks.fakeAuthToken, false);

Assert.True(result);
}
Assert.True(result);
}
}
}
54 changes: 54 additions & 0 deletions src/Twilio.AspNet.Core.UnitTests/TwilioControllerExtensionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
using Moq;
using Twilio.TwiML;
using Xunit;

namespace Twilio.AspNet.Core.UnitTests;

public class TwilioControllerExtensionTests
{

[Fact]
public async Task TwimlResult_Should_Write_VoiceResponse_To_ResponseBody()
{
var twiml = new VoiceResponse().Say("Hello World");
var result = TwilioControllerExtensions.TwiML(Mock.Of<ControllerBase>(), twiml);
var actionContext = CreateActionContext();
await result.ExecuteResultAsync(actionContext);

actionContext.HttpContext.Response.Body.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(actionContext.HttpContext.Response.Body);
var responseBody = await reader.ReadToEndAsync();
Assert.Equal(twiml.ToString(), responseBody);
}

[Fact]
public async Task TwimlResult_Should_Write_MessagingResponse_To_ResponseBody()
{
var twiml = new MessagingResponse().Message("Hello World");
var result = TwilioControllerExtensions.TwiML(Mock.Of<ControllerBase>(), twiml);
var actionContext = CreateActionContext();
await result.ExecuteResultAsync(actionContext);

actionContext.HttpContext.Response.Body.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(actionContext.HttpContext.Response.Body);
var responseBody = await reader.ReadToEndAsync();
Assert.Equal(twiml.ToString(), responseBody);
}

private ActionContext CreateActionContext()
{
var httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream();
return new ActionContext(httpContext,
new RouteData(),
new ActionDescriptor(),
new ModelStateDictionary());
}
}
Loading