damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

HashtagTests.swift (22050B)


      1 //
      2 //  HashtagTests.swift
      3 //  damusTests
      4 //
      5 //  Created by William Casarin on 2023-07-11.
      6 //  Modified by Jon Marrs on 2023-09-12.
      7 //
      8 
      9 import XCTest
     10 @testable import damus
     11 
     12 final class HashtagTests: XCTestCase {
     13     
     14     // Basic hashtag tests
     15     
     16     func testParseHashtag() {
     17         let parsed = parse_note_content(content: .content("some hashtag #bitcoin derp",nil)).blocks
     18 
     19         XCTAssertNotNil(parsed)
     20         XCTAssertEqual(parsed.count, 3)
     21         XCTAssertEqual(parsed[0].asText, "some hashtag ")
     22         XCTAssertEqual(parsed[1].asHashtag, "bitcoin")
     23         XCTAssertEqual(parsed[2].asText, " derp")
     24     }
     25     
     26     func testParseHashtagEnd() {
     27         let parsed = parse_note_content(content: .content("some hashtag #bitcoin",nil)).blocks
     28 
     29         XCTAssertNotNil(parsed)
     30         XCTAssertEqual(parsed.count, 2)
     31         XCTAssertEqual(parsed[0].asText, "some hashtag ")
     32         XCTAssertEqual(parsed[1].asHashtag, "bitcoin")
     33     }
     34     
     35     //------------------------------------------------------------
     36     // Test ASCII
     37     //------------------------------------------------------------
     38     
     39     // Test ASCII punctuation exceptions (punctuation that is allowed in hashtags)
     40     
     41     // Underscores are allowed in hashtags
     42     func testHashtagWithUnderscore() {
     43         let parsed = parse_note_content(content: .content("the #under_score is allowed in hashtags",nil)).blocks
     44 
     45         XCTAssertNotNil(parsed)
     46         XCTAssertEqual(parsed.count, 3)
     47         XCTAssertEqual(parsed[0].asText, "the ")
     48         XCTAssertEqual(parsed[1].asHashtag, "under_score")
     49         XCTAssertEqual(parsed[2].asText, " is allowed in hashtags")
     50     }
     51     
     52     // Test ASCII punctuation (not allowed in hashtags)
     53     
     54     func testHashtagWithComma() {
     55         let parsed = parse_note_content(content: .content("the #comma, is not allowed",nil)).blocks
     56 
     57         XCTAssertNotNil(parsed)
     58         XCTAssertEqual(parsed.count, 3)
     59         XCTAssertEqual(parsed[0].asText, "the ")
     60         XCTAssertEqual(parsed[1].asHashtag, "comma")
     61         XCTAssertEqual(parsed[2].asText, ", is not allowed")
     62     }
     63     
     64     func testHashtagWithPeriod() {
     65         let parsed = parse_note_content(content: .content("the #period. is not allowed",nil)).blocks
     66 
     67         XCTAssertNotNil(parsed)
     68         XCTAssertEqual(parsed.count, 3)
     69         XCTAssertEqual(parsed[0].asText, "the ")
     70         XCTAssertEqual(parsed[1].asHashtag, "period")
     71         XCTAssertEqual(parsed[2].asText, ". is not allowed")
     72     }
     73     
     74     func testHashtagWithQuestionMark() {
     75         let parsed = parse_note_content(content: .content("the #question?mark is not allowed",nil)).blocks
     76 
     77         XCTAssertNotNil(parsed)
     78         XCTAssertEqual(parsed.count, 3)
     79         XCTAssertEqual(parsed[0].asText, "the ")
     80         XCTAssertEqual(parsed[1].asHashtag, "question")
     81         XCTAssertEqual(parsed[2].asText, "?mark is not allowed")
     82     }
     83     
     84     func testHashtagWithGraveAccent() {
     85         let parsed = parse_note_content(content: .content("the #grave`accent is not allowed",nil)).blocks
     86 
     87         XCTAssertNotNil(parsed)
     88         XCTAssertEqual(parsed.count, 3)
     89         XCTAssertEqual(parsed[0].asText, "the ")
     90         XCTAssertEqual(parsed[1].asHashtag, "grave")
     91         XCTAssertEqual(parsed[2].asText, "`accent is not allowed")
     92     }
     93     
     94     func testHashtagWithTilde() {
     95         let parsed = parse_note_content(content: .content("the #tilde~ is not allowed",nil)).blocks
     96 
     97         XCTAssertNotNil(parsed)
     98         XCTAssertEqual(parsed.count, 3)
     99         XCTAssertEqual(parsed[0].asText, "the ")
    100         XCTAssertEqual(parsed[1].asHashtag, "tilde")
    101         XCTAssertEqual(parsed[2].asText, "~ is not allowed")
    102     }
    103     
    104     func testHashtagWithExclamationPoint() {
    105         let parsed = parse_note_content(content: .content("the #exclamation!point is not allowed",nil)).blocks
    106 
    107         XCTAssertNotNil(parsed)
    108         XCTAssertEqual(parsed.count, 3)
    109         XCTAssertEqual(parsed[0].asText, "the ")
    110         XCTAssertEqual(parsed[1].asHashtag, "exclamation")
    111         XCTAssertEqual(parsed[2].asText, "!point is not allowed")
    112     }
    113     
    114     func testHashtagWithAtSign() {
    115         let parsed = parse_note_content(content: .content("the #at@sign is not allowed",nil)).blocks
    116 
    117         XCTAssertNotNil(parsed)
    118         XCTAssertEqual(parsed.count, 3)
    119         XCTAssertEqual(parsed[0].asText, "the ")
    120         XCTAssertEqual(parsed[1].asHashtag, "at")
    121         XCTAssertEqual(parsed[2].asText, "@sign is not allowed")
    122     }
    123     
    124     func testHashtagWithDollarSign() {
    125         let parsed = parse_note_content(content: .content("the #dollar$sign is not allowed",nil)).blocks
    126 
    127         XCTAssertNotNil(parsed)
    128         XCTAssertEqual(parsed.count, 3)
    129         XCTAssertEqual(parsed[0].asText, "the ")
    130         XCTAssertEqual(parsed[1].asHashtag, "dollar")
    131         XCTAssertEqual(parsed[2].asText, "$sign is not allowed")
    132     }
    133     
    134     func testHashtagWithPercentSign() {
    135         let parsed = parse_note_content(content: .content("the #percent%sign is not allowed",nil)).blocks
    136 
    137         XCTAssertNotNil(parsed)
    138         XCTAssertEqual(parsed.count, 3)
    139         XCTAssertEqual(parsed[0].asText, "the ")
    140         XCTAssertEqual(parsed[1].asHashtag, "percent")
    141         XCTAssertEqual(parsed[2].asText, "%sign is not allowed")
    142     }
    143     
    144     func testHashtagWithCaret() {
    145         let parsed = parse_note_content(content: .content("the #caret^ is not allowed",nil)).blocks
    146 
    147         XCTAssertNotNil(parsed)
    148         XCTAssertEqual(parsed.count, 3)
    149         XCTAssertEqual(parsed[0].asText, "the ")
    150         XCTAssertEqual(parsed[1].asHashtag, "caret")
    151         XCTAssertEqual(parsed[2].asText, "^ is not allowed")
    152     }
    153     
    154     func testHashtagWithAmpersand() {
    155         let parsed = parse_note_content(content: .content("the #ampersand& is not allowed",nil)).blocks
    156 
    157         XCTAssertNotNil(parsed)
    158         XCTAssertEqual(parsed.count, 3)
    159         XCTAssertEqual(parsed[0].asText, "the ")
    160         XCTAssertEqual(parsed[1].asHashtag, "ampersand")
    161         XCTAssertEqual(parsed[2].asText, "& is not allowed")
    162     }
    163     
    164     func testHashtagWithAsterisk() {
    165         let parsed = parse_note_content(content: .content("the #asterisk* is not allowed",nil)).blocks
    166 
    167         XCTAssertNotNil(parsed)
    168         XCTAssertEqual(parsed.count, 3)
    169         XCTAssertEqual(parsed[0].asText, "the ")
    170         XCTAssertEqual(parsed[1].asHashtag, "asterisk")
    171         XCTAssertEqual(parsed[2].asText, "* is not allowed")
    172     }
    173     
    174     func testHashtagWithLeftParenthesis() {
    175         let parsed = parse_note_content(content: .content("the #left(parenthesis is not allowed",nil)).blocks
    176 
    177         XCTAssertNotNil(parsed)
    178         XCTAssertEqual(parsed.count, 3)
    179         XCTAssertEqual(parsed[0].asText, "the ")
    180         XCTAssertEqual(parsed[1].asHashtag, "left")
    181         XCTAssertEqual(parsed[2].asText, "(parenthesis is not allowed")
    182     }
    183     
    184     func testHashtagWithRightParenthesis() {
    185         let parsed = parse_note_content(content: .content("the #right)parenthesis is not allowed",nil)).blocks
    186 
    187         XCTAssertNotNil(parsed)
    188         XCTAssertEqual(parsed.count, 3)
    189         XCTAssertEqual(parsed[0].asText, "the ")
    190         XCTAssertEqual(parsed[1].asHashtag, "right")
    191         XCTAssertEqual(parsed[2].asText, ")parenthesis is not allowed")
    192     }
    193     
    194     func testHashtagWithDash() {
    195         let parsed = parse_note_content(content: .content("the #dash- is not allowed",nil)).blocks
    196 
    197         XCTAssertNotNil(parsed)
    198         XCTAssertEqual(parsed.count, 3)
    199         XCTAssertEqual(parsed[0].asText, "the ")
    200         XCTAssertEqual(parsed[1].asHashtag, "dash")
    201         XCTAssertEqual(parsed[2].asText, "- is not allowed")
    202     }
    203     
    204     func testHashtagWithPlusSign() {
    205         let parsed = parse_note_content(content: .content("the #plus+sign is not allowed",nil)).blocks
    206 
    207         XCTAssertNotNil(parsed)
    208         XCTAssertEqual(parsed.count, 3)
    209         XCTAssertEqual(parsed[0].asText, "the ")
    210         XCTAssertEqual(parsed[1].asHashtag, "plus")
    211         XCTAssertEqual(parsed[2].asText, "+sign is not allowed")
    212     }
    213     
    214     func testHashtagWithEqualsSign() {
    215         let parsed = parse_note_content(content: .content("the #equals=sign is not allowed",nil)).blocks
    216 
    217         XCTAssertNotNil(parsed)
    218         XCTAssertEqual(parsed.count, 3)
    219         XCTAssertEqual(parsed[0].asText, "the ")
    220         XCTAssertEqual(parsed[1].asHashtag, "equals")
    221         XCTAssertEqual(parsed[2].asText, "=sign is not allowed")
    222     }
    223     
    224     func testHashtagWithLeftBracket() {
    225         let parsed = parse_note_content(content: .content("the #left[bracket is not allowed",nil)).blocks
    226 
    227         XCTAssertNotNil(parsed)
    228         XCTAssertEqual(parsed.count, 3)
    229         XCTAssertEqual(parsed[0].asText, "the ")
    230         XCTAssertEqual(parsed[1].asHashtag, "left")
    231         XCTAssertEqual(parsed[2].asText, "[bracket is not allowed")
    232     }
    233     
    234     func testHashtagWithRightBracket() {
    235         let parsed = parse_note_content(content: .content("the #right]bracket is not allowed",nil)).blocks
    236 
    237         XCTAssertNotNil(parsed)
    238         XCTAssertEqual(parsed.count, 3)
    239         XCTAssertEqual(parsed[0].asText, "the ")
    240         XCTAssertEqual(parsed[1].asHashtag, "right")
    241         XCTAssertEqual(parsed[2].asText, "]bracket is not allowed")
    242     }
    243     
    244     func testHashtagWithLeftBrace() {
    245         let parsed = parse_note_content(content: .content("the #left{brace is not allowed",nil)).blocks
    246 
    247         XCTAssertNotNil(parsed)
    248         XCTAssertEqual(parsed.count, 3)
    249         XCTAssertEqual(parsed[0].asText, "the ")
    250         XCTAssertEqual(parsed[1].asHashtag, "left")
    251         XCTAssertEqual(parsed[2].asText, "{brace is not allowed")
    252     }
    253     
    254     func testHashtagWithRightBrace() {
    255         let parsed = parse_note_content(content: .content("the #right}brace is not allowed",nil)).blocks
    256 
    257         XCTAssertNotNil(parsed)
    258         XCTAssertEqual(parsed.count, 3)
    259         XCTAssertEqual(parsed[0].asText, "the ")
    260         XCTAssertEqual(parsed[1].asHashtag, "right")
    261         XCTAssertEqual(parsed[2].asText, "}brace is not allowed")
    262     }
    263     
    264     func testHashtagWithBackslash() {
    265         let parsed = parse_note_content(content: .content("the #back\\slash is not allowed",nil)).blocks
    266 
    267         XCTAssertNotNil(parsed)
    268         XCTAssertEqual(parsed.count, 3)
    269         XCTAssertEqual(parsed[0].asText, "the ")
    270         XCTAssertEqual(parsed[1].asHashtag, "back")
    271         XCTAssertEqual(parsed[2].asText, "\\slash is not allowed")
    272     }
    273     
    274     func testHashtagWithVerticalLine() {
    275         let parsed = parse_note_content(content: .content("the #vertical|line is not allowed",nil)).blocks
    276 
    277         XCTAssertNotNil(parsed)
    278         XCTAssertEqual(parsed.count, 3)
    279         XCTAssertEqual(parsed[0].asText, "the ")
    280         XCTAssertEqual(parsed[1].asHashtag, "vertical")
    281         XCTAssertEqual(parsed[2].asText, "|line is not allowed")
    282     }
    283     
    284     func testHashtagWithSemicolon() {
    285         let parsed = parse_note_content(content: .content("the #semicolon; is not allowed",nil)).blocks
    286 
    287         XCTAssertNotNil(parsed)
    288         XCTAssertEqual(parsed.count, 3)
    289         XCTAssertEqual(parsed[0].asText, "the ")
    290         XCTAssertEqual(parsed[1].asHashtag, "semicolon")
    291         XCTAssertEqual(parsed[2].asText, "; is not allowed")
    292     }
    293     
    294     func testHashtagWithColon() {
    295         let parsed = parse_note_content(content: .content("the #colon: is not allowed",nil)).blocks
    296 
    297         XCTAssertNotNil(parsed)
    298         XCTAssertEqual(parsed.count, 3)
    299         XCTAssertEqual(parsed[0].asText, "the ")
    300         XCTAssertEqual(parsed[1].asHashtag, "colon")
    301         XCTAssertEqual(parsed[2].asText, ": is not allowed")
    302     }
    303     
    304     func testHashtagWithApostrophe() {
    305         let parsed = parse_note_content(content: .content("the #apostrophe' is not allowed",nil)).blocks
    306 
    307         XCTAssertNotNil(parsed)
    308         XCTAssertEqual(parsed.count, 3)
    309         XCTAssertEqual(parsed[0].asText, "the ")
    310         XCTAssertEqual(parsed[1].asHashtag, "apostrophe")
    311         XCTAssertEqual(parsed[2].asText, "' is not allowed")
    312     }
    313     
    314     func testHashtagWithQuotationMark() {
    315         let parsed = parse_note_content(content: .content("the #quotation\"mark is not allowed",nil)).blocks
    316 
    317         XCTAssertNotNil(parsed)
    318         XCTAssertEqual(parsed.count, 3)
    319         XCTAssertEqual(parsed[0].asText, "the ")
    320         XCTAssertEqual(parsed[1].asHashtag, "quotation")
    321         XCTAssertEqual(parsed[2].asText, "\"mark is not allowed")
    322     }
    323     
    324     func testHashtagWithLessThanSign() {
    325         let parsed = parse_note_content(content: .content("the #lessthan<sign is not allowed",nil)).blocks
    326 
    327         XCTAssertNotNil(parsed)
    328         XCTAssertEqual(parsed.count, 3)
    329         XCTAssertEqual(parsed[0].asText, "the ")
    330         XCTAssertEqual(parsed[1].asHashtag, "lessthan")
    331         XCTAssertEqual(parsed[2].asText, "<sign is not allowed")
    332     }
    333     
    334     func testHashtagWithGreaterThanSign() {
    335         let parsed = parse_note_content(content: .content("the #greaterthan>sign is not allowed",nil)).blocks
    336 
    337         XCTAssertNotNil(parsed)
    338         XCTAssertEqual(parsed.count, 3)
    339         XCTAssertEqual(parsed[0].asText, "the ")
    340         XCTAssertEqual(parsed[1].asHashtag, "greaterthan")
    341         XCTAssertEqual(parsed[2].asText, ">sign is not allowed")
    342     }
    343     
    344     //------------------------------------------------------------
    345     // Test Unicode (UTF-8)
    346     //------------------------------------------------------------
    347     
    348     // Test UTF-8 Latin-1 Supplement Punctuation: U+00A1 to U+00BF (not allowed)
    349     
    350     // Test pound sign (£) (U+00A3)
    351     func testHashtagWithPoundSign() {
    352         let parsed = parse_note_content(content: .content("the #pound£sign is not allowed",nil)).blocks
    353 
    354         XCTAssertNotNil(parsed)
    355         XCTAssertEqual(parsed.count, 3)
    356         XCTAssertEqual(parsed[0].asText, "the ")
    357         XCTAssertEqual(parsed[1].asHashtag, "pound")
    358         XCTAssertEqual(parsed[2].asText, "£sign is not allowed")
    359     }
    360     
    361     // Test yen sign (¥) (U+00A5)
    362     func testHashtagWithYenSign() {
    363         let parsed = parse_note_content(content: .content("the #yen¥sign is not allowed",nil)).blocks
    364 
    365         XCTAssertNotNil(parsed)
    366         XCTAssertEqual(parsed.count, 3)
    367         XCTAssertEqual(parsed[0].asText, "the ")
    368         XCTAssertEqual(parsed[1].asHashtag, "yen")
    369         XCTAssertEqual(parsed[2].asText, "¥sign is not allowed")
    370     }
    371     
    372     // Test section sign (§) (U+00A7)
    373     func testHashtagWithSectionSign() {
    374         let parsed = parse_note_content(content: .content("the #section§sign is not allowed",nil)).blocks
    375 
    376         XCTAssertNotNil(parsed)
    377         XCTAssertEqual(parsed.count, 3)
    378         XCTAssertEqual(parsed[0].asText, "the ")
    379         XCTAssertEqual(parsed[1].asHashtag, "section")
    380         XCTAssertEqual(parsed[2].asText, "§sign is not allowed")
    381     }
    382     
    383     // Test plus-minus sign (±) (U+00B1)
    384     func testHashtagWithPlusMinusSign() {
    385         let parsed = parse_note_content(content: .content("the #plusminus±sign is not allowed",nil)).blocks
    386 
    387         XCTAssertNotNil(parsed)
    388         XCTAssertEqual(parsed.count, 3)
    389         XCTAssertEqual(parsed[0].asText, "the ")
    390         XCTAssertEqual(parsed[1].asHashtag, "plusminus")
    391         XCTAssertEqual(parsed[2].asText, "±sign is not allowed")
    392     }
    393     
    394     // Test inverted question mark (¿) (U+00BF)
    395     func testHashtagWithInvertedQuestionMark() {
    396         let parsed = parse_note_content(content: .content("the #invertedquestion¿mark is not allowed",nil)).blocks
    397 
    398         XCTAssertNotNil(parsed)
    399         XCTAssertEqual(parsed.count, 3)
    400         XCTAssertEqual(parsed[0].asText, "the ")
    401         XCTAssertEqual(parsed[1].asHashtag, "invertedquestion")
    402         XCTAssertEqual(parsed[2].asText, "¿mark is not allowed")
    403     }
    404     
    405     // Test UTF-8 Latin-1 Supplement Non-Punctuation: U+00C0 to U+00FF (allowed)
    406     
    407     // Test Latin small letter u with diaeresis (ü) (U+00FC) (allowed in hashtags)
    408     func testHashtagWithAccents() {
    409         let parsed = parse_note_content(content: .content("hello from #türkiye",nil)).blocks
    410 
    411         XCTAssertNotNil(parsed)
    412         XCTAssertEqual(parsed.count, 2)
    413         XCTAssertEqual(parsed[0].asText, "hello from ")
    414         XCTAssertEqual(parsed[1].asHashtag, "türkiye")
    415     }
    416     
    417     // Test UTF-8 General Punctuation: U+2000 to U+206F (not allowed in hashtags)
    418     
    419     // Test en dash (–) (U+2013)
    420     func testHashtagWithEnDash() {
    421         let parsed = parse_note_content(content: .content("the #en–dash is not allowed",nil)).blocks
    422 
    423         XCTAssertNotNil(parsed)
    424         XCTAssertEqual(parsed.count, 3)
    425         XCTAssertEqual(parsed[0].asText, "the ")
    426         XCTAssertEqual(parsed[1].asHashtag, "en")
    427         XCTAssertEqual(parsed[2].asText, "–dash is not allowed")
    428     }
    429     
    430     // Test em dash (—) (U+2014)
    431     func testHashtagWithEmDash() {
    432         let parsed = parse_note_content(content: .content("the #em—dash is not allowed",nil)).blocks
    433 
    434         XCTAssertNotNil(parsed)
    435         XCTAssertEqual(parsed.count, 3)
    436         XCTAssertEqual(parsed[0].asText, "the ")
    437         XCTAssertEqual(parsed[1].asHashtag, "em")
    438         XCTAssertEqual(parsed[2].asText, "—dash is not allowed")
    439     }
    440     
    441     // Test horizontal bar (―) (U+2015)
    442     func testHashtagWithHorizontalBar() {
    443         let parsed = parse_note_content(content: .content("the #horizontal―bar is not allowed",nil)).blocks
    444 
    445         XCTAssertNotNil(parsed)
    446         XCTAssertEqual(parsed.count, 3)
    447         XCTAssertEqual(parsed[0].asText, "the ")
    448         XCTAssertEqual(parsed[1].asHashtag, "horizontal")
    449         XCTAssertEqual(parsed[2].asText, "―bar is not allowed")
    450     }
    451     
    452     // Test horizontal ellipsis (…) (U+2026)
    453     func testHashtagWithHorizontalEllipsis() {
    454         let parsed = parse_note_content(content: .content("the #horizontal…ellipsis is not allowed",nil)).blocks
    455 
    456         XCTAssertNotNil(parsed)
    457         XCTAssertEqual(parsed.count, 3)
    458         XCTAssertEqual(parsed[0].asText, "the ")
    459         XCTAssertEqual(parsed[1].asHashtag, "horizontal")
    460         XCTAssertEqual(parsed[2].asText, "…ellipsis is not allowed")
    461     }
    462     
    463     // Test UTF-8 Currency Symbols: U+20A0 to U+20CF (not allowed in hashtags)
    464     
    465     // Test euro sign (€) (U+20AC)
    466     func testHashtagWithEuroSign() {
    467         let parsed = parse_note_content(content: .content("the #euro€sign is not allowed",nil)).blocks
    468 
    469         XCTAssertNotNil(parsed)
    470         XCTAssertEqual(parsed.count, 3)
    471         XCTAssertEqual(parsed[0].asText, "the ")
    472         XCTAssertEqual(parsed[1].asHashtag, "euro")
    473         XCTAssertEqual(parsed[2].asText, "€sign is not allowed")
    474     }
    475     
    476     // Test Bitcoin sign (₿) (U+20BF)
    477     func testHashtagWithBitcoinSign() {
    478         let parsed = parse_note_content(content: .content("the #bitcoin₿sign is not allowed",nil)).blocks
    479 
    480         XCTAssertNotNil(parsed)
    481         XCTAssertEqual(parsed.count, 3)
    482         XCTAssertEqual(parsed[0].asText, "the ")
    483         XCTAssertEqual(parsed[1].asHashtag, "bitcoin")
    484         XCTAssertEqual(parsed[2].asText, "₿sign is not allowed")
    485     }
    486     
    487     // Test UTF-8 Miscellaneous Symbols: U+2600 to U+26FF (allowed in hashtags)
    488     
    489     // Emojis such as ☕️ (U+2615) are allowed in hashtags
    490     func testHashtagWithEmoji() {
    491         let content = "some hashtag #bitcoin☕️ cool"
    492         let parsed = parse_note_content(content: .content(content, nil)).blocks
    493         let post_blocks = parse_post_blocks(content: content)
    494 
    495         XCTAssertNotNil(parsed)
    496         XCTAssertEqual(parsed.count, 3)
    497         XCTAssertEqual(parsed[0].asText, "some hashtag ")
    498         XCTAssertEqual(parsed[1].asHashtag, "bitcoin☕️")
    499         XCTAssertEqual(parsed[2].asText, " cool")
    500 
    501         XCTAssertEqual(post_blocks.count, 3)
    502         XCTAssertEqual(post_blocks[0].asText, "some hashtag ")
    503         XCTAssertEqual(post_blocks[1].asHashtag, "bitcoin☕️")
    504         XCTAssertEqual(post_blocks[2].asText, " cool")
    505     }
    506     
    507     // Test international Unicode (UTF-8) characters
    508     
    509     // Japanese: wave dash (〜) (U+301C) (allowed in hashtags)
    510     func testPowHashtag() {
    511         let content = "pow! #ぽわ〜"
    512         let parsed = parse_note_content(content: .content(content,nil)).blocks
    513         let post_blocks = parse_post_blocks(content: content)
    514 
    515         XCTAssertNotNil(parsed)
    516         XCTAssertEqual(parsed.count, 2)
    517         XCTAssertEqual(parsed[0].asText, "pow! ")
    518         XCTAssertEqual(parsed[1].asHashtag, "ぽわ〜")
    519 
    520         XCTAssertEqual(post_blocks.count, 2)
    521         XCTAssertEqual(post_blocks[0].asText, "pow! ")
    522         XCTAssertEqual(post_blocks[1].asHashtag, "ぽわ〜")
    523     }
    524     
    525     // Hangul: Hangul Syllable Si (시) (U+C2DC) and
    526     // Hangul Syllable Heom (험) (U+D5D8) (allowed in hashtags)
    527     func testHashtagWithNonLatinCharacters() {
    528         let parsed = parse_note_content(content: .content("this is a #시험 hope it works",nil)).blocks
    529 
    530         XCTAssertNotNil(parsed)
    531         XCTAssertEqual(parsed.count, 3)
    532         XCTAssertEqual(parsed[0].asText, "this is a ")
    533         XCTAssertEqual(parsed[1].asHashtag, "시험")
    534         XCTAssertEqual(parsed[2].asText, " hope it works")
    535     }
    536     
    537     // Japanese: fullwidth tilde (~) (U+FF5E) (allowed in hashtags)
    538     func testHashtagWithFullwidthTilde() {
    539         let parsed = parse_note_content(content: .content("pow! the fullwidth tilde #ぽわ~ is allowed in hashtags",nil)).blocks
    540         
    541         XCTAssertNotNil(parsed)
    542         XCTAssertEqual(parsed.count, 3)
    543         XCTAssertEqual(parsed[0].asText, "pow! the fullwidth tilde ")
    544         XCTAssertEqual(parsed[1].asHashtag, "ぽわ~")
    545         XCTAssertEqual(parsed[2].asText, " is allowed in hashtags")
    546     }
    547     
    548     // Japanese: bai (倍) (U+500D) (allowed in hashtags)
    549     func testHashtagWithBaiKanji() {
    550         let parsed = parse_note_content(content: .content("pow! #10倍界王拳 is allowed in hashtags",nil)).blocks
    551         
    552         XCTAssertNotNil(parsed)
    553         XCTAssertEqual(parsed.count, 3)
    554         XCTAssertEqual(parsed[0].asText, "pow! ")
    555         XCTAssertEqual(parsed[1].asHashtag, "10倍界王拳")
    556         XCTAssertEqual(parsed[2].asText, " is allowed in hashtags")
    557     }
    558 
    559 }