nip10: simplify and fix reply-to-root bugs
This removes EventRefs alltogether and uses the form we use in Damus Android. This simplifies our ThreadReply logic and fixes a reply-to-root bug Reported-by: NotBiebs <justinbieber@stemstr.app> Changelog-Fixed: Fix thread bug where a quote isn't picked up as a reply Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
@@ -26,36 +26,55 @@ final class NIP10Tests: XCTestCase {
|
||||
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
|
||||
}
|
||||
|
||||
func test_root_with_mention_nip10() {
|
||||
let root_id_hex = "a32d70d331f4bea7a859ac71d85a9b4e0c2d1fa9aaf7237a17f85a6227f52fdb"
|
||||
let root_id = NoteId(hex: root_id_hex)!
|
||||
let mention_hex = "e47b7e156acec6881c89a53f1a9e349a982024245e2c398f8a5b4973b7a89ab3"
|
||||
let mention_id = NoteId(hex: mention_hex)!
|
||||
|
||||
let tags =
|
||||
[["e", root_id_hex,"","root"],
|
||||
["e", mention_hex,"","mention"],
|
||||
["p","c4eabae1be3cf657bc1855ee05e69de9f059cb7a059227168b80b89761cbc4e0"],
|
||||
["p","604e96e099936a104883958b040b47672e0f048c98ac793f37ffe4c720279eb2"],
|
||||
["p","ffd375eb40eb486656a028edbc83825f58ff0d5c4a1ba22fe7745d284529ed08","","mention"],
|
||||
["q","e47b7e156acec6881c89a53f1a9e349a982024245e2c398f8a5b4973b7a89ab3"]
|
||||
]
|
||||
|
||||
let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
|
||||
let thread = ThreadReply(tags: note.tags)
|
||||
|
||||
XCTAssertNotNil(thread)
|
||||
guard let thread else { return }
|
||||
|
||||
XCTAssertEqual(thread.root.note_id, root_id)
|
||||
XCTAssertEqual(thread.reply.note_id, root_id)
|
||||
XCTAssertEqual(thread.mention?.ref.note_id, mention_id)
|
||||
}
|
||||
|
||||
func test_new_nip10() {
|
||||
let root_note_id_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d52"
|
||||
let direct_reply_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d51"
|
||||
let reply_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d53"
|
||||
let mention_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d54"
|
||||
|
||||
let tags = [
|
||||
["e", mention_hex, "", "mention"],
|
||||
["e", direct_reply_hex, "", "reply"],
|
||||
["e", root_note_id_hex, "", "root"],
|
||||
["e", reply_hex, "", "reply"],
|
||||
["e", "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d54", "", "mention"],
|
||||
]
|
||||
|
||||
let root_note_id = NoteId(hex: root_note_id_hex)!
|
||||
let direct_reply_id = NoteId(hex: direct_reply_hex)!
|
||||
let reply_id = NoteId(hex: reply_hex)!
|
||||
let mention_id = NoteId(hex: mention_hex)!
|
||||
|
||||
let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
|
||||
let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
|
||||
let tr = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
|
||||
}), [root_note_id])
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_direct_reply?.note_id { xs.append(note_id) }
|
||||
}), [direct_reply_id, reply_id])
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_reply?.note_id { xs.append(note_id) }
|
||||
}), [direct_reply_id, reply_id])
|
||||
XCTAssertEqual(tr?.root.note_id, root_note_id)
|
||||
XCTAssertEqual(tr?.reply.note_id, reply_id)
|
||||
XCTAssertEqual(tr?.mention?.ref.note_id, mention_id)
|
||||
}
|
||||
|
||||
func test_repost_root() {
|
||||
@@ -66,19 +85,9 @@ final class NIP10Tests: XCTestCase {
|
||||
|
||||
let mention_id = NoteId(hex: mention_hex)!
|
||||
let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
|
||||
let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
|
||||
let tr = note.thread_reply()
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
|
||||
}), [])
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_direct_reply?.note_id { xs.append(note_id) }
|
||||
}), [])
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_reply?.note_id { xs.append(note_id) }
|
||||
}), [])
|
||||
XCTAssertNil(tr)
|
||||
}
|
||||
|
||||
func test_direct_reply_old_nip10() {
|
||||
@@ -90,19 +99,14 @@ final class NIP10Tests: XCTestCase {
|
||||
let root_note_id = NoteId(hex: root_note_id_hex)!
|
||||
|
||||
let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
|
||||
let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
|
||||
let tr = note.thread_reply()
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
|
||||
}), [root_note_id])
|
||||
XCTAssertNotNil(tr)
|
||||
guard let tr else { return }
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_direct_reply?.note_id { xs.append(note_id) }
|
||||
}), [root_note_id])
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_reply?.note_id { xs.append(note_id) }
|
||||
}), [root_note_id])
|
||||
XCTAssertEqual(tr.root.note_id, root_note_id)
|
||||
XCTAssertEqual(tr.reply.note_id, root_note_id)
|
||||
XCTAssertEqual(tr.is_reply_to_root, true)
|
||||
}
|
||||
|
||||
func test_direct_reply_new_nip10() {
|
||||
@@ -114,24 +118,14 @@ final class NIP10Tests: XCTestCase {
|
||||
let root_note_id = NoteId(hex: root_note_id_hex)!
|
||||
|
||||
let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
|
||||
let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
|
||||
let tr = note.thread_reply()
|
||||
XCTAssertNotNil(tr)
|
||||
guard let tr else { return }
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
|
||||
}), [root_note_id])
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_direct_reply?.note_id { xs.append(note_id) }
|
||||
}), [root_note_id])
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_reply?.note_id { xs.append(note_id) }
|
||||
}), [root_note_id])
|
||||
|
||||
let nip10 = note.thread_reply(test_keypair)!
|
||||
XCTAssertEqual(nip10.is_reply_to_root, true)
|
||||
XCTAssertEqual(nip10.root.note_id, root_note_id)
|
||||
XCTAssertEqual(nip10.reply!.note_id, root_note_id)
|
||||
XCTAssertEqual(tr.root.note_id, root_note_id)
|
||||
XCTAssertEqual(tr.reply.note_id, root_note_id)
|
||||
XCTAssertNil(tr.mention)
|
||||
XCTAssertEqual(tr.is_reply_to_root, true)
|
||||
}
|
||||
|
||||
// seen in the wild by the gleasonator
|
||||
@@ -143,13 +137,14 @@ final class NIP10Tests: XCTestCase {
|
||||
|
||||
let root_note_id = NoteId(hex: root_note_id_hex)!
|
||||
let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
|
||||
let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
|
||||
let thread_reply = ThreadReply(event_refs: refs)!
|
||||
let tr = note.thread_reply()
|
||||
XCTAssertNotNil(tr)
|
||||
guard let tr else { return }
|
||||
|
||||
XCTAssertEqual(thread_reply.mention, nil)
|
||||
XCTAssertEqual(thread_reply.root.note_id, root_note_id)
|
||||
XCTAssertEqual(thread_reply.reply!.note_id, root_note_id)
|
||||
XCTAssertEqual(thread_reply.is_reply_to_root, true)
|
||||
XCTAssertNil(tr.mention)
|
||||
XCTAssertEqual(tr.root.note_id, root_note_id)
|
||||
XCTAssertEqual(tr.reply.note_id, root_note_id)
|
||||
XCTAssertEqual(tr.is_reply_to_root, true)
|
||||
}
|
||||
|
||||
func test_marker_reply() {
|
||||
@@ -193,7 +188,6 @@ final class NIP10Tests: XCTestCase {
|
||||
[
|
||||
["e", root_hex, "wss://nostr.mutinywallet.com/", "root"],
|
||||
["e", replying_to_hex, "", "reply"],
|
||||
//["e", last_reply_hex, "wss://relay.nostrplebs.com"],
|
||||
["p", "5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e"],
|
||||
["p", "6e75f7972397ca3295e0f4ca0fbc6eb9cc79be85bafdd56bd378220ca8eee74e"],
|
||||
])
|
||||
@@ -218,16 +212,13 @@ final class NIP10Tests: XCTestCase {
|
||||
let reply_id = NoteId(hex: reply_hex)!
|
||||
|
||||
let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
|
||||
let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
|
||||
}), [root_note_id])
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_reply?.note_id { xs.append(note_id) }
|
||||
}), [reply_id])
|
||||
let tr = note.thread_reply()
|
||||
XCTAssertNotNil(tr)
|
||||
guard let tr else { return }
|
||||
|
||||
XCTAssertEqual(tr.root.note_id, root_note_id)
|
||||
XCTAssertEqual(tr.reply.note_id, reply_id)
|
||||
XCTAssertEqual(tr.is_reply_to_root, false)
|
||||
}
|
||||
|
||||
func test_deprecated_nip10() {
|
||||
@@ -245,19 +236,13 @@ final class NIP10Tests: XCTestCase {
|
||||
let reply_id = NoteId(hex: reply_hex)!
|
||||
|
||||
let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
|
||||
let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
|
||||
let tr = note.thread_reply()
|
||||
XCTAssertNotNil(tr)
|
||||
guard let tr else { return }
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
|
||||
}), [root_note_id])
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_direct_reply?.note_id { xs.append(note_id) }
|
||||
}), [direct_reply_id, reply_id])
|
||||
|
||||
XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
|
||||
if let note_id = r.is_reply?.note_id { xs.append(note_id) }
|
||||
}), [direct_reply_id, reply_id])
|
||||
XCTAssertEqual(tr.root.note_id, root_note_id)
|
||||
XCTAssertEqual(tr.reply.note_id, reply_id)
|
||||
XCTAssertEqual(tr.is_reply_to_root, false)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,24 +18,6 @@ class ReplyTests: XCTestCase {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testMentionIsntReply() throws {
|
||||
let evid = NoteId(hex: "4090a9017a2beac3f17795d1aafb80d9f2b9eda97e4738501082ed5c927be014")!
|
||||
let content = "this is #[0] a mention"
|
||||
let tags = [evid.tag]
|
||||
let ev = NostrEvent(content: content, keypair: test_keypair, tags: tags)!
|
||||
let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks
|
||||
let event_refs = interpret_event_refs(blocks: blocks, tags: ev.tags)
|
||||
|
||||
XCTAssertEqual(event_refs.count, 1)
|
||||
|
||||
let ref = event_refs[0]
|
||||
|
||||
XCTAssertNil(ref.is_reply)
|
||||
XCTAssertNil(ref.is_thread_id)
|
||||
XCTAssertNil(ref.is_direct_reply)
|
||||
XCTAssertEqual(ref.is_mention, .some(.init(note_id: evid)))
|
||||
}
|
||||
|
||||
func testAtAtEnd() {
|
||||
let content = "what @"
|
||||
let blocks = parse_post_blocks(content: content)
|
||||
@@ -70,49 +52,20 @@ class ReplyTests: XCTestCase {
|
||||
XCTAssertEqual(blocks[2].asHashtag, "nope")
|
||||
}
|
||||
|
||||
func testRootReplyWithMention() throws {
|
||||
let content = "this is #[1] a mention"
|
||||
let thread_id = NoteId(hex: "c75e5cbafbefd5de2275f831c2a2386ea05ec5e5a78a5ccf60d467582db48945")!
|
||||
let mentioned_id = NoteId(hex: "5a534797e8cd3b9f4c1cf63e20e48bd0e8bd7f8c4d6353fbd576df000f6f54d3")!
|
||||
let tags = [thread_id.tag, mentioned_id.tag]
|
||||
let ev = NostrEvent(content: content, keypair: test_keypair, tags: tags)!
|
||||
let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks
|
||||
let event_refs = interpret_event_refs(blocks: blocks, tags: ev.tags)
|
||||
|
||||
XCTAssertEqual(event_refs.count, 2)
|
||||
XCTAssertNotNil(event_refs[0].is_reply)
|
||||
XCTAssertNotNil(event_refs[0].is_thread_id)
|
||||
XCTAssertNotNil(event_refs[0].is_reply)
|
||||
XCTAssertNotNil(event_refs[0].is_direct_reply)
|
||||
XCTAssertEqual(event_refs[0].is_reply, .some(NoteRef(note_id: thread_id)))
|
||||
XCTAssertEqual(event_refs[0].is_thread_id, .some(NoteRef(note_id: thread_id)))
|
||||
XCTAssertNotNil(event_refs[1].is_mention)
|
||||
XCTAssertEqual(event_refs[1].is_mention, .some(NoteRef(note_id: mentioned_id)))
|
||||
}
|
||||
|
||||
func testEmptyMention() throws {
|
||||
let content = "this is some & content"
|
||||
let ev = NostrEvent(content: content, keypair: test_keypair, tags: [])!
|
||||
let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks
|
||||
let post_blocks = parse_post_blocks(content: content)
|
||||
let post_tags = make_post_tags(post_blocks: post_blocks, tags: [])
|
||||
let event_refs = interpret_event_refs(blocks: blocks, tags: ev.tags)
|
||||
let tr = interpret_event_refs(tags: ev.tags)
|
||||
|
||||
XCTAssertEqual(event_refs.count, 0)
|
||||
XCTAssertNil(tr)
|
||||
XCTAssertEqual(post_tags.blocks.count, 1)
|
||||
XCTAssertEqual(post_tags.tags.count, 0)
|
||||
XCTAssertEqual(post_blocks.count, 1)
|
||||
}
|
||||
|
||||
func testManyMentions() throws {
|
||||
let content = "#[10]"
|
||||
let tags: [[String]] = [[],[],[],[],[],[],[],[],[],[],["p", "3e999f94e2cb34ef44a64b351141ac4e51b5121b2d31aed4a6c84602a1144692"]]
|
||||
let ev = NostrEvent(content: content, keypair: test_keypair, tags: tags)!
|
||||
let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks
|
||||
let mentions = blocks.filter { $0.asMention != nil }
|
||||
XCTAssertEqual(mentions.count, 1)
|
||||
}
|
||||
|
||||
func testNewlineMentions() throws {
|
||||
let bech32_pk = "npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s"
|
||||
let pk = bech32_pubkey_decode(bech32_pk)!
|
||||
@@ -145,17 +98,12 @@ class ReplyTests: XCTestCase {
|
||||
let reply_id = NoteId(hex: "80093e9bdb495728f54cda2bad4aed096877189552b3d41264e73b9a9595be22")!
|
||||
let tags = [thread_id.tag, reply_id.tag]
|
||||
let ev = NostrEvent(content: content, keypair: test_keypair, tags: tags)!
|
||||
let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks
|
||||
let event_refs = interpret_event_refs(blocks: blocks, tags: ev.tags)
|
||||
let tr = interpret_event_refs(tags: ev.tags)
|
||||
XCTAssertNotNil(tr)
|
||||
guard let tr else { return }
|
||||
|
||||
XCTAssertEqual(event_refs.count, 2)
|
||||
let r1 = event_refs[0]
|
||||
let r2 = event_refs[1]
|
||||
|
||||
XCTAssertEqual(r1.is_thread_id, .some(.note_id(thread_id)))
|
||||
XCTAssertEqual(r2.is_reply, .some(.note_id(reply_id)))
|
||||
XCTAssertEqual(r2.is_direct_reply, .some(.note_id(reply_id)))
|
||||
XCTAssertNil(r1.is_direct_reply)
|
||||
XCTAssertEqual(tr.root.note_id, thread_id)
|
||||
XCTAssertEqual(tr.reply.note_id, reply_id)
|
||||
}
|
||||
|
||||
func testRootReply() throws {
|
||||
@@ -163,16 +111,14 @@ class ReplyTests: XCTestCase {
|
||||
let thread_id = NoteId(hex: "53f60f5114c06f069ffe9da2bc033e533d09cae44d37a8462154a663771a4ce6")!
|
||||
let tags = [thread_id.tag]
|
||||
let ev = NostrEvent(content: content, keypair: test_keypair, tags: tags)!
|
||||
let blocks = parse_note_content(content: .content(ev.content,nil)).blocks
|
||||
let event_refs = interpret_event_refs(blocks: blocks, tags: ev.tags)
|
||||
let tr = interpret_event_refs(tags: ev.tags)
|
||||
|
||||
XCTAssertEqual(event_refs.count, 1)
|
||||
let r = event_refs[0]
|
||||
|
||||
XCTAssertEqual(r.is_direct_reply, .some(.note_id(thread_id)))
|
||||
XCTAssertEqual(r.is_reply, .some(.note_id(thread_id)))
|
||||
XCTAssertEqual(r.is_thread_id, .some(.note_id(thread_id)))
|
||||
XCTAssertNil(r.is_mention)
|
||||
XCTAssertNotNil(tr)
|
||||
guard let tr else { return }
|
||||
|
||||
XCTAssertEqual(tr.root.note_id, thread_id)
|
||||
XCTAssertEqual(tr.reply.note_id, thread_id)
|
||||
XCTAssertNil(tr.mention)
|
||||
}
|
||||
|
||||
func testAdjacentComposedMention() throws {
|
||||
@@ -262,28 +208,6 @@ class ReplyTests: XCTestCase {
|
||||
XCTAssertEqual(new_post.string, "cc @jb55 ")
|
||||
}
|
||||
|
||||
func testNoReply() throws {
|
||||
let content = "this is a #[0] reply"
|
||||
let ev = NostrEvent(content: content, keypair: test_keypair, tags: [])!
|
||||
let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks
|
||||
let event_refs = interpret_event_refs(blocks: blocks, tags:ev.tags)
|
||||
|
||||
XCTAssertEqual(event_refs.count, 0)
|
||||
}
|
||||
|
||||
func testParseMention() throws {
|
||||
let note_id = NoteId(hex: "53f60f5114c06f069ffe9da2bc033e533d09cae44d37a8462154a663771a4ce6")!
|
||||
let tags = [note_id.tag]
|
||||
let ev = NostrEvent(content: "this is #[0] a mention", keypair: test_keypair, tags: tags)!
|
||||
let parsed = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks
|
||||
|
||||
XCTAssertNotNil(parsed)
|
||||
XCTAssertEqual(parsed.count, 3)
|
||||
XCTAssertEqual(parsed[0].asText, "this is ")
|
||||
XCTAssertNotNil(parsed[1].asMention)
|
||||
XCTAssertEqual(parsed[2].asText, " a mention")
|
||||
}
|
||||
|
||||
func testEmptyPostReference() throws {
|
||||
let parsed = parse_post_blocks(content: "")
|
||||
XCTAssertEqual(parsed.count, 0)
|
||||
@@ -442,14 +366,4 @@ class ReplyTests: XCTestCase {
|
||||
XCTAssertEqual(t2, " event mention")
|
||||
}
|
||||
|
||||
func testParseInvalidMention() throws {
|
||||
let parsed = parse_note_content(content: .content("this is #[0] a mention",nil)).blocks
|
||||
|
||||
XCTAssertNotNil(parsed)
|
||||
XCTAssertEqual(parsed.count, 3)
|
||||
XCTAssertEqual(parsed[0].asText, "this is ")
|
||||
XCTAssertEqual(parsed[1].asText, "#[0]")
|
||||
XCTAssertEqual(parsed[2].asText, " a mention")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user