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