diff options
Diffstat (limited to 'src/DotNetOpenAuth.Test')
10 files changed, 173 insertions, 15 deletions
diff --git a/src/DotNetOpenAuth.Test/App.config b/src/DotNetOpenAuth.Test/App.config index 2182312..359e25f 100644 --- a/src/DotNetOpenAuth.Test/App.config +++ b/src/DotNetOpenAuth.Test/App.config @@ -1,8 +1,18 @@ <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> + <section name="uri" type="System.Configuration.UriSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <section name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection, DotNetOpenAuth"/> </configSections> + + <!-- The uri section is necessary to turn on .NET 3.5 support for IDN (international domain names), + which is necessary for OpenID urls with unicode characters in the domain/host name. + It is also required to put the Uri class into RFC 3986 escaping mode, which OpenID and OAuth require. --> + <uri> + <idn enabled="All"/> + <iriParsing enabled="true"/> + </uri> + <dotNetOpenAuth> <!-- The values here are carefully chosen to be somewhat weird so that tests can be reasonably confident that if the values are the weird ones here that they did diff --git a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs index ac94ea2..669abbc 100644 --- a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs @@ -79,8 +79,8 @@ namespace DotNetOpenAuth.Test.Messaging { Assert.AreEqual(HttpStatusCode.Redirect, response.Status); StringAssert.StartsWith(response.Headers[HttpResponseHeader.Location], "http://provider/path"); foreach (var pair in expected) { - string key = HttpUtility.UrlEncode(pair.Key); - string value = HttpUtility.UrlEncode(pair.Value); + string key = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Key); + string value = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Value); string substring = string.Format("{0}={1}", key, value); StringAssert.Contains(response.Headers[HttpResponseHeader.Location], substring); } diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs index 2b1d961..26ce4cd 100644 --- a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs @@ -22,7 +22,7 @@ namespace DotNetOpenAuth.Test.Messaging var args = new Dictionary<string, string>(); args.Add("a", "b"); args.Add("c/d", "e/f"); - Assert.AreEqual("a=b&c%2fd=e%2ff", MessagingUtilities.CreateQueryString(args)); + Assert.AreEqual("a=b&c%2Fd=e%2Ff", MessagingUtilities.CreateQueryString(args)); } [TestMethod] @@ -42,11 +42,11 @@ namespace DotNetOpenAuth.Test.Messaging args.Add("a", "b"); args.Add("c/d", "e/f"); MessagingUtilities.AppendQueryArgs(uri, args); - Assert.AreEqual("http://baseline.org/page?a=b&c%2fd=e%2ff", uri.Uri.AbsoluteUri); + Assert.AreEqual("http://baseline.org/page?a=b&c%2Fd=e%2Ff", uri.Uri.AbsoluteUri); args.Clear(); args.Add("g", "h"); MessagingUtilities.AppendQueryArgs(uri, args); - Assert.AreEqual("http://baseline.org/page?a=b&c%2fd=e%2ff&g=h", uri.Uri.AbsoluteUri); + Assert.AreEqual("http://baseline.org/page?a=b&c%2Fd=e%2Ff&g=h", uri.Uri.AbsoluteUri); } [TestMethod, ExpectedException(typeof(ArgumentNullException))] @@ -119,5 +119,25 @@ namespace DotNetOpenAuth.Test.Messaging Assert.AreEqual(headers[HttpResponseHeader.ContentType], response.ContentType); } + + /// <summary> + /// Verifies RFC 3986 compliant URI escaping, as required by the OpenID and OAuth specifications. + /// </summary> + /// <remarks> + /// The tests in this method come from http://wiki.oauth.net/TestCases + /// </remarks> + [TestMethod] + public void EscapeUriDataStringRfc3986Tests() { + Assert.AreEqual("abcABC123", MessagingUtilities.EscapeUriDataStringRfc3986("abcABC123")); + Assert.AreEqual("-._~", MessagingUtilities.EscapeUriDataStringRfc3986("-._~")); + Assert.AreEqual("%25", MessagingUtilities.EscapeUriDataStringRfc3986("%")); + Assert.AreEqual("%2B", MessagingUtilities.EscapeUriDataStringRfc3986("+")); + Assert.AreEqual("%26%3D%2A", MessagingUtilities.EscapeUriDataStringRfc3986("&=*")); + Assert.AreEqual("%0A", MessagingUtilities.EscapeUriDataStringRfc3986("\n")); + Assert.AreEqual("%20", MessagingUtilities.EscapeUriDataStringRfc3986(" ")); + Assert.AreEqual("%7F", MessagingUtilities.EscapeUriDataStringRfc3986("\u007f")); + Assert.AreEqual("%C2%80", MessagingUtilities.EscapeUriDataStringRfc3986("\u0080")); + Assert.AreEqual("%E3%80%81", MessagingUtilities.EscapeUriDataStringRfc3986("\u3001")); + } } } diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs index e654f02..3bbe6e3 100644 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs +++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs @@ -45,6 +45,12 @@ namespace DotNetOpenAuth.Test.Mocks { private EventWaitHandle incomingMessageSignal = new AutoResetEvent(false); /// <summary> + /// A thread-coordinating signal that is set briefly by this thread whenever + /// a message is picked up. + /// </summary> + private EventWaitHandle messageReceivedSignal = new AutoResetEvent(false); + + /// <summary> /// A flag used to indicate when this channel is waiting for a message /// to arrive. /// </summary> @@ -126,6 +132,12 @@ namespace DotNetOpenAuth.Test.Mocks { /// </summary> /// <param name="message">The message that this channel should receive. This message will be cloned.</param> internal void PostMessage(IProtocolMessage message) { + if (this.incomingMessage != null) { + // The remote party hasn't picked up the last message we sent them. + // Wait for a short period for them to pick it up before failing. + TestBase.TestLogger.Warn("We're blocked waiting to send a message to the remote party and they haven't processed the last message we sent them."); + this.RemoteChannel.messageReceivedSignal.WaitOne(500); + } ErrorUtilities.VerifyInternal(this.incomingMessage == null, "Oops, a message is already waiting for the remote party!"); this.incomingMessage = this.MessageDescriptions.GetAccessor(message).Serialize(); var directedMessage = message as IDirectedProtocolMessage; @@ -273,6 +285,11 @@ namespace DotNetOpenAuth.Test.Mocks { recipient = this.incomingMessageRecipient; this.incomingMessage = null; this.incomingMessageRecipient = null; + + // Briefly signal to another thread that might be waiting for our inbox to be empty + this.messageReceivedSignal.Set(); + this.messageReceivedSignal.Reset(); + return response; } } diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs index 2596bc5..fcdb5e8 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs @@ -14,7 +14,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { public class HmacSha1SigningBindingElementTests : MessagingTestBase { [TestMethod] public void SignatureTest() { - UnauthorizedTokenRequest message = SigningBindingElementBaseTests.CreateTestRequestTokenMessage(this.MessageDescriptions); + UnauthorizedTokenRequest message = SigningBindingElementBaseTests.CreateTestRequestTokenMessage(this.MessageDescriptions, null); HmacSha1SigningBindingElement_Accessor hmac = new HmacSha1SigningBindingElement_Accessor(); hmac.Channel = new TestChannel(this.MessageDescriptions); diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs index 401153d..449a033 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs @@ -15,6 +15,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { using System.Xml; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; + using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.Test.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -69,6 +70,41 @@ namespace DotNetOpenAuth.Test.ChannelElements { this.ParameterizedReceiveTest(HttpDeliveryMethods.AuthorizationHeaderRequest); } + /// <summary> + /// Verifies that the OAuth ReadFromRequest method gathers parameters + /// from the Authorization header, the query string and the entity form data. + /// </summary> + [TestMethod] + public void ReadFromRequestAuthorizationScattered() { + // Start by creating a standard POST HTTP request. + var fields = new Dictionary<string, string> { + { "age", "15" }, + }; + HttpRequestInfo requestInfo = CreateHttpRequestInfo(HttpDeliveryMethods.PostRequest, fields); + + // Now add another field to the request URL + UriBuilder builder = new UriBuilder(requestInfo.Url); + builder.Query = "Name=Andrew"; + requestInfo.Url = builder.Uri; + requestInfo.RawUrl = builder.Path + builder.Query + builder.Fragment; + + // Finally, add an Authorization header + fields = new Dictionary<string, string> { + { "Location", "http://hostb/pathB" }, + { "Timestamp", XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc) }, + }; + requestInfo.Headers.Add(HttpRequestHeader.Authorization, CreateAuthorizationHeader(fields)); + + IDirectedProtocolMessage requestMessage = this.channel.ReadFromRequest(requestInfo); + + Assert.IsNotNull(requestMessage); + Assert.IsInstanceOfType(requestMessage, typeof(TestMessage)); + TestMessage testMessage = (TestMessage)requestMessage; + Assert.AreEqual(15, testMessage.Age); + Assert.AreEqual("Andrew", testMessage.Name); + Assert.AreEqual("http://hostb/pathB", testMessage.Location.AbsoluteUri); + } + [TestMethod] public void ReadFromRequestForm() { this.ParameterizedReceiveTest(HttpDeliveryMethods.PostRequest); @@ -144,6 +180,42 @@ namespace DotNetOpenAuth.Test.ChannelElements { this.ParameterizedRequestTest(HttpDeliveryMethods.AuthorizationHeaderRequest); } + /// <summary> + /// Verifies that message parts can be distributed to the query, form, and Authorization header. + /// </summary> + [TestMethod] + public void RequestUsingAuthorizationHeaderScattered() { + TestDirectedMessage request = new TestDirectedMessage(MessageTransport.Direct) { + Age = 15, + Name = "Andrew", + Location = new Uri("http://hostb/pathB"), + Recipient = new Uri("http://localtest"), + Timestamp = DateTime.UtcNow, + HttpMethods = HttpDeliveryMethods.AuthorizationHeaderRequest, + }; + + // ExtraData should appear in the form since this is a POST request, + // and only official message parts get a place in the Authorization header. + ((IProtocolMessage)request).ExtraData["appearinform"] = "formish"; + request.Recipient = new Uri("http://localhost/?appearinquery=queryish"); + request.HttpMethods = HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest; + + HttpWebRequest webRequest = this.channel.InitializeRequest(request); + Assert.IsNotNull(webRequest); + Assert.AreEqual("POST", webRequest.Method); + Assert.AreEqual(request.Recipient, webRequest.RequestUri); + + var declaredParts = new Dictionary<string, string> { + { "age", request.Age.ToString() }, + { "Name", request.Name }, + { "Location", request.Location.AbsoluteUri }, + { "Timestamp", XmlConvert.ToString(request.Timestamp, XmlDateTimeSerializationMode.Utc) }, + }; + + Assert.AreEqual(CreateAuthorizationHeader(declaredParts), webRequest.Headers[HttpRequestHeader.Authorization]); + Assert.AreEqual("appearinform=formish", this.webRequestHandler.RequestEntityAsString); + } + [TestMethod] public void RequestUsingGet() { this.ParameterizedRequestTest(HttpDeliveryMethods.GetRequest); @@ -171,9 +243,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { } private static string CreateAuthorizationHeader(IDictionary<string, string> fields) { - if (fields == null) { - throw new ArgumentNullException("fields"); - } + ErrorUtilities.VerifyArgumentNotNull(fields, "fields"); StringBuilder authorization = new StringBuilder(); authorization.Append("OAuth "); @@ -219,6 +289,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { HttpRequestInfo request = new HttpRequestInfo { HttpMethod = method, Url = requestUri.Uri, + RawUrl = requestUri.Path + requestUri.Query + requestUri.Fragment, Headers = headers, InputStream = ms, }; @@ -230,6 +301,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { HttpRequestInfo info = new HttpRequestInfo { HttpMethod = request.Method, Url = request.RequestUri, + RawUrl = request.RequestUri.AbsolutePath + request.RequestUri.Query + request.RequestUri.Fragment, Headers = request.Headers, InputStream = postEntity, }; diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs index e890b6f..93c0b3f 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs @@ -15,15 +15,54 @@ namespace DotNetOpenAuth.Test.ChannelElements { public class SigningBindingElementBaseTests : MessagingTestBase { [TestMethod] public void BaseSignatureStringTest() { - UnauthorizedTokenRequest message = CreateTestRequestTokenMessage(this.MessageDescriptions); + // Tests a message sent by HTTP GET, with no query string included in the endpoint. + UnauthorizedTokenRequest message = CreateTestRequestTokenMessage( + this.MessageDescriptions, + new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest)); + Assert.AreEqual( + "GET&https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthGetRequestToken&oauth_consumer_key%3Dnerdbank.org%26oauth_nonce%3Dfe4045a3f0efdd1e019fa8f8ae3f5c38%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1222665749%26oauth_version%3D1.0%26scope%3Dhttp%253A%252F%252Fwww.google.com%252Fm8%252Ffeeds%252F", + SigningBindingElementBase_Accessor.ConstructSignatureBaseString(message, MessageDictionary_Accessor.AttachShadow(this.MessageDescriptions.GetAccessor(message)))); + + // Test HTTP GET with an attached query string. We're elevating the scope parameter to the query string + // and removing it from the extradata dictionary. This should NOT affect the base signature string. + message = CreateTestRequestTokenMessage( + this.MessageDescriptions, + new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.google.com/m8/feeds/", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest)); + message.ExtraData.Remove("scope"); // remove it from ExtraData since we put it in the URL + Assert.AreEqual( + "GET&https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthGetRequestToken&oauth_consumer_key%3Dnerdbank.org%26oauth_nonce%3Dfe4045a3f0efdd1e019fa8f8ae3f5c38%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1222665749%26oauth_version%3D1.0%26scope%3Dhttp%253A%252F%252Fwww.google.com%252Fm8%252Ffeeds%252F", + SigningBindingElementBase_Accessor.ConstructSignatureBaseString(message, MessageDictionary_Accessor.AttachShadow(this.MessageDescriptions.GetAccessor(message)))); + + // Test HTTP POST, with query string as well + message = CreateTestRequestTokenMessage( + this.MessageDescriptions, + new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.google.com/m8/feeds/", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest)); + message.ExtraData.Remove("scope"); // remove it from ExtraData since we put it in the URL + Assert.AreEqual( + "GET&https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthGetRequestToken&oauth_consumer_key%3Dnerdbank.org%26oauth_nonce%3Dfe4045a3f0efdd1e019fa8f8ae3f5c38%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1222665749%26oauth_version%3D1.0%26scope%3Dhttp%253A%252F%252Fwww.google.com%252Fm8%252Ffeeds%252F", + SigningBindingElementBase_Accessor.ConstructSignatureBaseString(message, MessageDictionary_Accessor.AttachShadow(this.MessageDescriptions.GetAccessor(message)))); + + // Test HTTP POST, with query string, but not using the Authorization header + message = CreateTestRequestTokenMessage( + this.MessageDescriptions, + new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.google.com/m8/feeds/", HttpDeliveryMethods.PostRequest)); + message.ExtraData.Remove("scope"); // remove it from ExtraData since we put it in the URL + Assert.AreEqual( + "GET&https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthGetRequestToken&oauth_consumer_key%3Dnerdbank.org%26oauth_nonce%3Dfe4045a3f0efdd1e019fa8f8ae3f5c38%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1222665749%26oauth_version%3D1.0%26scope%3Dhttp%253A%252F%252Fwww.google.com%252Fm8%252Ffeeds%252F", + SigningBindingElementBase_Accessor.ConstructSignatureBaseString(message, MessageDictionary_Accessor.AttachShadow(this.MessageDescriptions.GetAccessor(message)))); + // This is a simulation of receiving the message, where the query string is still in the URL, + // but has been read into ExtraData, so parameters in the query string appear twice. + message = CreateTestRequestTokenMessage( + this.MessageDescriptions, + new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.google.com/m8/feeds/", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest)); Assert.AreEqual( "GET&https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthGetRequestToken&oauth_consumer_key%3Dnerdbank.org%26oauth_nonce%3Dfe4045a3f0efdd1e019fa8f8ae3f5c38%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1222665749%26oauth_version%3D1.0%26scope%3Dhttp%253A%252F%252Fwww.google.com%252Fm8%252Ffeeds%252F", SigningBindingElementBase_Accessor.ConstructSignatureBaseString(message, MessageDictionary_Accessor.AttachShadow(this.MessageDescriptions.GetAccessor(message)))); } - internal static UnauthorizedTokenRequest CreateTestRequestTokenMessage(MessageDescriptionCollection messageDescriptions) { - MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest); + internal static UnauthorizedTokenRequest CreateTestRequestTokenMessage(MessageDescriptionCollection messageDescriptions, MessageReceivingEndpoint endpoint) { + endpoint = endpoint ?? new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest); UnauthorizedTokenRequest message = new UnauthorizedTokenRequest(endpoint); message.ConsumerKey = "nerdbank.org"; ((ITamperResistantOAuthMessage)message).ConsumerSecret = "nerdbanksecret"; diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs index 5af1caf..12f6e7a 100644 --- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs @@ -31,7 +31,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { this.factory = new StandardOpenIdExtensionFactory(); this.factory.RegisterExtension(MockOpenIdExtension.Factory); - this.rpElement = new ExtensionsBindingElement(this.factory, new RelyingPartySecuritySettings()); + this.rpElement = new ExtensionsBindingElement(this.factory); this.rpElement.Channel = new TestChannel(this.MessageDescriptions); this.request = new SignedResponseRequest(Protocol.Default.Version, OpenIdTestBase.OPUri, AuthenticationRequestMode.Immediate); } diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchResponseTests.cs index 0221ff4..d7082c3 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchResponseTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenId.Test.OpenId.Extensions { +namespace DotNetOpenAuth.Test.OpenId.Extensions { using System; using System.IO; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; diff --git a/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs index 53df6c8..3e599e9 100644 --- a/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs @@ -22,7 +22,7 @@ namespace DotNetOpenAuth.Test.OpenId { public void TryParseNoThrow() { Identifier id; Assert.IsFalse(Identifier.TryParse(null, out id)); - Assert.IsFalse(Identifier.TryParse("", out id)); + Assert.IsFalse(Identifier.TryParse(string.Empty, out id)); } [TestMethod] |