github.com/prebid/prebid-server/v2@v2.18.0/ortb/clone_test.go (about)

     1  package ortb
     2  
     3  import (
     4  	"encoding/json"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/prebid/openrtb/v20/adcom1"
     9  	"github.com/prebid/openrtb/v20/openrtb2"
    10  	"github.com/prebid/prebid-server/v2/util/ptrutil"
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  func TestCloneDataSlice(t *testing.T) {
    15  	t.Run("nil", func(t *testing.T) {
    16  		result := CloneDataSlice(nil)
    17  		assert.Nil(t, result)
    18  	})
    19  
    20  	t.Run("empty", func(t *testing.T) {
    21  		given := []openrtb2.Data{}
    22  		result := CloneDataSlice(given)
    23  		assert.Empty(t, result)
    24  		assert.NotSame(t, given, result)
    25  	})
    26  
    27  	t.Run("one", func(t *testing.T) {
    28  		given := []openrtb2.Data{
    29  			{ID: "1", Ext: json.RawMessage(`{"anyField":1}`)},
    30  		}
    31  		result := CloneDataSlice(given)
    32  		assert.Equal(t, given, result, "equality")
    33  		assert.NotSame(t, given[0], result[0], "item-pointer")
    34  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item-pointer-ext")
    35  	})
    36  
    37  	t.Run("many", func(t *testing.T) {
    38  		given := []openrtb2.Data{
    39  			{ID: "1", Ext: json.RawMessage(`{"anyField":1}`)},
    40  			{ID: "2", Ext: json.RawMessage(`{"anyField":2}`)},
    41  		}
    42  		result := CloneDataSlice(given)
    43  		assert.Equal(t, given, result, "equality")
    44  		assert.NotSame(t, given[0], result[0], "item0-pointer")
    45  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item0-pointer-ext")
    46  		assert.NotSame(t, given[1], result[1], "item1-pointer")
    47  		assert.NotSame(t, given[1].Ext, result[1].Ext, "item1-pointer-ext")
    48  	})
    49  }
    50  
    51  func TestCloneData(t *testing.T) {
    52  	t.Run("populated", func(t *testing.T) {
    53  		given := openrtb2.Data{
    54  			ID:      "anyID",
    55  			Name:    "anyName",
    56  			Segment: []openrtb2.Segment{{ID: "1", Ext: json.RawMessage(`{"anyField":1}`)}},
    57  			Ext:     json.RawMessage(`{"anyField":1}`),
    58  		}
    59  		result := CloneData(given)
    60  		assert.Equal(t, given, result, "equality")
    61  		assert.NotSame(t, given, result, "pointer")
    62  		assert.NotSame(t, given.Segment, result.Segment, "segment")
    63  		assert.NotSame(t, given.Segment[0], result.Segment[0], "segment-item")
    64  		assert.NotSame(t, given.Segment[0].Ext, result.Segment[0].Ext, "segment-item-ext")
    65  		assert.NotSame(t, given.Ext, result.Ext, "ext")
    66  	})
    67  
    68  	t.Run("assumptions", func(t *testing.T) {
    69  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.Data{})),
    70  			[]string{
    71  				"Segment",
    72  				"Ext",
    73  			})
    74  	})
    75  }
    76  
    77  func TestCloneSegmentSlice(t *testing.T) {
    78  	t.Run("nil", func(t *testing.T) {
    79  		result := CloneSegmentSlice(nil)
    80  		assert.Nil(t, result)
    81  	})
    82  
    83  	t.Run("empty", func(t *testing.T) {
    84  		given := []openrtb2.Segment{}
    85  		result := CloneSegmentSlice(given)
    86  		assert.Empty(t, result)
    87  		assert.NotSame(t, given, result)
    88  	})
    89  
    90  	t.Run("one", func(t *testing.T) {
    91  		given := []openrtb2.Segment{
    92  			{ID: "1", Ext: json.RawMessage(`{"anyField":1}`)},
    93  		}
    94  		result := CloneSegmentSlice(given)
    95  		assert.Equal(t, given, result, "equality")
    96  		assert.NotSame(t, given[0], result[0], "item-pointer")
    97  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item-pointer-ext")
    98  	})
    99  
   100  	t.Run("many", func(t *testing.T) {
   101  		given := []openrtb2.Segment{
   102  			{Ext: json.RawMessage(`{"anyField":1}`)},
   103  			{Ext: json.RawMessage(`{"anyField":2}`)},
   104  		}
   105  		result := CloneSegmentSlice(given)
   106  		assert.Equal(t, given, result, "equality")
   107  		assert.NotSame(t, given[0], result[0], "item0-pointer")
   108  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item0-pointer-ext")
   109  		assert.NotSame(t, given[1], result[1], "item1-pointer")
   110  		assert.NotSame(t, given[1].Ext, result[1].Ext, "item1-pointer-ext")
   111  	})
   112  }
   113  
   114  func TestCloneSegment(t *testing.T) {
   115  	t.Run("populated", func(t *testing.T) {
   116  		given := openrtb2.Segment{
   117  			ID:    "anyID",
   118  			Name:  "anyName",
   119  			Value: "anyValue",
   120  			Ext:   json.RawMessage(`{"anyField":1}`),
   121  		}
   122  		result := CloneSegment(given)
   123  		assert.Equal(t, given, result, "equality")
   124  		assert.NotSame(t, given, result, "pointer")
   125  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   126  	})
   127  
   128  	t.Run("assumptions", func(t *testing.T) {
   129  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.Segment{})),
   130  			[]string{
   131  				"Ext",
   132  			})
   133  	})
   134  }
   135  
   136  func TestCloneUser(t *testing.T) {
   137  	t.Run("nil", func(t *testing.T) {
   138  		result := CloneUser(nil)
   139  		assert.Nil(t, result)
   140  	})
   141  
   142  	t.Run("empty", func(t *testing.T) {
   143  		given := &openrtb2.User{}
   144  		result := CloneUser(given)
   145  		assert.Empty(t, result)
   146  		assert.NotSame(t, given, result)
   147  	})
   148  
   149  	t.Run("populated", func(t *testing.T) {
   150  		given := &openrtb2.User{
   151  			ID:         "anyID",
   152  			BuyerUID:   "anyBuyerUID",
   153  			Yob:        1,
   154  			Gender:     "anyGender",
   155  			Keywords:   "anyKeywords",
   156  			KwArray:    []string{"key1"},
   157  			CustomData: "anyCustomData",
   158  			Geo:        &openrtb2.Geo{Lat: ptrutil.ToPtr(1.2), Lon: ptrutil.ToPtr(2.3), Ext: json.RawMessage(`{"geo":1}`)},
   159  			Data:       []openrtb2.Data{{ID: "1", Ext: json.RawMessage(`{"data":1}`)}},
   160  			Consent:    "anyConsent",
   161  			EIDs:       []openrtb2.EID{{Source: "1", Ext: json.RawMessage(`{"eid":1}`)}},
   162  			Ext:        json.RawMessage(`{"anyField":1}`),
   163  		}
   164  		result := CloneUser(given)
   165  		assert.Equal(t, given, result, "equality")
   166  		assert.NotSame(t, given, result, "pointer")
   167  		assert.NotSame(t, given.KwArray, result.KwArray, "cat")
   168  		assert.NotSame(t, given.Geo, result.Geo, "geo")
   169  		assert.NotSame(t, given.Geo.Ext, result.Geo.Ext, "geo-ext")
   170  		assert.NotSame(t, given.Data[0], result.Data[0], "data-item")
   171  		assert.NotSame(t, given.Data[0].Ext, result.Data[0].Ext, "data-item-ext")
   172  		assert.NotSame(t, given.EIDs[0], result.EIDs[0], "eids-item")
   173  		assert.NotSame(t, given.EIDs[0].Ext, result.EIDs[0].Ext, "eids-item-ext")
   174  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   175  	})
   176  
   177  	t.Run("assumptions", func(t *testing.T) {
   178  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.User{})),
   179  			[]string{
   180  				"KwArray",
   181  				"Geo",
   182  				"Data",
   183  				"EIDs",
   184  				"Ext",
   185  			})
   186  	})
   187  }
   188  
   189  func TestCloneDevice(t *testing.T) {
   190  	t.Run("nil", func(t *testing.T) {
   191  		result := CloneDevice(nil)
   192  		assert.Nil(t, result)
   193  	})
   194  
   195  	t.Run("empty", func(t *testing.T) {
   196  		given := &openrtb2.Device{}
   197  		result := CloneDevice(given)
   198  		assert.Empty(t, result)
   199  		assert.NotSame(t, given, result)
   200  	})
   201  
   202  	t.Run("populated", func(t *testing.T) {
   203  		var n int8 = 1
   204  		np := &n
   205  		ct := adcom1.ConnectionWIFI
   206  
   207  		given := &openrtb2.Device{
   208  			Geo:            &openrtb2.Geo{Lat: ptrutil.ToPtr(1.2), Lon: ptrutil.ToPtr(2.3), Ext: json.RawMessage(`{"geo":1}`)},
   209  			DNT:            np,
   210  			Lmt:            np,
   211  			UA:             "UserAgent",
   212  			SUA:            &openrtb2.UserAgent{Mobile: np, Model: "iPad"},
   213  			IP:             "127.0.0.1",
   214  			IPv6:           "2001::",
   215  			DeviceType:     adcom1.DeviceTablet,
   216  			Make:           "Apple",
   217  			Model:          "iPad",
   218  			OS:             "macOS",
   219  			OSV:            "1.2.3",
   220  			HWV:            "mini",
   221  			H:              20,
   222  			W:              30,
   223  			PPI:            100,
   224  			PxRatio:        200,
   225  			JS:             ptrutil.ToPtr[int8](2),
   226  			GeoFetch:       ptrutil.ToPtr[int8](4),
   227  			FlashVer:       "1.22.33",
   228  			Language:       "En",
   229  			LangB:          "ENG",
   230  			Carrier:        "AT&T",
   231  			MCCMNC:         "111-222",
   232  			ConnectionType: &ct,
   233  			IFA:            "IFA",
   234  			DIDSHA1:        "DIDSHA1",
   235  			DIDMD5:         "DIDMD5",
   236  			DPIDSHA1:       "DPIDSHA1",
   237  			DPIDMD5:        "DPIDMD5",
   238  			MACSHA1:        "MACSHA1",
   239  			MACMD5:         "MACMD5",
   240  			Ext:            json.RawMessage(`{"anyField":1}`),
   241  		}
   242  		result := CloneDevice(given)
   243  		assert.Equal(t, given, result, "equality")
   244  		assert.NotSame(t, given, result, "pointer")
   245  		assert.NotSame(t, given.Geo, result.Geo, "geo")
   246  		assert.NotSame(t, given.Geo.Ext, result.Geo.Ext, "geo-ext")
   247  		assert.NotSame(t, given.DNT, result.DNT, "dnt")
   248  		assert.NotSame(t, given.Lmt, result.Lmt, "lmt")
   249  		assert.NotSame(t, given.SUA, result.SUA, "sua")
   250  		assert.NotSame(t, given.JS, result.JS, "js")
   251  		assert.NotSame(t, given.GeoFetch, result.GeoFetch, "geofetch")
   252  		assert.NotSame(t, given.ConnectionType, result.ConnectionType, "connectionType")
   253  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   254  	})
   255  
   256  	t.Run("assumptions", func(t *testing.T) {
   257  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.Device{})),
   258  			[]string{
   259  				"Geo",
   260  				"DNT",
   261  				"Lmt",
   262  				"SUA",
   263  				"JS",
   264  				"GeoFetch",
   265  				"ConnectionType",
   266  				"Ext",
   267  			})
   268  	})
   269  }
   270  
   271  func TestCloneUserAgent(t *testing.T) {
   272  	t.Run("nil", func(t *testing.T) {
   273  		result := CloneUserAgent(nil)
   274  		assert.Nil(t, result)
   275  	})
   276  
   277  	t.Run("empty", func(t *testing.T) {
   278  		given := &openrtb2.UserAgent{}
   279  		result := CloneUserAgent(given)
   280  		assert.Empty(t, result)
   281  		assert.NotSame(t, given, result)
   282  	})
   283  
   284  	t.Run("populated", func(t *testing.T) {
   285  		var n int8 = 1
   286  		np := &n
   287  
   288  		given := &openrtb2.UserAgent{
   289  			Browsers:     []openrtb2.BrandVersion{{Brand: "Apple"}},
   290  			Platform:     &openrtb2.BrandVersion{Brand: "Apple"},
   291  			Mobile:       np,
   292  			Architecture: "X86",
   293  			Bitness:      "64",
   294  			Model:        "iPad",
   295  			Source:       adcom1.UASourceLowEntropy,
   296  			Ext:          json.RawMessage(`{"anyField":1}`),
   297  		}
   298  		result := CloneUserAgent(given)
   299  		assert.Equal(t, given, result, "equality")
   300  		assert.NotSame(t, given, result, "pointer")
   301  		assert.NotSame(t, given.Browsers, result.Browsers, "browsers")
   302  		assert.NotSame(t, given.Platform, result.Platform, "platform")
   303  		assert.NotSame(t, given.Mobile, result.Mobile, "mobile")
   304  		assert.NotSame(t, given.Architecture, result.Architecture, "architecture")
   305  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   306  	})
   307  
   308  	t.Run("assumptions", func(t *testing.T) {
   309  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.UserAgent{})),
   310  			[]string{
   311  				"Browsers",
   312  				"Platform",
   313  				"Mobile",
   314  				"Ext",
   315  			})
   316  	})
   317  }
   318  
   319  func TestCloneBrandVersionSlice(t *testing.T) {
   320  	t.Run("nil", func(t *testing.T) {
   321  		result := CloneBrandVersionSlice(nil)
   322  		assert.Nil(t, result)
   323  	})
   324  
   325  	t.Run("empty", func(t *testing.T) {
   326  		given := []openrtb2.BrandVersion{}
   327  		result := CloneBrandVersionSlice(given)
   328  		assert.Empty(t, result)
   329  		assert.NotSame(t, given, result)
   330  	})
   331  
   332  	t.Run("one", func(t *testing.T) {
   333  		given := []openrtb2.BrandVersion{
   334  			{Brand: "1", Version: []string{"s1", "s2"}, Ext: json.RawMessage(`{"anyField":1}`)},
   335  		}
   336  		result := CloneBrandVersionSlice(given)
   337  		assert.Equal(t, given, result, "equality")
   338  		assert.NotSame(t, given[0], result[0], "item-pointer")
   339  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item-pointer-ext")
   340  	})
   341  
   342  	t.Run("many", func(t *testing.T) {
   343  		given := []openrtb2.BrandVersion{
   344  			{Brand: "1", Version: []string{"s1", "s2"}, Ext: json.RawMessage(`{"anyField":1}`)},
   345  			{Brand: "2", Version: []string{"s3", "s4"}, Ext: json.RawMessage(`{"anyField":1}`)},
   346  			{Brand: "3", Version: []string{"s5", "s6"}, Ext: json.RawMessage(`{"anyField":1}`)},
   347  		}
   348  		result := CloneBrandVersionSlice(given)
   349  		assert.Equal(t, given, result, "equality")
   350  		assert.NotSame(t, given[0], result[0], "item0-pointer")
   351  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item0-pointer-ext")
   352  		assert.NotSame(t, given[1], result[1], "item1-pointer")
   353  		assert.NotSame(t, given[1].Ext, result[1].Ext, "item1-pointer-ext")
   354  		assert.NotSame(t, given[2], result[2], "item1-pointer")
   355  		assert.NotSame(t, given[2].Ext, result[2].Ext, "item1-pointer-ext")
   356  	})
   357  }
   358  
   359  func TestCloneBrandVersion(t *testing.T) {
   360  	t.Run("nil", func(t *testing.T) {
   361  		result := CloneBrandVersion(nil)
   362  		assert.Nil(t, result)
   363  	})
   364  
   365  	t.Run("empty", func(t *testing.T) {
   366  		given := &openrtb2.BrandVersion{}
   367  		result := CloneBrandVersion(given)
   368  		assert.Empty(t, result)
   369  		assert.NotSame(t, given, result)
   370  	})
   371  
   372  	t.Run("populated", func(t *testing.T) {
   373  		given := &openrtb2.BrandVersion{
   374  			Brand:   "Apple",
   375  			Version: []string{"s1"},
   376  			Ext:     json.RawMessage(`{"anyField":1}`),
   377  		}
   378  		result := CloneBrandVersion(given)
   379  		assert.Equal(t, given, result, "equality")
   380  		assert.NotSame(t, given, result, "pointer")
   381  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   382  	})
   383  
   384  	t.Run("assumptions", func(t *testing.T) {
   385  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.BrandVersion{})),
   386  			[]string{
   387  				"Version",
   388  				"Ext",
   389  			})
   390  	})
   391  }
   392  
   393  func TestCloneSource(t *testing.T) {
   394  	t.Run("nil", func(t *testing.T) {
   395  		result := CloneSource(nil)
   396  		assert.Nil(t, result)
   397  	})
   398  
   399  	t.Run("empty", func(t *testing.T) {
   400  		given := &openrtb2.Source{}
   401  		result := CloneSource(given)
   402  		assert.Empty(t, result)
   403  		assert.NotSame(t, given, result)
   404  	})
   405  
   406  	t.Run("populated", func(t *testing.T) {
   407  
   408  		given := &openrtb2.Source{
   409  			FD:     ptrutil.ToPtr[int8](1),
   410  			TID:    "Tid",
   411  			PChain: "PChain",
   412  			SChain: &openrtb2.SupplyChain{
   413  				Complete: 1,
   414  				Nodes: []openrtb2.SupplyChainNode{
   415  					{ASI: "asi", Ext: json.RawMessage(`{"anyField":1}`)},
   416  				},
   417  				Ext: json.RawMessage(`{"anyField":2}`),
   418  			},
   419  			Ext: json.RawMessage(`{"anyField":1}`),
   420  		}
   421  		result := CloneSource(given)
   422  		assert.Equal(t, given, result, "equality")
   423  		assert.NotSame(t, given, result, "pointer")
   424  		assert.NotSame(t, given.FD, result.FD, "fd")
   425  		assert.NotSame(t, given.SChain, result.SChain, "schain")
   426  		assert.NotSame(t, given.SChain.Ext, result.SChain.Ext, "schain.ext")
   427  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   428  		assert.NotSame(t, given.SChain.Nodes[0].Ext, result.SChain.Nodes[0].Ext, "schain.nodes.ext")
   429  	})
   430  
   431  	t.Run("assumptions", func(t *testing.T) {
   432  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.Source{})),
   433  			[]string{
   434  				"FD",
   435  				"SChain",
   436  				"Ext",
   437  			})
   438  	})
   439  }
   440  
   441  func TestCloneSChain(t *testing.T) {
   442  	t.Run("nil", func(t *testing.T) {
   443  		result := CloneSource(nil)
   444  		assert.Nil(t, result)
   445  	})
   446  
   447  	t.Run("empty", func(t *testing.T) {
   448  		given := &openrtb2.SupplyChain{}
   449  		result := CloneSChain(given)
   450  		assert.Empty(t, result)
   451  		assert.NotSame(t, given, result)
   452  	})
   453  
   454  	t.Run("populated", func(t *testing.T) {
   455  		given := &openrtb2.SupplyChain{
   456  			Complete: 1,
   457  			Nodes: []openrtb2.SupplyChainNode{
   458  				{ASI: "asi", Ext: json.RawMessage(`{"anyField":1}`)},
   459  			},
   460  			Ext: json.RawMessage(`{"anyField":1}`),
   461  		}
   462  		result := CloneSChain(given)
   463  		assert.Equal(t, given, result, "equality")
   464  		assert.NotSame(t, given, result, "pointer")
   465  		assert.NotSame(t, given.Nodes, result.Nodes, "nodes")
   466  		assert.NotSame(t, given.Nodes[0].Ext, result.Nodes[0].Ext, "nodes.ext")
   467  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   468  	})
   469  
   470  	t.Run("assumptions", func(t *testing.T) {
   471  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.SupplyChain{})),
   472  			[]string{
   473  				"Nodes",
   474  				"Ext",
   475  			})
   476  	})
   477  }
   478  
   479  func TestCloneSupplyChainNodes(t *testing.T) {
   480  	var n int8 = 1
   481  	np := &n
   482  	t.Run("nil", func(t *testing.T) {
   483  		result := CloneSupplyChainNodes(nil)
   484  		assert.Nil(t, result)
   485  	})
   486  
   487  	t.Run("empty", func(t *testing.T) {
   488  		given := []openrtb2.SupplyChainNode{}
   489  		result := CloneSupplyChainNodes(given)
   490  		assert.Empty(t, result)
   491  		assert.NotSame(t, given, result)
   492  	})
   493  
   494  	t.Run("one", func(t *testing.T) {
   495  		given := []openrtb2.SupplyChainNode{
   496  			{ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)},
   497  		}
   498  		result := CloneSupplyChainNodes(given)
   499  		assert.Equal(t, given, result, "equality")
   500  		assert.NotSame(t, given[0], result[0], "item-pointer")
   501  		assert.NotSame(t, given[0].HP, result[0].HP, "item-pointer-hp")
   502  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item-pointer-ext")
   503  	})
   504  
   505  	t.Run("many", func(t *testing.T) {
   506  		given := []openrtb2.SupplyChainNode{
   507  			{ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)},
   508  			{ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)},
   509  			{ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)},
   510  		}
   511  		result := CloneSupplyChainNodes(given)
   512  		assert.Equal(t, given, result, "equality")
   513  		assert.NotSame(t, given[0], result[0], "item0-pointer")
   514  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item0-pointer-ext")
   515  		assert.NotSame(t, given[0].HP, result[0].HP, "item0-pointer-hp")
   516  		assert.NotSame(t, given[1], result[1], "item1-pointer")
   517  		assert.NotSame(t, given[1].Ext, result[1].Ext, "item1-pointer-ext")
   518  		assert.NotSame(t, given[1].HP, result[1].HP, "item1-pointer-hp")
   519  		assert.NotSame(t, given[2], result[2], "item2-pointer")
   520  		assert.NotSame(t, given[2].Ext, result[2].Ext, "item2-pointer-ext")
   521  		assert.NotSame(t, given[2].HP, result[2].HP, "item2-pointer-hp")
   522  	})
   523  }
   524  
   525  func TestCloneSupplyChainNode(t *testing.T) {
   526  	t.Run("populated", func(t *testing.T) {
   527  		var n int8 = 1
   528  		np := &n
   529  
   530  		given := openrtb2.SupplyChainNode{
   531  			ASI: "asi",
   532  			HP:  np,
   533  			Ext: json.RawMessage(`{"anyField":1}`),
   534  		}
   535  		result := CloneSupplyChainNode(given)
   536  		assert.Equal(t, given, result, "equality")
   537  		assert.NotSame(t, given, result, "pointer")
   538  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   539  		assert.NotSame(t, given.HP, result.HP, "hp")
   540  	})
   541  
   542  	t.Run("assumptions", func(t *testing.T) {
   543  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.SupplyChainNode{})),
   544  			[]string{
   545  				"HP",
   546  				"Ext",
   547  			})
   548  	})
   549  }
   550  
   551  func TestCloneGeo(t *testing.T) {
   552  	t.Run("nil", func(t *testing.T) {
   553  		result := CloneGeo(nil)
   554  		assert.Nil(t, result)
   555  	})
   556  
   557  	t.Run("empty", func(t *testing.T) {
   558  		given := &openrtb2.Geo{}
   559  		result := CloneGeo(given)
   560  		assert.Empty(t, result)
   561  		assert.NotSame(t, given, result)
   562  	})
   563  
   564  	t.Run("populated", func(t *testing.T) {
   565  		given := &openrtb2.Geo{
   566  			Lat:           ptrutil.ToPtr(1.234),
   567  			Lon:           ptrutil.ToPtr(5.678),
   568  			Type:          adcom1.LocationGPS,
   569  			Accuracy:      1,
   570  			LastFix:       2,
   571  			IPService:     adcom1.LocationServiceIP2Location,
   572  			Country:       "anyCountry",
   573  			Region:        "anyRegion",
   574  			RegionFIPS104: "anyRegionFIPS104",
   575  			Metro:         "anyMetro",
   576  			City:          "anyCity",
   577  			ZIP:           "anyZIP",
   578  			UTCOffset:     3,
   579  			Ext:           json.RawMessage(`{"anyField":1}`),
   580  		}
   581  		result := CloneGeo(given)
   582  		assert.Equal(t, given, result, "equality")
   583  		assert.NotSame(t, given, result, "pointer")
   584  		assert.NotSame(t, given.Lat, result.Lat, "lat")
   585  		assert.NotSame(t, given.Lon, result.Lon, "lon")
   586  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   587  	})
   588  
   589  	t.Run("assumptions", func(t *testing.T) {
   590  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.Geo{})),
   591  			[]string{
   592  				"Lat",
   593  				"Lon",
   594  				"Ext",
   595  			})
   596  	})
   597  }
   598  
   599  func TestCloneEIDSlice(t *testing.T) {
   600  	t.Run("nil", func(t *testing.T) {
   601  		result := CloneEIDSlice(nil)
   602  		assert.Nil(t, result)
   603  	})
   604  
   605  	t.Run("empty", func(t *testing.T) {
   606  		given := []openrtb2.EID{}
   607  		result := CloneEIDSlice(given)
   608  		assert.Empty(t, result)
   609  		assert.NotSame(t, given, result)
   610  	})
   611  
   612  	t.Run("one", func(t *testing.T) {
   613  		given := []openrtb2.EID{
   614  			{Source: "1", Ext: json.RawMessage(`{"anyField":1}`)},
   615  		}
   616  		result := CloneEIDSlice(given)
   617  		assert.Equal(t, given, result, "equality")
   618  		assert.NotSame(t, given[0], result[0], "item-pointer")
   619  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item-pointer-ext")
   620  	})
   621  
   622  	t.Run("many", func(t *testing.T) {
   623  		given := []openrtb2.EID{
   624  			{Source: "1", Ext: json.RawMessage(`{"anyField":1}`)},
   625  			{Source: "2", Ext: json.RawMessage(`{"anyField":2}`)},
   626  		}
   627  		result := CloneEIDSlice(given)
   628  		assert.Equal(t, given, result, "equality")
   629  		assert.NotSame(t, given[0], result[0], "item0-pointer")
   630  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item0-pointer-ext")
   631  		assert.NotSame(t, given[1], result[1], "item1-pointer")
   632  		assert.NotSame(t, given[1].Ext, result[1].Ext, "item1-pointer-ext")
   633  	})
   634  }
   635  
   636  func TestCloneEID(t *testing.T) {
   637  	t.Run("populated", func(t *testing.T) {
   638  		given := openrtb2.EID{
   639  			Source: "anySource",
   640  			UIDs:   []openrtb2.UID{{ID: "1", Ext: json.RawMessage(`{"uid":1}`)}},
   641  			Ext:    json.RawMessage(`{"anyField":1}`),
   642  		}
   643  		result := CloneEID(given)
   644  		assert.Equal(t, given, result, "equality")
   645  		assert.NotSame(t, given, result, "pointer")
   646  		assert.NotSame(t, given.UIDs, result.UIDs, "uids")
   647  		assert.NotSame(t, given.UIDs[0], result.UIDs[0], "uids-item")
   648  		assert.NotSame(t, given.UIDs[0].Ext, result.UIDs[0].Ext, "uids-item-ext")
   649  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   650  	})
   651  
   652  	t.Run("assumptions", func(t *testing.T) {
   653  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.EID{})),
   654  			[]string{
   655  				"UIDs",
   656  				"Ext",
   657  			})
   658  	})
   659  }
   660  
   661  func TestCloneUIDSlice(t *testing.T) {
   662  	t.Run("nil", func(t *testing.T) {
   663  		result := CloneUIDSlice(nil)
   664  		assert.Nil(t, result)
   665  	})
   666  
   667  	t.Run("empty", func(t *testing.T) {
   668  		given := []openrtb2.UID{}
   669  		result := CloneUIDSlice(given)
   670  		assert.Empty(t, result)
   671  		assert.NotSame(t, given, result)
   672  	})
   673  
   674  	t.Run("one", func(t *testing.T) {
   675  		given := []openrtb2.UID{
   676  			{ID: "1", Ext: json.RawMessage(`{"anyField":1}`)},
   677  		}
   678  		result := CloneUIDSlice(given)
   679  		assert.Equal(t, given, result, "equality")
   680  		assert.NotSame(t, given[0], result[0], "item-pointer")
   681  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item-pointer-ext")
   682  	})
   683  
   684  	t.Run("many", func(t *testing.T) {
   685  		given := []openrtb2.UID{
   686  			{ID: "1", Ext: json.RawMessage(`{"anyField":1}`)},
   687  			{ID: "2", Ext: json.RawMessage(`{"anyField":2}`)},
   688  		}
   689  		result := CloneUIDSlice(given)
   690  		assert.Equal(t, given, result, "equality")
   691  		assert.NotSame(t, given[0], result[0], "item0-pointer")
   692  		assert.NotSame(t, given[0].Ext, result[0].Ext, "item0-pointer-ext")
   693  		assert.NotSame(t, given[1], result[1], "item1-pointer")
   694  		assert.NotSame(t, given[1].Ext, result[1].Ext, "item1-pointer-ext")
   695  	})
   696  }
   697  
   698  func TestCloneUID(t *testing.T) {
   699  	t.Run("populated", func(t *testing.T) {
   700  		given := openrtb2.UID{
   701  			ID:    "anyID",
   702  			AType: adcom1.AgentTypePerson,
   703  			Ext:   json.RawMessage(`{"anyField":1}`),
   704  		}
   705  		result := CloneUID(given)
   706  		assert.Equal(t, given, result, "equality")
   707  		assert.NotSame(t, given, result, "pointer")
   708  		assert.NotSame(t, given.Ext, result.Ext, "ext")
   709  	})
   710  
   711  	t.Run("assumptions", func(t *testing.T) {
   712  		assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.UID{})),
   713  			[]string{
   714  				"Ext",
   715  			})
   716  	})
   717  }
   718  
   719  func TestCloneBidderReq(t *testing.T) {
   720  	t.Run("nil", func(t *testing.T) {
   721  		result := CloneBidRequestPartial(nil)
   722  		assert.Nil(t, result)
   723  	})
   724  
   725  	t.Run("empty", func(t *testing.T) {
   726  		given := &openrtb2.BidRequest{}
   727  		result := CloneBidRequestPartial(given)
   728  		assert.Equal(t, given, result)
   729  		assert.NotSame(t, given, result)
   730  	})
   731  
   732  	t.Run("populated", func(t *testing.T) {
   733  		given := &openrtb2.BidRequest{
   734  			ID:     "anyID",
   735  			User:   &openrtb2.User{ID: "testUserId"},
   736  			Device: &openrtb2.Device{Carrier: "testCarrier"},
   737  			Source: &openrtb2.Source{TID: "testTID"},
   738  		}
   739  		result := CloneBidRequestPartial(given)
   740  		assert.Equal(t, given, result)
   741  		assert.NotSame(t, given, result, "pointer")
   742  		assert.NotSame(t, given.Device, result.Device, "device")
   743  		assert.NotSame(t, given.User, result.User, "user")
   744  		assert.NotSame(t, given.Source, result.Source, "source")
   745  	})
   746  
   747  	// TODO: Implement a full bid request clone and track changes using an 'assumptions' test.
   748  }
   749  
   750  // discoverPointerFields returns the names of all fields of an object that are
   751  // pointers and would need to be cloned. This method is specific to types which can
   752  // appear within an OpenRTB data model object.
   753  func discoverPointerFields(t reflect.Type) []string {
   754  	var fields []string
   755  	for _, f := range reflect.VisibleFields(t) {
   756  		if f.Type.Kind() == reflect.Slice || f.Type.Kind() == reflect.Pointer {
   757  			fields = append(fields, f.Name)
   758  		}
   759  	}
   760  	return fields
   761  }