envelope_truncated_test.mx raw

   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