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(ApiKey.Create(""), ApiKey.Create(""), "", "");
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(ApiKey.Create(""), ApiKey.Create(""), "", "");
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(ApiKey.Create(""), ApiKey.Create(""), "", "");
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(ApiKey.Create(""), ApiKey.Create(""), "", "");
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(ApiKey.Create(""), ApiKey.Create(""), "", "");
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(ApiKey.Create(""), ApiKey.Create(""), "", "");
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(ApiKey.Create(""), ApiKey.Create(""), "", "");
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().ConfigureAwait(false));
316 Assert.Equal(0, mockHandler.QueueCount);
320 public async Task PostLazyAsync_MultipartTest()
322 using var mockHandler = new HttpMessageHandlerMock();
323 using var http = new HttpClient(mockHandler);
324 using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", "");
325 apiConnection.httpUpload = http;
327 using var image = TestUtils.CreateDummyImage();
328 using var media = new MemoryImageMediaItem(image);
330 mockHandler.Enqueue(async x =>
332 Assert.Equal(HttpMethod.Post, x.Method);
333 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
334 x.RequestUri.AbsoluteUri);
336 Assert.IsType<MultipartFormDataContent>(x.Content);
338 var boundary = x.Content.Headers.ContentType.Parameters.Cast<NameValueHeaderValue>()
339 .First(y => y.Name == "boundary").Value;
342 boundary = boundary.Substring(1, boundary.Length - 2);
345 $"--{boundary}\r\n" +
346 "Content-Type: text/plain; charset=utf-8\r\n" +
347 "Content-Disposition: form-data; name=aaaa\r\n" +
350 $"--{boundary}\r\n" +
351 "Content-Type: text/plain; charset=utf-8\r\n" +
352 "Content-Disposition: form-data; name=bbbb\r\n" +
355 $"--{boundary}\r\n" +
356 $"Content-Disposition: form-data; name=media1; filename={media.Name}; filename*=utf-8''{media.Name}\r\n" +
359 var expected = Encoding.UTF8.GetBytes(expectedText)
360 .Concat(image.Stream.ToArray())
361 .Concat(Encoding.UTF8.GetBytes($"\r\n--{boundary}--\r\n"));
363 Assert.Equal(expected, await x.Content.ReadAsByteArrayAsync().ConfigureAwait(false));
365 return new HttpResponseMessage(HttpStatusCode.OK)
367 Content = new StringContent("\"hogehoge\""),
371 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
372 var param = new Dictionary<string, string>
377 var mediaParam = new Dictionary<string, IMediaItem>
382 var result = await apiConnection.PostLazyAsync<string>(endpoint, param, mediaParam)
383 .ConfigureAwait(false);
385 Assert.Equal("hogehoge", await result.LoadJsonAsync().ConfigureAwait(false));
387 Assert.Equal(0, mockHandler.QueueCount);
391 public async Task PostLazyAsync_Multipart_NullTest()
393 using var mockHandler = new HttpMessageHandlerMock();
394 using var http = new HttpClient(mockHandler);
395 using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", "");
396 apiConnection.httpUpload = http;
398 mockHandler.Enqueue(async x =>
400 Assert.Equal(HttpMethod.Post, x.Method);
401 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
402 x.RequestUri.AbsoluteUri);
404 Assert.IsType<MultipartFormDataContent>(x.Content);
406 var boundary = x.Content.Headers.ContentType.Parameters.Cast<NameValueHeaderValue>()
407 .First(y => y.Name == "boundary").Value;
410 boundary = boundary.Substring(1, boundary.Length - 2);
413 $"--{boundary}\r\n" +
414 $"\r\n--{boundary}--\r\n";
416 var expected = Encoding.UTF8.GetBytes(expectedText);
418 Assert.Equal(expected, await x.Content.ReadAsByteArrayAsync().ConfigureAwait(false));
420 return new HttpResponseMessage(HttpStatusCode.OK)
422 Content = new StringContent("\"hogehoge\""),
426 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
428 var result = await apiConnection.PostLazyAsync<string>(endpoint, param: null, media: null)
429 .ConfigureAwait(false);
431 Assert.Equal("hogehoge", await result.LoadJsonAsync().ConfigureAwait(false));
433 Assert.Equal(0, mockHandler.QueueCount);
437 public async Task PostJsonAsync_Test()
439 using var mockHandler = new HttpMessageHandlerMock();
440 using var http = new HttpClient(mockHandler);
441 using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", "");
442 apiConnection.http = http;
444 mockHandler.Enqueue(async x =>
446 Assert.Equal(HttpMethod.Post, x.Method);
447 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
448 x.RequestUri.AbsoluteUri);
450 Assert.Equal("application/json; charset=utf-8", x.Content.Headers.ContentType.ToString());
452 var body = await x.Content.ReadAsStringAsync()
453 .ConfigureAwait(false);
455 Assert.Equal("{\"aaaa\": 1111}", body);
457 return new HttpResponseMessage(HttpStatusCode.NoContent);
460 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
462 await apiConnection.PostJsonAsync(endpoint, "{\"aaaa\": 1111}")
463 .ConfigureAwait(false);
465 Assert.Equal(0, mockHandler.QueueCount);
469 public async Task PostJsonAsync_T_Test()
471 using var mockHandler = new HttpMessageHandlerMock();
472 using var http = new HttpClient(mockHandler);
473 using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", "");
474 apiConnection.http = http;
476 mockHandler.Enqueue(async x =>
478 Assert.Equal(HttpMethod.Post, x.Method);
479 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
480 x.RequestUri.AbsoluteUri);
482 Assert.Equal("application/json; charset=utf-8", x.Content.Headers.ContentType.ToString());
484 var body = await x.Content.ReadAsStringAsync()
485 .ConfigureAwait(false);
487 Assert.Equal("{\"aaaa\": 1111}", body);
489 return new HttpResponseMessage(HttpStatusCode.OK)
491 Content = new StringContent("\"hogehoge\""),
495 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
497 var response = await apiConnection.PostJsonAsync<string>(endpoint, "{\"aaaa\": 1111}")
498 .ConfigureAwait(false);
500 var result = await response.LoadJsonAsync()
501 .ConfigureAwait(false);
503 Assert.Equal("hogehoge", result);
505 Assert.Equal(0, mockHandler.QueueCount);
509 public async Task DeleteAsync_Test()
511 using var mockHandler = new HttpMessageHandlerMock();
512 using var http = new HttpClient(mockHandler);
513 using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", "");
514 apiConnection.http = http;
516 mockHandler.Enqueue(x =>
518 Assert.Equal(HttpMethod.Delete, x.Method);
519 Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
520 x.RequestUri.AbsoluteUri);
522 return new HttpResponseMessage(HttpStatusCode.NoContent);
525 var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
527 await apiConnection.DeleteAsync(endpoint)
528 .ConfigureAwait(false);
530 Assert.Equal(0, mockHandler.QueueCount);