OSDN Git Service

DM送信完了時のレスポンスを即座にDirectタブに反映させる
[opentween/open-tween.git] / OpenTween.Tests / Api / TwitterApiTest.cs
index 3ab22f1..40f9f8e 100644 (file)
@@ -42,7 +42,7 @@ namespace OpenTween.Api
             this.MyCommonSetup();
         }
 
-        public void MyCommonSetup()
+        private void MyCommonSetup()
         {
             var mockAssembly = new Mock<_Assembly>();
             mockAssembly.Setup(m => m.GetName()).Returns(new AssemblyName("OpenTween"));
@@ -97,13 +97,14 @@ namespace OpenTween.Api
                         new Dictionary<string, string> {
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                             { "count", "200" },
                             { "max_id", "900" },
                             { "since_id", "100" },
                         },
                         "/statuses/home_timeline")
                 )
-                .ReturnsAsync(new TwitterStatus[0]);
+                .ReturnsAsync(Array.Empty<TwitterStatus>());
 
                 twitterApi.apiConnection = mock.Object;
 
@@ -126,13 +127,14 @@ namespace OpenTween.Api
                         new Dictionary<string, string> {
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                             { "count", "200" },
                             { "max_id", "900" },
                             { "since_id", "100" },
                         },
                         "/statuses/mentions_timeline")
                 )
-                .ReturnsAsync(new TwitterStatus[0]);
+                .ReturnsAsync(Array.Empty<TwitterStatus>());
 
                 twitterApi.apiConnection = mock.Object;
 
@@ -157,13 +159,14 @@ namespace OpenTween.Api
                             { "include_rts", "true" },
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                             { "count", "200" },
                             { "max_id", "900" },
                             { "since_id", "100" },
                         },
                         "/statuses/user_timeline")
                 )
-                .ReturnsAsync(new TwitterStatus[0]);
+                .ReturnsAsync(Array.Empty<TwitterStatus>());
 
                 twitterApi.apiConnection = mock.Object;
 
@@ -187,6 +190,7 @@ namespace OpenTween.Api
                             { "id", "100" },
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                         },
                         "/statuses/show/:id")
                 )
@@ -214,15 +218,50 @@ namespace OpenTween.Api
                             { "status", "hogehoge" },
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                             { "in_reply_to_status_id", "100" },
                             { "media_ids", "10,20" },
+                            { "auto_populate_reply_metadata", "true" },
+                            { "exclude_reply_user_ids", "100,200" },
+                            { "attachment_url", "https://twitter.com/twitterapi/status/22634515958" },
                         })
                 )
                 .ReturnsAsync(LazyJson.Create(new TwitterStatus()));
 
                 twitterApi.apiConnection = mock.Object;
 
-                await twitterApi.StatusesUpdate("hogehoge", replyToId: 100L, mediaIds: new[] { 10L, 20L })
+                await twitterApi.StatusesUpdate("hogehoge", replyToId: 100L, mediaIds: new[] { 10L, 20L },
+                        autoPopulateReplyMetadata: true, excludeReplyUserIds: new[] { 100L, 200L },
+                        attachmentUrl: "https://twitter.com/twitterapi/status/22634515958")
+                    .IgnoreResponse()
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
+        public async Task StatusesUpdate_ExcludeReplyUserIdsEmptyTest()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.PostLazyAsync<TwitterStatus>(
+                        new Uri("statuses/update.json", UriKind.Relative),
+                        new Dictionary<string, string> {
+                            { "status", "hogehoge" },
+                            { "include_entities", "true" },
+                            { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
+                            // exclude_reply_user_ids は空の場合には送信されない
+                        })
+                )
+                .ReturnsAsync(LazyJson.Create(new TwitterStatus()));
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.StatusesUpdate("hogehoge", replyToId: null, mediaIds: null, excludeReplyUserIds: Array.Empty<long>())
                     .IgnoreResponse()
                     .ConfigureAwait(false);
 
@@ -266,6 +305,7 @@ namespace OpenTween.Api
                             { "id", "100" },
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                         })
                 )
                 .ReturnsAsync(LazyJson.Create(new TwitterStatus()));
@@ -281,6 +321,39 @@ namespace OpenTween.Api
         }
 
         [Fact]
