OSDN Git Service

9266c60ff2dd3731f7cf0d57d6d362d68a03573a
[opentween/open-tween.git] / OpenTween.Tests / Api / TwitterApiTest.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2016 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
4 //
5 // This file is part of OpenTween.
6 //
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)
10 // any later version.
11 //
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
15 // for more details.
16 //
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.
21
22 using System;
23 using System.Collections.Generic;
24 using System.IO;
25 using System.Linq;
26 using System.Net;
27 using System.Net.Http;
28 using System.Reflection;
29 using System.Runtime.InteropServices;
30 using System.Text;
31 using System.Threading.Tasks;
32 using Moq;
33 using OpenTween.Api.DataModel;
34 using OpenTween.Connection;
35 using Xunit;
36
37 namespace OpenTween.Api
38 {
39     public class TwitterApiTest
40     {
41         public TwitterApiTest()
42             => this.MyCommonSetup();
43
44         private void MyCommonSetup()
45         {
46             var mockAssembly = new Mock<_Assembly>();
47             mockAssembly.Setup(m => m.GetName()).Returns(new AssemblyName("OpenTween"));
48
49             MyCommon.EntryAssembly = mockAssembly.Object;
50         }
51
52         [Fact]
53         public void Initialize_Test()
54         {
55             using var twitterApi = new TwitterApi();
56             var apiConnection = Assert.IsType<TwitterApiConnection>(twitterApi.Connection);
57             Assert.IsType<TwitterCredentialNone>(apiConnection.Credential);
58
59             var credential = new TwitterCredentialOAuth1(TwitterAppToken.GetDefault(), "*** AccessToken ***", "*** AccessSecret ***");
60             twitterApi.Initialize(credential, userId: 100L, screenName: "hogehoge");
61
62             apiConnection = Assert.IsType<TwitterApiConnection>(twitterApi.Connection);
63             Assert.Same(credential, apiConnection.Credential);
64
65             Assert.Equal(100L, twitterApi.CurrentUserId);
66             Assert.Equal("hogehoge", twitterApi.CurrentScreenName);
67
68             // 複数回 Initialize を実行した場合は新たに TwitterApiConnection が生成される
69             var credential2 = new TwitterCredentialOAuth1(TwitterAppToken.GetDefault(), "*** AccessToken2 ***", "*** AccessSecret2 ***");
70             twitterApi.Initialize(credential2, userId: 200L, screenName: "foobar");
71
72             var oldApiConnection = apiConnection;
73             Assert.True(oldApiConnection.IsDisposed);
74
75             apiConnection = Assert.IsType<TwitterApiConnection>(twitterApi.Connection);
76             Assert.Same(credential2, apiConnection.Credential);
77
78             Assert.Equal(200L, twitterApi.CurrentUserId);
79             Assert.Equal("foobar", twitterApi.CurrentScreenName);
80         }
81
82         private Mock<IApiConnectionLegacy> CreateApiConnectionMock<T>(Action<T> verifyRequest)
83             where T : IHttpRequest
84         {
85             Func<T, bool> verifyRequestWrapper = r =>
86             {
87                 verifyRequest(r);
88                 // Assert メソッドを使用する想定のため、失敗した場合は例外が発生しここまで到達しない
89                 return true;
90             };
91
92             var responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
93             var mock = new Mock<IApiConnectionLegacy>();
94             mock.Setup(x =>
95                 x.SendAsync(
96                     It.Is<T>(r => verifyRequestWrapper(r))
97                 )
98             )
99             .ReturnsAsync(new ApiResponse(responseMessage));
100
101             return mock;
102         }
103
104         [Fact]
105         public async Task StatusesHomeTimeline_Test()
106         {
107             var mock = new Mock<IApiConnectionLegacy>();
108             mock.Setup(x =>
109                 x.GetAsync<TwitterStatus[]>(
110                     new Uri("statuses/home_timeline.json", UriKind.Relative),
111                     new Dictionary<string, string>
112                     {
113                             { "include_entities", "true" },
114                             { "include_ext_alt_text", "true" },
115                             { "tweet_mode", "extended" },
116                             { "count", "200" },
117                             { "max_id", "900" },
118                             { "since_id", "100" },
119                     },
120                     "/statuses/home_timeline")
121             )
122             .ReturnsAsync(Array.Empty<TwitterStatus>());
123
124             using var twitterApi = new TwitterApi();
125             twitterApi.ApiConnection = mock.Object;
126
127             await twitterApi.StatusesHomeTimeline(200, maxId: new("900"), sinceId: new("100"));
128
129             mock.VerifyAll();
130         }
131
132         [Fact]
133         public async Task StatusesMentionsTimeline_Test()
134         {
135             var mock = new Mock<IApiConnectionLegacy>();
136             mock.Setup(x =>
137                 x.GetAsync<TwitterStatus[]>(
138                     new Uri("statuses/mentions_timeline.json", UriKind.Relative),
139                     new Dictionary<string, string>
140                     {
141                             { "include_entities", "true" },
142                             { "include_ext_alt_text", "true" },
143                             { "tweet_mode", "extended" },
144                             { "count", "200" },
145                             { "max_id", "900" },
146                             { "since_id", "100" },
147                     },
148                     "/statuses/mentions_timeline")
149             )
150             .ReturnsAsync(Array.Empty<TwitterStatus>());
151
152             using var twitterApi = new TwitterApi();
153             twitterApi.ApiConnection = mock.Object;
154
155             await twitterApi.StatusesMentionsTimeline(200, maxId: new("900"), sinceId: new("100"));
156
157             mock.VerifyAll();
158         }
159
160         [Fact]
161         public async Task StatusesUserTimeline_Test()
162         {
163             var mock = new Mock<IApiConnectionLegacy>();
164             mock.Setup(x =>
165                 x.GetAsync<TwitterStatus[]>(
166                     new Uri("statuses/user_timeline.json", UriKind.Relative),
167                     new Dictionary<string, string>
168                     {
169                             { "screen_name", "twitterapi" },
170                             { "include_rts", "true" },
171                             { "include_entities", "true" },
172                             { "include_ext_alt_text", "true" },
173                             { "tweet_mode", "extended" },
174                             { "count", "200" },
175                             { "max_id", "900" },
176                             { "since_id", "100" },
177                     },
178                     "/statuses/user_timeline")
179             )
180             .ReturnsAsync(Array.Empty<TwitterStatus>());
181
182             using var twitterApi = new TwitterApi();
183             twitterApi.ApiConnection = mock.Object;
184
185             await twitterApi.StatusesUserTimeline("twitterapi", count: 200, maxId: new("900"), sinceId: new("100"));
186
187             mock.VerifyAll();
188         }
189
190         [Fact]
191         public async Task StatusesShow_Test()
192         {
193             var mock = new Mock<IApiConnectionLegacy>();
194             mock.Setup(x =>
195                 x.GetAsync<TwitterStatus>(
196                     new Uri("statuses/show.json", UriKind.Relative),
197                     new Dictionary<string, string>
198                     {
199                             { "id", "100" },
200                             { "include_entities", "true" },
201                             { "include_ext_alt_text", "true" },
202                             { "tweet_mode", "extended" },
203                     },
204                     "/statuses/show/:id")
205             )
206             .ReturnsAsync(new TwitterStatus { Id = 100L });
207
208             using var twitterApi = new TwitterApi();
209             twitterApi.ApiConnection = mock.Object;
210
211             await twitterApi.StatusesShow(statusId: new("100"));
212
213             mock.VerifyAll();
214         }
215
216         [Fact]
217         public async Task StatusesLookup_Test()
218         {
219             var mock = new Mock<IApiConnectionLegacy>();
220             mock.Setup(x =>
221                 x.GetAsync<TwitterStatus[]>(
222                     new Uri("statuses/lookup.json", UriKind.Relative),
223                     new Dictionary<string, string>
224                     {
225                         { "id", "100,200" },
226                         { "include_entities", "true" },
227                         { "include_ext_alt_text", "true" },
228                         { "tweet_mode", "extended" },
229                     },
230                     "/statuses/lookup"
231                 )
232             )
233             .ReturnsAsync(Array.Empty<TwitterStatus>());
234
235             using var twitterApi = new TwitterApi();
236             twitterApi.ApiConnection = mock.Object;
237
238             await twitterApi.StatusesLookup(statusIds: new[] { "100", "200" });
239
240             mock.VerifyAll();
241         }
242
243         [Fact]
244         public async Task StatusesUpdate_Test()
245         {
246             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
247             {
248                 Assert.Equal(new("statuses/update.json", UriKind.Relative), r.RequestUri);
249                 var expectedQuery = new Dictionary<string, string>
250                 {
251                     ["status"] = "hogehoge",
252                     ["include_entities"] = "true",
253                     ["include_ext_alt_text"] = "true",
254                     ["tweet_mode"] = "extended",
255                     ["in_reply_to_status_id"] = "100",
256                     ["media_ids"] = "10,20",
257                     ["auto_populate_reply_metadata"] = "true",
258                     ["exclude_reply_user_ids"] = "100,200",
259                     ["attachment_url"] = "https://twitter.com/twitterapi/status/22634515958",
260                 };
261                 Assert.Equal(expectedQuery, r.Query);
262             });
263
264             using var twitterApi = new TwitterApi();
265             twitterApi.ApiConnection = mock.Object;
266
267             await twitterApi.StatusesUpdate(
268                     "hogehoge",
269                     replyToId: new("100"),
270                     mediaIds: new[] { 10L, 20L },
271                     autoPopulateReplyMetadata: true,
272                     excludeReplyUserIds: new[] { 100L, 200L },
273                     attachmentUrl: "https://twitter.com/twitterapi/status/22634515958"
274                 )
275                 .IgnoreResponse();
276
277             mock.VerifyAll();
278         }
279
280         [Fact]
281         public async Task StatusesUpdate_ExcludeReplyUserIdsEmptyTest()
282         {
283             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
284             {
285                 Assert.Equal(new("statuses/update.json", UriKind.Relative), r.RequestUri);
286                 var expectedQuery = new Dictionary<string, string>
287                 {
288                     ["status"] = "hogehoge",
289                     ["include_entities"] = "true",
290                     ["include_ext_alt_text"] = "true",
291                     ["tweet_mode"] = "extended",
292                     // exclude_reply_user_ids は空の場合には送信されない
293                 };
294                 Assert.Equal(expectedQuery, r.Query);
295             });
296
297             using var twitterApi = new TwitterApi();
298             twitterApi.ApiConnection = mock.Object;
299
300             await twitterApi.StatusesUpdate("hogehoge", replyToId: null, mediaIds: null, excludeReplyUserIds: Array.Empty<long>())
301                 .IgnoreResponse();
302
303             mock.VerifyAll();
304         }
305
306         [Fact]
307         public async Task StatusesDestroy_Test()
308         {
309             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
310             {
311                 Assert.Equal(new("statuses/destroy.json", UriKind.Relative), r.RequestUri);
312                 var expectedQuery = new Dictionary<string, string>
313                 {
314                     ["id"] = "100",
315                 };
316                 Assert.Equal(expectedQuery, r.Query);
317             });
318
319             using var twitterApi = new TwitterApi();
320             twitterApi.ApiConnection = mock.Object;
321
322             await twitterApi.StatusesDestroy(statusId: new("100"))
323                 .IgnoreResponse();
324
325             mock.VerifyAll();
326         }
327
328         [Fact]
329         public async Task StatusesRetweet_Test()
330         {
331             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
332             {
333                 Assert.Equal(new("statuses/retweet.json", UriKind.Relative), r.RequestUri);
334                 var expectedQuery = new Dictionary<string, string>
335                 {
336                     ["id"] = "100",
337                     ["include_entities"] = "true",
338                     ["include_ext_alt_text"] = "true",
339                     ["tweet_mode"] = "extended",
340                 };
341                 Assert.Equal(expectedQuery, r.Query);
342             });
343
344             using var twitterApi = new TwitterApi();
345             twitterApi.ApiConnection = mock.Object;
346
347             await twitterApi.StatusesRetweet(new("100"))
348                 .IgnoreResponse();
349
350             mock.VerifyAll();
351         }
352
353         [Fact]
354         public async Task SearchTweets_Test()
355         {
356             var mock = new Mock<IApiConnectionLegacy>();
357             mock.Setup(x =>
358                 x.GetAsync<TwitterSearchResult>(
359                     new Uri("search/tweets.json", UriKind.Relative),
360                     new Dictionary<string, string>
361                     {
362                             { "q", "from:twitterapi" },
363                             { "result_type", "recent" },
364                             { "include_entities", "true" },
365                             { "include_ext_alt_text", "true" },
366                             { "tweet_mode", "extended" },
367                             { "lang", "en" },
368                             { "count", "200" },
369                             { "max_id", "900" },
370                             { "since_id", "100" },
371                     },
372                     "/search/tweets")
373             )
374             .ReturnsAsync(new TwitterSearchResult());
375
376             using var twitterApi = new TwitterApi();
377             twitterApi.ApiConnection = mock.Object;
378
379             await twitterApi.SearchTweets("from:twitterapi", "en", count: 200, maxId: new("900"), sinceId: new("100"));
380
381             mock.VerifyAll();
382         }
383
384         [Fact]
385         public async Task ListsOwnerships_Test()
386         {
387             var mock = new Mock<IApiConnectionLegacy>();
388             mock.Setup(x =>
389                 x.GetAsync<TwitterLists>(
390                     new Uri("lists/ownerships.json", UriKind.Relative),
391                     new Dictionary<string, string>
392                     {
393                             { "screen_name", "twitterapi" },
394                             { "cursor", "-1" },
395                             { "count", "100" },
396                     },
397                     "/lists/ownerships")
398             )
399             .ReturnsAsync(new TwitterLists());
400
401             using var twitterApi = new TwitterApi();
402             twitterApi.ApiConnection = mock.Object;
403
404             await twitterApi.ListsOwnerships("twitterapi", cursor: -1L, count: 100);
405
406             mock.VerifyAll();
407         }
408
409         [Fact]
410         public async Task ListsSubscriptions_Test()
411         {
412             var mock = new Mock<IApiConnectionLegacy>();
413             mock.Setup(x =>
414                 x.GetAsync<TwitterLists>(
415                     new Uri("lists/subscriptions.json", UriKind.Relative),
416                     new Dictionary<string, string>
417                     {
418                             { "screen_name", "twitterapi" },
419                             { "cursor", "-1" },
420                             { "count", "100" },
421                     },
422                     "/lists/subscriptions")
423             )
424             .ReturnsAsync(new TwitterLists());
425
426             using var twitterApi = new TwitterApi();
427             twitterApi.ApiConnection = mock.Object;
428
429             await twitterApi.ListsSubscriptions("twitterapi", cursor: -1L, count: 100);
430
431             mock.VerifyAll();
432         }
433
434         [Fact]
435         public async Task ListsMemberships_Test()
436         {
437             var mock = new Mock<IApiConnectionLegacy>();
438             mock.Setup(x =>
439                 x.GetAsync<TwitterLists>(
440                     new Uri("lists/memberships.json", UriKind.Relative),
441                     new Dictionary<string, string>
442                     {
443                             { "screen_name", "twitterapi" },
444                             { "cursor", "-1" },
445                             { "count", "100" },
446                             { "filter_to_owned_lists", "true" },
447                     },
448                     "/lists/memberships")
449             )
450             .ReturnsAsync(new TwitterLists());
451
452             using var twitterApi = new TwitterApi();
453             twitterApi.ApiConnection = mock.Object;
454
455             await twitterApi.ListsMemberships("twitterapi", cursor: -1L, count: 100, filterToOwnedLists: true);
456
457             mock.VerifyAll();
458         }
459
460         [Fact]
461         public async Task ListsCreate_Test()
462         {
463             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
464             {
465                 Assert.Equal(new("lists/create.json", UriKind.Relative), r.RequestUri);
466                 var expectedQuery = new Dictionary<string, string>
467                 {
468                     ["name"] = "hogehoge",
469                     ["description"] = "aaaa",
470                     ["mode"] = "private",
471                 };
472                 Assert.Equal(expectedQuery, r.Query);
473             });
474
475             using var twitterApi = new TwitterApi();
476             twitterApi.ApiConnection = mock.Object;
477
478             await twitterApi.ListsCreate("hogehoge", description: "aaaa", @private: true)
479                 .IgnoreResponse();
480
481             mock.VerifyAll();
482         }
483
484         [Fact]
485         public async Task ListsUpdate_Test()
486         {
487             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
488             {
489                 Assert.Equal(new("lists/update.json", UriKind.Relative), r.RequestUri);
490                 var expectedQuery = new Dictionary<string, string>
491                 {
492                     ["list_id"] = "12345",
493                     ["name"] = "hogehoge",
494                     ["description"] = "aaaa",
495                     ["mode"] = "private",
496                 };
497                 Assert.Equal(expectedQuery, r.Query);
498             });
499
500             using var twitterApi = new TwitterApi();
501             twitterApi.ApiConnection = mock.Object;
502
503             await twitterApi.ListsUpdate(12345L, name: "hogehoge", description: "aaaa", @private: true)
504                 .IgnoreResponse();
505
506             mock.VerifyAll();
507         }
508
509         [Fact]
510         public async Task ListsDestroy_Test()
511         {
512             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
513             {
514                 Assert.Equal(new("lists/destroy.json", UriKind.Relative), r.RequestUri);
515                 var expectedQuery = new Dictionary<string, string>
516                 {
517                     ["list_id"] = "12345",
518                 };
519                 Assert.Equal(expectedQuery, r.Query);
520             });
521
522             using var twitterApi = new TwitterApi();
523             twitterApi.ApiConnection = mock.Object;
524
525             await twitterApi.ListsDestroy(12345L)
526                 .IgnoreResponse();
527
528             mock.VerifyAll();
529         }
530
531         [Fact]
532         public async Task ListsStatuses_Test()
533         {
534             var mock = new Mock<IApiConnectionLegacy>();
535             mock.Setup(x =>
536                 x.GetAsync<TwitterStatus[]>(
537                     new Uri("lists/statuses.json", UriKind.Relative),
538                     new Dictionary<string, string>
539                     {
540                             { "list_id", "12345" },
541                             { "include_entities", "true" },
542                             { "include_ext_alt_text", "true" },
543                             { "tweet_mode", "extended" },
544                             { "count", "200" },
545                             { "max_id", "900" },
546                             { "since_id", "100" },
547                             { "include_rts", "true" },
548                     },
549                     "/lists/statuses")
550             )
551             .ReturnsAsync(Array.Empty<TwitterStatus>());
552
553             using var twitterApi = new TwitterApi();
554             twitterApi.ApiConnection = mock.Object;
555
556             await twitterApi.ListsStatuses(12345L, count: 200, maxId: new("900"), sinceId: new("100"), includeRTs: true);
557
558             mock.VerifyAll();
559         }
560
561         [Fact]
562         public async Task ListsMembers_Test()
563         {
564             var mock = new Mock<IApiConnectionLegacy>();
565             mock.Setup(x =>
566                 x.GetAsync<TwitterUsers>(
567                     new Uri("lists/members.json", UriKind.Relative),
568                     new Dictionary<string, string>
569                     {
570                             { "list_id", "12345" },
571                             { "include_entities", "true" },
572                             { "include_ext_alt_text", "true" },
573                             { "tweet_mode", "extended" },
574                             { "cursor", "-1" },
575                     },
576                     "/lists/members")
577             )
578             .ReturnsAsync(new TwitterUsers());
579
580             using var twitterApi = new TwitterApi();
581             twitterApi.ApiConnection = mock.Object;
582
583             await twitterApi.ListsMembers(12345L, cursor: -1);
584
585             mock.VerifyAll();
586         }
587
588         [Fact]
589         public async Task ListsMembersShow_Test()
590         {
591             var mock = new Mock<IApiConnectionLegacy>();
592             mock.Setup(x =>
593                 x.GetAsync<TwitterUser>(
594                     new Uri("lists/members/show.json", UriKind.Relative),
595                     new Dictionary<string, string>
596                     {
597                             { "list_id", "12345" },
598                             { "screen_name", "twitterapi" },
599                             { "include_entities", "true" },
600                             { "include_ext_alt_text", "true" },
601                             { "tweet_mode", "extended" },
602                     },
603                     "/lists/members/show")
604             )
605             .ReturnsAsync(new TwitterUser());
606
607             using var twitterApi = new TwitterApi();
608             twitterApi.ApiConnection = mock.Object;
609
610             await twitterApi.ListsMembersShow(12345L, "twitterapi");
611
612             mock.VerifyAll();
613         }
614
615         [Fact]
616         public async Task ListsMembersCreate_Test()
617         {
618             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
619             {
620                 Assert.Equal(new("lists/members/create.json", UriKind.Relative), r.RequestUri);
621                 var expectedQuery = new Dictionary<string, string>
622                 {
623                     ["list_id"] = "12345",
624                     ["screen_name"] = "twitterapi",
625                     ["include_entities"] = "true",
626                     ["include_ext_alt_text"] = "true",
627                     ["tweet_mode"] = "extended",
628                 };
629                 Assert.Equal(expectedQuery, r.Query);
630             });
631
632             using var twitterApi = new TwitterApi();
633             twitterApi.ApiConnection = mock.Object;
634
635             await twitterApi.ListsMembersCreate(12345L, "twitterapi")
636                 .IgnoreResponse();
637
638             mock.VerifyAll();
639         }
640
641         [Fact]
642         public async Task ListsMembersDestroy_Test()
643         {
644             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
645             {
646                 Assert.Equal(new("lists/members/destroy.json", UriKind.Relative), r.RequestUri);
647                 var expectedQuery = new Dictionary<string, string>
648                 {
649                     ["list_id"] = "12345",
650                     ["screen_name"] = "twitterapi",
651                     ["include_entities"] = "true",
652                     ["include_ext_alt_text"] = "true",
653                     ["tweet_mode"] = "extended",
654                 };
655                 Assert.Equal(expectedQuery, r.Query);
656             });
657
658             using var twitterApi = new TwitterApi();
659             twitterApi.ApiConnection = mock.Object;
660
661             await twitterApi.ListsMembersDestroy(12345L, "twitterapi")
662                 .IgnoreResponse();
663
664             mock.VerifyAll();
665         }
666
667         [Fact]
668         public async Task DirectMessagesEventsList_Test()
669         {
670             var mock = new Mock<IApiConnectionLegacy>();
671             mock.Setup(x =>
672                 x.GetAsync<TwitterMessageEventList>(
673                     new Uri("direct_messages/events/list.json", UriKind.Relative),
674                     new Dictionary<string, string>
675                     {
676                             { "count", "50" },
677                             { "cursor", "12345abcdefg" },
678                     },
679                     "/direct_messages/events/list")
680             )
681             .ReturnsAsync(new TwitterMessageEventList());
682
683             using var twitterApi = new TwitterApi();
684             twitterApi.ApiConnection = mock.Object;
685
686             await twitterApi.DirectMessagesEventsList(count: 50, cursor: "12345abcdefg");
687
688             mock.VerifyAll();
689         }
690
691         [Fact]
692         public async Task DirectMessagesEventsNew_Test()
693         {
694             var requestJson = """
695                 {
696                   "event": {
697                     "type": "message_create",
698                     "message_create": {
699                       "target": {
700                         "recipient_id": "12345"
701                       },
702                       "message_data": {
703                         "text": "hogehoge",
704                         "attachment": {
705                           "type": "media",
706                           "media": {
707                             "id": "67890"
708                           }
709                         }
710                       }
711                     }
712                   }
713                 }
714                 """;
715
716             var mock = this.CreateApiConnectionMock<PostJsonRequest>(r =>
717             {
718                 Assert.Equal(new("direct_messages/events/new.json", UriKind.Relative), r.RequestUri);
719                 Assert.Equal(requestJson, r.JsonString);
720             });
721
722             using var twitterApi = new TwitterApi();
723             twitterApi.ApiConnection = mock.Object;
724
725             await twitterApi.DirectMessagesEventsNew(recipientId: 12345L, text: "hogehoge", mediaId: 67890L);
726
727             mock.VerifyAll();
728         }
729
730         [Fact]
731         public async Task DirectMessagesEventsDestroy_Test()
732         {
733             var mock = this.CreateApiConnectionMock<DeleteRequest>(r =>
734             {
735                 Assert.Equal(new("direct_messages/events/destroy.json", UriKind.Relative), r.RequestUri);
736                 var expectedQuery = new Dictionary<string, string>
737                 {
738                     ["id"] = "100",
739                 };
740                 Assert.Equal(expectedQuery, r.Query);
741             });
742
743             using var twitterApi = new TwitterApi();
744             twitterApi.ApiConnection = mock.Object;
745
746             await twitterApi.DirectMessagesEventsDestroy(eventId: new("100"));
747
748             mock.VerifyAll();
749         }
750
751         [Fact]
752         public async Task UsersShow_Test()
753         {
754             var mock = new Mock<IApiConnectionLegacy>();
755             mock.Setup(x =>
756                 x.GetAsync<TwitterUser>(
757                     new Uri("users/show.json", UriKind.Relative),
758                     new Dictionary<string, string>
759                     {
760                             { "screen_name", "twitterapi" },
761                             { "include_entities", "true" },
762                             { "include_ext_alt_text", "true" },
763                             { "tweet_mode", "extended" },
764                     },
765                     "/users/show/:id")
766             )
767             .ReturnsAsync(new TwitterUser { ScreenName = "twitterapi" });
768
769             using var twitterApi = new TwitterApi();
770             twitterApi.ApiConnection = mock.Object;
771
772             await twitterApi.UsersShow(screenName: "twitterapi");
773
774             mock.VerifyAll();
775         }
776
777         [Fact]
778         public async Task UsersLookup_Test()
779         {
780             var mock = new Mock<IApiConnectionLegacy>();
781             mock.Setup(x =>
782                 x.GetAsync<TwitterUser[]>(
783                     new Uri("users/lookup.json", UriKind.Relative),
784                     new Dictionary<string, string>
785                     {
786                             { "user_id", "11111,22222" },
787                             { "include_entities", "true" },
788                             { "include_ext_alt_text", "true" },
789                             { "tweet_mode", "extended" },
790                     },
791                     "/users/lookup")
792             )
793             .ReturnsAsync(Array.Empty<TwitterUser>());
794
795             using var twitterApi = new TwitterApi();
796             twitterApi.ApiConnection = mock.Object;
797
798             await twitterApi.UsersLookup(userIds: new[] { "11111", "22222" });
799
800             mock.VerifyAll();
801         }
802
803         [Fact]
804         public async Task UsersReportSpam_Test()
805         {
806             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
807             {
808                 Assert.Equal(new("users/report_spam.json", UriKind.Relative), r.RequestUri);
809                 var expectedQuery = new Dictionary<string, string>
810                 {
811                     ["screen_name"] = "twitterapi",
812                     ["tweet_mode"] = "extended",
813                 };
814                 Assert.Equal(expectedQuery, r.Query);
815             });
816
817             using var twitterApi = new TwitterApi();
818             twitterApi.ApiConnection = mock.Object;
819
820             await twitterApi.UsersReportSpam(screenName: "twitterapi")
821                 .IgnoreResponse();
822
823             mock.VerifyAll();
824         }
825
826         [Fact]
827         public async Task FavoritesList_Test()
828         {
829             var mock = new Mock<IApiConnectionLegacy>();
830             mock.Setup(x =>
831                 x.GetAsync<TwitterStatus[]>(
832                     new Uri("favorites/list.json", UriKind.Relative),
833                     new Dictionary<string, string>
834                     {
835                             { "include_entities", "true" },
836                             { "include_ext_alt_text", "true" },
837                             { "tweet_mode", "extended" },
838                             { "count", "200" },
839                             { "max_id", "900" },
840                             { "since_id", "100" },
841                     },
842                     "/favorites/list")
843             )
844             .ReturnsAsync(Array.Empty<TwitterStatus>());
845
846             using var twitterApi = new TwitterApi();
847             twitterApi.ApiConnection = mock.Object;
848
849             await twitterApi.FavoritesList(200, maxId: 900L, sinceId: 100L);
850
851             mock.VerifyAll();
852         }
853
854         [Fact]
855         public async Task FavoritesCreate_Test()
856         {
857             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
858             {
859                 Assert.Equal(new("favorites/create.json", UriKind.Relative), r.RequestUri);
860                 var expectedQuery = new Dictionary<string, string>
861                 {
862                     ["id"] = "100",
863                     ["tweet_mode"] = "extended",
864                 };
865                 Assert.Equal(expectedQuery, r.Query);
866             });
867
868             using var twitterApi = new TwitterApi();
869             twitterApi.ApiConnection = mock.Object;
870
871             await twitterApi.FavoritesCreate(statusId: new("100"))
872                 .IgnoreResponse();
873
874             mock.VerifyAll();
875         }
876
877         [Fact]
878         public async Task FavoritesDestroy_Test()
879         {
880             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
881             {
882                 Assert.Equal(new("favorites/destroy.json", UriKind.Relative), r.RequestUri);
883                 var expectedQuery = new Dictionary<string, string>
884                 {
885                     ["id"] = "100",
886                     ["tweet_mode"] = "extended",
887                 };
888                 Assert.Equal(expectedQuery, r.Query);
889             });
890
891             using var twitterApi = new TwitterApi();
892             twitterApi.ApiConnection = mock.Object;
893
894             await twitterApi.FavoritesDestroy(statusId: new("100"))
895                 .IgnoreResponse();
896
897             mock.VerifyAll();
898         }
899
900         [Fact]
901         public async Task FriendshipsShow_Test()
902         {
903             var mock = new Mock<IApiConnectionLegacy>();
904             mock.Setup(x =>
905                 x.GetAsync<TwitterFriendship>(
906                     new Uri("friendships/show.json", UriKind.Relative),
907                     new Dictionary<string, string> { { "source_screen_name", "twitter" }, { "target_screen_name", "twitterapi" } },
908                     "/friendships/show")
909             )
910             .ReturnsAsync(new TwitterFriendship());
911
912             using var twitterApi = new TwitterApi();
913             twitterApi.ApiConnection = mock.Object;
914
915             await twitterApi.FriendshipsShow(sourceScreenName: "twitter", targetScreenName: "twitterapi");
916
917             mock.VerifyAll();
918         }
919
920         [Fact]
921         public async Task FriendshipsCreate_Test()
922         {
923             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
924             {
925                 Assert.Equal(new("friendships/create.json", UriKind.Relative), r.RequestUri);
926                 var expectedQuery = new Dictionary<string, string>
927                 {
928                     ["screen_name"] = "twitterapi",
929                 };
930                 Assert.Equal(expectedQuery, r.Query);
931             });
932
933             using var twitterApi = new TwitterApi();
934             twitterApi.ApiConnection = mock.Object;
935
936             await twitterApi.FriendshipsCreate(screenName: "twitterapi")
937                 .IgnoreResponse();
938
939             mock.VerifyAll();
940         }
941
942         [Fact]
943         public async Task FriendshipsDestroy_Test()
944         {
945             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
946             {
947                 Assert.Equal(new("friendships/destroy.json", UriKind.Relative), r.RequestUri);
948                 var expectedQuery = new Dictionary<string, string>
949                 {
950                     ["screen_name"] = "twitterapi",
951                 };
952                 Assert.Equal(expectedQuery, r.Query);
953             });
954
955             using var twitterApi = new TwitterApi();
956             twitterApi.ApiConnection = mock.Object;
957
958             await twitterApi.FriendshipsDestroy(screenName: "twitterapi")
959                 .IgnoreResponse();
960
961             mock.VerifyAll();
962         }
963
964         [Fact]
965         public async Task NoRetweetIds_Test()
966         {
967             var mock = new Mock<IApiConnectionLegacy>();
968             mock.Setup(x =>
969                 x.GetAsync<long[]>(
970                     new Uri("friendships/no_retweets/ids.json", UriKind.Relative),
971                     null,
972                     "/friendships/no_retweets/ids")
973             )
974             .ReturnsAsync(Array.Empty<long>());
975
976             using var twitterApi = new TwitterApi();
977             twitterApi.ApiConnection = mock.Object;
978
979             await twitterApi.NoRetweetIds();
980
981             mock.VerifyAll();
982         }
983
984         [Fact]
985         public async Task FollowersIds_Test()
986         {
987             var mock = new Mock<IApiConnectionLegacy>();
988             mock.Setup(x =>
989                 x.GetAsync<TwitterIds>(
990                     new Uri("followers/ids.json", UriKind.Relative),
991                     new Dictionary<string, string> { { "cursor", "-1" } },
992                     "/followers/ids")
993             )
994             .ReturnsAsync(new TwitterIds());
995
996             using var twitterApi = new TwitterApi();
997             twitterApi.ApiConnection = mock.Object;
998
999             await twitterApi.FollowersIds(cursor: -1L);
1000
1001             mock.VerifyAll();
1002         }
1003
1004         [Fact]
1005         public async Task MutesUsersIds_Test()
1006         {
1007             var mock = new Mock<IApiConnectionLegacy>();
1008             mock.Setup(x =>
1009                 x.GetAsync<TwitterIds>(
1010                     new Uri("mutes/users/ids.json", UriKind.Relative),
1011                     new Dictionary<string, string> { { "cursor", "-1" } },
1012                     "/mutes/users/ids")
1013             )
1014             .ReturnsAsync(new TwitterIds());
1015
1016             using var twitterApi = new TwitterApi();
1017             twitterApi.ApiConnection = mock.Object;
1018
1019             await twitterApi.MutesUsersIds(cursor: -1L);
1020
1021             mock.VerifyAll();
1022         }
1023
1024         [Fact]
1025         public async Task BlocksIds_Test()
1026         {
1027             var mock = new Mock<IApiConnectionLegacy>();
1028             mock.Setup(x =>
1029                 x.GetAsync<TwitterIds>(
1030                     new Uri("blocks/ids.json", UriKind.Relative),
1031                     new Dictionary<string, string> { { "cursor", "-1" } },
1032                     "/blocks/ids")
1033             )
1034             .ReturnsAsync(new TwitterIds());
1035
1036             using var twitterApi = new TwitterApi();
1037             twitterApi.ApiConnection = mock.Object;
1038
1039             await twitterApi.BlocksIds(cursor: -1L);
1040
1041             mock.VerifyAll();
1042         }
1043
1044         [Fact]
1045         public async Task BlocksCreate_Test()
1046         {
1047             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
1048             {
1049                 Assert.Equal(new("blocks/create.json", UriKind.Relative), r.RequestUri);
1050                 var expectedQuery = new Dictionary<string, string>
1051                 {
1052                     ["screen_name"] = "twitterapi",
1053                     ["tweet_mode"] = "extended",
1054                 };
1055                 Assert.Equal(expectedQuery, r.Query);
1056             });
1057
1058             using var twitterApi = new TwitterApi();
1059             twitterApi.ApiConnection = mock.Object;
1060
1061             await twitterApi.BlocksCreate(screenName: "twitterapi")
1062                 .IgnoreResponse();
1063
1064             mock.VerifyAll();
1065         }
1066
1067         [Fact]
1068         public async Task BlocksDestroy_Test()
1069         {
1070             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
1071             {
1072                 Assert.Equal(new("blocks/destroy.json", UriKind.Relative), r.RequestUri);
1073                 var expectedQuery = new Dictionary<string, string>
1074                 {
1075                     ["screen_name"] = "twitterapi",
1076                     ["tweet_mode"] = "extended",
1077                 };
1078                 Assert.Equal(expectedQuery, r.Query);
1079             });
1080
1081             using var twitterApi = new TwitterApi();
1082             twitterApi.ApiConnection = mock.Object;
1083
1084             await twitterApi.BlocksDestroy(screenName: "twitterapi")
1085                 .IgnoreResponse();
1086
1087             mock.VerifyAll();
1088         }
1089
1090         [Fact]
1091         public async Task AccountVerifyCredentials_Test()
1092         {
1093             var mock = new Mock<IApiConnectionLegacy>();
1094             mock.Setup(x =>
1095                 x.GetAsync<TwitterUser>(
1096                     new Uri("account/verify_credentials.json", UriKind.Relative),
1097                     new Dictionary<string, string>
1098                     {
1099                             { "include_entities", "true" },
1100                             { "include_ext_alt_text", "true" },
1101                             { "tweet_mode", "extended" },
1102                     },
1103                     "/account/verify_credentials")
1104             )
1105             .ReturnsAsync(new TwitterUser
1106             {
1107                 Id = 100L,
1108                 ScreenName = "opentween",
1109             });
1110
1111             using var twitterApi = new TwitterApi();
1112             twitterApi.ApiConnection = mock.Object;
1113
1114             await twitterApi.AccountVerifyCredentials();
1115
1116             Assert.Equal(100L, twitterApi.CurrentUserId);
1117             Assert.Equal("opentween", twitterApi.CurrentScreenName);
1118
1119             mock.VerifyAll();
1120         }
1121
1122         [Fact]
1123         public async Task AccountUpdateProfile_Test()
1124         {
1125             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
1126             {
1127                 Assert.Equal(new("account/update_profile.json", UriKind.Relative), r.RequestUri);
1128                 var expectedQuery = new Dictionary<string, string>
1129                 {
1130                     ["include_entities"] = "true",
1131                     ["include_ext_alt_text"] = "true",
1132                     ["tweet_mode"] = "extended",
1133                     ["name"] = "Name",
1134                     ["url"] = "http://example.com/",
1135                     ["location"] = "Location",
1136                     ["description"] = "&lt;script&gt;alert(1)&lt;/script&gt;",
1137                 };
1138                 Assert.Equal(expectedQuery, r.Query);
1139             });
1140
1141             using var twitterApi = new TwitterApi();
1142             twitterApi.ApiConnection = mock.Object;
1143
1144             await twitterApi.AccountUpdateProfile(name: "Name", url: "http://example.com/", location: "Location", description: "<script>alert(1)</script>")
1145                 .IgnoreResponse();
1146
1147             mock.VerifyAll();
1148         }
1149
1150         [Fact]
1151         public async Task AccountUpdateProfileImage_Test()
1152         {
1153             using var image = TestUtils.CreateDummyImage();
1154             using var media = new MemoryImageMediaItem(image);
1155
1156             var mock = this.CreateApiConnectionMock<PostMultipartRequest>(r =>
1157             {
1158                 Assert.Equal(new("account/update_profile_image.json", UriKind.Relative), r.RequestUri);
1159                 var expectedQuery = new Dictionary<string, string>
1160                 {
1161                     ["include_entities"] = "true",
1162                     ["include_ext_alt_text"] = "true",
1163                     ["tweet_mode"] = "extended",
1164                 };
1165                 Assert.Equal(expectedQuery, r.Query);
1166                 var expectedMedia = new Dictionary<string, IMediaItem>
1167                 {
1168                     ["image"] = media,
1169                 };
1170                 Assert.Equal(expectedMedia, r.Media);
1171             });
1172
1173             using var twitterApi = new TwitterApi();
1174             twitterApi.ApiConnection = mock.Object;
1175
1176             await twitterApi.AccountUpdateProfileImage(media)
1177                 .IgnoreResponse();
1178
1179             mock.VerifyAll();
1180         }
1181
1182         [Fact]
1183         public async Task ApplicationRateLimitStatus_Test()
1184         {
1185             var mock = new Mock<IApiConnectionLegacy>();
1186             mock.Setup(x =>
1187                 x.GetAsync<TwitterRateLimits>(
1188                     new Uri("application/rate_limit_status.json", UriKind.Relative),
1189                     null,
1190                     "/application/rate_limit_status")
1191             )
1192             .ReturnsAsync(new TwitterRateLimits());
1193
1194             using var twitterApi = new TwitterApi();
1195             twitterApi.ApiConnection = mock.Object;
1196
1197             await twitterApi.ApplicationRateLimitStatus();
1198
1199             mock.VerifyAll();
1200         }
1201
1202         [Fact]
1203         public async Task Configuration_Test()
1204         {
1205             var mock = new Mock<IApiConnectionLegacy>();
1206             mock.Setup(x =>
1207                 x.GetAsync<TwitterConfiguration>(
1208                     new Uri("help/configuration.json", UriKind.Relative),
1209                     null,
1210                     "/help/configuration")
1211             )
1212             .ReturnsAsync(new TwitterConfiguration());
1213
1214             using var twitterApi = new TwitterApi();
1215             twitterApi.ApiConnection = mock.Object;
1216
1217             await twitterApi.Configuration();
1218
1219             mock.VerifyAll();
1220         }
1221
1222         [Fact]
1223         public async Task MediaUploadInit_Test()
1224         {
1225             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
1226             {
1227                 Assert.Equal(new("https://upload.twitter.com/1.1/media/upload.json"), r.RequestUri);
1228                 var expectedQuery = new Dictionary<string, string>
1229                 {
1230                     ["command"] = "INIT",
1231                     ["total_bytes"] = "123456",
1232                     ["media_type"] = "image/png",
1233                     ["media_category"] = "dm_image",
1234                 };
1235                 Assert.Equal(expectedQuery, r.Query);
1236             });
1237
1238             using var twitterApi = new TwitterApi();
1239             twitterApi.ApiConnection = mock.Object;
1240
1241             await twitterApi.MediaUploadInit(totalBytes: 123456L, mediaType: "image/png", mediaCategory: "dm_image")
1242                 .IgnoreResponse();
1243
1244             mock.VerifyAll();
1245         }
1246
1247         [Fact]
1248         public async Task MediaUploadAppend_Test()
1249         {
1250             using var image = TestUtils.CreateDummyImage();
1251             using var media = new MemoryImageMediaItem(image);
1252
1253             var mock = this.CreateApiConnectionMock<PostMultipartRequest>(r =>
1254             {
1255                 Assert.Equal(new("https://upload.twitter.com/1.1/media/upload.json"), r.RequestUri);
1256                 var expectedQuery = new Dictionary<string, string>
1257                 {
1258                     ["command"] = "APPEND",
1259                     ["media_id"] = "11111",
1260                     ["segment_index"] = "1",
1261                 };
1262                 Assert.Equal(expectedQuery, r.Query);
1263                 var expectedMedia = new Dictionary<string, IMediaItem>
1264                 {
1265                     ["media"] = media,
1266                 };
1267                 Assert.Equal(expectedMedia, r.Media);
1268             });
1269
1270             using var twitterApi = new TwitterApi();
1271             twitterApi.ApiConnection = mock.Object;
1272
1273             await twitterApi.MediaUploadAppend(mediaId: 11111L, segmentIndex: 1, media: media);
1274
1275             mock.VerifyAll();
1276         }
1277
1278         [Fact]
1279         public async Task MediaUploadFinalize_Test()
1280         {
1281             var mock = this.CreateApiConnectionMock<PostRequest>(r =>
1282             {
1283                 Assert.Equal(new("https://upload.twitter.com/1.1/media/upload.json"), r.RequestUri);
1284                 var expectedQuery = new Dictionary<string, string>
1285                 {
1286                     ["command"] = "FINALIZE",
1287                     ["media_id"] = "11111",
1288                 };
1289                 Assert.Equal(expectedQuery, r.Query);
1290             });
1291
1292             using var twitterApi = new TwitterApi();
1293             twitterApi.ApiConnection = mock.Object;
1294
1295             await twitterApi.MediaUploadFinalize(mediaId: 11111L)
1296                 .IgnoreResponse();
1297
1298             mock.VerifyAll();
1299         }
1300
1301         [Fact]
1302         public async Task MediaUploadStatus_Test()
1303         {
1304             var mock = new Mock<IApiConnectionLegacy>();
1305             mock.Setup(x =>
1306                 x.GetAsync<TwitterUploadMediaResult>(
1307                     new Uri("https://upload.twitter.com/1.1/media/upload.json", UriKind.Absolute),
1308                     new Dictionary<string, string>
1309                     {
1310                             { "command", "STATUS" },
1311                             { "media_id", "11111" },
1312                     },
1313                     null)
1314             )
1315             .ReturnsAsync(new TwitterUploadMediaResult());
1316
1317             using var twitterApi = new TwitterApi();
1318             twitterApi.ApiConnection = mock.Object;
1319
1320             await twitterApi.MediaUploadStatus(mediaId: 11111L);
1321
1322             mock.VerifyAll();
1323         }
1324
1325         [Fact]
1326         public async Task MediaMetadataCreate_Test()
1327         {
1328             var mock = this.CreateApiConnectionMock<PostJsonRequest>(r =>
1329             {
1330                 Assert.Equal(new("https://upload.twitter.com/1.1/media/metadata/create.json"), r.RequestUri);
1331                 Assert.Equal("""{"media_id": "12345", "alt_text": {"text": "hogehoge"}}""", r.JsonString);
1332             });
1333
1334             using var twitterApi = new TwitterApi();
1335             twitterApi.ApiConnection = mock.Object;
1336
1337             await twitterApi.MediaMetadataCreate(mediaId: 12345L, altText: "hogehoge");
1338
1339             mock.VerifyAll();
1340         }
1341     }
1342 }