filter_test.mx raw

   1  package filter
   2  
   3  import (
   4  	"bytes"
   5  	"testing"
   6  
   7  	"git.smesh.lol/nostr/pkg/event"
   8  	"git.smesh.lol/nostr/pkg/kind"
   9  	"git.smesh.lol/nostr/pkg/tag"
  10  	"git.smesh.lol/nostr/pkg/timestamp"
  11  )
  12  
  13  // tHash returns n copies of fill as a 32-byte hash-shaped value.
  14  func tHash(fill byte) (b []byte) {
  15  	b = []byte{:32}
  16  	for i := range b {
  17  		b[i] = fill
  18  	}
  19  	return
  20  }
  21  
  22  // tHex renders 32 copies of fill as lowercase hex.
  23  func tHex(fill byte) (s []byte) {
  24  	tbl := []byte("0123456789abcdef")
  25  	hi := tbl[int32(fill>>4)]
  26  	lo := tbl[int32(fill&0x0F)]
  27  	s = []byte{:64}
  28  	for i := 0; i < 32; i++ {
  29  		s[i*2] = hi
  30  		s[i*2+1] = lo
  31  	}
  32  	return
  33  }
  34  
  35  func tTag(k, v string) (tt *tag.T) {
  36  	return tag.NewFromBytesSlice([]byte(k), []byte(v))
  37  }
  38  
  39  func tEvent(id, pub []byte, k uint16, created int64, tags *tag.S) (ev *event.E) {
  40  	ev = event.New()
  41  	ev.ID = id
  42  	ev.Pubkey = pub
  43  	ev.Kind = k
  44  	ev.CreatedAt = created
  45  	ev.Tags = tags
  46  	return
  47  }
  48  
  49  func TestNewFilterAndSerialize(t *testing.T) {
  50  	f := New()
  51  	if f == nil {
  52  		t.Fatal("New returned nil")
  53  		return
  54  	}
  55  	if f.Ids == nil || f.Kinds == nil || f.Authors == nil || f.Tags == nil {
  56  		t.Fatal("New must initialize the list fields")
  57  	}
  58  	if f.Since == nil || f.Until == nil {
  59  		t.Fatal("New must initialize the timestamp fields")
  60  	}
  61  	if f.Ids.Len() != 0 || f.Kinds.Len() != 0 || f.Authors.Len() != 0 || f.Tags.Len() != 0 {
  62  		t.Fatal("New lists must start empty")
  63  	}
  64  	if f.Limit != nil || len(f.Search) != 0 || f.Extra != nil {
  65  		t.Fatal("New leaves Limit, Search and Extra unset")
  66  	}
  67  	if string(f.Serialize()) != "{}" {
  68  		t.Fatalf("empty Serialize = %s", string(f.Serialize()))
  69  	}
  70  	if string(f.Marshal(nil)) != "{}" {
  71  		t.Fatalf("empty Marshal = %s", string(f.Marshal(nil)))
  72  	}
  73  	// A zero F has every field absent and must marshal the same way.
  74  	z := &F{}
  75  	if string(z.Marshal(nil)) != "{}" {
  76  		t.Fatalf("zero F Marshal = %s", string(z.Marshal(nil)))
  77  	}
  78  	// Sort must tolerate every list being nil.
  79  	z.Sort()
  80  }
  81  
  82  func TestSortOrdersAllFields(t *testing.T) {
  83  	f := New()
  84  	f.Ids = tag.NewFromBytesSlice(tHash(3), tHash(1), tHash(2))
  85  	f.Authors = tag.NewFromBytesSlice(tHash(9), tHash(5))
  86  	f.Kinds = kind.FromIntSlice([]int32{9, 1, 5})
  87  	f.Tags = tag.NewS(
  88  		tag.NewFromBytesSlice([]byte("t"), []byte("c"), []byte("a"), []byte("b")),
  89  		tag.NewFromBytesSlice([]byte("a"), []byte("z")),
  90  	)
  91  	f.Sort()
  92  	if !bytes.Equal(f.Ids.T[0], tHash(1)) || !bytes.Equal(f.Ids.T[1], tHash(2)) || !bytes.Equal(f.Ids.T[2], tHash(3)) {
  93  		t.Fatal("Ids not sorted ascending")
  94  	}
  95  	if !bytes.Equal(f.Authors.T[0], tHash(5)) || !bytes.Equal(f.Authors.T[1], tHash(9)) {
  96  		t.Fatal("Authors not sorted ascending")
  97  	}
  98  	if f.Kinds.K[0].K != 1 || f.Kinds.K[1].K != 5 || f.Kinds.K[2].K != 9 {
  99  		t.Fatal("Kinds not sorted ascending")
 100  	}
 101  	if !bytes.Equal(f.Tags.T[0].T[0], []byte("a")) || !bytes.Equal(f.Tags.T[1].T[0], []byte("t")) {
 102  		t.Fatal("Tags not sorted by key")
 103  	}
 104  	tv := f.Tags.T[1]
 105  	if !bytes.Equal(tv.T[0], []byte("t")) {
 106  		t.Fatal("tag key moved during value sort")
 107  	}
 108  	if !bytes.Equal(tv.T[1], []byte("a")) || !bytes.Equal(tv.T[2], []byte("b")) || !bytes.Equal(tv.T[3], []byte("c")) {
 109  		t.Fatal("tag values not sorted")
 110  	}
 111  }
 112  
 113  func TestMatchesIgnoringTimestampConstraints(t *testing.T) {
 114  	ev := tEvent(tHash(1), tHash(2), 1, 100, tag.NewS(tTag("t", "alpha"), tTag("x", "beta")))
 115  	nf := New()
 116  	if nf.MatchesIgnoringTimestampConstraints(nil) {
 117  		t.Fatal("nil event must not match")
 118  	}
 119  	if !nf.MatchesIgnoringTimestampConstraints(ev) {
 120  		t.Fatal("an empty filter matches any event")
 121  	}
 122  
 123  	fid := New()
 124  	fid.Ids = tag.NewFromBytesSlice(tHash(1))
 125  	if !fid.MatchesIgnoringTimestampConstraints(ev) {
 126  		t.Fatal("ids: exact id must match")
 127  	}
 128  	fid2 := New()
 129  	fid2.Ids = tag.NewFromBytesSlice(tHash(7))
 130  	if fid2.MatchesIgnoringTimestampConstraints(ev) {
 131  		t.Fatal("ids: different id must not match")
 132  	}
 133  
 134  	fk := New()
 135  	fk.Kinds = kind.FromIntSlice([]int32{1})
 136  	if !fk.MatchesIgnoringTimestampConstraints(ev) {
 137  		t.Fatal("kinds: exact kind must match")
 138  	}
 139  	fk2 := New()
 140  	fk2.Kinds = kind.FromIntSlice([]int32{2})
 141  	if fk2.MatchesIgnoringTimestampConstraints(ev) {
 142  		t.Fatal("kinds: different kind must not match")
 143  	}
 144  
 145  	fa := New()
 146  	fa.Authors = tag.NewFromBytesSlice(tHash(2))
 147  	if !fa.MatchesIgnoringTimestampConstraints(ev) {
 148  		t.Fatal("authors: exact author must match")
 149  	}
 150  	fa2 := New()
 151  	fa2.Authors = tag.NewFromBytesSlice(tHash(8))
 152  	if fa2.MatchesIgnoringTimestampConstraints(ev) {
 153  		t.Fatal("authors: different author must not match")
 154  	}
 155  
 156  	ft := New()
 157  	ft.Tags = tag.NewS(tTag("t", "alpha"))
 158  	if !ft.MatchesIgnoringTimestampConstraints(ev) {
 159  		t.Fatal("tags: exact value must match")
 160  	}
 161  	ft2 := New()
 162  	ft2.Tags = tag.NewS(tTag("t", "gamma"))
 163  	if ft2.MatchesIgnoringTimestampConstraints(ev) {
 164  		t.Fatal("tags: different value must not match")
 165  	}
 166  	ft3 := New()
 167  	ft3.Tags = tag.NewS(tTag("z", "alpha"))
 168  	if ft3.MatchesIgnoringTimestampConstraints(ev) {
 169  		t.Fatal("tags: different name must not match")
 170  	}
 171  	// A filter tag with a key but no value is skipped, not enforced.
 172  	ft4 := New()
 173  	ft4.Tags = tag.NewS(tag.NewFromBytesSlice([]byte("t")))
 174  	if !ft4.MatchesIgnoringTimestampConstraints(ev) {
 175  		t.Fatal("one-field filter tag must be skipped")
 176  	}
 177  	// With no event tags a tag constraint cannot be satisfied.
 178  	evNoTags := tEvent(tHash(1), tHash(2), 1, 100, nil)
 179  	ft5 := New()
 180  	ft5.Tags = tag.NewS(tTag("t", "alpha"))
 181  	if ft5.MatchesIgnoringTimestampConstraints(evNoTags) {
 182  		t.Fatal("tag constraint must fail without event tags")
 183  	}
 184  	// Timestamp fields are deliberately ignored here.
 185  	fstamp := New()
 186  	fstamp.Since = timestamp.FromUnix(1000)
 187  	fstamp.Until = timestamp.FromUnix(2000)
 188  	if !fstamp.MatchesIgnoringTimestampConstraints(ev) {
 189  		t.Fatal("timestamps must be ignored by this predicate")
 190  	}
 191  }
 192  
 193  func TestMatchesTimestampBounds(t *testing.T) {
 194  	ev := tEvent(tHash(1), tHash(2), 1, 100, nil)
 195  	f := New()
 196  	f.Since = timestamp.FromUnix(50)
 197  	if !f.Matches(ev) {
 198  		t.Fatal("since in the past must match")
 199  	}
 200  	f2 := New()
 201  	f2.Since = timestamp.FromUnix(150)
 202  	if f2.Matches(ev) {
 203  		t.Fatal("since in the future must not match")
 204  	}
 205  	f3 := New()
 206  	f3.Since = timestamp.FromUnix(100)
 207  	if !f3.Matches(ev) {
 208  		t.Fatal("since equal to created_at must match")
 209  	}
 210  	f4 := New()
 211  	f4.Until = timestamp.FromUnix(150)
 212  	if !f4.Matches(ev) {
 213  		t.Fatal("until in the future must match")
 214  	}
 215  	f5 := New()
 216  	f5.Until = timestamp.FromUnix(50)
 217  	if f5.Matches(ev) {
 218  		t.Fatal("until in the past must not match")
 219  	}
 220  	f6 := New()
 221  	f6.Until = timestamp.FromUnix(100)
 222  	if !f6.Matches(ev) {
 223  		t.Fatal("until equal to created_at must match")
 224  	}
 225  	// Zero means "absent", not "the epoch".
 226  	f7 := New()
 227  	f7.Since = timestamp.FromUnix(0)
 228  	f7.Until = timestamp.FromUnix(0)
 229  	if !f7.Matches(ev) {
 230  		t.Fatal("zero timestamps must be ignored")
 231  	}
 232  	// A failed content predicate wins over a satisfied time window.
 233  	f8 := New()
 234  	f8.Kinds = kind.FromIntSlice([]int32{2})
 235  	f8.Since = timestamp.FromUnix(50)
 236  	if f8.Matches(ev) {
 237  		t.Fatal("kind mismatch must dominate")
 238  	}
 239  	f9 := New()
 240  	if f9.Matches(nil) {
 241  		t.Fatal("nil event must not match")
 242  	}
 243  }
 244  
 245  func TestMarshalEachField(t *testing.T) {
 246  	fid := New()
 247  	fid.Ids = tag.NewFromBytesSlice(tHash(1))
 248  	wantI := "{\"ids\":[\"" | tHex(1) | "\"]}"
 249  	if string(fid.Marshal(nil)) != string(wantI) {
 250  		t.Fatalf("ids only = %s", string(fid.Marshal(nil)))
 251  	}
 252  	fk := New()
 253  	fk.Kinds = kind.FromIntSlice([]int32{1})
 254  	if string(fk.Marshal(nil)) != "{\"kinds\":[1]}" {
 255  		t.Fatalf("kinds only = %s", string(fk.Marshal(nil)))
 256  	}
 257  	fa := New()
 258  	fa.Authors = tag.NewFromBytesSlice(tHash(2))
 259  	wantA := "{\"authors\":[\"" | tHex(2) | "\"]}"
 260  	if string(fa.Marshal(nil)) != string(wantA) {
 261  		t.Fatalf("authors only = %s", string(fa.Marshal(nil)))
 262  	}
 263  	ft := New()
 264  	ft.Tags = tag.NewS(tTag("t", "v"))
 265  	if string(ft.Marshal(nil)) != "{\"#t\":[\"v\"]}" {
 266  		t.Fatalf("tags only = %s", string(ft.Marshal(nil)))
 267  	}
 268  	fs := New()
 269  	fs.Since = timestamp.FromUnix(5)
 270  	if string(fs.Marshal(nil)) != "{\"since\":5}" {
 271  		t.Fatalf("since only = %s", string(fs.Marshal(nil)))
 272  	}
 273  	fu := New()
 274  	fu.Until = timestamp.FromUnix(6)
 275  	if string(fu.Marshal(nil)) != "{\"until\":6}" {
 276  		t.Fatalf("until only = %s", string(fu.Marshal(nil)))
 277  	}
 278  	fse := New()
 279  	fse.Search = []byte("hi")
 280  	if string(fse.Marshal(nil)) != "{\"search\":\"hi\"}" {
 281  		t.Fatalf("search only = %s", string(fse.Marshal(nil)))
 282  	}
 283  	fl := New()
 284  	fl.Limit = mkU32(9)
 285  	if string(fl.Marshal(nil)) != "{\"limit\":9}" {
 286  		t.Fatalf("limit only = %s", string(fl.Marshal(nil)))
 287  	}
 288  	// A zero timestamp is omitted entirely.
 289  	fz := New()
 290  	fz.Since = timestamp.FromUnix(0)
 291  	fz.Until = timestamp.FromUnix(0)
 292  	if string(fz.Marshal(nil)) != "{}" {
 293  		t.Fatalf("zero timestamps must be omitted: %s", string(fz.Marshal(nil)))
 294  	}
 295  	// A non-nil destination is appended to, not replaced.
 296  	fp := New()
 297  	fp.Kinds = kind.FromIntSlice([]int32{3})
 298  	if string(fp.Marshal([]byte("pre"))) != "pre{\"kinds\":[3]}" {
 299  		t.Fatalf("Marshal into dst = %s", string(fp.Marshal([]byte("pre"))))
 300  	}
 301  }
 302  
 303  func TestMarshalAllFields(t *testing.T) {
 304  	f := New()
 305  	f.Ids = tag.NewFromBytesSlice(tHash(1))
 306  	f.Kinds = kind.FromIntSlice([]int32{1, 2})
 307  	f.Authors = tag.NewFromBytesSlice(tHash(2))
 308  	f.Tags = tag.NewS(tag.NewFromBytesSlice([]byte("t"), []byte("beta"), []byte("alpha")))
 309  	f.Since = timestamp.FromUnix(100)
 310  	f.Until = timestamp.FromUnix(200)
 311  	f.Search = []byte("hello")
 312  	f.Limit = mkU32(7)
 313  	want := "{\"ids\":[\"" | tHex(1) | "\"],\"kinds\":[1,2],\"authors\":[\"" | tHex(2) |
 314  		"\"],\"#t\":[\"alpha\",\"beta\"],\"since\":100,\"until\":200,\"search\":\"hello\",\"limit\":7}"
 315  	if string(f.Marshal(nil)) != string(want) {
 316  		t.Fatalf("full marshal = %s", string(f.Marshal(nil)))
 317  	}
 318  }
 319  
 320  func TestMarshalTagFiltering(t *testing.T) {
 321  	f := New()
 322  	f.Tags = tag.NewS(
 323  		tag.NewFromBytesSlice([]byte("ab"), []byte("v")),
 324  		tag.NewFromBytesSlice([]byte("1"), []byte("v")),
 325  		tag.NewFromBytesSlice([]byte("t")),
 326  		tag.NewFromBytesSlice([]byte("t"), []byte("keep")),
 327  	)
 328  	// Two-character keys, non-alphabetic keys and value-less tags are all
 329  	// skipped; only the last tag is emitted.
 330  	if string(f.Marshal(nil)) != "{\"#t\":[\"keep\"]}" {
 331  		t.Fatalf("tag filtering = %s", string(f.Marshal(nil)))
 332  	}
 333  	// Uppercase tag keys pass the same alphabetic test.
 334  	fu := New()
 335  	fu.Tags = tag.NewS(tag.NewFromBytesSlice([]byte("E"), []byte("v")))
 336  	if string(fu.Marshal(nil)) != "{\"#E\":[\"v\"]}" {
 337  		t.Fatalf("uppercase tag key = %s", string(fu.Marshal(nil)))
 338  	}
 339  	// A tag after another emitted field takes the comma branch.
 340  	fc := New()
 341  	fc.Kinds = kind.FromIntSlice([]int32{1})
 342  	fc.Tags = tag.NewS(tTag("t", "v"))
 343  	if string(fc.Marshal(nil)) != "{\"kinds\":[1],\"#t\":[\"v\"]}" {
 344  		t.Fatalf("tag comma placement = %s", string(fc.Marshal(nil)))
 345  	}
 346  	// More than one value keeps the internal comma.
 347  	fm := New()
 348  	fm.Tags = tag.NewS(tag.NewFromBytesSlice([]byte("t"), []byte("a"), []byte("b")))
 349  	if string(fm.Marshal(nil)) != "{\"#t\":[\"a\",\"b\"]}" {
 350  		t.Fatalf("multi-value tag = %s", string(fm.Marshal(nil)))
 351  	}
 352  }
 353  
 354  func TestUnmarshalAllFields(t *testing.T) {
 355  	in := []byte("{")
 356  	in = in | "\"ids\":[\""
 357  	in = in | tHex(1)
 358  	in = in | "\"],\"kinds\":[1,2],\"authors\":[\""
 359  	in = in | tHex(2)
 360  	in = in | "\"],\"#t\":[\"a\",\"b\"],\"since\":100,\"until\":200,\"search\":\"hi\",\"limit\":7}"
 361  	f := New()
 362  	rem, err := f.Unmarshal(in)
 363  	if err != nil {
 364  		t.Fatalf("Unmarshal error = %s", string(err.Error()))
 365  		return
 366  	}
 367  	if len(rem) != 0 {
 368  		t.Fatalf("remainder = %s", string(rem))
 369  	}
 370  	if f.Ids.Len() != 1 || !bytes.Equal(f.Ids.T[0], tHash(1)) {
 371  		t.Fatal("ids not parsed")
 372  	}
 373  	if f.Kinds.Len() != 2 || f.Kinds.K[0].K != 1 || f.Kinds.K[1].K != 2 {
 374  		t.Fatal("kinds not parsed")
 375  	}
 376  	if f.Authors.Len() != 1 || !bytes.Equal(f.Authors.T[0], tHash(2)) {
 377  		t.Fatal("authors not parsed")
 378  	}
 379  	if f.Tags.Len() != 1 {
 380  		t.Fatal("tag filter not parsed")
 381  	}
 382  	tg := f.Tags.T[0]
 383  	if tg.Len() != 3 {
 384  		t.Fatalf("tag field count = %d", tg.Len())
 385  	}
 386  	if !bytes.Equal(tg.T[0], []byte("t")) || !bytes.Equal(tg.T[1], []byte("a")) || !bytes.Equal(tg.T[2], []byte("b")) {
 387  		t.Fatal("tag fields wrong")
 388  	}
 389  	if f.Since.I64() != 100 || f.Until.I64() != 200 {
 390  		t.Fatal("timestamps not parsed")
 391  	}
 392  	if string(f.Search) != "hi" {
 393  		t.Fatalf("search = %s", string(f.Search))
 394  	}
 395  	if f.Limit == nil || *f.Limit != 7 {
 396  		t.Fatal("limit not parsed")
 397  	}
 398  }
 399  
 400  func TestUnmarshalExtraAndNested(t *testing.T) {
 401  	in := []byte("{\"foo\":true,\"bar\":false,\"baz\":null,\"num\":-12.5e3,")
 402  	in = in | "\"txt\":\"str\",\"jarr\":[1,{\"k\":2}],\"jobj\":{\"a\":[true,null]}}"
 403  	f := New()
 404  	rem, err := f.Unmarshal(in)
 405  	if err != nil {
 406  		t.Fatalf("Unmarshal error = %s", string(err.Error()))
 407  		return
 408  	}
 409  	if len(rem) != 0 {
 410  		t.Fatalf("remainder = %s", string(rem))
 411  	}
 412  	if len(f.Extra) != 7 {
 413  		t.Fatalf("extra key count = %d", int32(len(f.Extra)))
 414  	}
 415  	if string(f.Extra["foo"]) != "true" {
 416  		t.Fatalf("foo = %s", string(f.Extra["foo"]))
 417  	}
 418  	if string(f.Extra["bar"]) != "false" {
 419  		t.Fatalf("bar = %s", string(f.Extra["bar"]))
 420  	}
 421  	if string(f.Extra["baz"]) != "null" {
 422  		t.Fatalf("baz = %s", string(f.Extra["baz"]))
 423  	}
 424  	if string(f.Extra["num"]) != "-12.5e3" {
 425  		t.Fatalf("num = %s", string(f.Extra["num"]))
 426  	}
 427  	if string(f.Extra["txt"]) != "\"str\"" {
 428  		t.Fatalf("txt = %s", string(f.Extra["txt"]))
 429  	}
 430  	if string(f.Extra["jarr"]) != "[1,{\"k\":2}]" {
 431  		t.Fatalf("jarr = %s", string(f.Extra["jarr"]))
 432  	}
 433  	if string(f.Extra["jobj"]) != "{\"a\":[true,null]}" {
 434  		t.Fatalf("jobj = %s", string(f.Extra["jobj"]))
 435  	}
 436  
 437  	// A key longer than the initial key buffer forces mxutil.Ensure to grow.
 438  	f2 := New()
 439  	longKey := "x0123456789abcdefgh"
 440  	longIn := []byte("{\"") | longKey | "\":1}"
 441  	rem2, err2 := f2.Unmarshal(longIn)
 442  	if err2 != nil {
 443  		t.Fatalf("long key error = %s", string(err2.Error()))
 444  		return
 445  	}
 446  	if len(rem2) != 0 || string(f2.Extra[longKey]) != "1" {
 447  		t.Fatal("long unknown key not preserved")
 448  	}
 449  
 450  	// An unknown key that shares its leading byte with a field name used to be
 451  	// routed into that field's parser: "arr" ('a', shorter than "authors")
 452  	// errored, and "iXXX" was parsed as ids, which swallowed the rest of the
 453  	// filter and dropped the "kinds" that followed it. Both belong in Extra.
 454  	f3 := New()
 455  	rem3, err3 := f3.Unmarshal([]byte("{\"arr\":[1],\"s\":1,\"iXXX\":\"hello\",\"kinds\":[1]}"))
 456  	if err3 != nil {
 457  		t.Fatalf("colliding unknown key error = %s", string(err3.Error()))
 458  		return
 459  	}
 460  	if len(rem3) != 0 {
 461  		t.Fatalf("colliding remainder = %s", string(rem3))
 462  	}
 463  	if string(f3.Extra["arr"]) != "[1]" {
 464  		t.Fatalf("arr = %s", string(f3.Extra["arr"]))
 465  	}
 466  	if string(f3.Extra["s"]) != "1" {
 467  		t.Fatalf("s = %s", string(f3.Extra["s"]))
 468  	}
 469  	if string(f3.Extra["iXXX"]) != "\"hello\"" {
 470  		t.Fatalf("iXXX = %s", string(f3.Extra["iXXX"]))
 471  	}
 472  	if f3.Kinds == nil || f3.Kinds.Len() != 1 {
 473  		t.Fatal("kinds after a colliding unknown key was dropped")
 474  	}
 475  }
 476  
 477  func TestUnmarshalTagKeys(t *testing.T) {
 478  	f := New()
 479  	rem, err := f.Unmarshal([]byte("{\"#e\":[\"abc\"]}"))
 480  	if err != nil {
 481  		t.Fatalf("tag parse error = %s", string(err.Error()))
 482  		return
 483  	}
 484  	if len(rem) != 0 {
 485  		t.Fatalf("tag remainder = %s", string(rem))
 486  	}
 487  	if f.Tags.Len() != 1 {
 488  		t.Fatal("tag filter not stored")
 489  	}
 490  	tg := f.Tags.T[0]
 491  	if tg.Len() != 2 {
 492  		t.Fatalf("tag field count = %d", tg.Len())
 493  	}
 494  	if string(tg.T[0]) != "e" || string(tg.T[1]) != "abc" {
 495  		t.Fatal("tag fields wrong")
 496  	}
 497  
 498  	// Any single character after '#' is accepted at parse time.
 499  	f2 := New()
 500  	_, err2 := f2.Unmarshal([]byte("{\"#1\":[\"x\"]}"))
 501  	if err2 != nil {
 502  		t.Fatalf("#1 error = %s", string(err2.Error()))
 503  		return
 504  	}
 505  	if f2.Tags.Len() != 1 || string(f2.Tags.T[0].T[0]) != "1" {
 506  		t.Fatal("#1 not stored")
 507  	}
 508  
 509  	// An empty value array still produces a one-field tag.
 510  	f3 := New()
 511  	_, err3 := f3.Unmarshal([]byte("{\"#t\":[]}"))
 512  	if err3 != nil {
 513  		t.Fatalf("#t empty error = %s", string(err3.Error()))
 514  		return
 515  	}
 516  	if f3.Tags.Len() != 1 || f3.Tags.T[0].Len() != 1 {
 517  		t.Fatal("empty tag value array")
 518  	}
 519  
 520  	// A receiver whose Tags is nil gets one allocated.
 521  	f4 := &F{}
 522  	_, err4 := f4.Unmarshal([]byte("{\"#t\":[\"v\"]}"))
 523  	if err4 != nil {
 524  		t.Fatalf("#t nil receiver error = %s", string(err4.Error()))
 525  		return
 526  	}
 527  	if f4.Tags == nil || f4.Tags.Len() != 1 {
 528  		t.Fatal("nil Tags was not initialized")
 529  	}
 530  
 531  	// Distinct tag names accumulate in order.
 532  	f5 := New()
 533  	_, err5 := f5.Unmarshal([]byte("{\"#t\":[\"a\"],\"#p\":[\"b\"]}"))
 534  	if err5 != nil {
 535  		t.Fatalf("#t/#p error = %s", string(err5.Error()))
 536  		return
 537  	}
 538  	if f5.Tags.Len() != 2 {
 539  		t.Fatalf("accumulated tag count = %d", f5.Tags.Len())
 540  	}
 541  	if string(f5.Tags.T[0].T[0]) != "t" || string(f5.Tags.T[1].T[0]) != "p" {
 542  		t.Fatal("tag order wrong")
 543  	}
 544  
 545  	// A control character inside a quoted tag value aborts the parse.
 546  	f6 := New()
 547  	bad := []byte("{\"#t\":[\"a") | []byte("\n") | "\"]}"
 548  	_, err6 := f6.Unmarshal(bad)
 549  	if err6 == nil {
 550  		t.Fatal("control character in tag value must fail")
 551  	}
 552  }
 553  
 554  func TestUnmarshalWhitespaceAndSeparators(t *testing.T) {
 555  	// A space before the closing brace drives betweenKV's '}' branch.
 556  	f := New()
 557  	rem, err := f.Unmarshal([]byte("{\"kinds\":[1] }"))
 558  	if err != nil {
 559  		t.Fatalf("trailing space error = %s", string(err.Error()))
 560  		return
 561  	}
 562  	if len(rem) != 0 || f.Kinds.Len() != 1 {
 563  		t.Fatal("trailing space filter")
 564  	}
 565  	// A space before a comma drives betweenKV's ',' branch.
 566  	f2 := New()
 567  	rem2, err2 := f2.Unmarshal([]byte("{\"kinds\":[1] ,\"since\":5}"))
 568  	if err2 != nil {
 569  		t.Fatalf("space comma error = %s", string(err2.Error()))
 570  		return
 571  	}
 572  	if len(rem2) != 0 || f2.Since.I64() != 5 {
 573  		t.Fatal("space comma filter")
 574  	}
 575  }
 576  
 577  func TestUnmarshalTaintedHex(t *testing.T) {
 578  	ClearTaint()
 579  	f := New()
 580  	rem, err := f.Unmarshal([]byte("{\"ids\":[\"abcd\"]}"))
 581  	if err != nil {
 582  		t.Fatalf("undersized id error = %s", string(err.Error()))
 583  		return
 584  	}
 585  	if !IsTainted() {
 586  		t.Fatal("undersized id must set Tainted")
 587  	}
 588  	if f.Ids.Len() != 0 {
 589  		t.Fatal("undersized id must be skipped")
 590  	}
 591  	if len(rem) != 0 {
 592  		t.Fatalf("id remainder = %s", string(rem))
 593  	}
 594  
 595  	ClearTaint()
 596  	f2 := New()
 597  	_, err2 := f2.Unmarshal([]byte("{\"authors\":[\"abcd\"]}"))
 598  	if err2 != nil {
 599  		t.Fatalf("undersized author error = %s", string(err2.Error()))
 600  		return
 601  	}
 602  	if !IsTainted() {
 603  		t.Fatal("undersized author must set Tainted")
 604  	}
 605  	if f2.Authors.Len() != 0 {
 606  		t.Fatal("undersized author must be skipped")
 607  	}
 608  
 609  	ClearTaint()
 610  	f3 := New()
 611  	good := []byte("{\"ids\":[\"") | tHex(1) | "\"]}"
 612  	_, err3 := f3.Unmarshal(good)
 613  	if err3 != nil {
 614  		t.Fatalf("full-length id error = %s", string(err3.Error()))
 615  		return
 616  	}
 617  	if IsTainted() {
 618  		t.Fatal("a full-length id must not taint")
 619  	}
 620  	if f3.Ids.Len() != 1 {
 621  		t.Fatal("full-length id must be stored")
 622  	}
 623  }
 624  
 625  func TestUnmarshalMalformed(t *testing.T) {
 626  	fa := New()
 627  	if _, e1 := fa.Unmarshal([]byte("{\"\":1}")); e1 == nil {
 628  		t.Fatal("empty key must fail")
 629  	}
 630  	if _, e2 := fa.Unmarshal([]byte("{")); e2 == nil {
 631  		t.Fatal("unterminated object must fail")
 632  	}
 633  	if _, e3 := fa.Unmarshal([]byte("no-brace")); e3 == nil {
 634  		t.Fatal("input without an object must fail")
 635  	}
 636  	// A key that only abbreviates or extends a field name is not that field:
 637  	// it is unknown, so it must land in Extra with its raw value. Testing only
 638  	// the first byte plus a length bound used to send these into the field
 639  	// parser, which errored on the short ones and mis-parsed the long ones.
 640  	unknownKeys := []string{"id", "k", "a", "u", "l", "s", "seabc", "sabcde", "arr", "iXXX"}
 641  	for _, uk := range unknownKeys {
 642  		fu := New()
 643  		uin := []byte("{\"") | uk | "\":[1]}"
 644  		if _, uerr := fu.Unmarshal(uin); uerr != nil {
 645  			t.Fatal("unknown key must parse into Extra: " | uk | " -> " | string(uerr.Error()))
 646  			return
 647  		}
 648  		if _, kept := fu.Extra[uk]; !kept {
 649  			t.Fatal("unknown key not kept in Extra: " | uk)
 650  		}
 651  	}
 652  	if _, e12 := fa.Unmarshal([]byte("{\"#ee\":[\"x\"]}")); e12 == nil {
 653  		t.Fatal("two-character tag key must fail")
 654  	}
 655  	if _, e13 := fa.Unmarshal([]byte("{\"kinds\":[x]}")); e13 == nil {
 656  		t.Fatal("non-numeric kind must fail")
 657  	}
 658  	if _, e14 := fa.Unmarshal([]byte("{\"ids\":[\"zz\"]}")); e14 == nil {
 659  		t.Fatal("invalid hex in ids must fail")
 660  	}
 661  	if _, e15 := fa.Unmarshal([]byte("{\"x\":z}")); e15 == nil {
 662  		t.Fatal("invalid value on an unknown key must fail")
 663  	}
 664  	if _, e16 := fa.Unmarshal([]byte("{\"authors\":[\"zz\"]}")); e16 == nil {
 665  		t.Fatal("invalid hex in authors must fail")
 666  	}
 667  	if _, e17 := fa.Unmarshal([]byte("{\"until\":x}")); e17 == nil {
 668  		t.Fatal("non-numeric until must fail")
 669  	}
 670  	if _, e18 := fa.Unmarshal([]byte("{\"limit\":x}")); e18 == nil {
 671  		t.Fatal("non-numeric limit must fail")
 672  	}
 673  	if _, e19 := fa.Unmarshal([]byte("{\"since\":x}")); e19 == nil {
 674  		t.Fatal("non-numeric since must fail")
 675  	}
 676  	if _, e20 := fa.Unmarshal([]byte("{\"search\":\"a") | []byte("\n") | "b\"}"); e20 == nil {
 677  		t.Fatal("control character in search must fail")
 678  	}
 679  	// A value array that runs to the end of the input returns cleanly.
 680  	if _, e21 := fa.Unmarshal([]byte("{\"ids\":[]")); e21 != nil {
 681  		t.Fatalf("truncated array error = %s", string(e21.Error()))
 682  	}
 683  }
 684  
 685  func TestSkipJSONValueValid(t *testing.T) {
 686  	type skipCase struct {
 687  		in  string
 688  		val string
 689  		rem string
 690  	}
 691  	cases := []skipCase{
 692  		{"true}", "true", "}"},
 693  		{"false]", "false", "]"},
 694  		{"null,", "null", ","},
 695  		{"trueX", "true", "X"},
 696  		{"123abc", "123", "abc"},
 697  		{"-4.5e2,", "-4.5e2", ","},
 698  		{"0", "0", ""},
 699  		{"[\"a\",1]tail", "[\"a\",1]", "tail"},
 700  		{"{\"a\":1}x", "{\"a\":1}", "x"},
 701  		{`"abc"tail`, `"abc"`, "tail"},
 702  		{`"a\"b"rest`, `"a\"b"`, "rest"},
 703  		{`{"a":"}"}x`, `{"a":"}"}`, "x"},
 704  	}
 705  	for _, vc := range cases {
 706  		v, r, e := skipJSONValue([]byte(vc.in))
 707  		if e != nil {
 708  			t.Fatalf("unexpected error for %s", vc.in)
 709  		}
 710  		if string(v) != vc.val {
 711  			t.Fatalf("value for %s = %s", vc.in, string(v))
 712  		}
 713  		if string(r) != vc.rem {
 714  			t.Fatalf("remainder for %s = %s", vc.in, string(r))
 715  		}
 716  	}
 717  }
 718  
 719  func TestSkipJSONValueInvalid(t *testing.T) {
 720  	bad := []string{"", "tru", "truX", "fals", "nul", "z", "{", "[1", `"abc`, "]"}
 721  	for _, bc := range bad {
 722  		_, _, e := skipJSONValue([]byte(bc))
 723  		if e == nil {
 724  			t.Fatalf("skipJSONValue(%s) should fail", bc)
 725  		}
 726  	}
 727  }
 728  
 729  func TestFindMatchingBrace(t *testing.T) {
 730  	var e int32
 731  	var er error
 732  	e, er = findMatchingBrace([]byte("{}"), '{', '}')
 733  	if er != nil || e != 2 {
 734  		t.Fatal("empty object")
 735  	}
 736  	e, er = findMatchingBrace([]byte("[]"), '[', ']')
 737  	if er != nil || e != 2 {
 738  		t.Fatal("empty array")
 739  	}
 740  	e, er = findMatchingBrace([]byte(`{"a":{"b":2}}`), '{', '}')
 741  	if er != nil || e != 13 {
 742  		t.Fatal("nested object")
 743  	}
 744  	e, er = findMatchingBrace([]byte(`["]"]`), '[', ']')
 745  	if er != nil || e != 5 {
 746  		t.Fatal("bracket inside a string")
 747  	}
 748  	e, er = findMatchingBrace([]byte(`{"a":"}"}`), '{', '}')
 749  	if er != nil || e != 9 {
 750  		t.Fatal("brace inside a string")
 751  	}
 752  	e, er = findMatchingBrace([]byte(`{"a":"\"}"}`), '{', '}')
 753  	if er != nil || e != 11 {
 754  		t.Fatal("escaped quote must not close the string")
 755  	}
 756  	e, er = findMatchingBrace([]byte(`{"a":1`), '{', '}')
 757  	if er == nil {
 758  		t.Fatal("unterminated object must fail")
 759  	}
 760  	e, er = findMatchingBrace([]byte("[1}"), '[', ']')
 761  	if er == nil {
 762  		t.Fatal("mismatched close must fail")
 763  	}
 764  	e, er = findMatchingBrace([]byte("[1]"), '{', '}')
 765  	if er == nil {
 766  		t.Fatal("wrong opening character must fail")
 767  	}
 768  	e, er = findMatchingBrace(nil, '{', '}')
 769  	if er == nil {
 770  		t.Fatal("empty input must fail")
 771  	}
 772  }
 773  
 774  func TestFindClosingQuote(t *testing.T) {
 775  	var e int32
 776  	var er error
 777  	e, er = findClosingQuote([]byte(`"abc"rest`))
 778  	if er != nil || e != 5 {
 779  		t.Fatal("simple quoted string")
 780  	}
 781  	e, er = findClosingQuote([]byte(`"a\"b"rest`))
 782  	if er != nil || e != 6 {
 783  		t.Fatal("escaped quote")
 784  	}
 785  	e, er = findClosingQuote([]byte(`""x`))
 786  	if er != nil || e != 2 {
 787  		t.Fatal("empty quoted string")
 788  	}
 789  	e, er = findClosingQuote([]byte(`"abc`))
 790  	if er == nil {
 791  		t.Fatal("unclosed string must fail")
 792  	}
 793  	e, er = findClosingQuote([]byte("abc"))
 794  	if er == nil {
 795  		t.Fatal("input not starting with a quote must fail")
 796  	}
 797  	e, er = findClosingQuote(nil)
 798  	if er == nil {
 799  		t.Fatal("empty input must fail")
 800  	}
 801  }
 802  
 803  func TestScanNumber(t *testing.T) {
 804  	if scanNumber([]byte("123")) != 3 {
 805  		t.Fatal("integer scan")
 806  	}
 807  	if scanNumber([]byte("12x")) != 2 {
 808  		t.Fatal("trailing non-number")
 809  	}
 810  	if scanNumber([]byte("x12")) != 0 {
 811  		t.Fatal("leading non-number")
 812  	}
 813  	if scanNumber([]byte("-4.5e2,")) != 6 {
 814  		t.Fatal("exponent scan")
 815  	}
 816  	if scanNumber([]byte("0")) != 1 {
 817  		t.Fatal("single digit")
 818  	}
 819  	if scanNumber(nil) != 0 {
 820  		t.Fatal("nil input")
 821  	}
 822  }
 823  
 824  func TestNewSAndMatch(t *testing.T) {
 825  	ev := tEvent(tHash(1), tHash(2), 2, 50, nil)
 826  	f1 := New()
 827  	f1.Kinds = kind.FromIntSlice([]int32{1})
 828  	f2 := New()
 829  	f2.Kinds = kind.FromIntSlice([]int32{2})
 830  	s := NewS(f1, f2)
 831  	if s == nil || len(s.F) != 2 {
 832  		t.Fatal("NewS did not keep its filters")
 833  	}
 834  	if !s.Match(ev) {
 835  		t.Fatal("the second filter should match kind 2")
 836  	}
 837  	if s.Match(tEvent(tHash(1), tHash(2), 3, 50, nil)) {
 838  		t.Fatal("no filter should match kind 3")
 839  	}
 840  	if NewS().Match(ev) {
 841  		t.Fatal("an empty filter list matches nothing")
 842  	}
 843  	if NewS().MatchIgnoringTimestampConstraints(ev) {
 844  		t.Fatal("an empty filter list matches nothing ignoring time")
 845  	}
 846  	// The ignoring variant must accept what the timestamped variant rejects.
 847  	tf := New()
 848  	tf.Kinds = kind.FromIntSlice([]int32{2})
 849  	tf.Since = timestamp.FromUnix(1000)
 850  	st := NewS(tf)
 851  	if st.Match(ev) {
 852  		t.Fatal("time constraint should reject the event")
 853  	}
 854  	if !st.MatchIgnoringTimestampConstraints(ev) {
 855  		t.Fatal("ignoring time should accept the event")
 856  	}
 857  	if len(NewS().F) != 0 {
 858  		t.Fatal("empty NewS")
 859  	}
 860  }
 861  
 862  func TestSMarshal(t *testing.T) {
 863  	m1 := NewS(New(), New())
 864  	if string(m1.Marshal(nil)) != "[{},{}]" {
 865  		t.Fatalf("S.Marshal = %s", string(m1.Marshal(nil)))
 866  	}
 867  	if string(m1.Marshal([]byte("pre"))) != "pre[{},{}]" {
 868  		t.Fatalf("S.Marshal into dst = %s", string(m1.Marshal([]byte("pre"))))
 869  	}
 870  	if string(NewS().Marshal(nil)) != "[]" {
 871  		t.Fatalf("empty S.Marshal = %s", string(NewS().Marshal(nil)))
 872  	}
 873  	// Nil entries are skipped, at the front and at the back.
 874  	m2 := NewS()
 875  	var nf *F
 876  	m2.F = push(m2.F, nf)
 877  	m2.F = push(m2.F, New())
 878  	m2.F = push(m2.F, nf)
 879  	if string(m2.Marshal(nil)) != "[{}]" {
 880  		t.Fatalf("nil entries = %s", string(m2.Marshal(nil)))
 881  	}
 882  	// A filter with content is rendered faithfully.
 883  	m3 := NewS(New())
 884  	m3.F[0].Kinds = kind.FromIntSlice([]int32{1})
 885  	if string(m3.Marshal(nil)) != "[{\"kinds\":[1]}]" {
 886  		t.Fatalf("content S.Marshal = %s", string(m3.Marshal(nil)))
 887  	}
 888  }
 889  
 890  func TestUnmarshalFilters(t *testing.T) {
 891  	out, rem, err := UnmarshalFilters([]byte("[{\"kinds\":[1]},{\"kinds\":[2,3]}]"))
 892  	if err != nil {
 893  		t.Fatalf("array filters error = %s", string(err.Error()))
 894  		return
 895  	}
 896  	if len(out.F) != 2 {
 897  		t.Fatalf("array filter count = %d", int32(len(out.F)))
 898  	}
 899  	if len(rem) != 0 {
 900  		t.Fatalf("array remainder = %s", string(rem))
 901  	}
 902  	if out.F[0].Kinds.Len() != 1 || out.F[0].Kinds.K[0].K != 1 {
 903  		t.Fatal("first filter kinds")
 904  	}
 905  	if out.F[1].Kinds.Len() != 2 || out.F[1].Kinds.K[1].K != 3 {
 906  		t.Fatal("second filter kinds")
 907  	}
 908  
 909  	out2, rem2, err2 := UnmarshalFilters([]byte("[]"))
 910  	if err2 != nil {
 911  		t.Fatalf("empty array error = %s", string(err2.Error()))
 912  		return
 913  	}
 914  	if len(out2.F) != 0 || len(rem2) != 0 {
 915  		t.Fatal("empty array should yield no filters")
 916  	}
 917  
 918  	out3, rem3, err3 := UnmarshalFilters(nil)
 919  	if err3 != nil || len(out3.F) != 0 || rem3 != nil {
 920  		t.Fatal("nil input should yield nothing")
 921  	}
 922  
 923  	// An unwrapped single filter is accepted.
 924  	out4, rem4, err4 := UnmarshalFilters([]byte("{\"kinds\":[1]}"))
 925  	if err4 != nil {
 926  		t.Fatalf("unwrapped error = %s", string(err4.Error()))
 927  		return
 928  	}
 929  	if len(out4.F) != 1 || len(rem4) != 0 {
 930  		t.Fatal("unwrapped single filter")
 931  	}
 932  
 933  	// Several unwrapped filters separated by commas.
 934  	out5, rem5, err5 := UnmarshalFilters([]byte("{\"kinds\":[1]},{\"kinds\":[2]}"))
 935  	if err5 != nil {
 936  		t.Fatalf("unwrapped list error = %s", string(err5.Error()))
 937  		return
 938  	}
 939  	if len(out5.F) != 2 || len(rem5) != 0 {
 940  		t.Fatalf("unwrapped list count = %d", int32(len(out5.F)))
 941  	}
 942  
 943  	// A trailing ']' on an unwrapped list is left in the remainder.
 944  	out6, rem6, err6 := UnmarshalFilters([]byte("{\"kinds\":[1]}]"))
 945  	if err6 != nil {
 946  		t.Fatalf("unwrapped trailing bracket error = %s", string(err6.Error()))
 947  		return
 948  	}
 949  	if len(out6.F) != 1 || string(rem6) != "]" {
 950  		t.Fatalf("unwrapped trailing remainder = %s", string(rem6))
 951  	}
 952  
 953  	// Junk after a filter is an error.
 954  	_, _, err7 := UnmarshalFilters([]byte("[{\"kinds\":[1]}x]"))
 955  	if err7 == nil {
 956  		t.Fatal("junk after a filter must fail")
 957  	}
 958  	// A filter that fails to parse propagates its error.
 959  	_, _, err8 := UnmarshalFilters([]byte("[{\"\":1}]"))
 960  	if err8 == nil {
 961  		t.Fatal("a malformed inner filter must fail")
 962  	}
 963  	// A trailing comma ends the loop with nothing left to parse.
 964  	out9, rem9, err9 := UnmarshalFilters([]byte("[{\"kinds\":[1]},"))
 965  	if err9 != nil {
 966  		t.Fatalf("trailing comma error = %s", string(err9.Error()))
 967  		return
 968  	}
 969  	if len(out9.F) != 1 || len(rem9) != 0 {
 970  		t.Fatal("trailing comma should keep the parsed filter")
 971  	}
 972  	// More filters than the initial Ensure batch forces the filter slice to
 973  	// grow a second time, exercising the copy path.
 974  	many := []byte("[")
 975  	for i := 0; i < 18; i++ {
 976  		if i > 0 {
 977  			many = many | ","
 978  		}
 979  		many = many | "{\"kinds\":[1]}"
 980  	}
 981  	many = many | "]"
 982  	outM, remM, errM := UnmarshalFilters(many)
 983  	if errM != nil {
 984  		t.Fatalf("many filters error = %s", string(errM.Error()))
 985  		return
 986  	}
 987  	if len(outM.F) != 18 || len(remM) != 0 {
 988  		t.Fatalf("many filter count = %d", int32(len(outM.F)))
 989  	}
 990  }
 991