go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/client/butlerlib/streamproto/handshake_test.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package streamproto
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/binary"
    20  	"fmt"
    21  	"testing"
    22  	"time"
    23  
    24  	. "github.com/smartystreets/goconvey/convey"
    25  	"go.chromium.org/luci/common/clock/clockflag"
    26  	. "go.chromium.org/luci/common/testing/assertions"
    27  )
    28  
    29  func TestHandshakeProtocol(t *testing.T) {
    30  	Convey(`test WriteHandshake/FromHandshake`, t, func() {
    31  		buf := &bytes.Buffer{}
    32  		writeUvarint := func(val uint64) {
    33  			uvarBuf := make([]byte, binary.MaxVarintLen64)
    34  			buf.Write(uvarBuf[:binary.PutUvarint(uvarBuf, val)])
    35  		}
    36  
    37  		f := &Flags{}
    38  
    39  		Convey(`Will fail if no handshake data is provided.`, func() {
    40  			So(f.FromHandshake(buf), ShouldErrLike, "reading magic number: EOF")
    41  		})
    42  
    43  		Convey(`Will fail with an invalid handshake protocol.`, func() {
    44  			buf.Write([]byte{0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA})
    45  			So(f.FromHandshake(buf), ShouldErrLike, "magic number mismatch")
    46  		})
    47  
    48  		Convey(`Loading a handshake frame starting with an invalid size varint value must fail.`, func() {
    49  			buf.Write(ProtocolFrameHeaderMagic)
    50  			buf.Write([]byte{
    51  				// invalid uvarint
    52  				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x79,
    53  			})
    54  			So(f.FromHandshake(buf), ShouldErrLike, "overflows a 64-bit integer")
    55  		})
    56  
    57  		Convey(`Loading a handshake frame larger than the maximum header size must fail.`, func() {
    58  			buf.Write(ProtocolFrameHeaderMagic)
    59  			writeUvarint(maxFrameSize + 1)
    60  			So(f.FromHandshake(buf), ShouldErrLike, "frame size exceeds maximum")
    61  		})
    62  
    63  		Convey(`Loading an JSON object with just a name`, func() {
    64  			data := `{"name": "test"}`
    65  			buf.Write(ProtocolFrameHeaderMagic)
    66  			writeUvarint(uint64(len(data)))
    67  			buf.Write([]byte(data))
    68  
    69  			So(f.FromHandshake(buf), ShouldBeNil)
    70  			So(f.Name, ShouldEqual, StreamNameFlag("test"))
    71  		})
    72  
    73  		Convey(`Loading a fully-specified configuration`, func() {
    74  			date := "2015-05-07T01:29:51+00:00"
    75  			timestamp, err := time.ParseInLocation(time.RFC3339, date, nil)
    76  			So(err, ShouldBeNil)
    77  
    78  			Convey(`manually written handshake`, func() {
    79  				data := fmt.Sprintf(`{
    80  				"name": "test", "timestamp": %q,
    81  				"contentType": "text/plain; charset=utf-8",
    82  				"tags": {"foo": "bar", "baz": "qux"}
    83  			}`, date)
    84  				buf.Write(ProtocolFrameHeaderMagic)
    85  				writeUvarint(uint64(len(data)))
    86  				buf.Write([]byte(data))
    87  			})
    88  			Convey(`WriteHandshake`, func() {
    89  				f.Name = "test"
    90  				f.Timestamp = clockflag.Time(timestamp)
    91  				f.ContentType = "text/plain; charset=utf-8"
    92  				f.Tags = TagMap{
    93  					"foo": "bar",
    94  					"baz": "qux",
    95  				}
    96  				So(f.WriteHandshake(buf), ShouldBeNil)
    97  				f = &Flags{}
    98  			})
    99  
   100  			So(f.FromHandshake(buf), ShouldBeNil)
   101  
   102  			So(f, ShouldResemble, &Flags{
   103  				Name:        "test",
   104  				ContentType: "text/plain; charset=utf-8",
   105  				Timestamp:   clockflag.Time(timestamp),
   106  				Tags: map[string]string{
   107  					"baz": "qux",
   108  					"foo": "bar",
   109  				},
   110  			})
   111  		})
   112  
   113  		Convey(`Loading a (valid) JSON array should fail to load.`, func() {
   114  			data := `["This is an array!"]`
   115  			buf.Write(ProtocolFrameHeaderMagic)
   116  			writeUvarint(uint64(len(data)))
   117  			buf.Write([]byte(data))
   118  
   119  			So(f.FromHandshake(buf), ShouldErrLike, "cannot unmarshal array")
   120  		})
   121  
   122  		Convey(`Loading an empty JSON object with a larger-than-necessary header size should fail.`, func() {
   123  			data := `{}`
   124  			buf.Write(ProtocolFrameHeaderMagic)
   125  			writeUvarint(uint64(len(data) + 10))
   126  			buf.Write([]byte(data))
   127  
   128  			So(f.FromHandshake(buf), ShouldErrLike, "handshake had 10 bytes of trailing data")
   129  		})
   130  
   131  		Convey(`Loading a JSON with bad field contents should fail.`, func() {
   132  			data := `{"timestamp": "text-for-some-reason"}`
   133  			buf.Write(ProtocolFrameHeaderMagic)
   134  			writeUvarint(uint64(len(data)))
   135  			buf.Write([]byte(data))
   136  
   137  			So(f.FromHandshake(buf), ShouldErrLike, "cannot parse")
   138  		})
   139  
   140  		Convey(`Loading an invalid JSON descriptor should fail.`, func() {
   141  			data := `invalid`
   142  			buf.Write(ProtocolFrameHeaderMagic)
   143  			writeUvarint(uint64(len(data)))
   144  			buf.Write([]byte(data))
   145  
   146  			So(f.FromHandshake(buf), ShouldErrLike, "invalid character 'i'")
   147  		})
   148  
   149  	})
   150  }