github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfscodec/unknown_fields_test_util.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package kbfscodec
     6  
     7  import (
     8  	"crypto/hmac"
     9  	"crypto/sha256"
    10  	"reflect"
    11  
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  type fakeEncryptedData struct {
    16  	Version       int
    17  	EncryptedData []byte
    18  	Nonce         []byte
    19  }
    20  
    21  // Extra contains some fake extra fields that can be embedded into a
    22  // struct to test handling of unknown fields.
    23  type Extra struct {
    24  	Extra1 fakeEncryptedData
    25  	Extra2 []byte
    26  	Extra3 string
    27  }
    28  
    29  // MakeExtraOrBust returns a filled-in Extra structure based on the
    30  // given prefix.
    31  func MakeExtraOrBust(prefix string, t require.TestingT) Extra {
    32  	mac := hmac.New(sha256.New, []byte("fake extra key"))
    33  	_, _ = mac.Write([]byte("fake extra buf"))
    34  	h := mac.Sum(nil)
    35  	return Extra{
    36  		Extra1: fakeEncryptedData{
    37  			Version:       2,
    38  			EncryptedData: []byte(prefix + " fake extra encrypted data"),
    39  			Nonce:         []byte(prefix + " fake extra nonce"),
    40  		},
    41  		Extra2: h,
    42  		Extra3: prefix + " extra string",
    43  	}
    44  }
    45  
    46  // Template for implementing the interfaces below for use with
    47  // TestStructUnknownFields, with MyType being the type to test, and
    48  // MySubType being the type of one of its sub-fields St, which may
    49  // also have unknown fields:
    50  //
    51  // type myTypeFuture struct {
    52  //   MyType
    53  //   // Override MyType.St.
    54  //   St mySubTypeFuture
    55  //   kbfscrypto.Extra
    56  // }
    57  //
    58  // func (mf myTypeFuture) toCurrent() MyType {
    59  //   m := mf.MyType
    60  //   m.St = m.St.toCurrent()
    61  //   return m
    62  // }
    63  //
    64  // func (mf myTypeFuture) ToCurrentStruct() kbfscrypto.CurrentStruct {
    65  //   return mf.toCurrent()
    66  // }
    67  //
    68  // func makeFakeMyTypeFuture(t *testing.T) myTypeFuture {
    69  //   mf := myTypeFuture{
    70  //     myType{
    71  //       // List elements (with nil for St) without keys, so that any change
    72  //       // to the struct will necessitate making the corresponding test
    73  //       // change.
    74  //       codec.UnknownFieldSet{},
    75  //     },
    76  //     makeFakeMySubTypeFuture(t),
    77  //     kbfscrypto.MakeExtraOrBust("MyType", t),
    78  //   }
    79  //   return mf
    80  // }
    81  //
    82  // func TestMyTypeUnknownFields(t *testing.T) {
    83  //   cFuture := kbfscodec.NewMsgpack()
    84  //   registerOpsFuture(cFuture)
    85  //
    86  //   cCurrent := kbfscodec.NewMsgpack()
    87  //   RegisterOps(cCurrent)
    88  //
    89  //   cCurrentKnownOnly := kbfscodec.NewMsgpackNoUnknownFields()
    90  //   RegisterOps(cCurrentKnownOnly)
    91  //
    92  //   kbfscodec.TestStructUnknownFields(t, makeMyTypeFuture(t))
    93  // }
    94  
    95  // CurrentStruct is an interface for the current version of a struct
    96  // type.
    97  type CurrentStruct interface{}
    98  
    99  // FutureStruct is an interface for a hypothetical future version of a
   100  // struct type.
   101  type FutureStruct interface {
   102  	// toCurrentStruct returns the fields of the current object
   103  	// copied to the current struct, with all unknown fields
   104  	// discarded.
   105  	ToCurrentStruct() CurrentStruct
   106  }
   107  
   108  // TestStructUnknownFields tests that hypothetical future versions of
   109  // a struct can be deserialized by current clients and preserve
   110  // unknown fields.
   111  func TestStructUnknownFields(t require.TestingT,
   112  	cFuture, cCurrent, cCurrentKnownOnly Codec,
   113  	sFuture FutureStruct) {
   114  	s := sFuture.ToCurrentStruct()
   115  
   116  	buf, err := cFuture.Encode(sFuture)
   117  	require.NoError(t, err)
   118  
   119  	// Make sure sFuture round-trips correctly.
   120  	sFuture2 := reflect.Zero(reflect.TypeOf(sFuture)).Interface()
   121  	err = cFuture.Decode(buf, &sFuture2)
   122  	require.NoError(t, err)
   123  	require.Equal(t, sFuture, sFuture2)
   124  
   125  	s2 := reflect.Zero(reflect.TypeOf(s)).Interface()
   126  	err = cCurrent.Decode(buf, &s2)
   127  	require.NoError(t, err)
   128  
   129  	knownS2 := reflect.Zero(reflect.TypeOf(s)).Interface()
   130  	err = cCurrentKnownOnly.Decode(buf, &knownS2)
   131  	require.NoError(t, err)
   132  
   133  	// Make sure known fields are the same.
   134  	require.Equal(t, s, knownS2)
   135  
   136  	buf2, err := cCurrent.Encode(s2)
   137  	require.NoError(t, err)
   138  
   139  	// Make sure serializing s preserves the extra fields.
   140  	require.Equal(t, buf, buf2)
   141  
   142  	// As a sanity test, make sure sFuture decodes back from buf2.
   143  	sFuture3 := reflect.Zero(reflect.TypeOf(sFuture)).Interface()
   144  	err = cFuture.Decode(buf2, &sFuture3)
   145  	require.NoError(t, err)
   146  	require.Equal(t, sFuture, sFuture3)
   147  }
   148  
   149  // TestStructUnknownFieldsMsgpack calls TestStructUnknownFields with
   150  // codecs with the msgpack codec.
   151  func TestStructUnknownFieldsMsgpack(t require.TestingT, sFuture FutureStruct) {
   152  	cFuture := NewMsgpack()
   153  	cCurrent := NewMsgpack()
   154  	cCurrentKnownOnly := NewMsgpackNoUnknownFields()
   155  
   156  	TestStructUnknownFields(t, cFuture, cCurrent, cCurrentKnownOnly, sFuture)
   157  }