1 // The envelope parsers' rejection paths. The tree's string parsers are lenient
2 // about a *missing* closing quote or bracket (they hand back what they read, a
3 // contract TestUnmarshalQuoted and TestSkipToTheEnd pin), so these cases use a
4 // remainder the parser cannot finish: a trailing `junk` after the fields, or a
5 // field that cannot be parsed at all.
6 package envelope
7 8 import (
9 "testing"
10 )
11 12 // envHex64 is a 64-character hex string, the event-id shape OK requires.
13 const envHex64 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
14 15 func TestUnmarshalRejectsTruncatedInput(t *testing.T) {
16 // EOSE: junk after the subscription, so the closing bracket can never come.
17 var eose EOSE
18 if _, err := eose.Unmarshal([]byte(`"s1"junk`)); err == nil {
19 t.Fatal("EOSE must reject a payload that never closes")
20 }
21 var eose2 EOSE
22 if _, err2 := eose2.Unmarshal([]byte(`"`)); err2 == nil {
23 t.Fatal("EOSE must reject an unterminated subscription")
24 }
25 26 // NOTICE.
27 var notice Notice
28 if _, err := notice.Unmarshal([]byte(`"hello"junk`)); err == nil {
29 t.Fatal("NOTICE must reject a payload that never closes")
30 }
31 32 // CLOSED: the reason is followed by junk. A missing separator between the
33 // two strings is tolerated, because UnmarshalQuoted skips to the next
34 // opening quote by contract.
35 var closed Closed
36 if _, err := closed.Unmarshal([]byte(`"s1","why"junk`)); err == nil {
37 t.Fatal("CLOSED must reject a payload that never closes")
38 }
39 var closed2 Closed
40 if _, err2 := closed2.Unmarshal([]byte(`"s1","`)); err2 == nil {
41 t.Fatal("CLOSED must reject an unterminated reason")
42 }
43 44 // OK: a valid id and bool, then junk.
45 var ok OK
46 if _, err := ok.Unmarshal([]byte(`"` | envHex64 | `",true,"saved"junk`)); err == nil {
47 t.Fatal("OK must reject a payload that never closes")
48 }
49 // The id has to be 32 bytes of hex; a short one is rejected before the bool.
50 var ok2 OK
51 if _, err := ok2.Unmarshal([]byte(`"abcd",true,"x"]`)); err == nil {
52 t.Fatal("OK must reject a short event id")
53 }
54 55 // AUTH.
56 var ac AuthChallenge
57 if _, err := ac.Unmarshal(nil); err == nil {
58 t.Fatal("AUTH must reject an empty payload")
59 }
60 var ac2 AuthChallenge
61 if _, err2 := ac2.Unmarshal([]byte(`"`)); err2 == nil {
62 t.Fatal("AUTH must reject an unterminated challenge")
63 }
64 }
65 66 func TestUnmarshalRejectsTruncatedFilters(t *testing.T) {
67 // The filter parses, but the envelope's closing bracket never arrives.
68 var rq Req
69 if _, err := rq.Unmarshal([]byte(`"sub",{"kinds":[1]}junk`)); err == nil {
70 t.Fatal("REQ must reject a payload that never closes")
71 }
72 var cr CountRequest
73 if _, err := cr.Unmarshal([]byte(`"sub",{"kinds":[1]}junk`)); err == nil {
74 t.Fatal("COUNT request must reject a payload that never closes")
75 }
76 // A filter object that cannot be parsed at all is reported.
77 var rq2 Req
78 if _, err2 := rq2.Unmarshal([]byte(`"sub",`)); err2 == nil {
79 t.Fatal("REQ must reject a missing filter")
80 }
81 // A filter object cut short inside a value used to read past the end of
82 // the slice and kill the relay: the reslice of an exhausted buffer gave
83 // the slice a negative length, so the filter parser's `len(r) == 0` guard
84 // never fired. The compilers clamp a reslice's low bound now (moxie
85 // dd8ce5e2), so the call returns instead of crashing.
86 var rq3 Req
87 if _, err3 := rq3.Unmarshal([]byte(`"sub",{"kinds":[1`)); err3 == nil {
88 if len(rq3.Filters.F) != 1 {
89 t.Fatal("a truncated filter must keep the kinds it read")
90 }
91 }
92 var cr2 CountRequest
93 cr2.Unmarshal([]byte(`"sub",{"kinds":[1`))
94 }
95 96 func TestUnmarshalRejectsTruncatedCount(t *testing.T) {
97 var cs CountResponse
98 if _, err := cs.Unmarshal([]byte(`"sub",1junk`)); err == nil {
99 t.Fatal("COUNT response must reject a payload that never closes")
100 }
101 var cs2 CountResponse
102 if _, err2 := cs2.Unmarshal([]byte(`"sub"`)); err2 == nil {
103 t.Fatal("COUNT response must reject a missing count")
104 }
105 }
106