+        public async Task SearchTweets_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.GetAsync<TwitterSearchResult>(
+                        new Uri("search/tweets.json", UriKind.Relative),
+                        new Dictionary<string, string> {
+                            { "q", "from:twitterapi" },
+                            { "result_type", "recent" },
+                            { "include_entities", "true" },
+                            { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
+                            { "lang", "en" },
+                            { "count", "200" },
+                            { "max_id", "900" },
+                            { "since_id", "100" },
+                        },
+                        "/search/tweets")
+                )
+                .ReturnsAsync(new TwitterSearchResult());
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.SearchTweets("from:twitterapi", "en", count: 200, maxId: 900L, sinceId: 100L)
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
         public async Task ListsOwnerships_Test()
         {
             using (var twitterApi = new TwitterApi())
@@ -292,6 +365,7 @@ namespace OpenTween.Api
                         new Dictionary<string, string> {
                             { "screen_name", "twitterapi" },
                             { "cursor", "-1" },
+                            { "count", "100" },
                         },
                         "/lists/ownerships")
                 )
@@ -299,7 +373,7 @@ namespace OpenTween.Api
 
                 twitterApi.apiConnection = mock.Object;
 
-                await twitterApi.ListsOwnerships("twitterapi", cursor: -1L)
+                await twitterApi.ListsOwnerships("twitterapi", cursor: -1L, count: 100)
                     .ConfigureAwait(false);
 
                 mock.VerifyAll();
@@ -318,6 +392,7 @@ namespace OpenTween.Api
                         new Dictionary<string, string> {
                             { "screen_name", "twitterapi" },
                             { "cursor", "-1" },
+                            { "count", "100" },
                         },
                         "/lists/subscriptions")
                 )
@@ -325,7 +400,35 @@ namespace OpenTween.Api
 
                 twitterApi.apiConnection = mock.Object;
 
-                await twitterApi.ListsSubscriptions("twitterapi", cursor: -1L)
+                await twitterApi.ListsSubscriptions("twitterapi", cursor: -1L, count: 100)
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
+        public async Task ListsMemberships_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.GetAsync<TwitterLists>(
+                        new Uri("lists/memberships.json", UriKind.Relative),
+                        new Dictionary<string, string> {
+                            { "screen_name", "twitterapi" },
+                            { "cursor", "-1" },
+                            { "count", "100" },
+                            { "filter_to_owned_lists", "true" },
+                        },
+                        "/lists/memberships")
+                )
+                .ReturnsAsync(new TwitterLists());
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.ListsMemberships("twitterapi", cursor: -1L, count: 100, filterToOwnedLists: true)
                     .ConfigureAwait(false);
 
                 mock.VerifyAll();
@@ -425,6 +528,7 @@ namespace OpenTween.Api
                             { "list_id", "12345" },
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                             { "count", "200" },
                             { "max_id", "900" },
                             { "since_id", "100" },
@@ -432,7 +536,7 @@ namespace OpenTween.Api
                         },
                         "/lists/statuses")
                 )
-                .ReturnsAsync(new TwitterStatus[0]);
+                .ReturnsAsync(Array.Empty<TwitterStatus>());
 
                 twitterApi.apiConnection = mock.Object;
 
@@ -444,29 +548,28 @@ namespace OpenTween.Api
         }
 
         [Fact]
