OSDN Git Service

PostClassインスタンスの生成処理をTwitterPostFactoryクラスに分離
[opentween/open-tween.git] / OpenTween.Tests / Models / TwitterPostFactoryTest.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2013 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 OpenTween.Api.DataModel;
25 using Xunit;
26
27 namespace OpenTween.Models
28 {
29     public class TwitterPostFactoryTest
30     {
31         private static readonly ISet<long> EmptyIdSet = new HashSet<long>();
32
33         private readonly Random random = new();
34
35         public TwitterPostFactoryTest()
36             => PostClass.ExpandedUrlInfo.AutoExpand = false;
37
38         private TabInformations CreateTabinfo()
39         {
40             var tabinfo = new TabInformations();
41             tabinfo.AddDefaultTabs();
42             return tabinfo;
43         }
44
45         private TwitterStatus CreateStatus()
46         {
47             var statusId = this.random.Next(10000);
48
49             return new()
50             {
51                 Id = statusId,
52                 IdStr = statusId.ToString(),
53                 CreatedAt = "Sat Jan 01 00:00:00 +0000 2022",
54                 FullText = "hoge",
55                 Source = "<a href=\"https://www.opentween.org/\" rel=\"nofollow\">OpenTween</a>",
56                 Entities = new(),
57                 User = this.CreateUser(),
58             };
59         }
60
61         private TwitterUser CreateUser()
62         {
63             var userId = this.random.Next(10000);
64
65             return new()
66             {
67                 Id = userId,
68                 IdStr = userId.ToString(),
69                 ScreenName = "tetete",
70                 Name = "ててて",
71                 ProfileImageUrlHttps = "https://example.com/profile.png",
72             };
73         }
74
75         [Fact]
76         public void CreateFromStatus_Test()
77         {
78             var factory = new TwitterPostFactory(this.CreateTabinfo());
79             var status = this.CreateStatus();
80             var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet);
81
82             Assert.Equal(status.Id, post.StatusId);
83             Assert.Equal(new DateTimeUtc(2022, 1, 1, 0, 0, 0), post.CreatedAt);
84             Assert.Equal("hoge", post.Text);
85             Assert.Equal("hoge", post.TextFromApi);
86             Assert.Equal("hoge", post.TextSingleLine);
87             Assert.Equal("hoge", post.AccessibleText);
88             Assert.Empty(post.ReplyToList);
89             Assert.Empty(post.QuoteStatusIds);
90             Assert.Empty(post.ExpandedUrls);
91             Assert.Empty(post.Media);
92             Assert.Null(post.PostGeo);
93             Assert.Equal("OpenTween", post.Source);
94             Assert.Equal("https://www.opentween.org/", post.SourceUri?.OriginalString);
95             Assert.Equal(0, post.FavoritedCount);
96             Assert.False(post.IsFav);
97             Assert.False(post.IsDm);
98             Assert.False(post.IsDeleted);
99             Assert.False(post.IsRead);
100             Assert.False(post.IsExcludeReply);
101             Assert.False(post.FilterHit);
102             Assert.False(post.IsMark);
103
104             Assert.False(post.IsReply);
105             Assert.Null(post.InReplyToStatusId);
106             Assert.Null(post.InReplyToUserId);
107             Assert.Null(post.InReplyToUser);
108
109             Assert.Null(post.RetweetedId);
110             Assert.Null(post.RetweetedBy);
111             Assert.Null(post.RetweetedByUserId);
112
113             Assert.Equal(status.User.Id, post.UserId);
114             Assert.Equal("tetete", post.ScreenName);
115             Assert.Equal("ててて", post.Nickname);
116             Assert.Equal("https://example.com/profile.png", post.ImageUrl);
117             Assert.False(post.IsProtect);
118             Assert.False(post.IsOwl);
119             Assert.False(post.IsMe);
120         }
121
122         [Fact]
123         public void CreateFromStatus_AuthorTest()
124         {
125             var factory = new TwitterPostFactory(this.CreateTabinfo());
126             var status = this.CreateStatus();
127             var selfUserId = status.User.Id;
128             var post = factory.CreateFromStatus(status, selfUserId, followerIds: EmptyIdSet);
129
130             Assert.True(post.IsMe);
131         }
132
133         [Fact]
134         public void CreateFromStatus_FollowerTest()
135         {
136             var factory = new TwitterPostFactory(this.CreateTabinfo());
137             var status = this.CreateStatus();
138             var followerIds = new HashSet<long> { status.User.Id };
139             var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds);
140
141             Assert.False(post.IsOwl);
142         }
143
144         [Fact]
145         public void CreateFromStatus_NotFollowerTest()
146         {
147             var factory = new TwitterPostFactory(this.CreateTabinfo());
148             var status = this.CreateStatus();
149             var followerIds = new HashSet<long> { 30000L };
150             var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds);
151
152             Assert.True(post.IsOwl);
153         }
154
155         [Fact]
156         public void CreateFromStatus_RetweetTest()
157         {
158             var factory = new TwitterPostFactory(this.CreateTabinfo());
159             var originalStatus = this.CreateStatus();
160
161             var retweetStatus = this.CreateStatus();
162             retweetStatus.RetweetedStatus = originalStatus;
163             retweetStatus.Source = "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>";
164
165             var post = factory.CreateFromStatus(retweetStatus, selfUserId: 20000L, followerIds: EmptyIdSet);
166
167             Assert.Equal(retweetStatus.Id, post.StatusId);
168             Assert.Equal(retweetStatus.User.Id, post.RetweetedByUserId);
169             Assert.Equal(originalStatus.Id, post.RetweetedId);
170             Assert.Equal(originalStatus.User.Id, post.UserId);
171
172             Assert.Equal("OpenTween", post.Source);
173             Assert.Equal("https://www.opentween.org/", post.SourceUri?.OriginalString);
174         }
175
176         private TwitterMessageEvent CreateDirectMessage(string senderId, string recipientId)
177         {
178             var messageId = this.random.Next(10000);
179
180             return new()
181             {
182                 Type = "message_create",
183                 Id = messageId.ToString(),
184                 CreatedTimestamp = "1640995200000",
185                 MessageCreate = new()
186                 {
187                     SenderId = senderId,
188                     Target = new()
189                     {
190                         RecipientId = recipientId,
191                     },
192                     MessageData = new()
193                     {
194                         Text = "hoge",
195                         Entities = new(),
196                     },
197                     SourceAppId = "22519141",
198                 },
199             };
200         }
201
202         private Dictionary<string, TwitterMessageEventList.App> CreateApps()
203         {
204             return new()
205             {
206                 ["22519141"] = new()
207                 {
208                     Id = "22519141",
209                     Name = "OpenTween",
210                     Url = "https://www.opentween.org/",
211                 },
212             };
213         }
214
215         [Fact]
216         public void CreateFromDirectMessageEvent_Test()
217         {
218             var factory = new TwitterPostFactory(this.CreateTabinfo());
219
220             var selfUser = this.CreateUser();
221             var otherUser = this.CreateUser();
222             var eventItem = this.CreateDirectMessage(senderId: otherUser.IdStr, recipientId: selfUser.IdStr);
223             var users = new Dictionary<string, TwitterUser>()
224             {
225                 [selfUser.IdStr] = selfUser,
226                 [otherUser.IdStr] = otherUser,
227             };
228             var apps = this.CreateApps();
229             var post = factory.CreateFromDirectMessageEvent(eventItem, users, apps, selfUserId: selfUser.Id);
230
231             Assert.Equal(long.Parse(eventItem.Id), post.StatusId);
232             Assert.Equal(new DateTimeUtc(2022, 1, 1, 0, 0, 0), post.CreatedAt);
233             Assert.Equal("hoge", post.Text);
234             Assert.Equal("hoge", post.TextFromApi);
235             Assert.Equal("hoge", post.TextSingleLine);
236             Assert.Equal("hoge", post.AccessibleText);
237             Assert.Empty(post.ReplyToList);
238             Assert.Empty(post.QuoteStatusIds);
239             Assert.Empty(post.ExpandedUrls);
240             Assert.Empty(post.Media);
241             Assert.Null(post.PostGeo);
242             Assert.Equal("OpenTween", post.Source);
243             Assert.Equal("https://www.opentween.org/", post.SourceUri?.OriginalString);
244             Assert.Equal(0, post.FavoritedCount);
245             Assert.False(post.IsFav);
246             Assert.True(post.IsDm);
247             Assert.False(post.IsDeleted);
248             Assert.False(post.IsRead);
249             Assert.False(post.IsExcludeReply);
250             Assert.False(post.FilterHit);
251             Assert.False(post.IsMark);
252
253             Assert.False(post.IsReply);
254             Assert.Null(post.InReplyToStatusId);
255             Assert.Null(post.InReplyToUserId);
256             Assert.Null(post.InReplyToUser);
257
258             Assert.Null(post.RetweetedId);
259             Assert.Null(post.RetweetedBy);
260             Assert.Null(post.RetweetedByUserId);
261
262             Assert.Equal(otherUser.Id, post.UserId);
263             Assert.Equal("tetete", post.ScreenName);
264             Assert.Equal("ててて", post.Nickname);
265             Assert.Equal("https://example.com/profile.png", post.ImageUrl);
266             Assert.False(post.IsProtect);
267             Assert.True(post.IsOwl);
268             Assert.False(post.IsMe);
269         }
270
271         [Fact]
272         public void CreateFromDirectMessageEvent_SenderTest()
273         {
274             var factory = new TwitterPostFactory(this.CreateTabinfo());
275
276             var selfUser = this.CreateUser();
277             var otherUser = this.CreateUser();
278             var eventItem = this.CreateDirectMessage(senderId: selfUser.IdStr, recipientId: otherUser.IdStr);
279             var users = new Dictionary<string, TwitterUser>()
280             {
281                 [selfUser.IdStr] = selfUser,
282                 [otherUser.IdStr] = otherUser,
283             };
284             var apps = this.CreateApps();
285             var post = factory.CreateFromDirectMessageEvent(eventItem, users, apps, selfUserId: selfUser.Id);
286
287             Assert.Equal(otherUser.Id, post.UserId);
288             Assert.False(post.IsOwl);
289             Assert.True(post.IsMe);
290         }
291
292         [Fact]
293         public void CreateFromStatus_MediaAltTest()
294         {
295             var factory = new TwitterPostFactory(this.CreateTabinfo());
296
297             var status = this.CreateStatus();
298             status.FullText = "https://t.co/hoge";
299             status.ExtendedEntities = new()
300             {
301                 Media = new[]
302                 {
303                     new TwitterEntityMedia
304                     {
305                         Indices = new[] { 0, 17 },
306                         Url = "https://t.co/hoge",
307                         DisplayUrl = "pic.twitter.com/hoge",
308                         ExpandedUrl = "https://twitter.com/hoge/status/1234567890/photo/1",
309                         AltText = "代替テキスト",
310                     },
311                 },
312             };
313
314             var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet);
315
316             var accessibleText = string.Format(Properties.Resources.ImageAltText, "代替テキスト");
317             Assert.Equal(accessibleText, post.AccessibleText);
318             Assert.Equal("<a href=\"https://t.co/hoge\" title=\"代替テキスト\">pic.twitter.com/hoge</a>", post.Text);
319             Assert.Equal("pic.twitter.com/hoge", post.TextFromApi);
320             Assert.Equal("pic.twitter.com/hoge", post.TextSingleLine);
321         }
322
323         [Fact]
324         public void CreateFromStatus_MediaNoAltTest()
325         {
326             var factory = new TwitterPostFactory(this.CreateTabinfo());
327
328             var status = this.CreateStatus();
329             status.FullText = "https://t.co/hoge";
330             status.ExtendedEntities = new()
331             {
332                 Media = new[]
333                 {
334                     new TwitterEntityMedia
335                     {
336                         Indices = new[] { 0, 17 },
337                         Url = "https://t.co/hoge",
338                         DisplayUrl = "pic.twitter.com/hoge",
339                         ExpandedUrl = "https://twitter.com/hoge/status/1234567890/photo/1",
340                         AltText = null,
341                     },
342                 },
343             };
344
345             var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet);
346
347             Assert.Equal("pic.twitter.com/hoge", post.AccessibleText);
348             Assert.Equal("<a href=\"https://t.co/hoge\" title=\"https://twitter.com/hoge/status/1234567890/photo/1\">pic.twitter.com/hoge</a>", post.Text);
349             Assert.Equal("pic.twitter.com/hoge", post.TextFromApi);
350             Assert.Equal("pic.twitter.com/hoge", post.TextSingleLine);
351         }
352
353         [Fact]
354         public void CreateFromStatus_QuotedUrlTest()
355         {
356             var factory = new TwitterPostFactory(this.CreateTabinfo());
357
358             var status = this.CreateStatus();
359             status.FullText = "https://t.co/hoge";
360             status.Entities = new()
361             {
362                 Urls = new[]
363                 {
364                     new TwitterEntityUrl
365                     {
366                         Indices = new[] { 0, 17 },
367                         Url = "https://t.co/hoge",
368                         DisplayUrl = "twitter.com/hoge/status/1…",
369                         ExpandedUrl = "https://twitter.com/hoge/status/1234567890",
370                     },
371                 },
372             };
373             status.QuotedStatus = new()
374             {
375                 Id = 1234567890L,
376                 IdStr = "1234567890",
377                 User = new()
378                 {
379                     Id = 1111,
380                     IdStr = "1111",
381                     ScreenName = "foo",
382                 },
383                 FullText = "test",
384             };
385
386             var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet);
387
388             var accessibleText = string.Format(Properties.Resources.QuoteStatus_AccessibleText, "foo", "test");
389             Assert.Equal(accessibleText, post.AccessibleText);
390             Assert.Equal("<a href=\"https://twitter.com/hoge/status/1234567890\" title=\"https://twitter.com/hoge/status/1234567890\">twitter.com/hoge/status/1…</a>", post.Text);
391             Assert.Equal("twitter.com/hoge/status/1…", post.TextFromApi);
392             Assert.Equal("twitter.com/hoge/status/1…", post.TextSingleLine);
393         }
394
395         [Fact]
396         public void CreateFromStatus_QuotedUrlWithPermelinkTest()
397         {
398             var factory = new TwitterPostFactory(this.CreateTabinfo());
399
400             var status = this.CreateStatus();
401             status.FullText = "hoge";
402             status.QuotedStatus = new()
403             {
404                 Id = 1234567890L,
405                 IdStr = "1234567890",
406                 User = new TwitterUser
407                 {
408                     Id = 1111,
409                     IdStr = "1111",
410                     ScreenName = "foo",
411                 },
412                 FullText = "test",
413             };
414             status.QuotedStatusPermalink = new()
415             {
416                 Url = "https://t.co/hoge",
417                 Display = "twitter.com/hoge/status/1…",
418                 Expanded = "https://twitter.com/hoge/status/1234567890",
419             };
420
421             var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet);
422
423             var accessibleText = "hoge " + string.Format(Properties.Resources.QuoteStatus_AccessibleText, "foo", "test");
424             Assert.Equal(accessibleText, post.AccessibleText);
425             Assert.Equal("hoge <a href=\"https://twitter.com/hoge/status/1234567890\" title=\"https://twitter.com/hoge/status/1234567890\">twitter.com/hoge/status/1…</a>", post.Text);
426             Assert.Equal("hoge twitter.com/hoge/status/1…", post.TextFromApi);
427             Assert.Equal("hoge twitter.com/hoge/status/1…", post.TextSingleLine);
428         }
429
430         [Fact]
431         public void CreateFromStatus_QuotedUrlNoReferenceTest()
432         {
433             var factory = new TwitterPostFactory(this.CreateTabinfo());
434
435             var status = this.CreateStatus();
436             status.FullText = "https://t.co/hoge";
437             status.Entities = new()
438             {
439                 Urls = new[]
440                 {
441                     new TwitterEntityUrl
442                     {
443                         Indices = new[] { 0, 17 },
444                         Url = "https://t.co/hoge",
445                         DisplayUrl = "twitter.com/hoge/status/1…",
446                         ExpandedUrl = "https://twitter.com/hoge/status/1234567890",
447                     },
448                 },
449             };
450             status.QuotedStatus = null;
451
452             var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet);
453
454             var accessibleText = "twitter.com/hoge/status/1…";
455             Assert.Equal(accessibleText, post.AccessibleText);
456             Assert.Equal("<a href=\"https://twitter.com/hoge/status/1234567890\" title=\"https://twitter.com/hoge/status/1234567890\">twitter.com/hoge/status/1…</a>", post.Text);
457             Assert.Equal("twitter.com/hoge/status/1…", post.TextFromApi);
458             Assert.Equal("twitter.com/hoge/status/1…", post.TextSingleLine);
459         }
460
461         [Fact]
462         public void CreateHtmlAnchor_Test()
463         {
464             var text = "@twitterapi #BreakingMyTwitter https://t.co/mIJcSoVSK3";
465             var entities = new TwitterEntities
466             {
467                 UserMentions = new[]
468                 {
469                     new TwitterEntityMention { Indices = new[] { 0, 11 }, ScreenName = "twitterapi" },
470                 },
471                 Hashtags = new[]
472                 {
473                     new TwitterEntityHashtag { Indices = new[] { 12, 30 }, Text = "BreakingMyTwitter" },
474                 },
475                 Urls = new[]
476                 {
477                     new TwitterEntityUrl
478                     {
479                         Indices = new[] { 31, 54 },
480                         Url = "https://t.co/mIJcSoVSK3",
481                         DisplayUrl = "apps-of-a-feather.com",
482                         ExpandedUrl = "http://apps-of-a-feather.com/",
483                     },
484                 },
485             };
486
487             var expectedHtml = @"<a class=""mention"" href=""https://twitter.com/twitterapi"">@twitterapi</a>"
488                 + @" <a class=""hashtag"" href=""https://twitter.com/search?q=%23BreakingMyTwitter"">#BreakingMyTwitter</a>"
489                 + @" <a href=""https://t.co/mIJcSoVSK3"" title=""https://t.co/mIJcSoVSK3"">apps-of-a-feather.com</a>";
490
491             Assert.Equal(expectedHtml, TwitterPostFactory.CreateHtmlAnchor(text, entities, quotedStatusLink: null));
492         }
493
494         [Fact]
495         public void CreateHtmlAnchor_NicovideoTest()
496         {
497             var text = "sm9";
498             var entities = new TwitterEntities();
499
500             var expectedHtml = @"<a href=""https://www.nicovideo.jp/watch/sm9"">sm9</a>";
501
502             Assert.Equal(expectedHtml, TwitterPostFactory.CreateHtmlAnchor(text, entities, quotedStatusLink: null));
503         }
504
505         [Fact]
506         public void CreateHtmlAnchor_QuotedUrlWithPermelinkTest()
507         {
508             var text = "hoge";
509             var entities = new TwitterEntities();
510             var quotedStatusLink = new TwitterQuotedStatusPermalink
511             {
512                 Url = "https://t.co/hoge",
513                 Display = "twitter.com/hoge/status/1…",
514                 Expanded = "https://twitter.com/hoge/status/1234567890",
515             };
516
517             var expectedHtml = @"hoge"
518                 + @" <a href=""https://twitter.com/hoge/status/1234567890"" title=""https://twitter.com/hoge/status/1234567890"">twitter.com/hoge/status/1…</a>";
519
520             Assert.Equal(expectedHtml, TwitterPostFactory.CreateHtmlAnchor(text, entities, quotedStatusLink));
521         }
522
523         [Fact]
524         public void ParseSource_Test()
525         {
526             var sourceHtml = "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>";
527
528             var expected = ("Twitter Web Client", new Uri("http://twitter.com/"));
529             Assert.Equal(expected, TwitterPostFactory.ParseSource(sourceHtml));
530         }
531
532         [Fact]
533         public void ParseSource_PlainTextTest()
534         {
535             var sourceHtml = "web";
536
537             var expected = ("web", (Uri?)null);
538             Assert.Equal(expected, TwitterPostFactory.ParseSource(sourceHtml));
539         }
540
541         [Fact]
542         public void ParseSource_RelativeUriTest()
543         {
544             // 参照: https://twitter.com/kim_upsilon/status/477796052049752064
545             var sourceHtml = "<a href=\"erased_45416\" rel=\"nofollow\">erased_45416</a>";
546
547             var expected = ("erased_45416", new Uri("https://twitter.com/erased_45416"));
548             Assert.Equal(expected, TwitterPostFactory.ParseSource(sourceHtml));
549         }
550
551         [Fact]
552         public void ParseSource_EmptyTest()
553         {
554             // 参照: https://twitter.com/kim_upsilon/status/595156014032244738
555             var sourceHtml = "";
556
557             var expected = ("", (Uri?)null);
558             Assert.Equal(expected, TwitterPostFactory.ParseSource(sourceHtml));
559         }
560
561         [Fact]
562         public void ParseSource_NullTest()
563         {
564             string? sourceHtml = null;
565
566             var expected = ("", (Uri?)null);
567             Assert.Equal(expected, TwitterPostFactory.ParseSource(sourceHtml));
568         }
569
570         [Fact]
571         public void ParseSource_UnescapeTest()
572         {
573             var sourceHtml = "<a href=\"http://example.com/?aaa=123&amp;bbb=456\" rel=\"nofollow\">&lt;&lt;hogehoge&gt;&gt;</a>";
574
575             var expected = ("<<hogehoge>>", new Uri("http://example.com/?aaa=123&bbb=456"));
576             Assert.Equal(expected, TwitterPostFactory.ParseSource(sourceHtml));
577         }
578
579         [Fact]
580         public void ParseSource_UnescapeNoUriTest()
581         {
582             var sourceHtml = "&lt;&lt;hogehoge&gt;&gt;";
583
584             var expected = ("<<hogehoge>>", (Uri?)null);
585             Assert.Equal(expected, TwitterPostFactory.ParseSource(sourceHtml));
586         }
587
588         [Fact]
589         public void GetQuoteTweetStatusIds_EntityTest()
590         {
591             var entities = new[]
592             {
593                 new TwitterEntityUrl
594                 {
595                     Url = "https://t.co/3HXq0LrbJb",
596                     ExpandedUrl = "https://twitter.com/kim_upsilon/status/599261132361072640",
597                 },
598             };
599
600             var statusIds = TwitterPostFactory.GetQuoteTweetStatusIds(entities, quotedStatusLink: null);
601             Assert.Equal(new[] { 599261132361072640L }, statusIds);
602         }
603
604         [Fact]
605         public void GetQuoteTweetStatusIds_QuotedStatusLinkTest()
606         {
607             var entities = new TwitterEntities();
608             var quotedStatusLink = new TwitterQuotedStatusPermalink
609             {
610                 Url = "https://t.co/3HXq0LrbJb",
611                 Expanded = "https://twitter.com/kim_upsilon/status/599261132361072640",
612             };
613
614             var statusIds = TwitterPostFactory.GetQuoteTweetStatusIds(entities, quotedStatusLink);
615             Assert.Equal(new[] { 599261132361072640L }, statusIds);
616         }
617
618         [Fact]
619         public void GetQuoteTweetStatusIds_UrlStringTest()
620         {
621             var urls = new[]
622             {
623                 "https://twitter.com/kim_upsilon/status/599261132361072640",
624             };
625
626             var statusIds = TwitterPostFactory.GetQuoteTweetStatusIds(urls);
627             Assert.Equal(new[] { 599261132361072640L }, statusIds);
628         }
629
630         [Fact]
631         public void GetQuoteTweetStatusIds_OverflowTest()
632         {
633             var urls = new[]
634             {
635                 // 符号付き 64 ビット整数の範囲を超える値
636                 "https://twitter.com/kim_upsilon/status/9999999999999999999",
637             };
638
639             var statusIds = TwitterPostFactory.GetQuoteTweetStatusIds(urls);
640             Assert.Empty(statusIds);
641         }
642     }
643 }