PageRenderTime 163ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/Octokit.Tests/Http/ConnectionTests.cs

https://gitlab.com/WoomyNightClub/GitHub-API-.NET
C# | 744 lines | 633 code | 104 blank | 7 comment | 58 complexity | 05fee03e5c3d8efa68e71590748ac0a0 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Http;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using NSubstitute;
  10. using Octokit.Internal;
  11. using Xunit;
  12. namespace Octokit.Tests.Http
  13. {
  14. public class ConnectionTests
  15. {
  16. const string exampleUrl = "http://example.com";
  17. static readonly Uri _exampleUri = new Uri(exampleUrl);
  18. public class TheGetMethod
  19. {
  20. [Fact]
  21. public async Task SendsProperlyFormattedRequest()
  22. {
  23. var httpClient = Substitute.For<IHttpClient>();
  24. IResponse response = new Response();
  25. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  26. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  27. _exampleUri,
  28. Substitute.For<ICredentialStore>(),
  29. httpClient,
  30. Substitute.For<IJsonSerializer>());
  31. await connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative));
  32. httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
  33. req.BaseAddress == _exampleUri &&
  34. req.ContentType == null &&
  35. req.Body == null &&
  36. req.Method == HttpMethod.Get &&
  37. req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
  38. }
  39. [Fact]
  40. public async Task CanMakeMultipleRequestsWithSameConnection()
  41. {
  42. var httpClient = Substitute.For<IHttpClient>();
  43. IResponse response = new Response();
  44. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  45. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  46. _exampleUri,
  47. Substitute.For<ICredentialStore>(),
  48. httpClient,
  49. Substitute.For<IJsonSerializer>());
  50. await connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative));
  51. await connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative));
  52. await connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative));
  53. httpClient.Received(3).Send(Arg.Is<IRequest>(req =>
  54. req.BaseAddress == _exampleUri &&
  55. req.Method == HttpMethod.Get &&
  56. req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
  57. }
  58. [Fact]
  59. public async Task ParsesApiInfoOnResponse()
  60. {
  61. var httpClient = Substitute.For<IHttpClient>();
  62. var headers = new Dictionary<string, string>
  63. {
  64. { "X-Accepted-OAuth-Scopes", "user" }
  65. };
  66. IResponse response = new Response(headers);
  67. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  68. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  69. _exampleUri,
  70. Substitute.For<ICredentialStore>(),
  71. httpClient,
  72. Substitute.For<IJsonSerializer>());
  73. var resp = await connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative));
  74. Assert.NotNull(resp.HttpResponse.ApiInfo);
  75. Assert.Equal("user", resp.HttpResponse.ApiInfo.AcceptedOauthScopes.First());
  76. }
  77. [Fact]
  78. public async Task ThrowsAuthorizationExceptionExceptionForUnauthorizedResponse()
  79. {
  80. var httpClient = Substitute.For<IHttpClient>();
  81. IResponse response = new Response(HttpStatusCode.Unauthorized, null, new Dictionary<string, string>(), "application/json");
  82. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  83. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  84. _exampleUri,
  85. Substitute.For<ICredentialStore>(),
  86. httpClient,
  87. Substitute.For<IJsonSerializer>());
  88. var exception = await Assert.ThrowsAsync<AuthorizationException>(
  89. () => connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative)));
  90. Assert.NotNull(exception);
  91. }
  92. [Theory]
  93. [InlineData("missing", "")]
  94. [InlineData("missing", "required; sms")]
  95. [InlineData("X-GitHub-OTP", "blah")]
  96. [InlineData("X-GitHub-OTP", "foo; sms")]
  97. public async Task ThrowsUnauthorizedExceptionExceptionWhenChallengedWithBadHeader(
  98. string headerKey,
  99. string otpHeaderValue)
  100. {
  101. var headers = new Dictionary<string, string> { { headerKey, otpHeaderValue } };
  102. var httpClient = Substitute.For<IHttpClient>();
  103. IResponse response = new Response(HttpStatusCode.Unauthorized, null, headers, "application/json");
  104. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  105. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  106. _exampleUri,
  107. Substitute.For<ICredentialStore>(),
  108. httpClient,
  109. Substitute.For<IJsonSerializer>());
  110. var exception = await Assert.ThrowsAsync<AuthorizationException>(
  111. () => connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative)));
  112. Assert.Equal(HttpStatusCode.Unauthorized, exception.StatusCode);
  113. }
  114. [Theory]
  115. [InlineData("X-GitHub-OTP", "required", TwoFactorType.Unknown)]
  116. [InlineData("X-GitHub-OTP", "required;", TwoFactorType.Unknown)]
  117. [InlineData("X-GitHub-OTP", "required; poo", TwoFactorType.Unknown)]
  118. [InlineData("X-GitHub-OTP", "required; app", TwoFactorType.AuthenticatorApp)]
  119. [InlineData("X-GitHub-OTP", "required; sms", TwoFactorType.Sms)]
  120. [InlineData("x-github-otp", "required; sms", TwoFactorType.Sms)]
  121. public async Task ThrowsTwoFactorExceptionExceptionWhenChallenged(
  122. string headerKey,
  123. string otpHeaderValue,
  124. TwoFactorType expectedFactorType)
  125. {
  126. var headers = new Dictionary<string, string> { { headerKey, otpHeaderValue } };
  127. IResponse response = new Response(HttpStatusCode.Unauthorized, null, headers, "application/json");
  128. var httpClient = Substitute.For<IHttpClient>();
  129. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  130. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  131. _exampleUri,
  132. Substitute.For<ICredentialStore>(),
  133. httpClient,
  134. Substitute.For<IJsonSerializer>());
  135. var exception = await Assert.ThrowsAsync<TwoFactorRequiredException>(
  136. () => connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative)));
  137. Assert.Equal(expectedFactorType, exception.TwoFactorType);
  138. }
  139. [Fact]
  140. public async Task ThrowsApiValidationExceptionFor422Response()
  141. {
  142. var httpClient = Substitute.For<IHttpClient>();
  143. IResponse response = new Response(
  144. (HttpStatusCode)422,
  145. @"{""errors"":[{""code"":""custom"",""field"":""key"",""message"":""key is " +
  146. @"already in use"",""resource"":""PublicKey""}],""message"":""Validation Failed""}",
  147. new Dictionary<string, string>(),
  148. "application/json"
  149. );
  150. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  151. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  152. _exampleUri,
  153. Substitute.For<ICredentialStore>(),
  154. httpClient,
  155. Substitute.For<IJsonSerializer>());
  156. var exception = await Assert.ThrowsAsync<ApiValidationException>(
  157. () => connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative)));
  158. Assert.Equal("Validation Failed", exception.Message);
  159. Assert.Equal("key is already in use", exception.ApiError.Errors[0].Message);
  160. }
  161. [Fact]
  162. public async Task ThrowsRateLimitExceededExceptionForForbidderResponse()
  163. {
  164. var httpClient = Substitute.For<IHttpClient>();
  165. IResponse response = new Response(
  166. HttpStatusCode.Forbidden,
  167. "{\"message\":\"API rate limit exceeded. " +
  168. "See http://developer.github.com/v3/#rate-limiting for details.\"}",
  169. new Dictionary<string, string>(),
  170. "application/json");
  171. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  172. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  173. _exampleUri,
  174. Substitute.For<ICredentialStore>(),
  175. httpClient,
  176. Substitute.For<IJsonSerializer>());
  177. var exception = await Assert.ThrowsAsync<RateLimitExceededException>(
  178. () => connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative)));
  179. Assert.Equal("API rate limit exceeded. See http://developer.github.com/v3/#rate-limiting for details.",
  180. exception.Message);
  181. }
  182. [Fact]
  183. public async Task ThrowsLoginAttemptsExceededExceptionForForbiddenResponse()
  184. {
  185. var httpClient = Substitute.For<IHttpClient>();
  186. IResponse response = new Response(
  187. HttpStatusCode.Forbidden,
  188. "{\"message\":\"Maximum number of login attempts exceeded\"," +
  189. "\"documentation_url\":\"http://developer.github.com/v3\"}",
  190. new Dictionary<string, string>(),
  191. "application/json");
  192. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  193. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  194. _exampleUri,
  195. Substitute.For<ICredentialStore>(),
  196. httpClient,
  197. Substitute.For<IJsonSerializer>());
  198. var exception = await Assert.ThrowsAsync<LoginAttemptsExceededException>(
  199. () => connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative)));
  200. Assert.Equal("Maximum number of login attempts exceeded", exception.Message);
  201. Assert.Equal("http://developer.github.com/v3", exception.ApiError.DocumentationUrl);
  202. }
  203. [Fact]
  204. public async Task ThrowsNotFoundExceptionForFileNotFoundResponse()
  205. {
  206. var httpClient = Substitute.For<IHttpClient>();
  207. IResponse response = new Response(
  208. HttpStatusCode.NotFound,
  209. "GONE BYE BYE!",
  210. new Dictionary<string, string>(),
  211. "application/json");
  212. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  213. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  214. _exampleUri,
  215. Substitute.For<ICredentialStore>(),
  216. httpClient,
  217. Substitute.For<IJsonSerializer>());
  218. var exception = await Assert.ThrowsAsync<NotFoundException>(
  219. () => connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative)));
  220. Assert.Equal("GONE BYE BYE!", exception.Message);
  221. }
  222. [Fact]
  223. public async Task ThrowsForbiddenExceptionForUnknownForbiddenResponse()
  224. {
  225. var httpClient = Substitute.For<IHttpClient>();
  226. IResponse response = new Response(
  227. HttpStatusCode.Forbidden,
  228. "YOU SHALL NOT PASS!",
  229. new Dictionary<string, string>(),
  230. "application/json");
  231. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  232. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  233. _exampleUri,
  234. Substitute.For<ICredentialStore>(),
  235. httpClient,
  236. Substitute.For<IJsonSerializer>());
  237. var exception = await Assert.ThrowsAsync<ForbiddenException>(
  238. () => connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative)));
  239. Assert.Equal("YOU SHALL NOT PASS!", exception.Message);
  240. }
  241. }
  242. public class TheGetHtmlMethod
  243. {
  244. [Fact]
  245. public async Task SendsProperlyFormattedRequestWithProperAcceptHeader()
  246. {
  247. var httpClient = Substitute.For<IHttpClient>();
  248. IResponse response = new Response();
  249. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  250. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  251. _exampleUri,
  252. Substitute.For<ICredentialStore>(),
  253. httpClient,
  254. Substitute.For<IJsonSerializer>());
  255. await connection.GetHtml(new Uri("endpoint", UriKind.Relative));
  256. httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
  257. req.BaseAddress == _exampleUri &&
  258. req.ContentType == null &&
  259. req.Body == null &&
  260. req.Method == HttpMethod.Get &&
  261. req.Headers["Accept"] == "application/vnd.github.html" &&
  262. req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
  263. }
  264. }
  265. public class ThePatchMethod
  266. {
  267. [Fact]
  268. public async Task RunsConfiguredAppWithAppropriateEnv()
  269. {
  270. var body = new object();
  271. var expectedData = SimpleJson.SerializeObject(body);
  272. var serializer = Substitute.For<IJsonSerializer>();
  273. serializer.Serialize(body).Returns(expectedData);
  274. IResponse response = new Response();
  275. var httpClient = Substitute.For<IHttpClient>();
  276. httpClient.Send(Args.Request, Args.CancellationToken)
  277. .Returns(Task.FromResult(response));
  278. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  279. _exampleUri,
  280. Substitute.For<ICredentialStore>(),
  281. httpClient,
  282. serializer);
  283. await connection.Patch<string>(new Uri("endpoint", UriKind.Relative), body);
  284. serializer.Received(1).Serialize(body);
  285. httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
  286. req.BaseAddress == _exampleUri &&
  287. (string)req.Body == expectedData &&
  288. req.Method == HttpVerb.Patch &&
  289. req.ContentType == "application/x-www-form-urlencoded" &&
  290. req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
  291. }
  292. [Fact]
  293. public async Task RunsConfiguredAppWithAcceptsOverride()
  294. {
  295. var httpClient = Substitute.For<IHttpClient>();
  296. IResponse response = new Response();
  297. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  298. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  299. _exampleUri,
  300. Substitute.For<ICredentialStore>(),
  301. httpClient,
  302. Substitute.For<IJsonSerializer>());
  303. await connection.Patch<string>(new Uri("endpoint", UriKind.Relative), new object(), "custom/accepts");
  304. httpClient.Received(1).Send(Arg.Is<IRequest>(req => req.Headers["Accept"] == "custom/accepts"), Args.CancellationToken);
  305. }
  306. }
  307. public class ThePutMethod
  308. {
  309. [Fact]
  310. public async Task MakesPutRequestWithData()
  311. {
  312. var body = new object();
  313. var serializer = Substitute.For<IJsonSerializer>();
  314. var expectedBody = SimpleJson.SerializeObject(body);
  315. var httpClient = Substitute.For<IHttpClient>();
  316. IResponse response = new Response();
  317. serializer.Serialize(body).Returns(expectedBody);
  318. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  319. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  320. _exampleUri,
  321. Substitute.For<ICredentialStore>(),
  322. httpClient,
  323. serializer);
  324. await connection.Put<string>(new Uri("endpoint", UriKind.Relative), body);
  325. serializer.Received(1).Serialize(body);
  326. httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
  327. req.BaseAddress == _exampleUri &&
  328. (string)req.Body == expectedBody &&
  329. req.Method == HttpMethod.Put &&
  330. req.ContentType == "application/x-www-form-urlencoded" &&
  331. req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
  332. }
  333. [Fact]
  334. public async Task MakesPutRequestWithNoData()
  335. {
  336. var body = RequestBody.Empty;
  337. var serializer = Substitute.For<IJsonSerializer>();
  338. var expectedBody = SimpleJson.SerializeObject(body);
  339. var httpClient = Substitute.For<IHttpClient>();
  340. IResponse response = new Response();
  341. serializer.Serialize(body).Returns(expectedBody);
  342. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  343. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  344. _exampleUri,
  345. Substitute.For<ICredentialStore>(),
  346. httpClient,
  347. serializer);
  348. await connection.Put<string>(new Uri("endpoint", UriKind.Relative), body);
  349. serializer.Received(1).Serialize(body);
  350. httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
  351. req.BaseAddress == _exampleUri &&
  352. (string)req.Body == expectedBody &&
  353. req.Method == HttpMethod.Put &&
  354. req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
  355. }
  356. [Fact]
  357. public async Task MakesPutRequestWithDataAndTwoFactor()
  358. {
  359. var body = new object();
  360. var serializer = Substitute.For<IJsonSerializer>();
  361. var expectedBody = SimpleJson.SerializeObject(body);
  362. var httpClient = Substitute.For<IHttpClient>();
  363. IResponse response = new Response();
  364. serializer.Serialize(body).Returns(expectedBody);
  365. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  366. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  367. _exampleUri,
  368. Substitute.For<ICredentialStore>(),
  369. httpClient,
  370. serializer);
  371. await connection.Put<string>(new Uri("endpoint", UriKind.Relative), body, "two-factor");
  372. serializer.Received(1).Serialize(body);
  373. httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
  374. req.BaseAddress == _exampleUri &&
  375. (string)req.Body == expectedBody &&
  376. req.Method == HttpMethod.Put &&
  377. req.Headers["X-GitHub-OTP"] == "two-factor" &&
  378. req.ContentType == "application/x-www-form-urlencoded" &&
  379. req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
  380. }
  381. [Fact]
  382. public async Task MakesPutRequestWithNoDataAndTwoFactor()
  383. {
  384. var body = RequestBody.Empty;
  385. var serializer = Substitute.For<IJsonSerializer>();
  386. var expectedBody = SimpleJson.SerializeObject(body);
  387. var httpClient = Substitute.For<IHttpClient>();
  388. IResponse response = new Response();
  389. serializer.Serialize(body).Returns(expectedBody);
  390. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  391. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  392. _exampleUri,
  393. Substitute.For<ICredentialStore>(),
  394. httpClient,
  395. serializer);
  396. await connection.Put<string>(new Uri("endpoint", UriKind.Relative), body, "two-factor");
  397. serializer.Received(1).Serialize(body);
  398. httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
  399. req.BaseAddress == _exampleUri &&
  400. (string)req.Body == expectedBody &&
  401. req.Method == HttpMethod.Put &&
  402. req.Headers["X-GitHub-OTP"] == "two-factor" &&
  403. req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
  404. }
  405. }
  406. public class ThePostMethod
  407. {
  408. [Fact]
  409. public async Task SendsProperlyFormattedPostRequest()
  410. {
  411. var body = new object();
  412. var serializer = Substitute.For<IJsonSerializer>();
  413. var data = SimpleJson.SerializeObject(body);
  414. var httpClient = Substitute.For<IHttpClient>();
  415. IResponse response = new Response();
  416. serializer.Serialize(body).Returns(data);
  417. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  418. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  419. _exampleUri,
  420. Substitute.For<ICredentialStore>(),
  421. httpClient,
  422. serializer);
  423. await connection.Post<string>(new Uri("endpoint", UriKind.Relative), body, null, null);
  424. serializer.Received(1).Serialize(body);
  425. httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
  426. req.BaseAddress == _exampleUri &&
  427. req.ContentType == "application/x-www-form-urlencoded" &&
  428. (string)req.Body == data &&
  429. req.Method == HttpMethod.Post &&
  430. req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
  431. }
  432. [Fact]
  433. public async Task SendsProperlyFormattedPostRequestWithCorrectHeaders()
  434. {
  435. var httpClient = Substitute.For<IHttpClient>();
  436. IResponse response = new Response();
  437. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  438. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  439. _exampleUri,
  440. Substitute.For<ICredentialStore>(),
  441. httpClient,
  442. Substitute.For<IJsonSerializer>());
  443. var body = new MemoryStream(new byte[] { 48, 49, 50 });
  444. await connection.Post<string>(
  445. new Uri("https://other.host.com/path?query=val"),
  446. body,
  447. null,
  448. "application/arbitrary");
  449. httpClient.Received().Send(Arg.Is<IRequest>(req =>
  450. req.BaseAddress == _exampleUri &&
  451. req.Body == body &&
  452. req.Headers["Accept"] == "application/vnd.github.quicksilver-preview+json; charset=utf-8, application/vnd.github.v3+json; charset=utf-8" &&
  453. req.ContentType == "application/arbitrary" &&
  454. req.Method == HttpMethod.Post &&
  455. req.Endpoint == new Uri("https://other.host.com/path?query=val")), Args.CancellationToken);
  456. }
  457. [Fact]
  458. public async Task SetsAcceptsHeader()
  459. {
  460. var httpClient = Substitute.For<IHttpClient>();
  461. IResponse response = new Response();
  462. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  463. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  464. _exampleUri,
  465. Substitute.For<ICredentialStore>(),
  466. httpClient,
  467. Substitute.For<IJsonSerializer>());
  468. var body = new MemoryStream(new byte[] { 48, 49, 50 });
  469. await connection.Post<string>(
  470. new Uri("https://other.host.com/path?query=val"),
  471. body,
  472. "application/json",
  473. null);
  474. httpClient.Received().Send(Arg.Is<IRequest>(req =>
  475. req.Headers["Accept"] == "application/json" &&
  476. req.ContentType == "application/x-www-form-urlencoded"), Args.CancellationToken);
  477. }
  478. }
  479. public class TheDeleteMethod
  480. {
  481. [Fact]
  482. public async Task SendsProperlyFormattedDeleteRequest()
  483. {
  484. var httpClient = Substitute.For<IHttpClient>();
  485. IResponse response = new Response();
  486. httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
  487. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  488. _exampleUri,
  489. Substitute.For<ICredentialStore>(),
  490. httpClient,
  491. Substitute.For<IJsonSerializer>());
  492. await connection.Delete(new Uri("endpoint", UriKind.Relative));
  493. httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
  494. req.BaseAddress == _exampleUri &&
  495. req.Body == null &&
  496. req.ContentType == null &&
  497. req.Method == HttpMethod.Delete &&
  498. req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
  499. }
  500. }
  501. public class TheConstructor
  502. {
  503. [Fact]
  504. public void EnsuresAbsoluteBaseAddress()
  505. {
  506. Assert.Throws<ArgumentException>(() =>
  507. new Connection(new ProductHeaderValue("TestRunner"), new Uri("foo", UriKind.Relative)));
  508. Assert.Throws<ArgumentException>(() =>
  509. new Connection(new ProductHeaderValue("TestRunner"), new Uri("foo", UriKind.RelativeOrAbsolute)));
  510. }
  511. [Fact]
  512. public void EnsuresNonNullArguments()
  513. {
  514. // 1 arg
  515. Assert.Throws<ArgumentNullException>(() => new Connection(null));
  516. // 2 args
  517. Assert.Throws<ArgumentNullException>(() => new Connection(null, new Uri("https://example.com")));
  518. Assert.Throws<ArgumentNullException>(() => new Connection(new ProductHeaderValue("test"), (Uri)null));
  519. // 3 args
  520. Assert.Throws<ArgumentNullException>(() => new Connection(null,
  521. new Uri("https://example.com"),
  522. Substitute.For<ICredentialStore>()));
  523. Assert.Throws<ArgumentNullException>(() => new Connection(new ProductHeaderValue("foo"),
  524. null,
  525. Substitute.For<ICredentialStore>()));
  526. Assert.Throws<ArgumentNullException>(() => new Connection(new ProductHeaderValue("foo"),
  527. new Uri("https://example.com"),
  528. null));
  529. // 5 Args
  530. Assert.Throws<ArgumentNullException>(() => new Connection(null
  531. , new Uri("https://example.com"),
  532. Substitute.For<ICredentialStore>(),
  533. Substitute.For<IHttpClient>(),
  534. Substitute.For<IJsonSerializer>()));
  535. Assert.Throws<ArgumentNullException>(() => new Connection(new ProductHeaderValue("foo"),
  536. new Uri("https://example.com"),
  537. Substitute.For<ICredentialStore>(),
  538. Substitute.For<IHttpClient>(),
  539. null));
  540. Assert.Throws<ArgumentNullException>(() => new Connection(new ProductHeaderValue("foo"),
  541. new Uri("https://example.com"),
  542. Substitute.For<ICredentialStore>(),
  543. null,
  544. Substitute.For<IJsonSerializer>()));
  545. Assert.Throws<ArgumentNullException>(() => new Connection(new ProductHeaderValue("foo"),
  546. new Uri("https://example.com"),
  547. null,
  548. Substitute.For<IHttpClient>(),
  549. Substitute.For<IJsonSerializer>()));
  550. Assert.Throws<ArgumentNullException>(() => new Connection(new ProductHeaderValue("foo"),
  551. null,
  552. Substitute.For<ICredentialStore>(),
  553. Substitute.For<IHttpClient>(),
  554. Substitute.For<IJsonSerializer>()));
  555. }
  556. [Fact]
  557. public void CreatesConnectionWithBaseAddress()
  558. {
  559. var connection = new Connection(new ProductHeaderValue("OctokitTests"), new Uri("https://github.com/"));
  560. Assert.Equal(new Uri("https://github.com/"), connection.BaseAddress);
  561. Assert.True(connection.UserAgent.StartsWith("OctokitTests ("));
  562. }
  563. }
  564. public class TheLastAPiInfoProperty
  565. {
  566. [Fact]
  567. public async Task ReturnsNullIfNew()
  568. {
  569. var httpClient = Substitute.For<IHttpClient>();
  570. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  571. _exampleUri,
  572. Substitute.For<ICredentialStore>(),
  573. httpClient,
  574. Substitute.For<IJsonSerializer>());
  575. var result = connection.GetLastApiInfo();
  576. Assert.Null(result);
  577. }
  578. [Fact]
  579. public async Task ReturnsObjectIfNotNew()
  580. {
  581. var apiInfo = new ApiInfo(
  582. new Dictionary<string, Uri>
  583. {
  584. {
  585. "next",
  586. new Uri("https://api.github.com/repos/rails/rails/issues?page=4&per_page=5")
  587. },
  588. {
  589. "last",
  590. new Uri("https://api.github.com/repos/rails/rails/issues?page=131&per_page=5")
  591. },
  592. {
  593. "first",
  594. new Uri("https://api.github.com/repos/rails/rails/issues?page=1&per_page=5")
  595. },
  596. {
  597. "prev",
  598. new Uri("https://api.github.com/repos/rails/rails/issues?page=2&per_page=5")
  599. }
  600. },
  601. new List<string>
  602. {
  603. "user"
  604. },
  605. new List<string>
  606. {
  607. "user",
  608. "public_repo",
  609. "repo",
  610. "gist"
  611. },
  612. "5634b0b187fd2e91e3126a75006cc4fa",
  613. new RateLimit(100, 75, 1372700873)
  614. );
  615. var httpClient = Substitute.For<IHttpClient>();
  616. // We really only care about the ApiInfo property...
  617. var expectedResponse = new Response(HttpStatusCode.OK, null, new Dictionary<string, string>(), "application/json")
  618. {
  619. ApiInfo = apiInfo
  620. };
  621. httpClient.Send(Arg.Any<IRequest>(), Arg.Any<CancellationToken>())
  622. .Returns(Task.FromResult<IResponse>(expectedResponse));
  623. var connection = new Connection(new ProductHeaderValue("OctokitTests"),
  624. _exampleUri,
  625. Substitute.For<ICredentialStore>(),
  626. httpClient,
  627. Substitute.For<IJsonSerializer>());
  628. connection.Get<PullRequest>(new Uri("https://example.com"), TimeSpan.MaxValue);
  629. var result = connection.GetLastApiInfo();
  630. // No point checking all of the values as they are tested elsewhere
  631. // Just provde that the ApiInfo is populated
  632. Assert.Equal(4, result.Links.Count);
  633. Assert.Equal(1, result.OauthScopes.Count);
  634. Assert.Equal(4, result.AcceptedOauthScopes.Count);
  635. Assert.Equal("5634b0b187fd2e91e3126a75006cc4fa", result.Etag);
  636. Assert.Equal(100, result.RateLimit.Limit);
  637. }
  638. }
  639. }
  640. }