-        public async Task DirectMessagesRecv_Test()
+        public async Task ListsMembers_Test()
         {
             using (var twitterApi = new TwitterApi())
             {
                 var mock = new Mock<IApiConnection>();
                 mock.Setup(x =>
-                    x.GetAsync<TwitterDirectMessage[]>(
-                        new Uri("direct_messages.json", UriKind.Relative),
+                    x.GetAsync<TwitterUsers>(
+                        new Uri("lists/members.json", UriKind.Relative),
                         new Dictionary<string, string> {
-                            { "full_text", "true" },
+                            { "list_id", "12345" },
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
-                            { "count", "200" },
-                            { "max_id", "900" },
-                            { "since_id", "100" },
+                            { "tweet_mode", "extended" },
+                            { "cursor", "-1" },
                         },
-                        "/direct_messages")
+                        "/lists/members")
                 )
-                .ReturnsAsync(new TwitterDirectMessage[0]);
+                .ReturnsAsync(new TwitterUsers());
 
                 twitterApi.apiConnection = mock.Object;
 
-                await twitterApi.DirectMessagesRecv(count: 200, maxId: 900L, sinceId: 100L)
+                await twitterApi.ListsMembers(12345L, cursor: -1)
                     .ConfigureAwait(false);
 
                 mock.VerifyAll();
@@ -474,29 +577,28 @@ namespace OpenTween.Api
         }
 
         [Fact]
-        public async Task DirectMessagesSent_Test()
+        public async Task ListsMembersShow_Test()
         {
             using (var twitterApi = new TwitterApi())
             {
                 var mock = new Mock<IApiConnection>();
                 mock.Setup(x =>
-                    x.GetAsync<TwitterDirectMessage[]>(
-                        new Uri("direct_messages/sent.json", UriKind.Relative),
+                    x.GetAsync<TwitterUser>(
+                        new Uri("lists/members/show.json", UriKind.Relative),
                         new Dictionary<string, string> {
-                            { "full_text", "true" },
+                            { "list_id", "12345" },
+                            { "screen_name", "twitterapi" },
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
-                            { "count", "200" },
-                            { "max_id", "900" },
-                            { "since_id", "100" },
+                            { "tweet_mode", "extended" },
                         },
-                        "/direct_messages/sent")
+                        "/lists/members/show")
                 )
-                .ReturnsAsync(new TwitterDirectMessage[0]);
+                .ReturnsAsync(new TwitterUser());
 
                 twitterApi.apiConnection = mock.Object;
 
-                await twitterApi.DirectMessagesSent(count: 200, maxId: 900L, sinceId: 100L)
+                await twitterApi.ListsMembersShow(12345L, "twitterapi")
                     .ConfigureAwait(false);
 
                 mock.VerifyAll();
@@ -504,24 +606,27 @@ namespace OpenTween.Api
         }
 
         [Fact]
-        public async Task DirectMessagesNew_Test()
+        public async Task ListsMembersCreate_Test()
         {
             using (var twitterApi = new TwitterApi())
             {
                 var mock = new Mock<IApiConnection>();
                 mock.Setup(x =>
-                    x.PostLazyAsync<TwitterDirectMessage>(
-                        new Uri("direct_messages/new.json", UriKind.Relative),
+                    x.PostLazyAsync<TwitterUser>(
+                        new Uri("lists/members/create.json", UriKind.Relative),
                         new Dictionary<string, string> {
-                            { "text", "hogehoge" },
-                            { "screen_name", "opentween" },
+                            { "list_id", "12345" },
+                            { "screen_name", "twitterapi" },
+                            { "include_entities", "true" },
+                            { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                         })
                 )
-                .ReturnsAsync(LazyJson.Create(new TwitterDirectMessage()));
+                .ReturnsAsync(LazyJson.Create(new TwitterUser()));
 
                 twitterApi.apiConnection = mock.Object;
 
-                await twitterApi.DirectMessagesNew("hogehoge", "opentween")
+                await twitterApi.ListsMembersCreate(12345L, "twitterapi")
                     .IgnoreResponse()
                     .ConfigureAwait(false);
 
@@ -530,21 +635,27 @@ namespace OpenTween.Api
         }
 
         [Fact]
-        public async Task DirectMessagesDestroy_Test()
+        public async Task ListsMembersDestroy_Test()
         {
             using (var twitterApi = new TwitterApi())
             {
                 var mock = new Mock<IApiConnection>();
                 mock.Setup(x =>
-                    x.PostLazyAsync<TwitterDirectMessage>(
-                        new Uri("direct_messages/destroy.json", UriKind.Relative),
-                        new Dictionary<string, string> { { "id", "100" } })
+                    x.PostLazyAsync<TwitterUser>(
+                        new Uri("lists/members/destroy.json", UriKind.Relative),
+                        new Dictionary<string, string> {
+                            { "list_id", "12345" },
+                            { "screen_name", "twitterapi" },
+                            { "include_entities", "true" },
+                            { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
+                        })
                 )
-                .ReturnsAsync(LazyJson.Create(new TwitterDirectMessage { Id = 100L }));
+                .ReturnsAsync(LazyJson.Create(new TwitterUser()));
 
                 twitterApi.apiConnection = mock.Object;
 
-                await twitterApi.DirectMessagesDestroy(statusId: 100L)
+                await twitterApi.ListsMembersDestroy(12345L, "twitterapi")
                     .IgnoreResponse()
                     .ConfigureAwait(false);
 
@@ -553,6 +664,93 @@ namespace OpenTween.Api
         }
 
         [Fact]
+        public async Task DirectMessagesEventsList_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.GetAsync<TwitterMessageEventList>(
+                        new Uri("direct_messages/events/list.json", UriKind.Relative),
+                        new Dictionary<string, string> {
+                            { "count", "50" },
+                            { "cursor", "12345abcdefg" },
+                        },
+                        "/direct_messages/events/list")
+                )
+                .ReturnsAsync(new TwitterMessageEventList());
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.DirectMessagesEventsList(count: 50, cursor: "12345abcdefg")
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
+        public async Task DirectMessagesEventsNew_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.PostJsonAsync<TwitterMessageEventSingle>(
+                        new Uri("direct_messages/events/new.json", UriKind.Relative),
+                        @"{
+  ""event"": {
+    ""type"": ""message_create"",
+    ""message_create"": {
+      ""target"": {
+        ""recipient_id"": ""12345""
+      },
+      ""message_data"": {
+        ""text"": ""hogehoge"",
+        ""attachment"": {
+          ""type"": ""media"",
+          ""media"": {
+            ""id"": ""67890""
+          }
+        }
+      }
+    }
+  }
+}")
+                )
+                .ReturnsAsync(LazyJson.Create(new TwitterMessageEventSingle()));
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.DirectMessagesEventsNew(recipientId: 12345L, text: "hogehoge", mediaId: 67890L)
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
+        public async Task DirectMessagesEventsDestroy_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.DeleteAsync(
+                        new Uri("direct_messages/events/destroy.json?id=100", UriKind.Relative))
+                )
+                .Returns(Task.CompletedTask);
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.DirectMessagesEventsDestroy(eventId: "100")
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
         public async Task UsersShow_Test()
         {
             using (var twitterApi = new TwitterApi())
@@ -565,6 +763,7 @@ namespace OpenTween.Api
                             { "screen_name", "twitterapi" },
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                         },
                         "/users/show/:id")
                 )
@@ -580,6 +779,34 @@ namespace OpenTween.Api
         }
 
         [Fact]
+        public async Task UsersLookup_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.GetAsync<TwitterUser[]>(
+                        new Uri("users/lookup.json", UriKind.Relative),
+                        new Dictionary<string, string> {
+                            { "user_id", "11111,22222" },
+                            { "include_entities", "true" },
+                            { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
+                        },
+                        "/users/lookup")
+                )
+                .ReturnsAsync(Array.Empty<TwitterUser>());
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.UsersLookup(userIds: new[] { "11111", "22222" })
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
         public async Task UsersReportSpam_Test()
         {
             using (var twitterApi = new TwitterApi())
@@ -588,7 +815,10 @@ namespace OpenTween.Api
                 mock.Setup(x =>
                     x.PostLazyAsync<TwitterUser>(
                         new Uri("users/report_spam.json", UriKind.Relative),
-                        new Dictionary<string, string> { { "screen_name", "twitterapi" } })
+                        new Dictionary<string, string> {
+                            { "screen_name", "twitterapi" },
+                            { "tweet_mode", "extended" },
+                        })
                 )
                 .ReturnsAsync(LazyJson.Create(new TwitterUser { ScreenName = "twitterapi" }));
 
@@ -603,6 +833,36 @@ namespace OpenTween.Api
         }
 
         [Fact]
