From 1bccebbf2a92234996e09d6c631f7c5dbee8a738 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Sat, 9 Dec 2023 11:29:14 -0300 Subject: [PATCH] fix ParseMessage() so it works with CLOSED and improve tests. --- envelopes.go | 34 +++++++++++++++++++++++++++++-- envelopes_test.go | 51 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/envelopes.go b/envelopes.go index 41963df..2af8f4a 100644 --- a/envelopes.go +++ b/envelopes.go @@ -16,6 +16,7 @@ func ParseMessage(message []byte) Envelope { return nil } label := message[0:firstComma] + var v Envelope switch { case bytes.Contains(label, []byte("EVENT")): @@ -34,11 +35,11 @@ func ParseMessage(message []byte) Envelope { v = &OKEnvelope{} case bytes.Contains(label, []byte("AUTH")): v = &AuthEnvelope{} + case bytes.Contains(label, []byte("CLOSED")): + v = &ClosedEnvelope{} case bytes.Contains(label, []byte("CLOSE")): x := CloseEnvelope("") v = &x - case bytes.Contains(label, []byte("CLOSED")): - v = &ClosedEnvelope{} default: return nil } @@ -53,6 +54,7 @@ type Envelope interface { Label() string UnmarshalJSON([]byte) error MarshalJSON() ([]byte, error) + String() string } type EventEnvelope struct { @@ -143,6 +145,10 @@ type CountEnvelope struct { } func (_ CountEnvelope) Label() string { return "COUNT" } +func (c CountEnvelope) String() string { + v, _ := json.Marshal(c) + return string(v) +} func (v *CountEnvelope) UnmarshalJSON(data []byte) error { r := gjson.ParseBytes(data) @@ -198,6 +204,10 @@ func (v CountEnvelope) MarshalJSON() ([]byte, error) { type NoticeEnvelope string func (_ NoticeEnvelope) Label() string { return "NOTICE" } +func (n NoticeEnvelope) String() string { + v, _ := json.Marshal(n) + return string(v) +} func (v *NoticeEnvelope) UnmarshalJSON(data []byte) error { r := gjson.ParseBytes(data) @@ -220,6 +230,10 @@ func (v NoticeEnvelope) MarshalJSON() ([]byte, error) { type EOSEEnvelope string func (_ EOSEEnvelope) Label() string { return "EOSE" } +func (e EOSEEnvelope) String() string { + v, _ := json.Marshal(e) + return string(v) +} func (v *EOSEEnvelope) UnmarshalJSON(data []byte) error { r := gjson.ParseBytes(data) @@ -242,6 +256,10 @@ func (v EOSEEnvelope) MarshalJSON() ([]byte, error) { type CloseEnvelope string func (_ CloseEnvelope) Label() string { return "CLOSE" } +func (c CloseEnvelope) String() string { + v, _ := json.Marshal(c) + return string(v) +} func (v *CloseEnvelope) UnmarshalJSON(data []byte) error { r := gjson.ParseBytes(data) @@ -269,6 +287,10 @@ type ClosedEnvelope struct { } func (_ ClosedEnvelope) Label() string { return "CLOSED" } +func (c ClosedEnvelope) String() string { + v, _ := json.Marshal(c) + return string(v) +} func (v *ClosedEnvelope) UnmarshalJSON(data []byte) error { r := gjson.ParseBytes(data) @@ -299,6 +321,10 @@ type OKEnvelope struct { } func (_ OKEnvelope) Label() string { return "OK" } +func (o OKEnvelope) String() string { + v, _ := json.Marshal(o) + return string(v) +} func (v *OKEnvelope) UnmarshalJSON(data []byte) error { r := gjson.ParseBytes(data) @@ -334,6 +360,10 @@ type AuthEnvelope struct { } func (_ AuthEnvelope) Label() string { return "AUTH" } +func (a AuthEnvelope) String() string { + v, _ := json.Marshal(a) + return string(v) +} func (v *AuthEnvelope) UnmarshalJSON(data []byte) error { r := gjson.ParseBytes(data) diff --git a/envelopes_test.go b/envelopes_test.go index b5410ab..df966cc 100644 --- a/envelopes_test.go +++ b/envelopes_test.go @@ -2,7 +2,6 @@ package nostr import ( "encoding/json" - "reflect" "testing" ) @@ -89,15 +88,18 @@ func TestOKEnvelopeEncodingAndDecoding(t *testing.T) { } func TestClosedEnvelopeEncodingAndDecoding(t *testing.T) { - src := `["CLOSED","_","error: something went wrong"]` - var env ClosedEnvelope - json.Unmarshal([]byte(src), &env) - if env.SubscriptionID != "_" { - t.Error("failed to decode CLOSED") - } - - if res, _ := json.Marshal(env); string(res) != src { - t.Errorf("failed to encode CLOSED: expected '%s', got '%s'", src, string(res)) + for _, src := range []string{ + `["CLOSED","_","error: something went wrong"]`, + `["CLOSED",":1","auth-required: take a selfie and send it to the CIA"]`, + } { + var env ClosedEnvelope + json.Unmarshal([]byte(src), &env) + if env.SubscriptionID != "_" && env.SubscriptionID != ":1" { + t.Error("failed to decode CLOSED") + } + if res, _ := json.Marshal(env); string(res) != src { + t.Errorf("failed to encode CLOSED: expected '%s', got '%s'", src, string(res)) + } } } @@ -129,7 +131,7 @@ func TestParseMessage(t *testing.T) { testCases := []struct { Name string Message []byte - ExpectedEnvelope interface{} + ExpectedEnvelope Envelope }{ { Name: "nil", @@ -146,14 +148,37 @@ func TestParseMessage(t *testing.T) { Message: []byte("invalid, input"), ExpectedEnvelope: nil, }, + { + Name: "CLOSED envelope", + Message: []byte(`["CLOSED",":1","error: we are broken"]`), + ExpectedEnvelope: &ClosedEnvelope{SubscriptionID: ":1", Reason: "error: we are broken"}, + }, + { + Name: "AUTH envelope", + Message: []byte(`["AUTH","bisteka"]`), + ExpectedEnvelope: &AuthEnvelope{Challenge: ptr("bisteka")}, + }, + { + Name: "REQ envelope", + Message: []byte(`["REQ","million", {"kinds": [1]}, {"kinds": [30023 ], "#d": ["buteko", "batuke"]}]`), + ExpectedEnvelope: &ReqEnvelope{SubscriptionID: "million", Filters: Filters{{Kinds: []int{1}}, {Kinds: []int{30023}, Tags: TagMap{"d": []string{"buteko", "batuke"}}}}}, + }, } for _, testCase := range testCases { t.Run(testCase.Name, func(t *testing.T) { envelope := ParseMessage(testCase.Message) - if !reflect.DeepEqual(testCase.ExpectedEnvelope, envelope) { - t.Fatal("unexpected output") + if testCase.ExpectedEnvelope == nil && envelope == nil { + return + } + if testCase.ExpectedEnvelope == nil && envelope != nil { + t.Fatalf("expected nil but got %v\n", envelope) + } + if testCase.ExpectedEnvelope.String() != envelope.String() { + t.Fatalf("unexpected output:\n %s\n != %s", testCase.ExpectedEnvelope, envelope) } }) } } + +func ptr[S any](s S) *S { return &s }