damus

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

HashtagTests.swift (22209B)


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