using System; using System.Collections.Generic; using System.Text; using Xunit; namespace Discord { /// /// Tests for the methods. /// public class TokenUtilsTests { /// /// Tests the usage of /// to see that when a null, empty or whitespace-only string is passed as the token, /// it will throw an ArgumentNullException. /// [Theory] [InlineData(null)] [InlineData("")] // string.Empty isn't a constant type [InlineData(" ")] [InlineData(" ")] [InlineData("\t")] public void NullOrWhitespaceToken(string token) { // an ArgumentNullException should be thrown, regardless of the TokenType Assert.Throws(() => TokenUtils.ValidateToken(TokenType.Bearer, token)); Assert.Throws(() => TokenUtils.ValidateToken(TokenType.Bot, token)); Assert.Throws(() => TokenUtils.ValidateToken(TokenType.Webhook, token)); } /// /// Tests the behavior of /// to see that valid Webhook tokens do not throw Exceptions. /// /// [Theory] [InlineData("123123123")] // bot token [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] // bearer token taken from discord docs [InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")] // client secret [InlineData("937it3ow87i4ery69876wqire")] public void WebhookTokenDoesNotThrowExceptions(string token) { TokenUtils.ValidateToken(TokenType.Webhook, token); } // No tests for invalid webhook token behavior, because there is nothing there yet. /// /// Tests the behavior of /// to see that valid Webhook tokens do not throw Exceptions. /// /// [Theory] [InlineData("123123123")] // bot token [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] // bearer token taken from discord docs [InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")] // client secret [InlineData("937it3ow87i4ery69876wqire")] public void BearerTokenDoesNotThrowExceptions(string token) { TokenUtils.ValidateToken(TokenType.Bearer, token); } // No tests for invalid bearer token behavior, because there is nothing there yet. /// /// Tests the behavior of /// to see that valid Bot tokens do not throw Exceptions. /// Valid Bot tokens can be strings of length 58 or above. /// [Theory] // missing a single character from the end, 58 char. still should be valid [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKW")] // 59 char token [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWss")] public void BotTokenDoesNotThrowExceptions(string token) { // This example token is pulled from the Discord Docs // https://discord.com/developers/docs/reference#authentication-example-bot-token-authorization-header // should not throw any exception TokenUtils.ValidateToken(TokenType.Bot, token); } /// /// Tests the usage of with /// a Bot token that is invalid. /// [Theory] [InlineData("This is invalid")] // bearer token [InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")] // client secret [InlineData("937it3ow87i4ery69876wqire")] // 57 char bot token [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kK")] // ends with invalid characters [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k ")] [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\n")] [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\t")] [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\r\n")] // starts with invalid characters [InlineData(" MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")] [InlineData("\nMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")] [InlineData("\tMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")] [InlineData("\r\nMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")] [InlineData("This is an invalid token, but it passes the check for string length.")] // valid token, but passed in twice [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWsMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] public void BotTokenInvalidThrowsArgumentException(string token) { Assert.Throws(() => TokenUtils.ValidateToken(TokenType.Bot, token)); } /// /// Tests the behavior of /// to see that an is thrown when an invalid /// is supplied as a parameter. /// /// /// The type is treated as an invalid . /// [Theory] // out of range TokenType [InlineData(-1)] [InlineData(4)] [InlineData(7)] public void UnrecognizedTokenType(int type) { Assert.Throws(() => TokenUtils.ValidateToken((TokenType)type, "MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")); } /// /// Checks the method for expected output. /// /// The Bot Token to test. /// The expected result. [Theory] // this method only checks the first part of the JWT [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4..", true)] [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kK", true)] [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4. this part is invalid. this part is also invalid", true)] [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.", false)] [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4", false)] [InlineData("NDI4NDc3OTQ0MDA5MTk1NTIw.xxxx.xxxxx", true)] // should not throw an unexpected exception [InlineData("", false)] [InlineData(null, false)] public void CheckBotTokenValidity(string token, bool expected) { Assert.Equal(expected, TokenUtils.CheckBotTokenValidity(token)); } [Theory] // cannot pass a ulong? as a param in InlineData, so have to have a separate param // indicating if a value is null [InlineData("NDI4NDc3OTQ0MDA5MTk1NTIw", false, 428477944009195520)] // should return null w/o throwing other exceptions [InlineData("", true, 0)] [InlineData(" ", true, 0)] [InlineData(null, true, 0)] [InlineData("these chars aren't allowed @U#)*@#!)*", true, 0)] public void DecodeBase64UserId(string encodedUserId, bool isNull, ulong expectedUserId) { var result = TokenUtils.DecodeBase64UserId(encodedUserId); if (isNull) Assert.Null(result); else Assert.Equal(expectedUserId, result); } } }