github.com/prebid/prebid-server/v2@v2.18.0/util/jsonutil/jsonutil_test.go (about) 1 package jsonutil 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "strings" 8 "testing" 9 "unsafe" 10 11 jsoniter "github.com/json-iterator/go" 12 "github.com/modern-go/reflect2" 13 "github.com/stretchr/testify/assert" 14 ) 15 16 func TestDropElement(t *testing.T) { 17 tests := []struct { 18 description string 19 input []byte 20 elementToRemove []string 21 output []byte 22 errorExpected bool 23 errorContains string 24 }{ 25 { 26 description: "Drop Single Element After Another Element", 27 input: []byte(`{"consent": "TESTCONSENT","consented_providers_settings": {"test": 1,"consented_providers": [1608,765,492]}}`), 28 elementToRemove: []string{"consented_providers"}, 29 output: []byte(`{"consent": "TESTCONSENT","consented_providers_settings": {"test": 1}}`), 30 errorExpected: false, 31 errorContains: "", 32 }, 33 { 34 description: "Drop Single Element Before Another Element", 35 input: []byte(`{"consent": "TESTCONSENT","consented_providers_settings": {"consented_providers": [1608,765,492],"test": 1}}`), 36 elementToRemove: []string{"consented_providers"}, 37 output: []byte(`{"consent": "TESTCONSENT","consented_providers_settings": {"test": 1}}`), 38 errorExpected: false, 39 errorContains: "", 40 }, 41 { 42 description: "Drop Single Element", 43 input: []byte(`{"consent": "TESTCONSENT","consented_providers_settings": {"consented_providers": [1545,2563,1411]}}`), 44 elementToRemove: []string{"consented_providers"}, 45 output: []byte(`{"consent": "TESTCONSENT","consented_providers_settings": {}}`), 46 errorExpected: false, 47 errorContains: "", 48 }, 49 { 50 description: "Drop Single Element string", 51 input: []byte(`{"consent": "TESTCONSENT","consented_providers_settings": {"consented_providers": "test"}}`), 52 elementToRemove: []string{"consented_providers"}, 53 output: []byte(`{"consent": "TESTCONSENT","consented_providers_settings": {}}`), 54 errorExpected: false, 55 errorContains: "", 56 }, 57 { 58 description: "Drop Parent Element Between Two Elements", 59 input: []byte(`{"consent": "TESTCONSENT","consented_providers_settings": {"consented_providers": [1608,765,492], "test": 1},"test": 123}`), 60 elementToRemove: []string{"consented_providers_settings"}, 61 output: []byte(`{"consent": "TESTCONSENT","test": 123}`), 62 errorExpected: false, 63 errorContains: "", 64 }, 65 { 66 description: "Drop Parent Element Before Element", 67 input: []byte(`{"consented_providers_settings": {"consented_providers": [1608,765,492], "test": 1},"test": 123}`), 68 elementToRemove: []string{"consented_providers_settings"}, 69 output: []byte(`{"test": 123}`), 70 errorExpected: false, 71 errorContains: "", 72 }, 73 { 74 description: "Drop Parent Element After Element", 75 input: []byte(`{"consent": "TESTCONSENT","consented_providers_settings": {"consented_providers": [1608,765,492], "test": 1}}`), 76 elementToRemove: []string{"consented_providers_settings"}, 77 output: []byte(`{"consent": "TESTCONSENT"}`), 78 errorExpected: false, 79 errorContains: "", 80 }, 81 { 82 description: "Drop Parent Element Only", 83 input: []byte(`{"consented_providers_settings": {"consented_providers": [1608,765,492], "test": 1}}`), 84 elementToRemove: []string{"consented_providers_settings"}, 85 output: []byte(`{}`), 86 errorExpected: false, 87 errorContains: "", 88 }, 89 { 90 description: "Drop Parent Element List", 91 input: []byte(`{"consented_providers_settings":{"consented_providers":[1608,765,492],"test":1},"data": [{"test1":5},{"test2": [1,2,3]}]}`), 92 elementToRemove: []string{"data"}, 93 output: []byte(`{"consented_providers_settings":{"consented_providers":[1608,765,492],"test":1}}`), 94 errorExpected: false, 95 errorContains: "", 96 }, 97 { 98 description: "Drop Element That Doesn't Exist", 99 input: []byte(`{"consented_providers_settings": {"consented_providers": [1608,765,492], "test": 1}}`), 100 elementToRemove: []string{"test2"}, 101 output: []byte(`{"consented_providers_settings": {"consented_providers": [1608,765,492], "test": 1}}`), 102 errorExpected: false, 103 errorContains: "", 104 }, 105 { 106 description: "Drop Nested Element Single Occurrence", 107 input: []byte(`{"consented_providers_settings":{"consented_providers":[1608,765,492],"test":1},"data": [{"test1":5},{"test2": [1,2,3]}]}`), 108 elementToRemove: []string{"consented_providers_settings", "test"}, 109 output: []byte(`{"consented_providers_settings":{"consented_providers":[1608,765,492]},"data": [{"test1":5},{"test2": [1,2,3]}]}`), 110 errorExpected: false, 111 errorContains: "", 112 }, 113 { 114 description: "Drop Nested Element Multiple Occurrence", 115 input: []byte(`{"consented_providers_settings":{"consented_providers":[1608,765,492],"test":1},"data": [{"test":5},{"test": [1,2,3]}]}`), 116 elementToRemove: []string{"consented_providers_settings", "test"}, 117 output: []byte(`{"consented_providers_settings":{"consented_providers":[1608,765,492]},"data": [{"test":5},{"test": [1,2,3]}]}`), 118 errorExpected: false, 119 errorContains: "", 120 }, 121 { 122 description: "Drop Nested Element Multiple Occurrence Skip Path", 123 input: []byte(`{"consented_providers_settings":{"consented_providers":[1608,765,492],"data": {"amp":1, "test": 25}}}`), 124 elementToRemove: []string{"consented_providers_settings", "test"}, 125 output: []byte(`{"consented_providers_settings":{"consented_providers":[1608,765,492],"data": {"amp":1}}}`), 126 errorExpected: false, 127 errorContains: "", 128 }, 129 { 130 description: "Drop Nested Structure Single Occurrence", 131 input: []byte(`{"consented_providers":{"providers":[1608,765,492],"test":{"nested":true}},"data": [{"test":5},{"test": [1,2,3]}]}`), 132 elementToRemove: []string{"consented_providers", "test"}, 133 output: []byte(`{"consented_providers":{"providers":[1608,765,492]},"data": [{"test":5},{"test": [1,2,3]}]}`), 134 errorExpected: false, 135 errorContains: "", 136 }, 137 { 138 description: "Drop Nested Structure Single Occurrence Deep Nested", 139 input: []byte(`{"consented_providers":{"providers":[1608,765,492],"test":{"nested":true, "nested2": {"test6": 123}}},"data": [{"test":5},{"test": [1,2,3]}]}`), 140 elementToRemove: []string{"consented_providers", "test6"}, 141 output: []byte(`{"consented_providers":{"providers":[1608,765,492],"test":{"nested":true, "nested2": {}}},"data": [{"test":5},{"test": [1,2,3]}]}`), 142 errorExpected: false, 143 errorContains: "", 144 }, 145 { 146 description: "Drop Nested Structure Single Occurrence Deep Nested Full Path", 147 input: []byte(`{"consented_providers":{"providers":[1608,765,492],"test":{"nested":true,"nested2": {"test6": 123}}},"data": [{"test":5},{"test": [1,2,3]}]}`), 148 elementToRemove: []string{"consented_providers", "test", "nested"}, 149 output: []byte(`{"consented_providers":{"providers":[1608,765,492],"test":{"nested2": {"test6": 123}}},"data": [{"test":5},{"test": [1,2,3]}]}`), 150 errorExpected: false, 151 errorContains: "", 152 }, 153 { 154 description: "Drop Nested Structure Doesn't Exist", 155 input: []byte(`{"consented_providers":{"providers":[1608,765,492]},"test":{"nested":true}},"data": [{"test":5},{"test": [1,2,3]}]}`), 156 elementToRemove: []string{"consented_providers", "test2"}, 157 output: []byte(`{"consented_providers":{"providers":[1608,765,492]},"test":{"nested":true}},"data": [{"test":5},{"test": [1,2,3]}]}`), 158 errorExpected: false, 159 errorContains: "", 160 }, 161 //Errors 162 { 163 description: "Error Decode", 164 input: []byte(`{"consented_providers_settings": {"consented_providers": ["123",1,,1365,5678,1545,2563,1411], "test": 1}}`), 165 elementToRemove: []string{"consented_providers"}, 166 output: []byte(``), 167 errorExpected: true, 168 errorContains: "looking for beginning of value", 169 }, 170 { 171 description: "Error Malformed", 172 input: []byte(`{consented_providers_settings: {"consented_providers": [1365,5678,1545,2563,1411], "test": 1}}`), 173 elementToRemove: []string{"consented_providers"}, 174 output: []byte(``), 175 errorExpected: true, 176 errorContains: "invalid character", 177 }, 178 } 179 180 for _, tt := range tests { 181 res, err := DropElement(tt.input, tt.elementToRemove...) 182 183 if tt.errorExpected { 184 assert.Error(t, err, "Error should not be nil") 185 assert.True(t, strings.Contains(err.Error(), tt.errorContains)) 186 } else { 187 assert.NoError(t, err, "Error should be nil") 188 assert.Equal(t, tt.output, res, "Result is incorrect") 189 } 190 } 191 } 192 193 func TestTryExtractErrorMessage(t *testing.T) { 194 tests := []struct { 195 name string 196 givenErr string 197 expectedMsg string 198 }{ 199 { 200 name: "level-1", 201 givenErr: "readObjectStart: expect { or n, but found m, error found in #1 byte of ...|malformed|..., bigger context ...|malformed|..", 202 expectedMsg: "expect { or n, but found m", 203 }, 204 { 205 name: "level-2", 206 givenErr: "openrtb_ext.ExtRequestPrebidCache.Bids: readObjectStart: expect { or n, but found t, error found in #10 byte of ...|:{\"bids\":true}}|..., bigger context ...|{\"cache\":{\"bids\":true}}|...", 207 expectedMsg: "cannot unmarshal openrtb_ext.ExtRequestPrebidCache.Bids: expect { or n, but found t", 208 }, 209 { 210 name: "level-3+", 211 givenErr: "openrtb_ext.ExtRequestPrebid.Cache: openrtb_ext.ExtRequestPrebidCache.Bids: readObjectStart: expect { or n, but found t, error found in #10 byte of ...|:{\"bids\":true}}|..., bigger context ...|{\"cache\":{\"bids\":true}}|...", 212 expectedMsg: "cannot unmarshal openrtb_ext.ExtRequestPrebidCache.Bids: expect { or n, but found t", 213 }, 214 { 215 name: "error-msg", 216 givenErr: "Skip: do not know how to skip: 109, error found in #10 byte of ...|prebid\": malformed}|..., bigger context ...|{\"prebid\": malformed}|...", 217 expectedMsg: "do not know how to skip: 109", 218 }, 219 { 220 name: "specific", 221 givenErr: "openrtb_ext.ExtDevicePrebid.Interstitial: unmarshalerDecoder: request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100, error found in #10 byte of ...| }\n }|..., bigger context ...|: 120,\n \"minheightperc\": 60\n }\n }|...", 222 expectedMsg: "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100", 223 }, 224 { 225 name: "normal", 226 givenErr: "normal error message", 227 expectedMsg: "normal error message", 228 }, 229 { 230 name: "norma-false-start", 231 givenErr: "false: normal error message", 232 expectedMsg: "false: normal error message", 233 }, 234 { 235 name: "norma-false-end", 236 givenErr: "normal error message, error found in #10 but doesn't follow format", 237 expectedMsg: "normal error message, error found in #10 but doesn't follow format", 238 }, 239 } 240 241 for _, test := range tests { 242 t.Run(test.name, func(t *testing.T) { 243 result := tryExtractErrorMessage(errors.New(test.givenErr)) 244 assert.Equal(t, test.expectedMsg, result) 245 }) 246 } 247 } 248 249 func TestCreateEncoder(t *testing.T) { 250 testCases := []struct { 251 desc string 252 inType reflect2.Type 253 expectedValEncoder jsoniter.ValEncoder 254 }{ 255 { 256 desc: "With_extension", 257 inType: reflect2.TypeOfPtr((*jsoniter.Any)(nil)).Elem(), 258 expectedValEncoder: nil, 259 }, 260 { 261 desc: "No_extension", 262 inType: reflect2.TypeOfPtr(&json.RawMessage{}).Elem(), 263 expectedValEncoder: &rawMessageCodec{}, 264 }, 265 } 266 267 for _, tc := range testCases { 268 t.Run(tc.desc, func(t *testing.T) { 269 extension := &RawMessageExtension{} 270 encoder := extension.CreateEncoder(tc.inType) 271 assert.IsType(t, encoder, tc.expectedValEncoder) 272 }) 273 } 274 } 275 276 func TestEncode(t *testing.T) { 277 jsonBlob := json.RawMessage(`{ 278 "properties": { 279 "string": "Blanks spaces in between words to not be removed if compacted", 280 "integer": 5, 281 "string_array": [ 282 "string array elem one", 283 "string array elem two" 284 ] 285 } 286 }`) 287 288 t.Run( 289 "Nil_pointer", 290 func(t *testing.T) { 291 // set test 292 encoder := &rawMessageCodec{} 293 output := bytes.NewBuffer([]byte{}) 294 stream := jsoniter.NewStream(jsonConfigValidationOn, output, len(jsonBlob)) 295 296 // run 297 encoder.Encode(nil, stream) 298 299 // assertions 300 assert.Equal(t, "", output.String()) 301 assert.Equal(t, true, encoder.IsEmpty(nil)) 302 }, 303 ) 304 t.Run( 305 "json.RawMessage_compact_JSON", 306 func(t *testing.T) { 307 // set test 308 encoder := &rawMessageCodec{} 309 output := bytes.NewBuffer([]byte{}) 310 stream := jsoniter.NewStream(jsonConfigValidationOn, output, len(jsonBlob)) 311 312 // run 313 encoder.Encode(unsafe.Pointer(&jsonBlob), stream) 314 315 // assertions 316 assert.Equal( 317 t, 318 `{"properties":{"string":"Blanks spaces in between words to not be removed if compacted","integer":5,"string_array":["string array elem one","string array elem two"]}}`, 319 output.String(), 320 ) 321 assert.Equal(t, false, encoder.IsEmpty(unsafe.Pointer(&jsonBlob))) 322 }, 323 ) 324 }