+        public async Task FavoritesList_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.GetAsync<TwitterStatus[]>(
+                        new Uri("favorites/list.json", UriKind.Relative),
+                        new Dictionary<string, string> {
+                            { "include_entities", "true" },
+                            { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
+                            { "count", "200" },
+                            { "max_id", "900" },
+                            { "since_id", "100" },
+                        },
+                        "/favorites/list")
+                )
+                .ReturnsAsync(Array.Empty<TwitterStatus>());
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.FavoritesList(200, maxId: 900L, sinceId: 100L)
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
         public async Task FavoritesCreate_Test()
         {
             using (var twitterApi = new TwitterApi())
@@ -611,7 +871,10 @@ namespace OpenTween.Api
                 mock.Setup(x =>
                     x.PostLazyAsync<TwitterStatus>(
                         new Uri("favorites/create.json", UriKind.Relative),
-                        new Dictionary<string, string> { { "id", "100" } })
+                        new Dictionary<string, string> {
+                            { "id", "100" },
+                            { "tweet_mode", "extended" },
+                        })
                 )
                 .ReturnsAsync(LazyJson.Create(new TwitterStatus { Id = 100L }));
 
@@ -634,7 +897,10 @@ namespace OpenTween.Api
                 mock.Setup(x =>
                     x.PostLazyAsync<TwitterStatus>(
                         new Uri("favorites/destroy.json", UriKind.Relative),
-                        new Dictionary<string, string> { { "id", "100" } })
+                        new Dictionary<string, string> {
+                            { "id", "100" },
+                            { "tweet_mode", "extended" },
+                        })
                 )
                 .ReturnsAsync(LazyJson.Create(new TwitterStatus { Id = 100L }));
 
