package nostr import ( "encoding/json" "testing" ) func TestEventEnvelopeMarshalJSON(t *testing.T) { event := &Event{ ID: "abc123", PubKey: "def456", CreatedAt: 1704067200, Kind: 1, Tags: Tags{}, Content: "Hello", Sig: "sig789", } t.Run("client to relay", func(t *testing.T) { env := EventEnvelope{Event: event} data, err := env.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() error = %v", err) } var arr []json.RawMessage if err := json.Unmarshal(data, &arr); err != nil { t.Fatalf("Invalid JSON: %v", err) } if len(arr) != 2 { t.Errorf("Array length = %d, want 2", len(arr)) } var label string json.Unmarshal(arr[0], &label) if label != "EVENT" { t.Errorf("Label = %s, want EVENT", label) } }) t.Run("relay to client", func(t *testing.T) { env := EventEnvelope{SubscriptionID: "sub1", Event: event} data, err := env.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() error = %v", err) } var arr []json.RawMessage if err := json.Unmarshal(data, &arr); err != nil { t.Fatalf("Invalid JSON: %v", err) } if len(arr) != 3 { t.Errorf("Array length = %d, want 3", len(arr)) } }) } func TestReqEnvelopeMarshalJSON(t *testing.T) { env := ReqEnvelope{ SubscriptionID: "sub1", Filters: []Filter{ {Kinds: []int{1}}, {Authors: []string{"abc123"}}, }, } data, err := env.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() error = %v", err) } var arr []json.RawMessage if err := json.Unmarshal(data, &arr); err != nil { t.Fatalf("Invalid JSON: %v", err) } if len(arr) != 4 { // ["REQ", "sub1", filter1, filter2] t.Errorf("Array length = %d, want 4", len(arr)) } var label string json.Unmarshal(arr[0], &label) if label != "REQ" { t.Errorf("Label = %s, want REQ", label) } var subID string json.Unmarshal(arr[1], &subID) if subID != "sub1" { t.Errorf("SubscriptionID = %s, want sub1", subID) } } func TestCloseEnvelopeMarshalJSON(t *testing.T) { env := CloseEnvelope{SubscriptionID: "sub1"} data, err := env.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() error = %v", err) } var arr []interface{} if err := json.Unmarshal(data, &arr); err != nil { t.Fatalf("Invalid JSON: %v", err) } if len(arr) != 2 { t.Errorf("Array length = %d, want 2", len(arr)) } if arr[0] != "CLOSE" { t.Errorf("Label = %v, want CLOSE", arr[0]) } if arr[1] != "sub1" { t.Errorf("SubscriptionID = %v, want sub1", arr[1]) } } func TestOKEnvelopeMarshalJSON(t *testing.T) { env := OKEnvelope{ EventID: "event123", OK: true, Message: "accepted", } data, err := env.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() error = %v", err) } var arr []interface{} if err := json.Unmarshal(data, &arr); err != nil { t.Fatalf("Invalid JSON: %v", err) } if len(arr) != 4 { t.Errorf("Array length = %d, want 4", len(arr)) } if arr[0] != "OK" { t.Errorf("Label = %v, want OK", arr[0]) } if arr[1] != "event123" { t.Errorf("EventID = %v, want event123", arr[1]) } if arr[2] != true { t.Errorf("OK = %v, want true", arr[2]) } if arr[3] != "accepted" { t.Errorf("Message = %v, want accepted", arr[3]) } } func TestEOSEEnvelopeMarshalJSON(t *testing.T) { env := EOSEEnvelope{SubscriptionID: "sub1"} data, err := env.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() error = %v", err) } var arr []interface{} if err := json.Unmarshal(data, &arr); err != nil { t.Fatalf("Invalid JSON: %v", err) } if len(arr) != 2 { t.Errorf("Array length = %d, want 2", len(arr)) } if arr[0] != "EOSE" { t.Errorf("Label = %v, want EOSE", arr[0]) } } func TestClosedEnvelopeMarshalJSON(t *testing.T) { env := ClosedEnvelope{ SubscriptionID: "sub1", Message: "rate limited", } data, err := env.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() error = %v", err) } var arr []interface{} if err := json.Unmarshal(data, &arr); err != nil { t.Fatalf("Invalid JSON: %v", err) } if len(arr) != 3 { t.Errorf("Array length = %d, want 3", len(arr)) } if arr[0] != "CLOSED" { t.Errorf("Label = %v, want CLOSED", arr[0]) } } func TestNoticeEnvelopeMarshalJSON(t *testing.T) { env := NoticeEnvelope{Message: "error: rate limited"} data, err := env.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() error = %v", err) } var arr []interface{} if err := json.Unmarshal(data, &arr); err != nil { t.Fatalf("Invalid JSON: %v", err) } if len(arr) != 2 { t.Errorf("Array length = %d, want 2", len(arr)) } if arr[0] != "NOTICE" { t.Errorf("Label = %v, want NOTICE", arr[0]) } } func TestParseEnvelopeEvent(t *testing.T) { t.Run("client to relay", func(t *testing.T) { data := `["EVENT",{"id":"abc123","pubkey":"def456","created_at":1704067200,"kind":1,"tags":[],"content":"Hello","sig":"sig789"}]` env, err := ParseEnvelope([]byte(data)) if err != nil { t.Fatalf("ParseEnvelope() error = %v", err) } eventEnv, ok := env.(*EventEnvelope) if !ok { t.Fatalf("Expected *EventEnvelope, got %T", env) } if eventEnv.SubscriptionID != "" { t.Errorf("SubscriptionID = %s, want empty", eventEnv.SubscriptionID) } if eventEnv.Event.ID != "abc123" { t.Errorf("Event.ID = %s, want abc123", eventEnv.Event.ID) } }) t.Run("relay to client", func(t *testing.T) { data := `["EVENT","sub1",{"id":"abc123","pubkey":"def456","created_at":1704067200,"kind":1,"tags":[],"content":"Hello","sig":"sig789"}]` env, err := ParseEnvelope([]byte(data)) if err != nil { t.Fatalf("ParseEnvelope() error = %v", err) } eventEnv, ok := env.(*EventEnvelope) if !ok { t.Fatalf("Expected *EventEnvelope, got %T", env) } if eventEnv.SubscriptionID != "sub1" { t.Errorf("SubscriptionID = %s, want sub1", eventEnv.SubscriptionID) } }) } func TestParseEnvelopeReq(t *testing.T) { data := `["REQ","sub1",{"kinds":[1]},{"authors":["abc123"]}]` env, err := ParseEnvelope([]byte(data)) if err != nil { t.Fatalf("ParseEnvelope() error = %v", err) } reqEnv, ok := env.(*ReqEnvelope) if !ok { t.Fatalf("Expected *ReqEnvelope, got %T", env) } if reqEnv.SubscriptionID != "sub1" { t.Errorf("SubscriptionID = %s, want sub1", reqEnv.SubscriptionID) } if len(reqEnv.Filters) != 2 { t.Errorf("Filters length = %d, want 2", len(reqEnv.Filters)) } } func TestParseEnvelopeClose(t *testing.T) { data := `["CLOSE","sub1"]` env, err := ParseEnvelope([]byte(data)) if err != nil { t.Fatalf("ParseEnvelope() error = %v", err) } closeEnv, ok := env.(*CloseEnvelope) if !ok { t.Fatalf("Expected *CloseEnvelope, got %T", env) } if closeEnv.SubscriptionID != "sub1" { t.Errorf("SubscriptionID = %s, want sub1", closeEnv.SubscriptionID) } } func TestParseEnvelopeOK(t *testing.T) { data := `["OK","event123",true,"accepted"]` env, err := ParseEnvelope([]byte(data)) if err != nil { t.Fatalf("ParseEnvelope() error = %v", err) } okEnv, ok := env.(*OKEnvelope) if !ok { t.Fatalf("Expected *OKEnvelope, got %T", env) } if okEnv.EventID != "event123" { t.Errorf("EventID = %s, want event123", okEnv.EventID) } if !okEnv.OK { t.Error("OK = false, want true") } if okEnv.Message != "accepted" { t.Errorf("Message = %s, want accepted", okEnv.Message) } } func TestParseEnvelopeEOSE(t *testing.T) { data := `["EOSE","sub1"]` env, err := ParseEnvelope([]byte(data)) if err != nil { t.Fatalf("ParseEnvelope() error = %v", err) } eoseEnv, ok := env.(*EOSEEnvelope) if !ok { t.Fatalf("Expected *EOSEEnvelope, got %T", env) } if eoseEnv.SubscriptionID != "sub1" { t.Errorf("SubscriptionID = %s, want sub1", eoseEnv.SubscriptionID) } } func TestParseEnvelopeClosed(t *testing.T) { data := `["CLOSED","sub1","rate limited"]` env, err := ParseEnvelope([]byte(data)) if err != nil { t.Fatalf("ParseEnvelope() error = %v", err) } closedEnv, ok := env.(*ClosedEnvelope) if !ok { t.Fatalf("Expected *ClosedEnvelope, got %T", env) } if closedEnv.SubscriptionID != "sub1" { t.Errorf("SubscriptionID = %s, want sub1", closedEnv.SubscriptionID) } if closedEnv.Message != "rate limited" { t.Errorf("Message = %s, want rate limited", closedEnv.Message) } } func TestParseEnvelopeNotice(t *testing.T) { data := `["NOTICE","error: rate limited"]` env, err := ParseEnvelope([]byte(data)) if err != nil { t.Fatalf("ParseEnvelope() error = %v", err) } noticeEnv, ok := env.(*NoticeEnvelope) if !ok { t.Fatalf("Expected *NoticeEnvelope, got %T", env) } if noticeEnv.Message != "error: rate limited" { t.Errorf("Message = %s, want 'error: rate limited'", noticeEnv.Message) } } func TestParseEnvelopeErrors(t *testing.T) { tests := []struct { name string data string }{ {"invalid json", "not json"}, {"not array", `{"type":"EVENT"}`}, {"empty array", `[]`}, {"single element", `["EVENT"]`}, {"unknown type", `["UNKNOWN","data"]`}, {"invalid event length", `["EVENT","a","b","c"]`}, {"invalid ok length", `["OK","id",true]`}, {"invalid eose length", `["EOSE"]`}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := ParseEnvelope([]byte(tt.data)) if err == nil { t.Error("ParseEnvelope() expected error, got nil") } }) } } func TestEnvelopeLabel(t *testing.T) { tests := []struct { env Envelope label string }{ {EventEnvelope{}, "EVENT"}, {ReqEnvelope{}, "REQ"}, {CloseEnvelope{}, "CLOSE"}, {OKEnvelope{}, "OK"}, {EOSEEnvelope{}, "EOSE"}, {ClosedEnvelope{}, "CLOSED"}, {NoticeEnvelope{}, "NOTICE"}, } for _, tt := range tests { t.Run(tt.label, func(t *testing.T) { if got := tt.env.Label(); got != tt.label { t.Errorf("Label() = %s, want %s", got, tt.label) } }) } }