kind_test.mx raw

   1  package kind
   2  
   3  import "testing"
   4  
   5  type nameCase struct {
   6  	k    uint16
   7  	want string
   8  }
   9  
  10  type classificationCase struct {
  11  	k    uint16
  12  	want string
  13  }
  14  
  15  type atoiCase struct {
  16  	in   []byte
  17  	want int32
  18  }
  19  
  20  type itoaCase struct {
  21  	n    int32
  22  	want string
  23  }
  24  
  25  // TestGetKindData pins the shape of the parsed embedded table: the four range
  26  // definitions and a couple of representative entries.
  27  func TestGetKindData(t *testing.T) {
  28  	kd := GetKindData()
  29  	if kd.Ranges.Regular.Start != 1000 || kd.Ranges.Regular.End != 9999 {
  30  		t.Fatalf("regular range = %d..%d", kd.Ranges.Regular.Start, kd.Ranges.Regular.End)
  31  	}
  32  	if kd.Ranges.Replaceable.Start != 10000 || kd.Ranges.Replaceable.End != 19999 {
  33  		t.Fatalf("replaceable range = %d..%d", kd.Ranges.Replaceable.Start, kd.Ranges.Replaceable.End)
  34  	}
  35  	if kd.Ranges.Ephemeral.Start != 20000 || kd.Ranges.Ephemeral.End != 29999 {
  36  		t.Fatalf("ephemeral range = %d..%d", kd.Ranges.Ephemeral.Start, kd.Ranges.Ephemeral.End)
  37  	}
  38  	if kd.Ranges.Parameterized.Start != 30000 || kd.Ranges.Parameterized.End != 39999 {
  39  		t.Fatalf("parameterized range = %d..%d", kd.Ranges.Parameterized.Start, kd.Ranges.Parameterized.End)
  40  	}
  41  	if len(kd.Kinds) != 184 {
  42  		t.Fatalf("kinds table length = %d", len(kd.Kinds))
  43  	}
  44  	if kd.Kinds["1"].Name != "Short Text Note" {
  45  		t.Fatalf("kind 1 name = %s", kd.Kinds["1"].Name)
  46  	}
  47  	if kd.Kinds["30023"].Name != "Long-form Content" {
  48  		t.Fatalf("kind 30023 name = %s", kd.Kinds["30023"].Name)
  49  	}
  50  }
  51  
  52  func TestEnsure(t *testing.T) {
  53  	Ensure()
  54  	kd := GetKindData()
  55  	if kd == nil {
  56  		t.Fatal("Ensure should materialize the table")
  57  	}
  58  	if GetString(1) != "Short Text Note" {
  59  		t.Fatalf("kind 1 after Ensure = %s", GetString(1))
  60  	}
  61  }
  62  
  63  func TestGetKindInfo(t *testing.T) {
  64  	info, ok := GetKindInfo(1)
  65  	if !ok {
  66  		t.Fatal("kind 1 should be known")
  67  	}
  68  	if info.Name != "Short Text Note" {
  69  		t.Fatalf("kind 1 name = %s", info.Name)
  70  	}
  71  	if info.NIP == nil || *info.NIP != "01" {
  72  		t.Fatal("kind 1 nip")
  73  	}
  74  	if info.Description != "Short-form text post (like a tweet)" {
  75  		t.Fatalf("kind 1 description = %s", info.Description)
  76  	}
  77  	info2, ok2 := GetKindInfo(12345)
  78  	if !ok2 {
  79  		t.Fatal("kind 12345 should be known")
  80  	}
  81  	if info2.NIP != nil {
  82  		t.Fatal("kind 12345 carries a null nip")
  83  	}
  84  	if info2.Spec != "orly" || info2.Classification != "replaceable" {
  85  		t.Fatalf("kind 12345 spec=%s classification=%s", info2.Spec, info2.Classification)
  86  	}
  87  	if _, ok3 := GetKindInfo(65535); ok3 {
  88  		t.Fatal("kind 65535 should be unknown")
  89  	}
  90  }
  91  
  92  func TestGetDescription(t *testing.T) {
  93  	cases := []nameCase{
  94  		{1, "Short-form text post (like a tweet)"},
  95  		{30023, "Blog post, article in markdown"},
  96  		{65535, ""},
  97  	}
  98  	for _, c := range cases {
  99  		if got := GetDescription(c.k); got != c.want {
 100  			t.Fatalf("GetDescription(%d) = %s, want %s", c.k, got, c.want)
 101  		}
 102  	}
 103  }
 104  
 105  func TestGetNIP(t *testing.T) {
 106  	cases := []nameCase{
 107  		{1, "01"},
 108  		{30023, "23"},
 109  		{12345, ""},
 110  		{65535, ""},
 111  	}
 112  	for _, c := range cases {
 113  		if got := GetNIP(c.k); got != c.want {
 114  			t.Fatalf("GetNIP(%d) = %s, want %s", c.k, got, c.want)
 115  		}
 116  	}
 117  }
 118  
 119  func TestIsDeprecated(t *testing.T) {
 120  	if !IsDeprecated(2) {
 121  		t.Fatal("kind 2 is deprecated")
 122  	}
 123  	if !IsDeprecated(4) {
 124  		t.Fatal("kind 4 is deprecated")
 125  	}
 126  	if IsDeprecated(1) {
 127  		t.Fatal("kind 1 is not deprecated")
 128  	}
 129  	if IsDeprecated(65535) {
 130  		t.Fatal("an unknown kind is not deprecated")
 131  	}
 132  }
 133  
 134  // TestGetClassification covers the explicit classification, every numeric
 135  // range bucket and both sides of each boundary.
 136  func TestGetClassification(t *testing.T) {
 137  	cases := []classificationCase{
 138  		{0, "replaceable"},
 139  		{1, "regular"},
 140  		{3, "replaceable"},
 141  		{1018, "regular"},
 142  		{10000, "replaceable"},
 143  		{19998, "replaceable"},
 144  		{19999, "regular"},
 145  		{20000, "ephemeral"},
 146  		{21000, "ephemeral"},
 147  		{29998, "ephemeral"},
 148  		{29999, "regular"},
 149  		{30000, "parameterized"},
 150  		{39999, "parameterized"},
 151  		{40000, "regular"},
 152  		{65535, "regular"},
 153  	}
 154  	for _, c := range cases {
 155  		if got := GetClassification(c.k); got != c.want {
 156  			t.Fatalf("GetClassification(%d) = %s, want %s", c.k, got, c.want)
 157  		}
 158  	}
 159  
 160  	// Kinds 0 and 3 carry an explicit classification, so the numeric fallback
 161  	// for them is only reachable with those rows temporarily removed.
 162  	kd := GetKindData()
 163  	i0 := kd.Kinds["0"]
 164  	i3 := kd.Kinds["3"]
 165  	delete(kd.Kinds, "0")
 166  	delete(kd.Kinds, "3")
 167  	c0 := GetClassification(0)
 168  	c3 := GetClassification(3)
 169  	kd.Kinds["0"] = i0
 170  	kd.Kinds["3"] = i3
 171  	if c0 != "replaceable" {
 172  		t.Fatalf("fallback classification for 0 = %s", c0)
 173  	}
 174  	if c3 != "replaceable" {
 175  		t.Fatalf("fallback classification for 3 = %s", c3)
 176  	}
 177  	if GetClassification(0) != "replaceable" {
 178  		t.Fatal("kind 0 classification after restore")
 179  	}
 180  }
 181  
 182  func TestKindAccessors(t *testing.T) {
 183  	k := New(uint16(30023))
 184  	if k.ToInt() != 30023 {
 185  		t.Fatalf("ToInt = %d", k.ToInt())
 186  	}
 187  	if k.ToI32() != 30023 {
 188  		t.Fatalf("ToI32 = %d", k.ToI32())
 189  	}
 190  	if k.ToU16() != 30023 {
 191  		t.Fatalf("ToU16 = %d", k.ToU16())
 192  	}
 193  	if k.ToU64() != 30023 {
 194  		t.Fatalf("ToU64 = %d", k.ToU64())
 195  	}
 196  	var nk *K
 197  	if nk.ToInt() != 0 || nk.ToI32() != 0 || nk.ToU16() != 0 || nk.ToU64() != 0 {
 198  		t.Fatal("a nil kind reads as zero")
 199  	}
 200  }
 201  
 202  func TestKindName(t *testing.T) {
 203  	cases := []nameCase{
 204  		{1, "Short Text Note"},
 205  		{30023, "Long-form Content"},
 206  		{65535, ""},
 207  	}
 208  	for _, c := range cases {
 209  		if got := string(New(c.k).Name()); got != c.want {
 210  			t.Fatalf("Name(%d) = %s, want %s", c.k, got, c.want)
 211  		}
 212  	}
 213  }
 214  
 215  func TestKindEqual(t *testing.T) {
 216  	k := New(uint16(30023))
 217  	if !k.Equal(30023) {
 218  		t.Fatal("kind 30023 equals itself")
 219  	}
 220  	if k.Equal(1) {
 221  		t.Fatal("kind 30023 does not equal 1")
 222  	}
 223  	var nk *K
 224  	if nk.Equal(0) {
 225  		t.Fatal("a nil kind equals nothing")
 226  	}
 227  }
 228  
 229  func TestKindMarshalUnmarshal(t *testing.T) {
 230  	k := New(uint16(30023))
 231  	if string(k.Marshal(nil)) != "30023" {
 232  		t.Fatalf("Marshal = %s", string(k.Marshal(nil)))
 233  	}
 234  	if string(k.Marshal([]byte("k="))) != "k=30023" {
 235  		t.Fatalf("Marshal into dst = %s", string(k.Marshal([]byte("k="))))
 236  	}
 237  	if string(New(uint16(0)).Marshal(nil)) != "0" {
 238  		t.Fatal("Marshal zero")
 239  	}
 240  	ku := New(uint16(0))
 241  	rem, err := ku.Unmarshal([]byte("30023rest"))
 242  	if err != nil {
 243  		t.Fatal(err)
 244  	}
 245  	if ku.K != 30023 {
 246  		t.Fatalf("Unmarshal value = %d", ku.K)
 247  	}
 248  	if string(rem) != "rest" {
 249  		t.Fatalf("Unmarshal remainder = %s", string(rem))
 250  	}
 251  	bad := New(uint16(0))
 252  	if _, err2 := bad.Unmarshal(nil); err2 == nil {
 253  		t.Fatal("K.Unmarshal of an empty input should fail")
 254  	}
 255  }
 256  
 257  func TestGetString(t *testing.T) {
 258  	if GetString(30023) != "Long-form Content" {
 259  		t.Fatalf("GetString(30023) = %s", GetString(30023))
 260  	}
 261  	if GetString(1) != "Short Text Note" {
 262  		t.Fatalf("GetString(1) = %s", GetString(1))
 263  	}
 264  	if GetString(65535) != "" {
 265  		t.Fatalf("GetString(65535) = %s", GetString(65535))
 266  	}
 267  }
 268  
 269  func TestIsChannelKind(t *testing.T) {
 270  	if !IsChannelKind(40) || !IsChannelKind(42) || !IsChannelKind(44) {
 271  		t.Fatal("kinds 40..44 are channel kinds")
 272  	}
 273  	if IsChannelKind(39) || IsChannelKind(45) || IsChannelKind(65535) {
 274  		t.Fatal("kinds outside 40..44 are not channel kinds")
 275  	}
 276  }
 277  
 278  func TestIsDiscoverableChannelKind(t *testing.T) {
 279  	if !IsDiscoverableChannelKind(40) {
 280  		t.Fatal("channel creation is discoverable")
 281  	}
 282  	if !IsDiscoverableChannelKind(41) {
 283  		t.Fatal("channel metadata is discoverable")
 284  	}
 285  	if IsDiscoverableChannelKind(42) {
 286  		t.Fatal("channel message is not discoverable")
 287  	}
 288  }
 289  
 290  func TestIsPrivileged(t *testing.T) {
 291  	if !IsPrivileged(4) || !IsPrivileged(13) || !IsPrivileged(1059) || !IsPrivileged(30078) {
 292  		t.Fatal("expected privileged kinds")
 293  	}
 294  	if IsPrivileged(1) || IsPrivileged(65535) {
 295  		t.Fatal("kind 1 and unknown kinds are not privileged")
 296  	}
 297  	if !NewS(New(uint16(4))).IsPrivileged() {
 298  		t.Fatal("S with a privileged kind")
 299  	}
 300  	if !NewS(New(uint16(1)), New(uint16(30078))).IsPrivileged() {
 301  		t.Fatal("S with a privileged kind later in the list")
 302  	}
 303  	if NewS(New(uint16(1))).IsPrivileged() {
 304  		t.Fatal("S without a privileged kind")
 305  	}
 306  	if NewS().IsPrivileged() {
 307  		t.Fatal("empty S is not privileged")
 308  	}
 309  }
 310  
 311  func TestIsDirectoryEvent(t *testing.T) {
 312  	if !IsDirectoryEvent(0) || !IsDirectoryEvent(5) || !IsDirectoryEvent(10000) {
 313  		t.Fatal("expected directory kinds")
 314  	}
 315  	if IsDirectoryEvent(1) || IsDirectoryEvent(65535) {
 316  		t.Fatal("kind 1 and unknown kinds are not directory events")
 317  	}
 318  }
 319  
 320  func TestIsEphemeral(t *testing.T) {
 321  	if !IsEphemeral(20000) || !IsEphemeral(29998) {
 322  		t.Fatal("kinds in the ephemeral range")
 323  	}
 324  	if IsEphemeral(19999) || IsEphemeral(29999) {
 325  		t.Fatal("ephemeral range is half-open")
 326  	}
 327  }
 328  
 329  func TestIsReplaceable(t *testing.T) {
 330  	if !IsReplaceable(0) || !IsReplaceable(3) || !IsReplaceable(10000) || !IsReplaceable(19998) {
 331  		t.Fatal("expected replaceable kinds")
 332  	}
 333  	if IsReplaceable(1) || IsReplaceable(9999) || IsReplaceable(19999) {
 334  		t.Fatal("unexpected replaceable kinds")
 335  	}
 336  }
 337  
 338  func TestIsParameterizedReplaceable(t *testing.T) {
 339  	if !IsParameterizedReplaceable(30000) || !IsParameterizedReplaceable(39999) {
 340  		t.Fatal("kinds in the parameterized range")
 341  	}
 342  	if IsParameterizedReplaceable(29999) || IsParameterizedReplaceable(40000) {
 343  		t.Fatal("parameterized range is half-open")
 344  	}
 345  }
 346  
 347  func TestFromIntSlice(t *testing.T) {
 348  	s := FromIntSlice([]int32{1, 5, 0, 30023, 65535})
 349  	if s.Len() != 5 {
 350  		t.Fatalf("Len = %d", s.Len())
 351  	}
 352  	if s.K[0].K != 1 || s.K[1].K != 5 || s.K[2].K != 0 || s.K[3].K != 30023 || s.K[4].K != 65535 {
 353  		t.Fatalf("values = %d %d %d %d %d", s.K[0].K, s.K[1].K, s.K[2].K, s.K[3].K, s.K[4].K)
 354  	}
 355  	e := FromIntSlice(nil)
 356  	if e.Len() != 0 {
 357  		t.Fatal("an empty int slice yields an empty S")
 358  	}
 359  }
 360  
 361  func TestSLenLessSwap(t *testing.T) {
 362  	s := NewS(New(uint16(1)), New(uint16(2)), New(uint16(3)))
 363  	if s.Len() != 3 {
 364  		t.Fatalf("Len = %d", s.Len())
 365  	}
 366  	if !s.Less(0, 2) {
 367  		t.Fatal("1 < 3")
 368  	}
 369  	if s.Less(2, 0) {
 370  		t.Fatal("3 < 1 is false")
 371  	}
 372  	s.Swap(0, 2)
 373  	if s.K[0].K != 3 || s.K[2].K != 1 {
 374  		t.Fatalf("Swap left %d .. %d", s.K[0].K, s.K[2].K)
 375  	}
 376  	e := NewS()
 377  	if e.Len() != 0 {
 378  		t.Fatal("empty S length")
 379  	}
 380  	var ns *S
 381  	if ns.Len() != 0 {
 382  		t.Fatal("nil S length")
 383  	}
 384  }
 385  
 386  func TestNewWithCap(t *testing.T) {
 387  	s := NewWithCap(8)
 388  	if s.Len() != 0 {
 389  		t.Fatalf("Len = %d", s.Len())
 390  	}
 391  	s.K = push(s.K, New(uint16(7)))
 392  	if s.Len() != 1 || s.K[0].K != 7 {
 393  		t.Fatal("push into a presized S")
 394  	}
 395  }
 396  
 397  func TestSContains(t *testing.T) {
 398  	s := NewS(New(uint16(1)), New(uint16(5)), New(uint16(30023)))
 399  	if !s.Contains(5) {
 400  		t.Fatal("contains 5")
 401  	}
 402  	if !s.Contains(30023) {
 403  		t.Fatal("contains 30023")
 404  	}
 405  	if s.Contains(2) {
 406  		t.Fatal("does not contain 2")
 407  	}
 408  	if NewS().Contains(1) {
 409  		t.Fatal("an empty S contains nothing")
 410  	}
 411  }
 412  
 413  func TestSEquals(t *testing.T) {
 414  	s := NewS(New(uint16(1)), New(uint16(2)))
 415  	if !s.Equals(s) {
 416  		t.Fatal("Equals self")
 417  	}
 418  	c := s.Clone()
 419  	if !s.Equals(c) {
 420  		t.Fatal("Equals clone")
 421  	}
 422  	if s.Equals(NewS(New(uint16(1)))) {
 423  		t.Fatal("different length is not equal")
 424  	}
 425  	if s.Equals(NewS(New(uint16(1)), New(uint16(9)))) {
 426  		t.Fatal("different element is not equal")
 427  	}
 428  }
 429  
 430  func TestSMarshalAndString(t *testing.T) {
 431  	s := NewS(New(uint16(1)), New(uint16(2)), New(uint16(10000)))
 432  	if string(s.Marshal(nil)) != "[1,2,10000]" {
 433  		t.Fatalf("Marshal = %s", string(s.Marshal(nil)))
 434  	}
 435  	if string(s.Marshal([]byte("k="))) != "k=[1,2,10000]" {
 436  		t.Fatalf("Marshal into dst = %s", string(s.Marshal([]byte("k="))))
 437  	}
 438  	if s.String() != "[1,2,10000]" {
 439  		t.Fatalf("String = %s", s.String())
 440  	}
 441  	if string(NewS().Marshal(nil)) != "[]" {
 442  		t.Fatalf("empty Marshal = %s", string(NewS().Marshal(nil)))
 443  	}
 444  }
 445  
 446  func TestSClone(t *testing.T) {
 447  	s := NewS(New(uint16(1)), New(uint16(30023)))
 448  	c := s.Clone()
 449  	if c.Len() != 2 {
 450  		t.Fatalf("clone len = %d", c.Len())
 451  	}
 452  	if c.K[0].K != 1 || c.K[1].K != 30023 {
 453  		t.Fatalf("clone values = %d %d", c.K[0].K, c.K[1].K)
 454  	}
 455  	if !c.Equals(s) {
 456  		t.Fatal("clone equals its source")
 457  	}
 458  }
 459  
 460  func TestSToUint16(t *testing.T) {
 461  	s := NewS(New(uint16(1)), New(uint16(30023)), New(uint16(65535)))
 462  	o := s.ToUint16()
 463  	if len(o) != 3 {
 464  		t.Fatalf("len = %d", len(o))
 465  	}
 466  	if o[0] != 1 || o[1] != 30023 || o[2] != 65535 {
 467  		t.Fatalf("values = %d %d %d", o[0], o[1], o[2])
 468  	}
 469  	if len(NewS().ToUint16()) != 0 {
 470  		t.Fatal("empty ToUint16")
 471  	}
 472  }
 473  
 474  func TestSUnmarshal(t *testing.T) {
 475  	s := &S{}
 476  	rem, err := s.Unmarshal([]byte("[1,2,3]"))
 477  	if err != nil {
 478  		t.Fatal(err)
 479  	}
 480  	if s.Len() != 3 {
 481  		t.Fatalf("parsed len = %d", s.Len())
 482  	}
 483  	if s.K[0].K != 1 || s.K[2].K != 3 {
 484  		t.Fatalf("parsed values = %d .. %d", s.K[0].K, s.K[2].K)
 485  	}
 486  	if len(rem) != 0 {
 487  		t.Fatalf("remainder = %s", string(rem))
 488  	}
 489  
 490  	s2 := &S{}
 491  	rem2, err2 := s2.Unmarshal([]byte("[30023,1]tail"))
 492  	if err2 != nil {
 493  		t.Fatal(err2)
 494  	}
 495  	if s2.Len() != 2 || s2.K[0].K != 30023 || s2.K[1].K != 1 {
 496  		t.Fatal("two-element parse")
 497  	}
 498  	if string(rem2) != "tail" {
 499  		t.Fatalf("remainder2 = %s", string(rem2))
 500  	}
 501  
 502  	// More kinds than push's first lazy-init batch exercises the reserve.
 503  	s3 := &S{}
 504  	rem3, err3 := s3.Unmarshal([]byte("[1,2,3,4,5,6]"))
 505  	if err3 != nil {
 506  		t.Fatal(err3)
 507  	}
 508  	if s3.Len() != 6 || s3.K[5].K != 6 {
 509  		t.Fatalf("six-element parse len = %d", s3.Len())
 510  	}
 511  	if len(rem3) != 0 {
 512  		t.Fatalf("remainder3 = %s", string(rem3))
 513  	}
 514  
 515  	s4 := &S{}
 516  	if _, err4 := s4.Unmarshal([]byte("1,2")); err4 == nil {
 517  		t.Fatal("a missing opening bracket should fail")
 518  	}
 519  	if s4.Len() != 0 {
 520  		t.Fatal("a failed parse leaves no kinds")
 521  	}
 522  
 523  	s5 := &S{}
 524  	if _, err5 := s5.Unmarshal(nil); err5 == nil {
 525  		t.Fatal("empty input should fail")
 526  	}
 527  
 528  	// An immediately closed list produces no kinds.
 529  	se := &S{}
 530  	if _, erre := se.Unmarshal([]byte("[]")); erre != nil {
 531  		t.Fatal(erre)
 532  	}
 533  	if se.Len() != 0 {
 534  		t.Fatal("an empty list parses to no kinds")
 535  	}
 536  
 537  	// Leading and doubled commas are skipped between elements.
 538  	sl := &S{}
 539  	reml, errl := sl.Unmarshal([]byte("[,1,,2]"))
 540  	if errl != nil {
 541  		t.Fatal(errl)
 542  	}
 543  	if sl.Len() != 2 || sl.K[0].K != 1 || sl.K[1].K != 2 {
 544  		t.Fatal("commas between elements")
 545  	}
 546  	if len(reml) != 0 {
 547  		t.Fatalf("remainder4 = %s", string(reml))
 548  	}
 549  
 550  	// A non-numeric element aborts the parse.
 551  	sx := &S{}
 552  	if _, errx := sx.Unmarshal([]byte("[x]")); errx == nil {
 553  		t.Fatal("a non-numeric element should fail")
 554  	}
 555  	if sx.Len() != 0 {
 556  		t.Fatal("a failed element parse leaves no kinds")
 557  	}
 558  
 559  	// An opened list with no elements simply ends.
 560  	sb := &S{}
 561  	if _, errb := sb.Unmarshal([]byte("[")); errb != nil {
 562  		t.Fatal(errb)
 563  	}
 564  	if sb.Len() != 0 {
 565  		t.Fatal("an opened empty list leaves no kinds")
 566  	}
 567  }
 568  
 569  func TestAtoi(t *testing.T) {
 570  	cases := []atoiCase{
 571  		{[]byte("0"), 0},
 572  		{[]byte("1"), 1},
 573  		{[]byte("30023"), 30023},
 574  		{[]byte("65535"), 65535},
 575  		{[]byte(""), -1},
 576  		{nil, -1},
 577  		{[]byte("12a"), -1},
 578  		{[]byte("-1"), -1},
 579  		{[]byte("1 2"), -1},
 580  	}
 581  	for _, c := range cases {
 582  		if got := atoi(c.in); got != c.want {
 583  			t.Fatalf("atoi(%s) = %d, want %d", string(c.in), got, c.want)
 584  		}
 585  	}
 586  }
 587  
 588  func TestItoa(t *testing.T) {
 589  	cases := []itoaCase{
 590  		{0, "0"},
 591  		{1, "1"},
 592  		{7, "7"},
 593  		{30023, "30023"},
 594  		{65535, "65535"},
 595  		{-7, "-7"},
 596  		{-123, "-123"},
 597  	}
 598  	for _, c := range cases {
 599  		if got := itoa(c.n); got != c.want {
 600  			t.Fatalf("itoa(%d) = %s, want %s", c.n, got, c.want)
 601  		}
 602  	}
 603  }
 604  
 605  func TestIndexBytes(t *testing.T) {
 606  	if indexBytes([]byte("hello world"), []byte("world")) != 6 {
 607  		t.Fatal("substring at offset 6")
 608  	}
 609  	if indexBytes([]byte("hello"), []byte("hello")) != 0 {
 610  		t.Fatal("whole-string match")
 611  	}
 612  	if indexBytes([]byte("hello"), []byte("xyz")) != -1 {
 613  		t.Fatal("absent needle")
 614  	}
 615  	if indexBytes([]byte("ab"), []byte("abc")) != -1 {
 616  		t.Fatal("needle longer than haystack")
 617  	}
 618  	if indexBytes(nil, []byte("a")) != -1 {
 619  		t.Fatal("nil haystack")
 620  	}
 621  	if indexBytes([]byte("xab"), []byte("ab")) != 1 {
 622  		t.Fatal("match after a partial prefix")
 623  	}
 624  }
 625  
 626  func TestSkipWS(t *testing.T) {
 627  	if string(skipWS([]byte(" \t\n\r abc"))) != "abc" {
 628  		t.Fatal("leading whitespace")
 629  	}
 630  	if string(skipWS([]byte("abc"))) != "abc" {
 631  		t.Fatal("no whitespace")
 632  	}
 633  	if len(skipWS([]byte("   "))) != 0 {
 634  		t.Fatal("all whitespace")
 635  	}
 636  	if len(skipWS(nil)) != 0 {
 637  		t.Fatal("nil input")
 638  	}
 639  }
 640  
 641  func TestMatchBrace(t *testing.T) {
 642  	if matchBrace([]byte("{}")) != 1 {
 643  		t.Fatal("empty object")
 644  	}
 645  	if matchBrace([]byte(`{"a":1}`)) != 6 {
 646  		t.Fatal("flat object")
 647  	}
 648  	if matchBrace([]byte(`{"a":{"b":2}}`)) != 12 {
 649  		t.Fatal("nested object")
 650  	}
 651  	if matchBrace([]byte(`{"a":"}"}`)) != 8 {
 652  		t.Fatal("brace inside a string")
 653  	}
 654  	if matchBrace([]byte(`{"a":"\"}"}`)) != 10 {
 655  		t.Fatal("escaped quote must not close the string")
 656  	}
 657  	if matchBrace([]byte(`{"a":1`)) != 5 {
 658  		t.Fatal("unterminated object returns the last index")
 659  	}
 660  	if matchBrace(nil) != -1 {
 661  		t.Fatal("empty input")
 662  	}
 663  }
 664  
 665  func TestParseQuoted(t *testing.T) {
 666  	v, rest := parseQuoted([]byte(`"abc"tail`))
 667  	if string(v) != "abc" || string(rest) != "tail" {
 668  		t.Fatalf("simple: %s %s", string(v), string(rest))
 669  	}
 670  	v2, rest2 := parseQuoted([]byte(`"a\"b"rest`))
 671  	if string(v2) != `a\"b` || string(rest2) != "rest" {
 672  		t.Fatalf("escaped: %s %s", string(v2), string(rest2))
 673  	}
 674  	v3, rest3 := parseQuoted([]byte(`"abc`))
 675  	if v3 != nil || rest3 != nil {
 676  		t.Fatal("unterminated string")
 677  	}
 678  	v4, rest4 := parseQuoted([]byte("abc"))
 679  	if v4 != nil || rest4 != nil {
 680  		t.Fatal("input does not start with a quote")
 681  	}
 682  	v5, rest5 := parseQuoted(nil)
 683  	if v5 != nil || rest5 != nil {
 684  		t.Fatal("nil input")
 685  	}
 686  	v6, rest6 := parseQuoted([]byte(`""x`))
 687  	if len(v6) != 0 || string(rest6) != "x" {
 688  		t.Fatal("empty quoted string")
 689  	}
 690  }
 691  
 692  func TestGetStringField(t *testing.T) {
 693  	obj := []byte(`{"name":"Foo","sp" : "Spaced","raw":5}`)
 694  	if string(getStringField(obj, []byte(`"name"`))) != "Foo" {
 695  		t.Fatal("name field")
 696  	}
 697  	if string(getStringField(obj, []byte(`"sp"`))) != "Spaced" {
 698  		t.Fatal("value separated by spaces")
 699  	}
 700  	if getStringField(obj, []byte(`"raw"`)) != nil {
 701  		t.Fatal("an unquoted value is nil")
 702  	}
 703  	if getStringField(obj, []byte(`"absent"`)) != nil {
 704  		t.Fatal("an absent key is nil")
 705  	}
 706  	if getStringField(nil, []byte(`"name"`)) != nil {
 707  		t.Fatal("nil object")
 708  	}
 709  	if string(getStringField([]byte(`{"name":""}`), []byte(`"name"`))) != "" {
 710  		t.Fatal("empty value")
 711  	}
 712  }
 713  
 714  func TestGetIntField(t *testing.T) {
 715  	obj := []byte(`{"start":1000,"end": 9999,"x":"7"}`)
 716  	if getIntField(obj, []byte(`"start"`)) != 1000 {
 717  		t.Fatal("start")
 718  	}
 719  	if getIntField(obj, []byte(`"end"`)) != 9999 {
 720  		t.Fatal("end with leading space")
 721  	}
 722  	if getIntField(obj, []byte(`"absent"`)) != 0 {
 723  		t.Fatal("absent key is zero")
 724  	}
 725  	if getIntField(obj, []byte(`"x"`)) != 0 {
 726  		t.Fatal("non-numeric value is zero")
 727  	}
 728  	if getIntField(nil, []byte(`"start"`)) != 0 {
 729  		t.Fatal("nil object")
 730  	}
 731  }
 732  
 733  func TestParseRange(t *testing.T) {
 734  	data := []byte(`{"regular":{"start":1000,"end":9999,"description":"Reg"},"ephemeral":{"start":20000,"end":29999,"description":"Eph"}}`)
 735  	r := parseRange(data, []byte(`"regular"`))
 736  	if r.Start != 1000 || r.End != 9999 || r.Description != "Reg" {
 737  		t.Fatalf("regular = %d..%d %s", r.Start, r.End, r.Description)
 738  	}
 739  	r2 := parseRange(data, []byte(`"ephemeral"`))
 740  	if r2.Start != 20000 || r2.End != 29999 || r2.Description != "Eph" {
 741  		t.Fatalf("ephemeral = %d..%d %s", r2.Start, r2.End, r2.Description)
 742  	}
 743  	r3 := parseRange(data, []byte(`"missing"`))
 744  	if r3.Start != 0 || r3.End != 0 || r3.Description != "" {
 745  		t.Fatal("a missing key yields the zero Range")
 746  	}
 747  	r4 := parseRange([]byte(`"regular": 5`), []byte(`"regular"`))
 748  	if r4.Start != 0 || r4.End != 0 {
 749  		t.Fatal("a key without an object yields the zero Range")
 750  	}
 751  	r5 := parseRange([]byte(`"regular" : {"start":7}`), []byte(`"regular"`))
 752  	if r5.Start != 7 || r5.End != 0 {
 753  		t.Fatalf("whitespace before brace: start=%d", r5.Start)
 754  	}
 755  }
 756  
 757  func TestParseKindInfoObj(t *testing.T) {
 758  	obj := []byte(`{"name":"Foo","nip":"42","description":"Desc","classification":"regular","deprecated":true,"deprecatedBy":"NIP-99","spec":"spec-x","rangeEnd":1234}`)
 759  	info, rest := parseKindInfoObj(obj)
 760  	if info.Name != "Foo" || info.Description != "Desc" || info.Classification != "regular" {
 761  		t.Fatalf("basic fields: %s", info.Name)
 762  	}
 763  	if info.DeprecatedBy != "NIP-99" || info.Spec != "spec-x" || info.RangeEnd != 1234 {
 764  		t.Fatalf("extra fields: %s %s %d", info.DeprecatedBy, info.Spec, info.RangeEnd)
 765  	}
 766  	if !info.Deprecated {
 767  		t.Fatal("deprecated true")
 768  	}
 769  	if info.NIP == nil || *info.NIP != "42" {
 770  		t.Fatal("nip")
 771  	}
 772  	if len(rest) != 0 {
 773  		t.Fatalf("rest = %s", string(rest))
 774  	}
 775  
 776  	info2, rest2 := parseKindInfoObj([]byte(`{"name":"T"}tail`))
 777  	if info2.Name != "T" || string(rest2) != "tail" {
 778  		t.Fatalf("trailing rest = %s", string(rest2))
 779  	}
 780  
 781  	info3, _ := parseKindInfoObj([]byte(`{"name":"Baz","nip":null}`))
 782  	if info3.NIP != nil {
 783  		t.Fatal("a null nip stays nil")
 784  	}
 785  	if info3.Deprecated {
 786  		t.Fatal("absent deprecated is false")
 787  	}
 788  
 789  	info4, _ := parseKindInfoObj([]byte(`{"name":"Sp","deprecated": true}`))
 790  	if !info4.Deprecated {
 791  		t.Fatal("deprecated with a space")
 792  	}
 793  
 794  	info5, rest5 := parseKindInfoObj([]byte("x{}"))
 795  	if rest5 != nil || info5.Name != "" {
 796  		t.Fatal("non-object input yields the zero KindInfo")
 797  	}
 798  	info6, rest6 := parseKindInfoObj(nil)
 799  	if rest6 != nil || info6.Name != "" {
 800  		t.Fatal("empty input yields the zero KindInfo")
 801  	}
 802  
 803  	info7, rest7 := parseKindInfoObj([]byte(`{"name":"N","description":"has { and } inside"}`))
 804  	if info7.Description != "has { and } inside" || len(rest7) != 0 {
 805  		t.Fatalf("braces inside a string: %s", info7.Description)
 806  	}
 807  }
 808  
 809  func TestParseKindsJSON(t *testing.T) {
 810  	kd := GetKindData()
 811  	before := len(kd.Kinds)
 812  
 813  	// No kinds key at all: the table must be untouched.
 814  	parseKindsJSON([]byte(`{"version":"x"}`))
 815  	if len(kd.Kinds) != before {
 816  		t.Fatal("json without a kinds key changed the table")
 817  	}
 818  	// Kinds key present but no object follows.
 819  	parseKindsJSON([]byte(`"kinds" oops`))
 820  	if len(kd.Kinds) != before {
 821  		t.Fatal("kinds key without an object changed the table")
 822  	}
 823  	// Malformed entries must break out without inserting anything.
 824  	parseKindsJSON([]byte(`{"kinds":{"60010"}}`))
 825  	if kd.Kinds["60010"].Name != "" {
 826  		t.Fatal("entry without a colon was inserted")
 827  	}
 828  	parseKindsJSON([]byte(`{"kinds":{x}}`))
 829  	parseKindsJSON([]byte(`{"kinds":{"60011":x}}`))
 830  	if kd.Kinds["60011"].Name != "" {
 831  		t.Fatal("entry without an object was inserted")
 832  	}
 833  
 834  	// A well-formed object, including a leading comma, a spaced value, a
 835  	// missing nip and a non-numeric key.
 836  	parseKindsJSON([]byte(`{"kinds":{, "60001":{"name":"Custom One","nip":"99","description":"d1","classification":"regular","deprecated":true,"deprecatedBy":"NIP-99","spec":"s","rangeEnd":60009},"60002":{"name":"Custom Two"},"abc":{"name":"NonNumeric"}}}`))
 837  
 838  	i1 := kd.Kinds["60001"]
 839  	if i1.Name != "Custom One" || i1.Description != "d1" || i1.Classification != "regular" {
 840  		t.Fatalf("custom entry fields: %s", i1.Name)
 841  	}
 842  	if i1.DeprecatedBy != "NIP-99" || i1.Spec != "s" || i1.RangeEnd != 60009 || !i1.Deprecated {
 843  		t.Fatalf("custom entry extras: %s %s %d", i1.DeprecatedBy, i1.Spec, i1.RangeEnd)
 844  	}
 845  	if i1.NIP == nil || *i1.NIP != "99" {
 846  		t.Fatal("custom entry nip")
 847  	}
 848  	if kd.Kinds["60002"].Name != "Custom Two" {
 849  		t.Fatal("second custom entry")
 850  	}
 851  	if kd.Kinds["abc"].Name != "NonNumeric" {
 852  		t.Fatal("a non-numeric key is kept in the table")
 853  	}
 854  	if Map[uint16(60001)] != "Custom One" || Map[uint16(60002)] != "Custom Two" {
 855  		t.Fatal("numeric custom names missing from Map")
 856  	}
 857  
 858  	delete(kd.Kinds, "60001")
 859  	delete(kd.Kinds, "60002")
 860  	delete(kd.Kinds, "abc")
 861  	delete(Map, uint16(60001))
 862  	delete(Map, uint16(60002))
 863  	if len(kd.Kinds) != before {
 864  		t.Fatalf("custom entries not cleaned up: %d", len(kd.Kinds))
 865  	}
 866  }
 867  
 868  // A truncated kinds array read one past the end of its input and the relay
 869  // died on a malformed REQ (`{"kinds":[1`). The parse stays lenient about the
 870  // missing bracket, as `[` already documented, but it must not read past the
 871  // end.
 872  func TestSUnmarshalTruncatedArray(t *testing.T) {
 873  	s := NewWithCap(0)
 874  	if _, err := s.Unmarshal([]byte("[1,2")); err != nil {
 875  		t.Fatal(err)
 876  		return
 877  	}
 878  	if s.Len() != 2 {
 879  		t.Fatalf("a truncated array parsed %d kinds", s.Len())
 880  	}
 881  	s2 := NewWithCap(0)
 882  	if _, err := s2.Unmarshal([]byte("[")); err != nil {
 883  		t.Fatal(err)
 884  		return
 885  	}
 886  	if s2.Len() != 0 {
 887  		t.Fatal("an opened empty list leaves no kinds")
 888  	}
 889  	// The bound did not change the complete forms.
 890  	s3 := NewWithCap(0)
 891  	if _, err := s3.Unmarshal([]byte("[1,2]")); err != nil {
 892  		t.Fatal(err)
 893  		return
 894  	}
 895  	if s3.Len() != 2 {
 896  		t.Fatalf("parsed kinds = %d", s3.Len())
 897  	}
 898  }
 899