github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/persist/fs/msgpack/decoder_test.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE 20 21 package msgpack 22 23 import ( 24 "testing" 25 26 "github.com/m3db/m3/src/dbnode/digest" 27 "github.com/stretchr/testify/require" 28 ) 29 30 func testGenEncodeNumObjectFieldsForFn( 31 enc *Encoder, 32 targetType objectType, 33 delta int, 34 ) encodeNumObjectFieldsForFn { 35 return func(objType objectType) { 36 if objType == targetType { 37 _, curr := numFieldsForType(objType) 38 enc.encodeArrayLenFn(curr + delta) 39 return 40 } 41 enc.encodeNumObjectFieldsFor(objType) 42 } 43 } 44 45 func TestDecodeNewerVersionThanExpected(t *testing.T) { 46 var ( 47 enc = NewEncoder() 48 dec = NewDecoder(nil) 49 ) 50 51 // Intentionally bump client-side version 52 enc.encodeVersionFn = func(version int) { 53 enc.encodeVersion(version + 1) 54 } 55 56 // Verify decoding index info results in an error 57 require.NoError(t, enc.EncodeIndexInfo(testIndexInfo)) 58 dec.Reset(NewByteDecoderStream(enc.Bytes())) 59 _, err := dec.DecodeIndexInfo() 60 require.Error(t, err) 61 62 // Verify decoding index entry results in an error 63 require.NoError(t, enc.EncodeIndexEntry(testIndexEntry)) 64 dec.Reset(NewByteDecoderStream(enc.Bytes())) 65 _, err = dec.DecodeIndexEntry(nil) 66 require.Error(t, err) 67 68 // Verify decoding log info results in an error 69 require.NoError(t, enc.EncodeLogInfo(testLogInfo)) 70 dec.Reset(NewByteDecoderStream(enc.Bytes())) 71 _, err = dec.DecodeLogInfo() 72 require.Error(t, err) 73 74 // Verify decoding log entry results in an error 75 require.NoError(t, enc.EncodeLogEntry(testLogEntry)) 76 dec.Reset(NewByteDecoderStream(enc.Bytes())) 77 _, err = dec.DecodeLogEntry() 78 require.Error(t, err) 79 80 // Verify decoding log metadata results in an error 81 require.NoError(t, enc.EncodeLogMetadata(testLogMetadata)) 82 dec.Reset(NewByteDecoderStream(enc.Bytes())) 83 _, err = dec.DecodeLogMetadata() 84 require.Error(t, err) 85 } 86 87 func TestDecodeRootObjectMoreFieldsThanExpected(t *testing.T) { 88 var ( 89 enc = NewEncoder() 90 dec = NewDecoder(nil) 91 ) 92 93 // Intentionally bump number of fields for the root object 94 enc.encodeNumObjectFieldsForFn = testGenEncodeNumObjectFieldsForFn(enc, rootObjectType, 1) 95 require.NoError(t, enc.EncodeIndexInfo(testIndexInfo)) 96 require.NoError(t, enc.enc.EncodeInt64(1234)) 97 98 // Verify we can successfully skip unnecessary fields 99 dec.Reset(NewByteDecoderStream(enc.Bytes())) 100 res, err := dec.DecodeIndexInfo() 101 require.NoError(t, err) 102 require.Equal(t, testIndexInfo, res) 103 } 104 105 func TestDecodeIndexInfoMoreFieldsThanExpected(t *testing.T) { 106 var ( 107 enc = NewEncoder() 108 dec = NewDecoder(nil) 109 ) 110 111 // Intentionally bump number of fields for the index info object 112 enc.encodeNumObjectFieldsForFn = testGenEncodeNumObjectFieldsForFn(enc, indexInfoType, 1) 113 require.NoError(t, enc.EncodeIndexInfo(testIndexInfo)) 114 require.NoError(t, enc.enc.EncodeInt64(1234)) 115 116 // Verify we can successfully skip unnecessary fields 117 dec.Reset(NewByteDecoderStream(enc.Bytes())) 118 res, err := dec.DecodeIndexInfo() 119 require.NoError(t, err) 120 require.Equal(t, testIndexInfo, res) 121 } 122 123 func TestDecodeIndexEntryMoreFieldsThanExpected(t *testing.T) { 124 var ( 125 enc = NewEncoder() 126 dec = NewDecoder(nil) 127 ) 128 129 // Intentionally bump number of fields for the index entry object 130 enc.encodeNumObjectFieldsForFn = testGenEncodeNumObjectFieldsForFn(enc, indexEntryType, 1) 131 require.NoError(t, enc.EncodeIndexEntry(testIndexEntry)) 132 133 // This hokey bit of logic is done so we can add extra fields in the correct location (since new IndexEntry fields 134 // will be added *before* the checksum). Confirm current checksum is correct, strip it, add unexpected field, 135 // and re-add updated checksum value 136 137 // Validate existing checksum 138 checksumPos := len(enc.Bytes()) - 5 // 5 bytes = 1 byte for integer code + 4 bytes for checksum 139 dec.Reset(NewByteDecoderStream(enc.Bytes()[checksumPos:])) 140 currChecksum := dec.decodeVarint() 141 require.Equal(t, currChecksum, int64(digest.Checksum(enc.Bytes()[:checksumPos]))) 142 143 // Strip checksum, add new field, add updated checksum 144 enc.buf.Truncate(len(enc.Bytes()) - 5) 145 require.NoError(t, enc.enc.EncodeInt64(1234)) 146 checksum := int64(digest.Checksum(enc.Bytes())) 147 require.NoError(t, enc.enc.EncodeInt64(checksum)) 148 expected := testIndexEntry 149 expected.IndexChecksum = checksum 150 151 // Verify we can successfully skip unnecessary fields 152 dec.Reset(NewByteDecoderStream(enc.Bytes())) 153 res, err := dec.DecodeIndexEntry(nil) 154 require.NoError(t, err) 155 require.Equal(t, expected, res) 156 } 157 158 func TestDecodeLogInfoMoreFieldsThanExpected(t *testing.T) { 159 var ( 160 enc = NewEncoder() 161 dec = NewDecoder(nil) 162 ) 163 164 // Intentionally bump number of fields for the log info object 165 enc.encodeNumObjectFieldsForFn = testGenEncodeNumObjectFieldsForFn(enc, logInfoType, 1) 166 require.NoError(t, enc.EncodeLogInfo(testLogInfo)) 167 require.NoError(t, enc.enc.EncodeInt64(1234)) 168 169 // Verify we can successfully skip unnecessary fields 170 dec.Reset(NewByteDecoderStream(enc.Bytes())) 171 res, err := dec.DecodeLogInfo() 172 require.NoError(t, err) 173 require.Equal(t, testLogInfo, res) 174 } 175 176 func TestDecodeLogEntryMoreFieldsThanExpected(t *testing.T) { 177 var ( 178 enc = NewEncoder() 179 dec = NewDecoder(nil) 180 ) 181 182 // Intentionally bump number of fields for the log entry object 183 enc.encodeNumObjectFieldsForFn = testGenEncodeNumObjectFieldsForFn(enc, logEntryType, 1) 184 require.NoError(t, enc.EncodeLogEntry(testLogEntry)) 185 require.NoError(t, enc.enc.EncodeInt64(1234)) 186 187 // Verify we can successfully skip unnecessary fields 188 dec.Reset(NewByteDecoderStream(enc.Bytes())) 189 res, err := dec.DecodeLogEntry() 190 require.NoError(t, err) 191 require.Equal(t, testLogEntry, res) 192 } 193 194 func TestDecodeLogMetadataMoreFieldsThanExpected(t *testing.T) { 195 var ( 196 enc = NewEncoder() 197 dec = NewDecoder(nil) 198 ) 199 200 // Intentionally bump number of fields for the log metadata object 201 enc.encodeNumObjectFieldsForFn = testGenEncodeNumObjectFieldsForFn(enc, logMetadataType, 1) 202 require.NoError(t, enc.EncodeLogMetadata(testLogMetadata)) 203 require.NoError(t, enc.enc.EncodeInt64(1234)) 204 205 // Verify we can successfully skip unnecessary fields 206 dec.Reset(NewByteDecoderStream(enc.Bytes())) 207 res, err := dec.DecodeLogMetadata() 208 require.NoError(t, err) 209 require.Equal(t, testLogMetadata, res) 210 } 211 212 func TestDecodeLogEntryFewerFieldsThanExpected(t *testing.T) { 213 var ( 214 enc = NewEncoder() 215 dec = NewDecoder(nil) 216 ) 217 218 // Intentionally bump number of fields for the log entry object 219 enc.encodeNumObjectFieldsForFn = testGenEncodeNumObjectFieldsForFn(enc, logEntryType, -1) 220 require.NoError(t, enc.EncodeLogEntry(testLogEntry)) 221 222 // Verify we can successfully skip unnecessary fields 223 dec.Reset(NewByteDecoderStream(enc.Bytes())) 224 _, err := dec.DecodeLogEntry() 225 require.Error(t, err) 226 } 227 228 func TestDecodeBytesNoAlloc(t *testing.T) { 229 var ( 230 enc = NewEncoder() 231 dec = NewDecoder(nil) 232 ) 233 234 require.NoError(t, enc.EncodeIndexEntry(testIndexEntry)) 235 data := enc.Bytes() 236 dec.Reset(NewByteDecoderStream(data)) 237 res, err := dec.DecodeIndexEntry(nil) 238 require.NoError(t, err) 239 require.Equal(t, []byte("testIndexEntry"), res.ID) 240 241 // Verify ID points to part of encoded data stream 242 for i := 0; i < len(data); i++ { 243 data[i] = byte('a') 244 } 245 require.Equal(t, []byte("aaaaaaaaaaaaaa"), res.ID) 246 } 247 248 func TestDecodeBytesAllocNew(t *testing.T) { 249 var ( 250 enc = NewEncoder() 251 dec = NewDecoder(NewDecodingOptions().SetAllocDecodedBytes(true)) 252 ) 253 254 require.NoError(t, enc.EncodeIndexEntry(testIndexEntry)) 255 data := enc.Bytes() 256 dec.Reset(NewByteDecoderStream(data)) 257 res, err := dec.DecodeIndexEntry(nil) 258 require.NoError(t, err) 259 require.Equal(t, []byte("testIndexEntry"), res.ID) 260 261 // Verify ID is not part of the encoded byte stream 262 for i := 0; i < len(data); i++ { 263 data[i] = byte('a') 264 } 265 require.Equal(t, []byte("testIndexEntry"), res.ID) 266 } 267 268 func TestDecodeIndexEntryInvalidWideEntry(t *testing.T) { 269 var ( 270 enc = NewEncoder() 271 dec = NewDecoder(nil) 272 ) 273 require.NoError(t, enc.EncodeIndexEntry(testIndexEntry)) 274 275 // Update to invalid checksum 276 enc.buf.Truncate(len(enc.Bytes()) - 5) // 5 bytes = 1 byte for integer code + 4 bytes for checksum 277 require.NoError(t, enc.enc.EncodeInt64(1234)) 278 279 dec.Reset(NewByteDecoderStream(enc.Bytes())) 280 _, err := dec.DecodeIndexEntry(nil) 281 require.EqualError(t, err, errorIndexEntryChecksumMismatch.Error()) 282 } 283 284 func TestDecodeIndexEntryIncompleteFile(t *testing.T) { 285 var ( 286 enc = NewEncoder() 287 dec = NewDecoder(nil) 288 ) 289 require.NoError(t, enc.EncodeIndexEntry(testIndexEntry)) 290 291 enc.buf.Truncate(len(enc.Bytes()) - 4) 292 293 dec.Reset(NewByteDecoderStream(enc.Bytes())) 294 _, err := dec.DecodeIndexEntry(nil) 295 require.EqualError(t, err, "decode index entry encountered error: EOF") 296 }