1 // OpenTween - Client of Twitter
2 // Copyright (c) 2016 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
5 // This file is part of OpenTween.
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
23 using System.Collections.Generic;
27 using System.Net.Http;
28 using System.Net.Http.Headers;
29 using System.Reflection;
30 using System.Runtime.InteropServices;
32 using System.Threading.Tasks;
36 using OpenTween.Api.DataModel;
39 namespace OpenTween.Connection
41 public class TwitterApiConnectionTest
43 public TwitterApiConnectionTest()
44 => this.MyCommonSetup();
46 private void MyCommonSetup()
48 var mockAssembly = new Mock<_Assembly>();
49 mockAssembly.Setup(m => m.GetName()).Returns(new AssemblyName("OpenTween"));
51 MyCommon.EntryAssembly = mockAssembly.Object;
55 public async Task GetAsync_Test()
57 using var mockHandler = new HttpMessageHandlerMock();
58 using var http = new HttpClient(mockHandler);
59 using var apiConnection = new TwitterApiConnection("", "");
60 apiConnection.http = http;
62 mockHandler.Enqueue(x =>
64 Assert.Equal(HttpMethod.Get, x.Method);
65 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
66 x.RequestUri.GetLeftPart(UriPartial.Path));
68 var query = HttpUtility.ParseQueryString(x.RequestUri.Query);
70 Assert.Equal("1111", query["aaaa"]);
71 Assert.Equal("2222", query["bbbb"]);
73 return new HttpResponseMessage(HttpStatusCode.OK)
75 Content = new StringContent("\"hogehoge\""),
79 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
80 var param = new Dictionary<string, string>
86 var result = await apiConnection.GetAsync<string>(endpoint, param, endpointName: "/hoge/tetete")
87 .ConfigureAwait(false);
88 Assert.Equal("hogehoge", result);
90 Assert.Equal(0, mockHandler.QueueCount);
94 public async Task GetAsync_AbsoluteUriTest()
96 using var mockHandler = new HttpMessageHandlerMock();
97 using var http = new HttpClient(mockHandler);
98 using var apiConnection = new TwitterApiConnection("", "");
99 apiConnection.http = http;
101 mockHandler.Enqueue(x =>
103 Assert.Equal(HttpMethod.Get, x.Method);
104 Assert.Equal("http://example.com/hoge/tetete.json",
105 x.RequestUri.GetLeftPart(UriPartial.Path));
107 var query = HttpUtility.ParseQueryString(x.RequestUri.Query);
109 Assert.Equal("1111", query["aaaa"]);
110 Assert.Equal("2222", query["bbbb"]);
112 return new HttpResponseMessage(HttpStatusCode.OK)
114 Content = new StringContent("\"hogehoge\""),
118 var endpoint = new Uri("http://example.com/hoge/tetete.json", UriKind.Absolute);
119 var param = new Dictionary<string, string>
125 await apiConnection.GetAsync<string>(endpoint, param, endpointName: "/hoge/tetete")
126 .ConfigureAwait(false);
128 Assert.Equal(0, mockHandler.QueueCount);
132 public async Task GetAsync_UpdateRateLimitTest()
134 using var mockHandler = new HttpMessageHandlerMock();
135 using var http = new HttpClient(mockHandler);
136 using var apiConnection = new TwitterApiConnection("", "");
137 apiConnection.http = http;
139 mockHandler.Enqueue(x =>
141 Assert.Equal(HttpMethod.Get, x.Method);
142 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
143 x.RequestUri.GetLeftPart(UriPartial.Path));
145 return new HttpResponseMessage(HttpStatusCode.OK)
149 { "X-Rate-Limit-Limit", "150" },
150 { "X-Rate-Limit-Remaining", "100" },
151 { "X-Rate-Limit-Reset", "1356998400" },
152 { "X-Access-Level", "read-write-directmessages" },
154 Content = new StringContent("\"hogehoge\""),
158 var apiStatus = new TwitterApiStatus();
159 MyCommon.TwitterApiInfo = apiStatus;
161 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
163 await apiConnection.GetAsync<string>(endpoint, null, endpointName: "/hoge/tetete")
164 .ConfigureAwait(false);
166 Assert.Equal(TwitterApiAccessLevel.ReadWriteAndDirectMessage, apiStatus.AccessLevel);
167 Assert.Equal(new ApiLimit(150, 100, new DateTimeUtc(2013, 1, 1, 0, 0, 0)), apiStatus.AccessLimit["/hoge/tetete"]);
169 Assert.Equal(0, mockHandler.QueueCount);
173 public async Task GetAsync_ErrorStatusTest()
175 using var mockHandler = new HttpMessageHandlerMock();
176 using var http = new HttpClient(mockHandler);
177 using var apiConnection = new TwitterApiConnection("", "");
178 apiConnection.http = http;
180 mockHandler.Enqueue(x =>
182 return new HttpResponseMessage(HttpStatusCode.BadGateway)
184 Content = new StringContent("### Invalid JSON Response ###"),
188 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
190 var exception = await Assert.ThrowsAsync<TwitterApiException>(() => apiConnection.GetAsync<string>(endpoint, null, endpointName: "/hoge/tetete"))
191 .ConfigureAwait(false);
193 // エラーレスポンスの読み込みに失敗した場合はステータスコードをそのままメッセージに使用する
194 Assert.Equal("BadGateway", exception.Message);
195 Assert.Null(exception.ErrorResponse);
197 Assert.Equal(0, mockHandler.QueueCount);
201 public async Task GetAsync_ErrorJsonTest()
203 using var mockHandler = new HttpMessageHandlerMock();
204 using var http = new HttpClient(mockHandler);
205 using var apiConnection = new TwitterApiConnection("", "");
206 apiConnection.http = http;
208 mockHandler.Enqueue(x =>
210 return new HttpResponseMessage(HttpStatusCode.Forbidden)
212 Content = new StringContent("{\"errors\":[{\"code\":187,\"message\":\"Status is a duplicate.\"}]}"),
216 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
218 var exception = await Assert.ThrowsAsync<TwitterApiException>(() => apiConnection.GetAsync<string>(endpoint, null, endpointName: "/hoge/tetete"))
219 .ConfigureAwait(false);
221 // エラーレスポンスの JSON に含まれるエラーコードに基づいてメッセージを出力する
222 Assert.Equal("DuplicateStatus", exception.Message);
224 Assert.Equal(TwitterErrorCode.DuplicateStatus, exception.Errors[0].Code);
225 Assert.Equal("Status is a duplicate.", exception.Errors[0].Message);
227 Assert.Equal(0, mockHandler.QueueCount);
231 public async Task GetStreamAsync_Test()
233 using var mockHandler = new HttpMessageHandlerMock();
234 using var http = new HttpClient(mockHandler);
235 using var apiConnection = new TwitterApiConnection("", "");
236 using var image = TestUtils.CreateDummyImage();
237 apiConnection.http = http;
239 mockHandler.Enqueue(x =>
241 Assert.Equal(HttpMethod.Get, x.Method);
242 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
243 x.RequestUri.GetLeftPart(UriPartial.Path));
245 var query = HttpUtility.ParseQueryString(x.RequestUri.Query);
247 Assert.Equal("1111", query["aaaa"]);
248 Assert.Equal("2222", query["bbbb"]);
250 return new HttpResponseMessage(HttpStatusCode.OK)
252 Content = new ByteArrayContent(image.Stream.ToArray()),
256 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
257 var param = new Dictionary<string, string>
263 var stream = await apiConnection.GetStreamAsync(endpoint, param)
264 .ConfigureAwait(false);
266 using (var memoryStream = new MemoryStream())
268 // 内容の比較のために MemoryStream にコピー
269 await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
271 Assert.Equal(image.Stream.ToArray(), memoryStream.ToArray());
274 Assert.Equal(0, mockHandler.QueueCount);
278 public async Task PostLazyAsync_Test()
280 using var mockHandler = new HttpMessageHandlerMock();
281 using var http = new HttpClient(mockHandler);
282 using var apiConnection = new TwitterApiConnection("", "");
283 apiConnection.http = http;
285 mockHandler.Enqueue(async x =>
287 Assert.Equal(HttpMethod.Post, x.Method);
288 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
289 x.RequestUri.AbsoluteUri);
291 var body = await x.Content.ReadAsStringAsync()
292 .ConfigureAwait(false);
293 var query = HttpUtility.ParseQueryString(body);
295 Assert.Equal("1111", query["aaaa"]);
296 Assert.Equal("2222", query["bbbb"]);
298 return new HttpResponseMessage(HttpStatusCode.OK)
300 Content = new StringContent("\"hogehoge\""),
304 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
305 var param = new Dictionary<string, string>
311 var result = await apiConnection.PostLazyAsync<string>(endpoint, param)
312 .ConfigureAwait(false);
314 Assert.Equal("hogehoge", await result.LoadJsonAsync()
315 .ConfigureAwait(false));
317 Assert.Equal(0, mockHandler.QueueCount);
321 public async Task PostLazyAsync_MultipartTest()
323 using var mockHandler = new HttpMessageHandlerMock();
324 using var http = new HttpClient(mockHandler);
325 using var apiConnection = new TwitterApiConnection("", "");
326 apiConnection.httpUpload = http;
328 using var image = TestUtils.CreateDummyImage();
329 using var media = new MemoryImageMediaItem(image);
331 mockHandler.Enqueue(async x =>
333 Assert.Equal(HttpMethod.Post, x.Method);
334 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
335 x.RequestUri.AbsoluteUri);
337 Assert.IsType<MultipartFormDataContent>(x.Content);
339 var boundary = x.Content.Headers.ContentType.Parameters.Cast<NameValueHeaderValue>()
340 .First(y => y.Name == "boundary").Value;
343 boundary = boundary.Substring(1, boundary.Length - 2);
346 $"--{boundary}\r\n" +
347 "Content-Type: text/plain; charset=utf-8\r\n" +
348 "Content-Disposition: form-data; name=aaaa\r\n" +
351 $"--{boundary}\r\n" +
352 "Content-Type: text/plain; charset=utf-8\r\n" +
353 "Content-Disposition: form-data; name=bbbb\r\n" +
356 $"--{boundary}\r\n" +
357 $"Content-Disposition: form-data; name=media1; filename={media.Name}; filename*=utf-8''{media.Name}\r\n" +
360 var expected = Encoding.UTF8.GetBytes(expectedText)
361 .Concat(image.Stream.ToArray())
362 .Concat(Encoding.UTF8.GetBytes($"\r\n--{boundary}--\r\n"));
364 Assert.Equal(expected, await x.Content.ReadAsByteArrayAsync().ConfigureAwait(false));
366 return new HttpResponseMessage(HttpStatusCode.OK)
368 Content = new StringContent("\"hogehoge\""),
372 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
373 var param = new Dictionary<string, string>
378 var mediaParam = new Dictionary<string, IMediaItem>
383 var result = await apiConnection.PostLazyAsync<string>(endpoint, param, mediaParam)
384 .ConfigureAwait(false);
386 Assert.Equal("hogehoge", await result.LoadJsonAsync()
387 .ConfigureAwait(false));
389 Assert.Equal(0, mockHandler.QueueCount);
393 public async Task PostLazyAsync_Multipart_NullTest()
395 using var mockHandler = new HttpMessageHandlerMock();
396 using var http = new HttpClient(mockHandler);
397 using var apiConnection = new TwitterApiConnection("", "");
398 apiConnection.httpUpload = http;
400 mockHandler.Enqueue(async x =>
402 Assert.Equal(HttpMethod.Post, x.Method);
403 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
404 x.RequestUri.AbsoluteUri);
406 Assert.IsType<MultipartFormDataContent>(x.Content);
408 var boundary = x.Content.Headers.ContentType.Parameters.Cast<NameValueHeaderValue>()
409 .First(y => y.Name == "boundary").Value;
412 boundary = boundary.Substring(1, boundary.Length - 2);
415 $"--{boundary}\r\n" +
416 $"\r\n--{boundary}--\r\n";
418 var expected = Encoding.UTF8.GetBytes(expectedText);
420 Assert.Equal(expected, await x.Content.ReadAsByteArrayAsync().ConfigureAwait(false));
422 return new HttpResponseMessage(HttpStatusCode.OK)
424 Content = new StringContent("\"hogehoge\""),
428 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
430 var result = await apiConnection.PostLazyAsync<string>(endpoint, param: null, media: null)
431 .ConfigureAwait(false);
433 Assert.Equal("hogehoge", await result.LoadJsonAsync()
434 .ConfigureAwait(false));
436 Assert.Equal(0, mockHandler.QueueCount);
440 public async Task PostJsonAsync_Test()
442 using var mockHandler = new HttpMessageHandlerMock();
443 using var http = new HttpClient(mockHandler);
444 using var apiConnection = new TwitterApiConnection("", "");
445 apiConnection.http = http;
447 mockHandler.Enqueue(async x =>
449 Assert.Equal(HttpMethod.Post, x.Method);
450 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
451 x.RequestUri.AbsoluteUri);
453 Assert.Equal("application/json; charset=utf-8", x.Content.Headers.ContentType.ToString());
455 var body = await x.Content.ReadAsStringAsync()
456 .ConfigureAwait(false);
458 Assert.Equal("{\"aaaa\": 1111}", body);
460 return new HttpResponseMessage(HttpStatusCode.NoContent);
463 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
465 await apiConnection.PostJsonAsync(endpoint, "{\"aaaa\": 1111}")
466 .ConfigureAwait(false);
468 Assert.Equal(0, mockHandler.QueueCount);
472 public async Task PostJsonAsync_T_Test()
474 using var mockHandler = new HttpMessageHandlerMock();
475 using var http = new HttpClient(mockHandler);
476 using var apiConnection = new TwitterApiConnection("", "");
477 apiConnection.http = http;
479 mockHandler.Enqueue(async x =>
481 Assert.Equal(HttpMethod.Post, x.Method);
482 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
483 x.RequestUri.AbsoluteUri);
485 Assert.Equal("application/json; charset=utf-8", x.Content.Headers.ContentType.ToString());
487 var body = await x.Content.ReadAsStringAsync()
488 .ConfigureAwait(false);
490 Assert.Equal("{\"aaaa\": 1111}", body);
492 return new HttpResponseMessage(HttpStatusCode.OK)
494 Content = new StringContent("\"hogehoge\""),
498 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
500 var response = await apiConnection.PostJsonAsync<string>(endpoint, "{\"aaaa\": 1111}")
501 .ConfigureAwait(false);
503 var result = await response.LoadJsonAsync()
504 .ConfigureAwait(false);
506 Assert.Equal("hogehoge", result);
508 Assert.Equal(0, mockHandler.QueueCount);
512 public async Task DeleteAsync_Test()
514 using var mockHandler = new HttpMessageHandlerMock();
515 using var http = new HttpClient(mockHandler);
516 using var apiConnection = new TwitterApiConnection("", "");
517 apiConnection.http = http;
519 mockHandler.Enqueue(x =>
521 Assert.Equal(HttpMethod.Delete, x.Method);
522 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
523 x.RequestUri.AbsoluteUri);
525 return new HttpResponseMessage(HttpStatusCode.NoContent);
528 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
530 await apiConnection.DeleteAsync(endpoint)
531 .ConfigureAwait(false);
533 Assert.Equal(0, mockHandler.QueueCount);