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  }