You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Tests.TokenUtils.cs 9.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using Xunit;
  5. namespace Discord
  6. {
  7. public class TokenUtilsTests
  8. {
  9. /// <summary>
  10. /// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
  11. /// to see that when a null, empty or whitespace-only string is passed as the token,
  12. /// it will throw an ArgumentNullException.
  13. /// </summary>
  14. [Theory]
  15. [InlineData(null)]
  16. [InlineData("")] // string.Empty isn't a constant type
  17. [InlineData(" ")]
  18. [InlineData(" ")]
  19. [InlineData("\t")]
  20. public void TestNullOrWhitespaceToken(string token)
  21. {
  22. // an ArgumentNullException should be thrown, regardless of the TokenType
  23. Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bearer, token));
  24. Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bot, token));
  25. Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Webhook, token));
  26. }
  27. /// <summary>
  28. /// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
  29. /// to see that valid Webhook tokens do not throw Exceptions.
  30. /// </summary>
  31. /// <param name="token"></param>
  32. [Theory]
  33. [InlineData("123123123")]
  34. // bot token
  35. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
  36. // bearer token taken from discord docs
  37. [InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
  38. // client secret
  39. [InlineData("937it3ow87i4ery69876wqire")]
  40. public void TestWebhookTokenDoesNotThrowExceptions(string token)
  41. {
  42. TokenUtils.ValidateToken(TokenType.Webhook, token);
  43. }
  44. // No tests for invalid webhook token behavior, because there is nothing there yet.
  45. /// <summary>
  46. /// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
  47. /// to see that valid Webhook tokens do not throw Exceptions.
  48. /// </summary>
  49. /// <param name="token"></param>
  50. [Theory]
  51. [InlineData("123123123")]
  52. // bot token
  53. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
  54. // bearer token taken from discord docs
  55. [InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
  56. // client secret
  57. [InlineData("937it3ow87i4ery69876wqire")]
  58. public void TestBearerTokenDoesNotThrowExceptions(string token)
  59. {
  60. TokenUtils.ValidateToken(TokenType.Bearer, token);
  61. }
  62. // No tests for invalid bearer token behavior, because there is nothing there yet.
  63. /// <summary>
  64. /// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
  65. /// to see that valid Bot tokens do not throw Exceptions.
  66. /// Valid Bot tokens can be strings of length 58 or above.
  67. /// </summary>
  68. [Theory]
  69. // missing a single character from the end, 58 char. still should be valid
  70. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKW")]
  71. // 59 char token
  72. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
  73. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWss")]
  74. // simulated token with a very old user id
  75. [InlineData("ODIzNjQ4MDEzNTAxMDcxMzY=.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKW")]
  76. public void TestBotTokenDoesNotThrowExceptions(string token)
  77. {
  78. // This example token is pulled from the Discord Docs
  79. // https://discordapp.com/developers/docs/reference#authentication-example-bot-token-authorization-header
  80. // should not throw any exception
  81. TokenUtils.ValidateToken(TokenType.Bot, token);
  82. }
  83. /// <summary>
  84. /// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> with
  85. /// a Bot token that is invalid.
  86. /// </summary>
  87. [Theory]
  88. [InlineData("This is invalid")]
  89. // bearer token
  90. [InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
  91. // client secret
  92. [InlineData("937it3ow87i4ery69876wqire")]
  93. // 57 char bot token
  94. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kK")]
  95. [InlineData("This is an invalid token, but it passes the check for string length.")]
  96. // valid token, but passed in twice
  97. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWsMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
  98. public void TestBotTokenInvalidThrowsArgumentException(string token)
  99. {
  100. Assert.Throws<ArgumentException>(() => TokenUtils.ValidateToken(TokenType.Bot, token));
  101. }
  102. /// <summary>
  103. /// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
  104. /// to see that an <see cref="ArgumentException"/> is thrown when an invalid
  105. /// <see cref="TokenType"/> is supplied as a parameter.
  106. /// </summary>
  107. /// <remarks>
  108. /// The <see cref="TokenType.User"/> type is treated as an invalid <see cref="TokenType"/>.
  109. /// </remarks>
  110. [Theory]
  111. // TokenType.User
  112. [InlineData(0)]
  113. // out of range TokenType
  114. [InlineData(-1)]
  115. [InlineData(4)]
  116. [InlineData(7)]
  117. public void TestUnrecognizedTokenType(int type)
  118. {
  119. Assert.Throws<ArgumentException>(() =>
  120. TokenUtils.ValidateToken((TokenType)type, "MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs"));
  121. }
  122. /// <summary>
  123. /// Checks the <see cref="TokenUtils.CheckBotTokenValidity(string)"/> method for expected output.
  124. /// </summary>
  125. /// <param name="token"> The Bot Token to test.</param>
  126. /// <param name="expected"> The expected result. </param>
  127. [Theory]
  128. // this method only checks the first part of the JWT
  129. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4..", true)]
  130. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kK", true)]
  131. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4. this part is invalid. this part is also invalid", true)]
  132. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.", false)]
  133. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4", false)]
  134. [InlineData("NDI4NDc3OTQ0MDA5MTk1NTIw.xxxx.xxxxx", true)]
  135. // should not throw an unexpected exception
  136. [InlineData("", false)]
  137. [InlineData(null, false)]
  138. public void TestCheckBotTokenValidity(string token, bool expected)
  139. {
  140. Assert.Equal(expected, TokenUtils.CheckBotTokenValidity(token));
  141. }
  142. [Theory]
  143. // cannot pass a ulong? as a param in InlineData, so have to have a separate param
  144. // indicating if a value is null
  145. [InlineData("NDI4NDc3OTQ0MDA5MTk1NTIw", false, 428477944009195520)]
  146. // user id that has base 64 '=' padding
  147. [InlineData("ODIzNjQ4MDEzNTAxMDcxMzY=", false, 82364801350107136)]
  148. // user id that does not have '=' padding, and needs it
  149. [InlineData("ODIzNjQ4MDEzNTAxMDcxMzY", false, 82364801350107136)]
  150. // should return null w/o throwing other exceptions
  151. [InlineData("", true, 0)]
  152. [InlineData(" ", true, 0)]
  153. [InlineData(null, true, 0)]
  154. [InlineData("these chars aren't allowed @U#)*@#!)*", true, 0)]
  155. public void TestDecodeBase64UserId(string encodedUserId, bool isNull, ulong expectedUserId)
  156. {
  157. var result = TokenUtils.DecodeBase64UserId(encodedUserId);
  158. if (isNull)
  159. Assert.Null(result);
  160. else
  161. Assert.Equal(expectedUserId, result);
  162. }
  163. [Theory]
  164. [InlineData("QQ", "QQ==")] // "A" encoded
  165. [InlineData("QUE", "QUE=")] // "AA"
  166. [InlineData("QUFB", "QUFB")] // "AAA"
  167. [InlineData("QUFBQQ", "QUFBQQ==")] // "AAAA"
  168. [InlineData("QUFBQUFB", "QUFBQUFB")] // "AAAAAA"
  169. // strings that already contain padding will be returned, even if invalid
  170. [InlineData("QUFBQQ==", "QUFBQQ==")]
  171. [InlineData("QUFBQQ=", "QUFBQQ=")]
  172. [InlineData("=", "=")]
  173. public void TestPadBase64String(string input, string expected)
  174. {
  175. Assert.Equal(expected, TokenUtils.PadBase64String(input));
  176. }
  177. [Theory]
  178. // no null, empty, or whitespace
  179. [InlineData("", typeof(ArgumentNullException))]
  180. [InlineData(" ", typeof(ArgumentNullException))]
  181. [InlineData("\t", typeof(ArgumentNullException))]
  182. [InlineData(null, typeof(ArgumentNullException))]
  183. // cannot require 3 padding chars
  184. [InlineData("A", typeof(FormatException))]
  185. [InlineData("QUFBQ", typeof(FormatException))]
  186. public void TestPadBase64StringException(string input, Type type)
  187. {
  188. Assert.Throws(type, () =>
  189. {
  190. TokenUtils.PadBase64String(input);
  191. });
  192. }
  193. }
  194. }