ReplyTests.swift (18986B)
1 // 2 // ReplyTests.swift 3 // damusTests 4 // 5 // Created by William Casarin on 2022-05-08. 6 // 7 8 import XCTest 9 @testable import damus 10 11 class ReplyTests: XCTestCase { 12 13 override func setUpWithError() throws { 14 // Put setup code here. This method is called before the invocation of each test method in the class. 15 } 16 17 override func tearDownWithError() throws { 18 // Put teardown code here. This method is called after the invocation of each test method in the class. 19 } 20 21 func testMentionIsntReply() throws { 22 let evid = NoteId(hex: "4090a9017a2beac3f17795d1aafb80d9f2b9eda97e4738501082ed5c927be014")! 23 let content = "this is #[0] a mention" 24 let tags = [evid.tag] 25 let ev = NostrEvent(content: content, keypair: test_keypair, tags: tags)! 26 let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks 27 let event_refs = interpret_event_refs(blocks: blocks, tags: ev.tags) 28 29 XCTAssertEqual(event_refs.count, 1) 30 31 let ref = event_refs[0] 32 33 XCTAssertNil(ref.is_reply) 34 XCTAssertNil(ref.is_thread_id) 35 XCTAssertNil(ref.is_direct_reply) 36 XCTAssertEqual(ref.is_mention, .some(.init(note_id: evid))) 37 } 38 39 func testAtAtEnd() { 40 let content = "what @" 41 let blocks = parse_post_blocks(content: content) 42 43 XCTAssertEqual(blocks.count, 1) 44 XCTAssertEqual(blocks[0].asText, "what @") 45 } 46 47 func testHashtagsInQuote() { 48 let content = "This is my \"#awesome post\"" 49 let blocks = parse_post_blocks(content: content) 50 51 XCTAssertEqual(blocks.count, 3) 52 XCTAssertEqual(blocks[0].asText, "This is my \"") 53 XCTAssertEqual(blocks[1].asHashtag, "awesome") 54 XCTAssertEqual(blocks[2].asText, " post\"") 55 } 56 57 func testHashtagAtStartWorks() { 58 let content = "#hashtag" 59 let blocks = parse_post_blocks(content: content) 60 XCTAssertEqual(blocks.count, 1) 61 XCTAssertEqual(blocks[0].asHashtag, "hashtag") 62 } 63 64 func testGroupOfHashtags() { 65 let content = "#hashtag#what#nope" 66 let blocks = parse_post_blocks(content: content) 67 XCTAssertEqual(blocks.count, 3) 68 XCTAssertEqual(blocks[0].asHashtag, "hashtag") 69 XCTAssertEqual(blocks[1].asHashtag, "what") 70 XCTAssertEqual(blocks[2].asHashtag, "nope") 71 } 72 73 func testRootReplyWithMention() throws { 74 let content = "this is #[1] a mention" 75 let thread_id = NoteId(hex: "c75e5cbafbefd5de2275f831c2a2386ea05ec5e5a78a5ccf60d467582db48945")! 76 let mentioned_id = NoteId(hex: "5a534797e8cd3b9f4c1cf63e20e48bd0e8bd7f8c4d6353fbd576df000f6f54d3")! 77 let tags = [thread_id.tag, mentioned_id.tag] 78 let ev = NostrEvent(content: content, keypair: test_keypair, tags: tags)! 79 let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks 80 let event_refs = interpret_event_refs(blocks: blocks, tags: ev.tags) 81 82 XCTAssertEqual(event_refs.count, 2) 83 XCTAssertNotNil(event_refs[0].is_reply) 84 XCTAssertNotNil(event_refs[0].is_thread_id) 85 XCTAssertNotNil(event_refs[0].is_reply) 86 XCTAssertNotNil(event_refs[0].is_direct_reply) 87 XCTAssertEqual(event_refs[0].is_reply, .some(NoteRef(note_id: thread_id))) 88 XCTAssertEqual(event_refs[0].is_thread_id, .some(NoteRef(note_id: thread_id))) 89 XCTAssertNotNil(event_refs[1].is_mention) 90 XCTAssertEqual(event_refs[1].is_mention, .some(NoteRef(note_id: mentioned_id))) 91 } 92 93 func testEmptyMention() throws { 94 let content = "this is some & content" 95 let ev = NostrEvent(content: content, keypair: test_keypair, tags: [])! 96 let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks 97 let post_blocks = parse_post_blocks(content: content) 98 let post_tags = make_post_tags(post_blocks: post_blocks, tags: []) 99 let event_refs = interpret_event_refs(blocks: blocks, tags: ev.tags) 100 101 XCTAssertEqual(event_refs.count, 0) 102 XCTAssertEqual(post_tags.blocks.count, 1) 103 XCTAssertEqual(post_tags.tags.count, 0) 104 XCTAssertEqual(post_blocks.count, 1) 105 } 106 107 func testManyMentions() throws { 108 let content = "#[10]" 109 let tags: [[String]] = [[],[],[],[],[],[],[],[],[],[],["p", "3e999f94e2cb34ef44a64b351141ac4e51b5121b2d31aed4a6c84602a1144692"]] 110 let ev = NostrEvent(content: content, keypair: test_keypair, tags: tags)! 111 let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks 112 let mentions = blocks.filter { $0.asMention != nil } 113 XCTAssertEqual(mentions.count, 1) 114 } 115 116 func testNewlineMentions() throws { 117 let bech32_pk = "npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s" 118 let pk = bech32_pubkey_decode(bech32_pk)! 119 120 let profile = Profile(name: "jb55") 121 let post = user_tag_attr_string(profile: profile, pubkey: pk) 122 post.append(.init(string: "\n")) 123 post.append(user_tag_attr_string(profile: profile, pubkey: pk)) 124 post.append(.init(string: "\n")) 125 126 let post_note = build_post(state: test_damus_state, post: post, action: .posting(.none), uploadedMedias: [], references: [.pubkey(pk)]) 127 128 let expected_render = "nostr:\(pk.npub)\nnostr:\(pk.npub)" 129 XCTAssertEqual(post_note.content, expected_render) 130 131 let blocks = parse_note_content(content: .content(post_note.content,nil)).blocks 132 let rendered = blocks.map { $0.asString }.joined(separator: "") 133 134 XCTAssertEqual(rendered, expected_render) 135 136 XCTAssertEqual(blocks.count, 3) 137 XCTAssertEqual(blocks[0].asMention, Mention<MentionRef>.any(.pubkey(pk))) 138 XCTAssertEqual(blocks[1].asText, "\n") 139 XCTAssertEqual(blocks[2].asMention, Mention<MentionRef>.any(.pubkey(pk))) 140 } 141 142 func testThreadedReply() throws { 143 let content = "this is some content" 144 let thread_id = NoteId(hex: "da256fb52146dc565c6c6b9ef906117c665864dc02b14a7b853eca244729c2f2")! 145 let reply_id = NoteId(hex: "80093e9bdb495728f54cda2bad4aed096877189552b3d41264e73b9a9595be22")! 146 let tags = [thread_id.tag, reply_id.tag] 147 let ev = NostrEvent(content: content, keypair: test_keypair, tags: tags)! 148 let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks 149 let event_refs = interpret_event_refs(blocks: blocks, tags: ev.tags) 150 151 XCTAssertEqual(event_refs.count, 2) 152 let r1 = event_refs[0] 153 let r2 = event_refs[1] 154 155 XCTAssertEqual(r1.is_thread_id, .some(.note_id(thread_id))) 156 XCTAssertEqual(r2.is_reply, .some(.note_id(reply_id))) 157 XCTAssertEqual(r2.is_direct_reply, .some(.note_id(reply_id))) 158 XCTAssertNil(r1.is_direct_reply) 159 } 160 161 func testRootReply() throws { 162 let content = "this is a reply" 163 let thread_id = NoteId(hex: "53f60f5114c06f069ffe9da2bc033e533d09cae44d37a8462154a663771a4ce6")! 164 let tags = [thread_id.tag] 165 let ev = NostrEvent(content: content, keypair: test_keypair, tags: tags)! 166 let blocks = parse_note_content(content: .content(ev.content,nil)).blocks 167 let event_refs = interpret_event_refs(blocks: blocks, tags: ev.tags) 168 169 XCTAssertEqual(event_refs.count, 1) 170 let r = event_refs[0] 171 172 XCTAssertEqual(r.is_direct_reply, .some(.note_id(thread_id))) 173 XCTAssertEqual(r.is_reply, .some(.note_id(thread_id))) 174 XCTAssertEqual(r.is_thread_id, .some(.note_id(thread_id))) 175 XCTAssertNil(r.is_mention) 176 } 177 178 func testAdjacentComposedMention() throws { 179 let content = "cc@jb55" 180 181 let profile = Profile(name: "jb55") 182 let tag = user_tag_attr_string(profile: profile, pubkey: test_pubkey) 183 let appended = append_user_tag(tag: tag, post: .init(string: content), word_range: .init(2...6)) 184 let new_post = appended.post 185 186 try new_post.testAttributes(conditions: [ 187 { let link = $0[.link] as? String; XCTAssertNil(link) }, 188 { let link = $0[.link] as! String; XCTAssertEqual(link, "damus:nostr:\(test_pubkey.npub)") }, 189 { let link = $0[.link] as? String; XCTAssertNil(link) } 190 ]) 191 192 XCTAssertEqual(new_post.string, "cc @jb55 ") 193 } 194 195 func testAdjacentEmojiComposedMention() throws { 196 let content = "😎@jb55" 197 198 let profile = Profile(name: "jb55") 199 let tag = user_tag_attr_string(profile: profile, pubkey: test_pubkey) 200 let appended = append_user_tag(tag: tag, post: .init(string: content), word_range: .init(2...6)) 201 let new_post = appended.post 202 203 try new_post.testAttributes(conditions: [ 204 { let link = $0[.link] as? String; XCTAssertNil(link) }, 205 { let link = $0[.link] as! String; XCTAssertEqual(link, "damus:nostr:\(test_pubkey.npub)") }, 206 { let link = $0[.link] as? String; XCTAssertNil(link) } 207 ]) 208 209 XCTAssertEqual(new_post.string, "😎 @jb55 ") 210 } 211 212 func testComposedMentionNewline() throws { 213 let content = """ 214 215 @jb55 216 """ 217 218 let profile = Profile(name: "jb55") 219 let tag = user_tag_attr_string(profile: profile, pubkey: test_pubkey) 220 let appended = append_user_tag(tag: tag, post: .init(string: content), word_range: .init(1...5)) 221 let new_post = appended.post 222 223 try new_post.testAttributes(conditions: [ 224 { let link = $0[.link] as? String; XCTAssertNil(link) }, 225 { let link = $0[.link] as! String; XCTAssertEqual(link, "damus:nostr:\(test_pubkey.npub)") }, 226 { let link = $0[.link] as? String; XCTAssertNil(link) }, 227 ]) 228 229 XCTAssertEqual(new_post.string, "\n@jb55 ") 230 } 231 232 func testComposedMention() throws { 233 let content = "@jb55" 234 235 let profile = Profile(name: "jb55") 236 let tag = user_tag_attr_string(profile: profile, pubkey: test_pubkey) 237 let appended = append_user_tag(tag: tag, post: .init(string: content), word_range: .init(0...4)) 238 let new_post = appended.post 239 240 try new_post.testAttributes(conditions: [ 241 { let link = $0[.link] as! String; XCTAssertEqual(link, "damus:nostr:\(test_pubkey.npub)") }, 242 { let link = $0[.link] as? String; XCTAssertNil(link) }, 243 ]) 244 245 XCTAssertEqual(new_post.string, "@jb55 ") 246 } 247 248 func testAdjacentSpaceComposedMention() throws { 249 let content = "cc @jb55" 250 251 let profile = Profile(name: "jb55") 252 let tag = user_tag_attr_string(profile: profile, pubkey: test_pubkey) 253 let appended = append_user_tag(tag: tag, post: .init(string: content), word_range: .init(3...7)) 254 let new_post = appended.post 255 256 try new_post.testAttributes(conditions: [ 257 { let link = $0[.link] as? String; XCTAssertNil(link) }, 258 { let link = $0[.link] as! String; XCTAssertEqual(link, "damus:nostr:\(test_pubkey.npub)") }, 259 { let link = $0[.link] as? String; XCTAssertNil(link) } 260 ]) 261 262 XCTAssertEqual(new_post.string, "cc @jb55 ") 263 } 264 265 func testNoReply() throws { 266 let content = "this is a #[0] reply" 267 let ev = NostrEvent(content: content, keypair: test_keypair, tags: [])! 268 let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks 269 let event_refs = interpret_event_refs(blocks: blocks, tags:ev.tags) 270 271 XCTAssertEqual(event_refs.count, 0) 272 } 273 274 func testParseMention() throws { 275 let note_id = NoteId(hex: "53f60f5114c06f069ffe9da2bc033e533d09cae44d37a8462154a663771a4ce6")! 276 let tags = [note_id.tag] 277 let ev = NostrEvent(content: "this is #[0] a mention", keypair: test_keypair, tags: tags)! 278 let parsed = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks 279 280 XCTAssertNotNil(parsed) 281 XCTAssertEqual(parsed.count, 3) 282 XCTAssertEqual(parsed[0].asText, "this is ") 283 XCTAssertNotNil(parsed[1].asMention) 284 XCTAssertEqual(parsed[2].asText, " a mention") 285 } 286 287 func testEmptyPostReference() throws { 288 let parsed = parse_post_blocks(content: "") 289 XCTAssertEqual(parsed.count, 0) 290 } 291 292 func testBech32MentionAtStart() throws { 293 let pk = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")! 294 let content = "@\(pk.npub) hello there" 295 let blocks = parse_post_blocks(content: content) 296 297 XCTAssertEqual(blocks.count, 2) 298 XCTAssertEqual(blocks[0].asMention, .any(.pubkey(pk))) 299 XCTAssertEqual(blocks[1].asText, " hello there") 300 301 } 302 303 func testBech32MentionAtEnd() throws { 304 let pk = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")! 305 let content = "this is a @\(pk.npub)" 306 let blocks = parse_post_blocks(content: content) 307 308 XCTAssertEqual(blocks.count, 2) 309 XCTAssertEqual(blocks[1].asMention, .any(.pubkey(pk))) 310 XCTAssertEqual(blocks[0].asText, "this is a ") 311 } 312 313 func testNpubMention() throws { 314 let evid = NoteId(hex: "71ba3e5ddaf48103be294aa370e470fb60b6c8bca3fb01706eecd00054c2f588")! 315 let pk = Pubkey(hex: "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245")! 316 let content = "this is a @\(pk.npub) mention" 317 let blocks = parse_post_blocks(content: content) 318 let post = NostrPost(content: content, references: [.event(evid)]) 319 let ev = post_to_event(post: post, keypair: test_keypair_full)! 320 321 XCTAssertEqual(ev.tags.count, 2) 322 XCTAssertEqual(blocks.count, 3) 323 XCTAssertEqual(blocks[1].asMention, .any(.pubkey(pk))) 324 XCTAssertEqual(ev.content, "this is a nostr:npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s mention") 325 } 326 327 func testNsecMention() throws { 328 let evid = NoteId(hex: "71ba3e5ddaf48103be294aa370e470fb60b6c8bca3fb01706eecd00054c2f588")! 329 let pk = Pubkey(hex: "ccf95d668650178defca5ac503693b6668eb77895f610178ff8ed9fe5cf9482e")! 330 let nsec = "nsec1jmzdz7d0ldqctdxwm5fzue277ttng2pk28n2u8wntc2r4a0w96ssnyukg7" 331 let content = "this is a @\(nsec) mention" 332 let blocks = parse_post_blocks(content: content) 333 let post = NostrPost(content: content, references: [.event(evid)]) 334 let ev = post_to_event(post: post, keypair: test_keypair_full)! 335 336 XCTAssertEqual(ev.tags.count, 2) 337 XCTAssertEqual(blocks.count, 3) 338 XCTAssertEqual(blocks[1].asMention, .any(.pubkey(pk))) 339 XCTAssertEqual(ev.content, "this is a nostr:npub1enu46e5x2qtcmm72ttzsx6fmve5wkauftassz78l3mvluh8efqhqejf3v4 mention") 340 } 341 342 func testReplyMentions() throws { 343 let pubkey = Pubkey(hex: "30c6d1dc7f7c156794fa15055e651b758a61b99f50fcf759de59386050bf6ae2")! 344 let thread_id = NoteId(hex: "a250fc93570c3e87f9c9b08d6b3ef7b8e05d346df8a52c69e30ffecdb178fb9e")! 345 let reply_id = NoteId(hex: "9a180a10f16dac9566543ad1fc29616aab272b0cf123ab5d58843e16f4ef03a3")! 346 347 let refs: [RefId] = [ 348 .event(thread_id), 349 .event(reply_id), 350 .pubkey(pubkey) 351 ] 352 353 let post = NostrPost(content: "this is a (@\(pubkey.npub)) mention", references: refs) 354 let ev = post_to_event(post: post, keypair: test_keypair_full)! 355 356 XCTAssertEqual(ev.content, "this is a (nostr:\(pubkey.npub)) mention") 357 XCTAssertEqual(ev.tags[2][1].string(), pubkey.description) 358 } 359 360 func testInvalidPostReference() throws { 361 let pk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e24" 362 let content = "this is a @\(pk) mention" 363 let parsed = parse_post_blocks(content: content) 364 XCTAssertEqual(parsed.count, 1) 365 guard case .text(let txt) = parsed[0] else { 366 XCTAssert(false) 367 return 368 } 369 XCTAssertEqual(txt, content) 370 } 371 372 func testInvalidPostReferenceEmptyAt() throws { 373 let content = "this is a @ mention" 374 let parsed = parse_post_blocks(content: content) 375 XCTAssertEqual(parsed.count, 1) 376 guard case .text(let txt) = parsed[0] else { 377 XCTAssert(false) 378 return 379 } 380 XCTAssertEqual(txt, content) 381 } 382 383 func testInvalidUriReference() throws { 384 let id = "6fec2ee6cfff779fe8560976b3d9df782b74577f0caefa7a77c0ed4c3749b5de" 385 let content = "this is a nostr:z:\(id) event mention" 386 let parsed = parse_post_blocks(content: content) 387 388 XCTAssertNotNil(parsed) 389 XCTAssertEqual(parsed.count, 1) 390 391 guard case .text(let txt) = parsed[0] else { 392 XCTAssertTrue(false) 393 return 394 } 395 396 XCTAssertEqual(txt, content) 397 } 398 399 func testParsePostUriPubkeyReference() throws { 400 let id = Pubkey(hex: "6fec2ee6cfff779fe8560976b3d9df782b74577f0caefa7a77c0ed4c3749b5de")! 401 let parsed = parse_post_blocks(content: "this is a nostr:\(id.npub) event mention") 402 403 XCTAssertNotNil(parsed) 404 XCTAssertEqual(parsed.count, 3) 405 XCTAssertEqual(parsed[0].asText, "this is a ") 406 XCTAssertEqual(parsed[1].asMention, .any(.pubkey(id))) 407 XCTAssertEqual(parsed[2].asText, " event mention") 408 409 guard case .text(let t1) = parsed[0] else { 410 XCTAssertTrue(false) 411 return 412 } 413 XCTAssertEqual(t1, "this is a ") 414 415 guard case .text(let t2) = parsed[2] else { 416 XCTAssertTrue(false) 417 return 418 } 419 XCTAssertEqual(t2, " event mention") 420 } 421 422 func testParsePostUriReference() throws { 423 let id = NoteId(hex: "6fec2ee6cfff779fe8560976b3d9df782b74577f0caefa7a77c0ed4c3749b5de")! 424 let parsed = parse_post_blocks(content: "this is a nostr:\(id.bech32) event mention") 425 426 XCTAssertNotNil(parsed) 427 XCTAssertEqual(parsed.count, 3) 428 XCTAssertEqual(parsed[0].asText, "this is a ") 429 XCTAssertEqual(parsed[1].asMention, .any(.note(id))) 430 XCTAssertEqual(parsed[2].asText, " event mention") 431 432 guard case .text(let t1) = parsed[0] else { 433 XCTAssertTrue(false) 434 return 435 } 436 XCTAssertEqual(t1, "this is a ") 437 438 guard case .text(let t2) = parsed[2] else { 439 XCTAssertTrue(false) 440 return 441 } 442 XCTAssertEqual(t2, " event mention") 443 } 444 445 func testParseInvalidMention() throws { 446 let parsed = parse_note_content(content: .content("this is #[0] a mention",nil)).blocks 447 448 XCTAssertNotNil(parsed) 449 XCTAssertEqual(parsed.count, 3) 450 XCTAssertEqual(parsed[0].asText, "this is ") 451 XCTAssertEqual(parsed[1].asText, "#[0]") 452 XCTAssertEqual(parsed[2].asText, " a mention") 453 } 454 455 }