// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Web; using System.Web.Helpers.Test; using System.Web.TestUtil; using System.Web.WebPages.Scope; using Moq; using Xunit; using Assert = Microsoft.TestCommon.AssertEx; namespace Microsoft.Web.Helpers.Test { public class ReCaptchaTest { [Fact] public void ReCaptchaOptionsMissingWhenNoOptionsAndDefaultRendering() { var html = ReCaptcha.GetHtml(GetContext(), "PUBLIC_KEY"); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void ReCaptchaOptionsWhenOneOptionAndDefaultRendering() { var html = ReCaptcha.GetHtml(GetContext(), "PUBLIC_KEY", options: new { theme = "white" }); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void ReCaptchaOptionsWhenMultipleOptionsAndDefaultRendering() { var html = ReCaptcha.GetHtml(GetContext(), "PUBLIC_KEY", options: new { theme = "white", tabindex = 5 }); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void ReCaptchaOptionsWhenMultipleOptionsFromDictionaryAndDefaultRendering() { // verifies that a dictionary will serialize the same as a projection var options = new Dictionary { { "theme", "white" }, { "tabindex", 5 } }; var html = ReCaptcha.GetHtml(GetContext(), "PUBLIC_KEY", options: options); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void RenderUsesLastError() { HttpContextBase context = GetContext(); ReCaptcha.HandleValidateResponse(context, "false\nincorrect-captcha-sol"); var html = ReCaptcha.GetHtml(context, "PUBLIC_KEY"); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void RenderWhenConnectionIsSecure() { var html = ReCaptcha.GetHtml(GetContext(isSecure: true), "PUBLIC_KEY"); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void ValidateThrowsWhenRemoteAddressNotAvailable() { HttpContextBase context = GetContext(); VirtualPathUtilityBase virtualPathUtility = GetVirtualPathUtility(); context.Request.Form["recaptcha_challenge_field"] = "CHALLENGE"; context.Request.Form["recaptcha_response_field"] = "RESPONSE"; Assert.Throws(() => { ReCaptcha.Validate(context, privateKey: "PRIVATE_KEY", virtualPathUtility: virtualPathUtility).ToString(); }, "The captcha cannot be validated because the remote address was not found in the request."); } [Fact] public void ValidateReturnsFalseWhenChallengeNotPosted() { HttpContextBase context = GetContext(); VirtualPathUtilityBase virtualPathUtility = GetVirtualPathUtility(); context.Request.ServerVariables["REMOTE_ADDR"] = "127.0.0.1"; Assert.False(ReCaptcha.Validate(context, privateKey: "PRIVATE_KEY", virtualPathUtility: virtualPathUtility)); } [Fact] public void ValidatePostData() { HttpContextBase context = GetContext(); VirtualPathUtilityBase virtualPathUtility = GetVirtualPathUtility(); context.Request.ServerVariables["REMOTE_ADDR"] = "127.0.0.1"; context.Request.Form["recaptcha_challenge_field"] = "CHALLENGE"; context.Request.Form["recaptcha_response_field"] = "RESPONSE"; Assert.Equal("privatekey=PRIVATE_KEY&remoteip=127.0.0.1&challenge=CHALLENGE&response=RESPONSE", ReCaptcha.GetValidatePostData(context, "PRIVATE_KEY", virtualPathUtility)); } [Fact] public void ValidatePostDataWhenNoResponse() { // Arrange HttpContextBase context = GetContext(); VirtualPathUtilityBase virtualPathUtility = GetVirtualPathUtility(); context.Request.ServerVariables["REMOTE_ADDR"] = "127.0.0.1"; context.Request.Form["recaptcha_challenge_field"] = "CHALLENGE"; // Act var validatePostData = ReCaptcha.GetValidatePostData(context, "PRIVATE_KEY", virtualPathUtility); // Assert Assert.Equal("privatekey=PRIVATE_KEY&remoteip=127.0.0.1&challenge=CHALLENGE&response=", validatePostData); } [Fact] public void ValidateResponseReturnsFalseOnEmptyReCaptchaResponse() { HttpContextBase context = GetContext(); Assert.False(ReCaptcha.HandleValidateResponse(context, "")); Assert.Equal(String.Empty, ReCaptcha.GetLastError(context)); } [Fact] public void ValidateResponseReturnsTrueOnSuccess() { HttpContextBase context = GetContext(); Assert.True(ReCaptcha.HandleValidateResponse(context, "true\nsuccess")); Assert.Equal(String.Empty, ReCaptcha.GetLastError(context)); } [Fact] public void ValidateResponseReturnsFalseOnError() { HttpContextBase context = GetContext(); Assert.False(ReCaptcha.HandleValidateResponse(context, "false\nincorrect-captcha-sol")); Assert.Equal("incorrect-captcha-sol", ReCaptcha.GetLastError(context)); } [Fact] public void ReCaptchaPrivateKeyThowsWhenSetToNull() { Assert.ThrowsArgumentNull(() => ReCaptcha.PrivateKey = null, "value"); } [Fact] public void ReCaptchaPrivateKeyUsesScopeStorage() { // Arrange var value = "value"; // Act ReCaptcha.PrivateKey = value; // Assert Assert.Equal(ReCaptcha.PrivateKey, value); Assert.Equal(ScopeStorage.CurrentScope[ReCaptcha._privateKey], value); } [Fact] public void PublicKeyThowsWhenSetToNull() { Assert.ThrowsArgumentNull(() => ReCaptcha.PublicKey = null, "value"); } [Fact] public void ReCaptchaPublicKeyUsesScopeStorage() { // Arrange var value = "value"; // Act ReCaptcha.PublicKey = value; // Assert Assert.Equal(ReCaptcha.PublicKey, value); Assert.Equal(ScopeStorage.CurrentScope[ReCaptcha._publicKey], value); } private HttpContextBase GetContext(bool isSecure = false) { // mock HttpRequest Mock requestMock = new Mock(); requestMock.Setup(request => request.IsSecureConnection).Returns(isSecure); requestMock.Setup(request => request.Form).Returns(new NameValueCollection()); requestMock.Setup(request => request.ServerVariables).Returns(new NameValueCollection()); // mock HttpContext Mock contextMock = new Mock(); contextMock.Setup(context => context.Items).Returns(new Hashtable()); contextMock.Setup(context => context.Request).Returns(requestMock.Object); return contextMock.Object; } private static VirtualPathUtilityBase GetVirtualPathUtility() { var virtualPathUtility = new Mock(); virtualPathUtility.Setup(c => c.ToAbsolute(It.IsAny())).Returns(_ => _); return virtualPathUtility.Object; } } }