package nostr import ( "strings" "testing" ) func TestEmptyObjectKeyIsRejectedNotPanicked(t *testing.T) { cases := []string{ `{"":1}`, `{"id":"","":2}`, `{"kinds":[1],"":null}`, } for _, raw := range cases { if _, err := ParseEvent([]byte(raw)); err == nil { t.Errorf("event %s accepted", raw) } if _, err := ParseFilter([]byte(raw)); err == nil { t.Errorf("filter %s accepted", raw) } } // A nested empty key in an unknown field must be skipped, not panic. if _, err := ParseEvent([]byte(`{"unknown":{"":1}}`)); err == nil { t.Fatal("expected missing-field error, got nil (but no panic)") } } func TestEmptyKeyExtractID(t *testing.T) { raw := `{"":1,"id":"eaeb41f2e1d44abc3949052406db478aeed872a64e46022daace5955cd65b316"}` if got := ExtractID([]byte(raw)); got != "eaeb41f2e1d44abc3949052406db478aeed872a64e46022daace5955cd65b316" { t.Fatalf("ExtractID = %q", got) } if got := ExtractID([]byte(`{"":1}`)); got != "" { t.Fatalf("ExtractID on empty key = %q", got) } } func TestSurrogateBounds(t *testing.T) { if _, err := ParseJSONString([]byte(`"\uD800\u"`)); err == nil { t.Fatal("truncated surrogate accepted") } if _, err := ParseJSONString([]byte(`"\uD800`)); err == nil { t.Fatal("truncated escape accepted") } if _, err := ParseJSONString([]byte(`"\uDC00"`)); err == nil { t.Fatal("lone low surrogate accepted") } got, err := ParseJSONString([]byte(`"\uD83D\uDE80"`)) if err != nil || got != "🚀" { t.Fatalf("valid pair: %q %v", got, err) } } func TestSplitArray(t *testing.T) { parts, err := SplitArray([]byte(`["REQ","s",{"kinds":[1],"#e":["x"]}]`)) if err != nil || len(parts) != 3 { t.Fatalf("parts=%q err=%v", parts, err) } if string(parts[0]) != `"REQ"` || string(parts[1]) != `"s"` { t.Fatalf("parts: %q", parts) } f, err := ParseFilter(parts[2]) if err != nil || len(f.Kinds) != 1 || len(f.Tags["e"]) != 1 { t.Fatalf("filter: %+v %v", f, err) } if _, err := SplitArray([]byte(`["EVENT",{"":1}]`)); err != nil { t.Fatalf("split: %v", err) } } func TestCodecRejectsMalformed(t *testing.T) { cases := []string{ `{`, `{"id":1}`, `{"id":"zz"}`, `{"kinds":[1.5]}`, `{"kinds":[-1]}`, `{"limit":1e3}`, `{"#e":"x"}`, `{"tags":[["e",1]]}`, `{"content":"\q"}`, } for _, raw := range cases { key := "event" if strings.HasPrefix(raw, `{"kinds`) || strings.HasPrefix(raw, `{"#`) || strings.HasPrefix(raw, `{"limit`) { key = "filter" } if key == "event" { f := &Filter{} _ = f if _, err := ParseEvent([]byte(raw)); err == nil { t.Errorf("event %s accepted", raw) } } else { if _, err := ParseFilter([]byte(raw)); err == nil { t.Errorf("filter %s accepted", raw) } } } } func TestParseEventRoundTrip(t *testing.T) { var sk [32]byte sk[31] = 3 ev := &Event{Kind: 1, CreatedAt: 1700000000, Tags: Tags{{"t", "json"}}, Content: "hello \"json\"\n"} if err := ev.Sign(sk); err != nil { t.Fatal(err) } got, err := ParseEvent(ev.AppendJSON(nil)) if err != nil { t.Fatal(err) } if got.ID != ev.ID || got.Content != ev.Content || len(got.Tags) != 1 { t.Fatalf("round trip: %+v", got) } }