@@ -729,7 +995,7 @@ namespace OpenTween.Api
                         null,
                         "/friendships/no_retweets/ids")
                 )
-                .ReturnsAsync(new long[0]);
+                .ReturnsAsync(Array.Empty<long>());
 
                 twitterApi.apiConnection = mock.Object;
 
@@ -818,7 +1084,10 @@ namespace OpenTween.Api
                 mock.Setup(x =>
                     x.PostLazyAsync<TwitterUser>(
                         new Uri("blocks/create.json", UriKind.Relative),
-                        new Dictionary<string, string> { { "screen_name", "twitterapi" } })
+                        new Dictionary<string, string> {
+                            { "screen_name", "twitterapi" },
+                            { "tweet_mode", "extended" },
+                        })
                 )
                 .ReturnsAsync(LazyJson.Create(new TwitterUser()));
 
@@ -841,7 +1110,10 @@ namespace OpenTween.Api
                 mock.Setup(x =>
                     x.PostLazyAsync<TwitterUser>(
                         new Uri("blocks/destroy.json", UriKind.Relative),
-                        new Dictionary<string, string> { { "screen_name", "twitterapi" } })
+                        new Dictionary<string, string> {
+                            { "screen_name", "twitterapi" },
+                            { "tweet_mode", "extended" },
+                        })
                 )
                 .ReturnsAsync(LazyJson.Create(new TwitterUser()));
 
@@ -856,6 +1128,39 @@ namespace OpenTween.Api
         }
 
         [Fact]
+        public async Task AccountVerifyCredentials_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.GetAsync<TwitterUser>(
+                        new Uri("account/verify_credentials.json", UriKind.Relative),
+                        new Dictionary<string, string> {
+                            { "include_entities", "true" },
+                            { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
+                        },
+                        "/account/verify_credentials")
+                )
+                .ReturnsAsync(new TwitterUser {
+                    Id = 100L,
+                    ScreenName = "opentween",
+                });
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.AccountVerifyCredentials()
+                    .ConfigureAwait(false);
+
+                Assert.Equal(100L, twitterApi.CurrentUserId);
+                Assert.Equal("opentween", twitterApi.CurrentScreenName);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
         public async Task AccountUpdateProfile_Test()
         {
             using (var twitterApi = new TwitterApi())
@@ -867,6 +1172,7 @@ namespace OpenTween.Api
                         new Dictionary<string, string> {
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                             { "name", "Name" },
                             { "url", "http://example.com/" },
                             { "location", "Location" },
@@ -899,6 +1205,7 @@ namespace OpenTween.Api
                         new Dictionary<string, string> {
                             { "include_entities", "true" },
                             { "include_ext_alt_text", "true" },
+                            { "tweet_mode", "extended" },
                         },
                         new Dictionary<string, IMediaItem> { { "image", media } })
                 )
@@ -961,7 +1268,35 @@ namespace OpenTween.Api
         }
 
         [Fact]
