From 39bde22639307fc9a2509c7f3f974bd9d62c350f Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Fri, 7 Mar 2025 09:43:42 -0300 Subject: [PATCH] sonic ast visitor approach. now we're getting faster. BenchmarkParseMessage/relay/jsonstdlib-4 206 8630635 ns/op BenchmarkParseMessage/relay/easyjson-4 278 4311793 ns/op BenchmarkParseMessage/relay/simdjson-4 422 2943387 ns/op BenchmarkParseMessage/relay/sonic-4 849 1576884 ns/op BenchmarkParseMessage/client/jsonstdlib-4 196 6140585 ns/op BenchmarkParseMessage/client/easyjson-4 385 2826706 ns/op BenchmarkParseMessage/client/simdjson-4 405 2628675 ns/op BenchmarkParseMessage/client/sonic-4 552 2413731 ns/op --- envelopes_benchmark_test.go | 3 +- envelopes_sonic.go | 596 ++++++++++++++++++++++++++---------- envelopes_test.go | 16 +- 3 files changed, 450 insertions(+), 165 deletions(-) diff --git a/envelopes_benchmark_test.go b/envelopes_benchmark_test.go index 797e141..dbd2a90 100644 --- a/envelopes_benchmark_test.go +++ b/envelopes_benchmark_test.go @@ -42,10 +42,9 @@ func BenchmarkParseMessage(b *testing.B) { }) b.Run("sonic", func(b *testing.B) { - smp := SonicMessageParser{} for i := 0; i < b.N; i++ { for _, msg := range messages { - _, _ = smp.ParseMessage(msg) + _, _ = ParseMessageSonic(msg) } } }) diff --git a/envelopes_sonic.go b/envelopes_sonic.go index 013bc35..3956bb8 100644 --- a/envelopes_sonic.go +++ b/envelopes_sonic.go @@ -2,184 +2,472 @@ package nostr import ( "encoding/hex" + stdlibjson "encoding/json" "fmt" "github.com/bytedance/sonic/ast" ) -type SonicMessageParser struct{} +type sonicParserPosition int -func (smp *SonicMessageParser) ParseMessage(message []byte) (Envelope, error) { - var err error +const ( + inEnvelope sonicParserPosition = iota - tlarr, _ := ast.NewParser(string(message)).Parse() - label, _ := tlarr.Index(0).StrictString() + inEvent + inReq + inOk + inEose + inCount + inAuth + inClose + inClosed + inNotice - var v Envelope - switch label { - case "EVENT": - env := &EventEnvelope{} - sndN := tlarr.Index(1) - var evtN *ast.Node - switch sndN.TypeSafe() { - case ast.V_STRING: - subId, _ := sndN.StrictString() - env.SubscriptionID = &subId - evtN = tlarr.Index(2) - case ast.V_OBJECT: - evtN = sndN - } - err = eventFromSonicAst(&env.Event, evtN) - v = env - case "REQ": - env := &ReqEnvelope{} - nodes, _ := tlarr.ArrayUseNode() - env.SubscriptionID, _ = nodes[1].StrictString() - env.Filters = make(Filters, len(nodes)-2) - for i, node := range nodes[2:] { - err = filterFromSonicAst(&env.Filters[i], &node) - } - v = env - case "COUNT": - env := &CountEnvelope{} - env.SubscriptionID, _ = tlarr.Index(1).StrictString() - trdN := tlarr.Index(2) - if countN := trdN.Get("count"); countN.Exists() { - count, _ := countN.Int64() - env.Count = &count - hll, _ := trdN.Get("hll").StrictString() - if len(hll) == 512 { - env.HyperLogLog, _ = hex.DecodeString(hll) - } - } else { - err = filterFromSonicAst(&env.Filter, trdN) - } - v = env - case "NOTICE": - notice, _ := tlarr.Index(1).StrictString() - env := NoticeEnvelope(notice) - v = &env - case "EOSE": - subId, _ := tlarr.Index(1).StrictString() - env := EOSEEnvelope(subId) - v = &env - case "OK": - env := &OKEnvelope{} - env.EventID, _ = tlarr.Index(1).StrictString() - env.OK, _ = tlarr.Index(2).Bool() - env.Reason, _ = tlarr.Index(3).StrictString() - v = env - case "AUTH": - env := &AuthEnvelope{} - sndN := tlarr.Index(1) - switch sndN.TypeSafe() { - case ast.V_STRING: - challenge, _ := sndN.StrictString() - env.Challenge = &challenge - case ast.V_OBJECT: - err = eventFromSonicAst(&env.Event, sndN) - } - v = env - case "CLOSED": - env := &ClosedEnvelope{} - env.SubscriptionID, _ = tlarr.Index(1).StrictString() - env.Reason, _ = tlarr.Index(2).StrictString() - v = env - case "CLOSE": - reason, _ := tlarr.Index(1).StrictString() - env := CloseEnvelope(reason) - v = &env + inFilterObject + inEventObject + inCountObject + + inSince + inLimit + inUntil + inIds + inAuthors + inKinds + inSearch + inAFilterTag + + inId + inCreatedAt + inKind + inContent + inPubkey + inSig + inTags + inAnEventTag +) + +func (spp sonicParserPosition) String() string { + switch spp { + case inEnvelope: + return "inEnvelope" + case inEvent: + return "inEvent" + case inReq: + return "inReq" + case inOk: + return "inOk" + case inEose: + return "inEose" + case inCount: + return "inCount" + case inAuth: + return "inAuth" + case inClose: + return "inClose" + case inClosed: + return "inClosed" + case inNotice: + return "inNotice" + case inFilterObject: + return "inFilterObject" + case inEventObject: + return "inEventObject" + case inCountObject: + return "inCountObject" + case inSince: + return "inSince" + case inLimit: + return "inLimit" + case inUntil: + return "inUntil" + case inIds: + return "inIds" + case inAuthors: + return "inAuthors" + case inKinds: + return "inKinds" + case inAFilterTag: + return "inAFilterTag" + case inId: + return "inId" + case inCreatedAt: + return "inCreatedAt" + case inKind: + return "inKind" + case inContent: + return "inContent" + case inPubkey: + return "inPubkey" + case inSig: + return "inSig" + case inTags: + return "inTags" + case inAnEventTag: + return "inAnEventTag" default: - return nil, UnknownLabel + return "" } - - return v, err } -func eventFromSonicAst(evt *Event, node *ast.Node) error { - evt.ID, _ = node.Get("id").StrictString() - evt.PubKey, _ = node.Get("pubkey").StrictString() - evt.Content, _ = node.Get("content").StrictString() - evt.Sig, _ = node.Get("sig").StrictString() - kind, _ := node.Get("kind").Int64() - evt.Kind = int(kind) - createdAt, _ := node.Get("created_at").Int64() - evt.CreatedAt = Timestamp(createdAt) - tagsN, err := node.Get("tags").ArrayUseNode() - if err != nil { - return fmt.Errorf("invalid tags: %w", err) +type SonicMessageParser struct { + event *EventEnvelope + req *ReqEnvelope + ok *OKEnvelope + eose *EOSEEnvelope + count *CountEnvelope + auth *AuthEnvelope + close *CloseEnvelope + closed *ClosedEnvelope + notice *NoticeEnvelope + + whereWeAre sonicParserPosition + + currentEvent *Event + currentEventTag Tag + + currentFilter *Filter + currentFilterTagList []string + currentFilterTagName string + + mainEnvelope Envelope +} + +func (smp *SonicMessageParser) OnArrayBegin(capacity int) error { + // fmt.Println("***", "OnArrayBegin", "==", smp.whereWeAre) + + switch smp.whereWeAre { + case inTags: + if smp.currentEvent.Tags == nil { + smp.currentEvent.Tags = make(Tags, 0, 10) + smp.currentEventTag = make(Tag, 0, 20) + } else { + smp.whereWeAre = inAnEventTag + } + case inAFilterTag: + // we have already created this } - evt.Tags = make(Tags, len(tagsN)) - for i, tagN := range tagsN { - itemsN, err := tagN.ArrayUseNode() - if err != nil { - return fmt.Errorf("invalid tag: %w", err) - } - tag := make(Tag, len(itemsN)) - for j, itemN := range itemsN { - tag[j], _ = itemN.StrictString() - } + return nil +} - evt.Tags[i] = tag +func (smp *SonicMessageParser) OnArrayEnd() error { + // fmt.Println("***", "OnArrayEnd", "==", smp.whereWeAre) + + switch smp.whereWeAre { + // envelopes + case inEvent: + smp.mainEnvelope = smp.event + case inReq: + smp.mainEnvelope = smp.req + case inOk: + smp.mainEnvelope = smp.ok + case inEose: + smp.mainEnvelope = smp.eose + case inCount: + smp.mainEnvelope = smp.count + case inAuth: + smp.mainEnvelope = smp.auth + case inClose: + smp.mainEnvelope = smp.close + case inClosed: + smp.mainEnvelope = smp.closed + case inNotice: + smp.mainEnvelope = smp.notice + + // filter object properties + case inIds, inAuthors, inKinds, inSearch: + smp.whereWeAre = inFilterObject + case inAFilterTag: + smp.currentFilter.Tags[smp.currentFilterTagName] = smp.currentFilterTagList + // reuse the same underlying slice because we know nothing else will be appended to it + smp.currentFilterTagList = smp.currentFilterTagList[len(smp.currentFilterTagList):] + smp.whereWeAre = inFilterObject + + // event object properties + case inAnEventTag: + smp.currentEvent.Tags = append(smp.currentEvent.Tags, smp.currentEventTag) + // reuse the same underlying slice because we know nothing else will be appended to it + smp.currentEventTag = smp.currentEventTag[len(smp.currentEventTag):] + smp.whereWeAre = inTags + case inTags: + smp.whereWeAre = inEventObject + + default: + return fmt.Errorf("unexpected array end at %v", smp.whereWeAre) } return nil } -func filterFromSonicAst(filter *Filter, node *ast.Node) error { - var err error +func (smp *SonicMessageParser) OnObjectBegin(capacity int) error { + // fmt.Println("***", "OnObjectBegin", "==", smp.whereWeAre) - node.ForEach(func(path ast.Sequence, node *ast.Node) bool { - switch *path.Key { - case "limit": - limit, _ := node.Int64() - filter.Limit = int(limit) - filter.LimitZero = filter.Limit == 0 - case "since": - since, _ := node.Int64() - filter.Since = (*Timestamp)(&since) - case "until": - until, _ := node.Int64() - filter.Until = (*Timestamp)(&until) - case "search": - filter.Search, _ = node.StrictString() - case "ids": - idsN, _ := node.ArrayUseNode() - filter.IDs = make([]string, len(idsN)) - for i, idN := range idsN { - filter.IDs[i], _ = idN.StrictString() - } - case "authors": - authorsN, _ := node.ArrayUseNode() - filter.Authors = make([]string, len(authorsN)) - for i, authorN := range authorsN { - filter.Authors[i], _ = authorN.StrictString() - } - case "kinds": - kindsN, _ := node.ArrayUseNode() - filter.Kinds = make([]int, len(kindsN)) - for i, kindN := range kindsN { - kind, _ := kindN.Int64() - filter.Kinds[i] = int(kind) - } + switch smp.whereWeAre { + case inEvent: + smp.whereWeAre = inEventObject + smp.currentEvent = &Event{} + case inAuth: + smp.whereWeAre = inEventObject + smp.currentEvent = &Event{} + case inReq: + smp.whereWeAre = inFilterObject + smp.currentFilter = &Filter{} + case inCount: + // set this temporarily, we will switch to a filterObject if we see "count" or "hll" + smp.whereWeAre = inFilterObject + smp.currentFilter = &Filter{} + default: + return fmt.Errorf("unexpected object begin at %v", smp.whereWeAre) + } + + return nil +} + +func (smp *SonicMessageParser) OnObjectKey(key string) error { + // fmt.Println("***", "OnObjectKey", key, "==", smp.whereWeAre) + + switch smp.whereWeAre { + case inEventObject: + switch key { + case "id": + smp.whereWeAre = inId + case "sig": + smp.whereWeAre = inSig + case "pubkey": + smp.whereWeAre = inPubkey + case "content": + smp.whereWeAre = inContent + case "created_at": + smp.whereWeAre = inCreatedAt + case "kind": + smp.whereWeAre = inKind + case "tags": + smp.whereWeAre = inTags default: - if len(*path.Key) > 1 && (*path.Key)[0] == '#' { - if filter.Tags == nil { - filter.Tags = make(TagMap, 2) + return fmt.Errorf("unexpected event attr %s", key) + } + case inFilterObject: + switch key { + case "limit": + smp.whereWeAre = inLimit + case "since": + smp.whereWeAre = inSince + case "until": + smp.whereWeAre = inUntil + case "ids": + smp.whereWeAre = inIds + smp.currentFilter.IDs = make([]string, 0, 25) + case "authors": + smp.whereWeAre = inAuthors + smp.currentFilter.Authors = make([]string, 0, 25) + case "kinds": + smp.whereWeAre = inKinds + smp.currentFilter.IDs = make([]string, 0, 12) + case "search": + smp.whereWeAre = inSearch + case "count", "hll": + // oops, switch to a countObject + smp.whereWeAre = inCountObject + default: + if len(key) > 1 && key[0] == '#' { + if smp.currentFilter.Tags == nil { + smp.currentFilter.Tags = make(TagMap, 1) + smp.currentFilterTagList = make([]string, 0, 25) } - tagsN, _ := node.ArrayUseNode() - tags := make([]string, len(tagsN)) - for i, authorN := range tagsN { - tags[i], _ = authorN.StrictString() - } - filter.Tags[(*path.Key)[1:]] = tags + smp.whereWeAre = inAFilterTag + smp.currentFilterTagName = key[1:] } else { - err = fmt.Errorf("unexpected field '%s'", *path.Key) - return false + return fmt.Errorf("unexpected filter attr %s", key) } } - return true - }) + case inCountObject: + // we'll judge by the shape of the value so ignore this + default: + return fmt.Errorf("unexpected object key %s at %s", key, smp.whereWeAre) + } - return err + return nil +} + +func (smp *SonicMessageParser) OnObjectEnd() error { + // fmt.Println("***", "OnObjectEnd", "==", smp.whereWeAre) + + switch smp.whereWeAre { + case inEventObject: + if smp.event != nil { + smp.event.Event = *smp.currentEvent + smp.whereWeAre = inEvent + } else { + smp.auth.Event = *smp.currentEvent + smp.whereWeAre = inAuth + } + case inFilterObject: + if smp.req != nil { + smp.req.Filters = append(smp.req.Filters, *smp.currentFilter) + smp.whereWeAre = inReq + } else { + smp.count.Filter = *smp.currentFilter + smp.whereWeAre = inCount + } + case inCountObject: + smp.whereWeAre = inCount + default: + return fmt.Errorf("unexpected object end at %s", smp.whereWeAre) + } + + return nil +} + +func (smp *SonicMessageParser) OnString(v string) error { + // fmt.Println("***", "OnString", v, "==", smp.whereWeAre) + + switch smp.whereWeAre { + case inEnvelope: + switch v { + case "EVENT": + smp.event = &EventEnvelope{} + smp.whereWeAre = inEvent + case "REQ": + smp.req = &ReqEnvelope{Filters: make(Filters, 0, 1)} + smp.whereWeAre = inReq + case "OK": + smp.ok = &OKEnvelope{} + smp.whereWeAre = inOk + case "EOSE": + smp.whereWeAre = inEose + case "COUNT": + smp.count = &CountEnvelope{} + smp.whereWeAre = inCount + case "AUTH": + smp.auth = &AuthEnvelope{} + smp.whereWeAre = inAuth + case "CLOSE": + smp.whereWeAre = inClose + case "CLOSED": + smp.closed = &ClosedEnvelope{} + smp.whereWeAre = inClosed + case "NOTICE": + smp.whereWeAre = inNotice + } + + // in an envelope + case inEvent: + smp.event.SubscriptionID = &v + case inReq: + smp.req.SubscriptionID = v + case inOk: + if smp.ok.EventID == "" { + smp.ok.EventID = v + } else { + smp.ok.Reason = v + } + case inEose: + smp.eose = (*EOSEEnvelope)(&v) + case inCount: + smp.count.SubscriptionID = v + case inAuth: + smp.auth.Challenge = &v + case inClose: + smp.close = (*CloseEnvelope)(&v) + case inClosed: + if smp.closed.SubscriptionID == "" { + smp.closed.SubscriptionID = v + } else { + smp.closed.Reason = v + } + case inNotice: + smp.notice = (*NoticeEnvelope)(&v) + + // filter object properties + case inIds: + smp.currentFilter.IDs = append(smp.currentFilter.IDs, v) + case inAuthors: + smp.currentFilter.Authors = append(smp.currentFilter.Authors, v) + case inSearch: + smp.currentFilter.Search = v + smp.whereWeAre = inFilterObject + case inAFilterTag: + smp.currentFilterTagList = append(smp.currentFilterTagList, v) + + // id object properties + case inId: + smp.currentEvent.ID = v + smp.whereWeAre = inEventObject + case inContent: + smp.currentEvent.Content = v + smp.whereWeAre = inEventObject + case inPubkey: + smp.currentEvent.PubKey = v + smp.whereWeAre = inEventObject + case inSig: + smp.currentEvent.Sig = v + smp.whereWeAre = inEventObject + case inAnEventTag: + smp.currentEventTag = append(smp.currentEventTag, v) + + // count object properties + case inCountObject: + smp.count.HyperLogLog, _ = hex.DecodeString(v) + + default: + return fmt.Errorf("unexpected string %s at %v", v, smp.whereWeAre) + } + return nil +} + +func (smp *SonicMessageParser) OnInt64(v int64, _ stdlibjson.Number) error { + // fmt.Println("***", "OnInt64", v, "==", smp.whereWeAre) + + switch smp.whereWeAre { + // event object + case inCreatedAt: + smp.currentEvent.CreatedAt = Timestamp(v) + smp.whereWeAre = inEventObject + case inKind: + smp.currentEvent.Kind = int(v) + smp.whereWeAre = inEventObject + + // filter object + case inLimit: + smp.currentFilter.Limit = int(v) + smp.currentFilter.LimitZero = v == 0 + case inSince: + smp.currentFilter.Since = (*Timestamp)(&v) + case inUntil: + smp.currentFilter.Until = (*Timestamp)(&v) + case inKinds: + smp.currentFilter.Kinds = append(smp.currentFilter.Kinds, int(v)) + + // count object + case inCountObject: + smp.count.Count = &v + } + return nil +} + +func (smp *SonicMessageParser) OnBool(v bool) error { + // fmt.Println("***", "OnBool", v, "==", smp.whereWeAre) + + if smp.whereWeAre == inOk { + smp.ok.OK = v + return nil + } else { + return fmt.Errorf("unexpected boolean") + } +} + +func (_ SonicMessageParser) OnNull() error { + return fmt.Errorf("null shouldn't be anywhere in a message") +} + +func (_ SonicMessageParser) OnFloat64(v float64, n stdlibjson.Number) error { + return fmt.Errorf("float shouldn't be anywhere in a message") +} + +func ParseMessageSonic(message []byte) (Envelope, error) { + smp := &SonicMessageParser{} + smp.whereWeAre = inEnvelope + + err := ast.Preorder(string(message), smp, nil) + + return smp.mainEnvelope, err } diff --git a/envelopes_test.go b/envelopes_test.go index 6bb5928..1c2e2ed 100644 --- a/envelopes_test.go +++ b/envelopes_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/minio/simdjson-go" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -41,8 +40,8 @@ func TestParseMessage(t *testing.T) { }, { Name: "EVENT envelope with tags", - Message: []byte(`["EVENT",{"kind":3,"id":"9e662bdd7d8abc40b5b15ee1ff5e9320efc87e9274d8d440c58e6eed2dddfbe2","pubkey":"373ebe3d45ec91977296a178d9f19f326c70631d2a1b0bbba5c5ecc2eb53b9e7","created_at":1644844224,"tags":[["p","3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"],["p","75fc5ac2487363293bd27fb0d14fb966477d0f1dbc6361d37806a6a740eda91e"],["p","46d0dfd3a724a302ca9175163bdf788f3606b3fd1bb12d5fe055d1e418cb60ea"]],"content":"{\"wss://nostr-pub.wellorder.net\":{\"read\":true,\"write\":true},\"wss://nostr.bitcoiner.social\":{\"read\":false,\"write\":true},\"wss://expensive-relay.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relayer.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relay.bitid.nz\":{\"read\":true,\"write\":true},\"wss://nostr.rocks\":{\"read\":true,\"write\":true}}","sig":"811355d3484d375df47581cb5d66bed05002c2978894098304f20b595e571b7e01b2efd906c5650080ffe49cf1c62b36715698e9d88b9e8be43029a2f3fa66be"}]`), - ExpectedEnvelope: &EventEnvelope{Event: Event{Kind: 3, ID: "9e662bdd7d8abc40b5b15ee1ff5e9320efc87e9274d8d440c58e6eed2dddfbe2", PubKey: "373ebe3d45ec91977296a178d9f19f326c70631d2a1b0bbba5c5ecc2eb53b9e7", CreatedAt: 1644844224, Tags: Tags{Tag{"p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"}, Tag{"p", "75fc5ac2487363293bd27fb0d14fb966477d0f1dbc6361d37806a6a740eda91e"}, Tag{"p", "46d0dfd3a724a302ca9175163bdf788f3606b3fd1bb12d5fe055d1e418cb60ea"}}, Content: "{\"wss://nostr-pub.wellorder.net\":{\"read\":true,\"write\":true},\"wss://nostr.bitcoiner.social\":{\"read\":false,\"write\":true},\"wss://expensive-relay.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relayer.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relay.bitid.nz\":{\"read\":true,\"write\":true},\"wss://nostr.rocks\":{\"read\":true,\"write\":true}}", Sig: "811355d3484d375df47581cb5d66bed05002c2978894098304f20b595e571b7e01b2efd906c5650080ffe49cf1c62b36715698e9d88b9e8be43029a2f3fa66be"}}, + Message: []byte(`["EVENT",{"kind":3,"id":"9e662bdd7d8abc40b5b15ee1ff5e9320efc87e9274d8d440c58e6eed2dddfbe2","pubkey":"373ebe3d45ec91977296a178d9f19f326c70631d2a1b0bbba5c5ecc2eb53b9e7","created_at":1644844224,"tags":[["p","3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"],["e","75fc5ac2487363293bd27fb0d14fb966477d0f1dbc6361d37806a6a740eda91e"],["p","46d0dfd3a724a302ca9175163bdf788f3606b3fd1bb12d5fe055d1e418cb60ea"]],"content":"{\"wss://nostr-pub.wellorder.net\":{\"read\":true,\"write\":true},\"wss://nostr.bitcoiner.social\":{\"read\":false,\"write\":true},\"wss://expensive-relay.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relayer.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relay.bitid.nz\":{\"read\":true,\"write\":true},\"wss://nostr.rocks\":{\"read\":true,\"write\":true}}","sig":"811355d3484d375df47581cb5d66bed05002c2978894098304f20b595e571b7e01b2efd906c5650080ffe49cf1c62b36715698e9d88b9e8be43029a2f3fa66be"}]`), + ExpectedEnvelope: &EventEnvelope{Event: Event{Kind: 3, ID: "9e662bdd7d8abc40b5b15ee1ff5e9320efc87e9274d8d440c58e6eed2dddfbe2", PubKey: "373ebe3d45ec91977296a178d9f19f326c70631d2a1b0bbba5c5ecc2eb53b9e7", CreatedAt: 1644844224, Tags: Tags{Tag{"p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"}, Tag{"e", "75fc5ac2487363293bd27fb0d14fb966477d0f1dbc6361d37806a6a740eda91e"}, Tag{"p", "46d0dfd3a724a302ca9175163bdf788f3606b3fd1bb12d5fe055d1e418cb60ea"}}, Content: "{\"wss://nostr-pub.wellorder.net\":{\"read\":true,\"write\":true},\"wss://nostr.bitcoiner.social\":{\"read\":false,\"write\":true},\"wss://expensive-relay.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relayer.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relay.bitid.nz\":{\"read\":true,\"write\":true},\"wss://nostr.rocks\":{\"read\":true,\"write\":true}}", Sig: "811355d3484d375df47581cb5d66bed05002c2978894098304f20b595e571b7e01b2efd906c5650080ffe49cf1c62b36715698e9d88b9e8be43029a2f3fa66be"}}, }, { Name: "NOTICE envelope", @@ -115,12 +114,12 @@ func TestParseMessage(t *testing.T) { } if testCase.ExpectedEnvelope == nil { - assert.Nil(t, envelope, "expected nil but got %v", envelope) + require.Nil(t, envelope, "expected nil but got %v", envelope) return } - assert.NotNil(t, envelope, "expected non-nil envelope but got nil") - assert.Equal(t, testCase.ExpectedEnvelope.String(), envelope.String()) + require.NotNil(t, envelope, "expected non-nil envelope but got nil") + require.Equal(t, testCase.ExpectedEnvelope.String(), envelope.String()) }) } }) @@ -148,10 +147,9 @@ func TestParseMessage(t *testing.T) { }) t.Run("sonic", func(t *testing.T) { - smp := SonicMessageParser{} for _, testCase := range testCases { t.Run(testCase.Name, func(t *testing.T) { - envelope, err := smp.ParseMessage(testCase.Message) + envelope, err := ParseMessageSonic(testCase.Message) if testCase.ExpectedEnvelope == nil && envelope == nil { return @@ -164,7 +162,7 @@ func TestParseMessage(t *testing.T) { require.NoError(t, err) require.NotNil(t, envelope, "expected non-nil envelope but got nil") - require.Equal(t, testCase.ExpectedEnvelope.String(), envelope.String()) + require.Equal(t, testCase.ExpectedEnvelope, envelope) }) } })