-        public async Task MediaUpload_Test()
+        public async Task MediaUploadInit_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.PostLazyAsync<TwitterUploadMediaInit>(
+                        new Uri("https://upload.twitter.com/1.1/media/upload.json", UriKind.Absolute),
+                        new Dictionary<string, string> {
+                            { "command", "INIT" },
+                            { "total_bytes", "123456" },
+                            { "media_type", "image/png" },
+                            { "media_category", "dm_image" },
+                        })
+                )
+                .ReturnsAsync(LazyJson.Create(new TwitterUploadMediaInit()));
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.MediaUploadInit(totalBytes: 123456L, mediaType: "image/png", mediaCategory: "dm_image")
+                    .IgnoreResponse()
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
+        public async Task MediaUploadAppend_Test()
         {
             using (var twitterApi = new TwitterApi())
             using (var image = TestUtils.CreateDummyImage())
@@ -969,16 +1304,45 @@ namespace OpenTween.Api
             {
                 var mock = new Mock<IApiConnection>();
                 mock.Setup(x =>
-                    x.PostLazyAsync<TwitterUploadMediaResult>(
+                    x.PostAsync(
                         new Uri("https://upload.twitter.com/1.1/media/upload.json", UriKind.Absolute),
-                        null,
+                        new Dictionary<string, string> {
+                            { "command", "APPEND" },
+                            { "media_id", "11111" },
+                            { "segment_index", "1" },
+                        },
                         new Dictionary<string, IMediaItem> { { "media", media } })
                 )
+                .Returns(Task.CompletedTask);
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.MediaUploadAppend(mediaId: 11111L, segmentIndex: 1, media: media)
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
+        public async Task MediaUploadFinalize_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.PostLazyAsync<TwitterUploadMediaResult>(
+                        new Uri("https://upload.twitter.com/1.1/media/upload.json", UriKind.Absolute),
+                        new Dictionary<string, string> {
+                            { "command", "FINALIZE" },
+                            { "media_id", "11111" },
+                        })
+                )
                 .ReturnsAsync(LazyJson.Create(new TwitterUploadMediaResult()));
 
                 twitterApi.apiConnection = mock.Object;
 
-                await twitterApi.MediaUpload(media)
+                await twitterApi.MediaUploadFinalize(mediaId: 11111L)
                     .IgnoreResponse()
                     .ConfigureAwait(false);
 
@@ -987,13 +1351,61 @@ namespace OpenTween.Api
         }
 
         [Fact]
+        public async Task MediaUploadStatus_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.GetAsync<TwitterUploadMediaResult>(
+                        new Uri("https://upload.twitter.com/1.1/media/upload.json", UriKind.Absolute),
+                        new Dictionary<string, string> {
+                            { "command", "STATUS" },
+                            { "media_id", "11111" },
+                        },
+                        null)
+                )
+                .ReturnsAsync(new TwitterUploadMediaResult());
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.MediaUploadStatus(mediaId: 11111L)
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
+        public async Task MediaMetadataCreate_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.PostJsonAsync(
+                        new Uri("https://upload.twitter.com/1.1/media/metadata/create.json", UriKind.Absolute),
+                        "{\"media_id\": \"12345\", \"alt_text\": {\"text\": \"hogehoge\"}}")
+                )
+                .Returns(Task.CompletedTask);
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.MediaMetadataCreate(mediaId: 12345L, altText: "hogehoge")
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
         public async Task UserStreams_Test()
         {
             using (var twitterApi = new TwitterApi())
             {
                 var mock = new Mock<IApiConnection>();
                 mock.Setup(x =>
-                    x.GetStreamAsync(
+                    x.GetStreamingStreamAsync(
                         new Uri("https://userstream.twitter.com/1.1/user.json", UriKind.Absolute),
                         new Dictionary<string, string> {
                             { "replies", "all" },
@@ -1004,10 +1416,8 @@ namespace OpenTween.Api
 
                 twitterApi.apiConnection = mock.Object;
 
-                var stream = await twitterApi.UserStreams(replies: "all", track: "OpenTween")
-                    .ConfigureAwait(false);
-
-                stream.Dispose();
+                var observable = twitterApi.UserStreams(replies: "all", track: "OpenTween");
+                await observable.ForEachAsync(x => { });
 
                 mock.VerifyAll();
             }