github.com/prebid/prebid-server/v2@v2.18.0/usersync/syncersbuilder_test.go (about)

     1  package usersync
     2  
     3  import (
     4  	"errors"
     5  	"testing"
     6  
     7  	"github.com/prebid/prebid-server/v2/config"
     8  	"github.com/prebid/prebid-server/v2/macros"
     9  	"github.com/stretchr/testify/assert"
    10  )
    11  
    12  func TestSyncerBuildError(t *testing.T) {
    13  	err := SyncerBuildError{
    14  		Bidder:    "anyBidder",
    15  		SyncerKey: "anyKey",
    16  		Err:       errors.New("anyError"),
    17  	}
    18  	assert.Equal(t, err.Error(), "cannot create syncer for bidder anyBidder with key anyKey: anyError")
    19  }
    20  
    21  func TestBuildSyncers(t *testing.T) {
    22  	var (
    23  		hostConfig              = config.Configuration{ExternalURL: "http://host.com", UserSync: config.UserSync{RedirectURL: "{{.ExternalURL}}/{{.SyncerKey}}/host"}}
    24  		iframeConfig            = &config.SyncerEndpoint{URL: "https://bidder.com/iframe?redirect={{.RedirectURL}}"}
    25  		iframeConfigError       = &config.SyncerEndpoint{URL: "https://bidder.com/iframe?redirect={{xRedirectURL}}"} // Error caused by invalid macro
    26  		infoKeyAPopulated       = config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Key: "a", IFrame: iframeConfig}}
    27  		infoKeyADisabled        = config.BidderInfo{Disabled: true, Syncer: &config.Syncer{Key: "a", IFrame: iframeConfig}}
    28  		infoKeyAEmpty           = config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Key: "a"}}
    29  		infoKeyAError           = config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Key: "a", IFrame: iframeConfigError}}
    30  		infoKeyASupportsOnly    = config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Supports: []string{"iframe"}}}
    31  		infoKeyBPopulated       = config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Key: "b", IFrame: iframeConfig}}
    32  		infoKeyBEmpty           = config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Key: "b"}}
    33  		infoKeyMissingPopulated = config.BidderInfo{Disabled: false, Syncer: &config.Syncer{IFrame: iframeConfig}}
    34  	)
    35  
    36  	// NOTE: The hostConfig includes the syncer key in the RedirectURL to distinguish between the syncer keys
    37  	// in these tests. Look carefully at the end of the expected iframe urls to see the syncer key.
    38  
    39  	testCases := []struct {
    40  		description         string
    41  		givenConfig         config.Configuration
    42  		givenBidderInfos    config.BidderInfos
    43  		expectedIFramesURLs map[string]string
    44  		expectedErrors      []string
    45  	}{
    46  		{
    47  			description:      "One",
    48  			givenConfig:      hostConfig,
    49  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated},
    50  			expectedIFramesURLs: map[string]string{
    51  				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder1%2Fhost",
    52  			},
    53  		},
    54  		{
    55  			description:      "One - Missing Key - Defaults To Bidder Name",
    56  			givenConfig:      hostConfig,
    57  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyMissingPopulated},
    58  			expectedIFramesURLs: map[string]string{
    59  				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder1%2Fhost",
    60  			},
    61  		},
    62  		{
    63  			description:      "One - Syncer Error",
    64  			givenConfig:      hostConfig,
    65  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAError},
    66  			expectedErrors: []string{
    67  				"cannot create syncer for bidder bidder1 with key a: iframe template: bidder1_usersync_url:1: function \"xRedirectURL\" not defined",
    68  			},
    69  		},
    70  		{
    71  			description:      "Many - Different Syncers",
    72  			givenConfig:      hostConfig,
    73  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated, "bidder2": infoKeyBPopulated},
    74  			expectedIFramesURLs: map[string]string{
    75  				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder1%2Fhost",
    76  				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost",
    77  			},
    78  		},
    79  		{
    80  			description:      "Many - Same Syncers - One Primary",
    81  			givenConfig:      hostConfig,
    82  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated, "bidder2": infoKeyAEmpty},
    83  			expectedIFramesURLs: map[string]string{
    84  				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder1%2Fhost",
    85  				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost",
    86  			},
    87  		},
    88  		{
    89  			description:      "Many - Same Syncers - Many Primaries",
    90  			givenConfig:      hostConfig,
    91  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated, "bidder2": infoKeyAPopulated},
    92  			expectedErrors: []string{
    93  				"bidders bidder1, bidder2 define endpoints (iframe and/or redirect) for the same syncer key, but only one bidder is permitted to define endpoints",
    94  			},
    95  		},
    96  		{
    97  			description:      "Many - Same Syncers - Many Primaries - None Populated",
    98  			givenConfig:      hostConfig,
    99  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAEmpty, "bidder2": infoKeyAEmpty},
   100  			expectedErrors: []string{
   101  				"bidders bidder1, bidder2 share the same syncer key, but none define endpoints (iframe and/or redirect)",
   102  			},
   103  		},
   104  		{
   105  			description:      "Many - Sync Error - Bidder Correct",
   106  			givenConfig:      hostConfig,
   107  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAEmpty, "bidder2": infoKeyAError},
   108  			expectedErrors: []string{
   109  				"cannot create syncer for bidder bidder2 with key a: iframe template: bidder1_usersync_url:1: function \"xRedirectURL\" not defined",
   110  				"cannot create syncer for bidder bidder2 with key a: iframe template: bidder2_usersync_url:1: function \"xRedirectURL\" not defined",
   111  			},
   112  		},
   113  		{
   114  			description:      "Many - Empty Syncers Ignored",
   115  			givenConfig:      hostConfig,
   116  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": {}, "bidder2": infoKeyBPopulated},
   117  			expectedIFramesURLs: map[string]string{
   118  				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost",
   119  			},
   120  		},
   121  		{
   122  			description:      "Many - Disabled Syncers Ignored",
   123  			givenConfig:      hostConfig,
   124  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyADisabled, "bidder2": infoKeyBPopulated},
   125  			expectedIFramesURLs: map[string]string{
   126  				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost",
   127  			},
   128  		},
   129  		{
   130  			description:      "Many - Supports Only Syncers Ignored",
   131  			givenConfig:      hostConfig,
   132  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyASupportsOnly, "bidder2": infoKeyBPopulated},
   133  			expectedIFramesURLs: map[string]string{
   134  				"bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost",
   135  			},
   136  		},
   137  		{
   138  			description:      "Many - Multiple Errors",
   139  			givenConfig:      hostConfig,
   140  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAError, "bidder2": infoKeyBEmpty},
   141  			expectedErrors: []string{
   142  				"cannot create syncer for bidder bidder1 with key a: iframe template: bidder1_usersync_url:1: function \"xRedirectURL\" not defined",
   143  				"cannot create syncer for bidder bidder2 with key b: at least one endpoint (iframe and/or redirect) is required",
   144  			},
   145  		},
   146  		{
   147  			description:      "ExternalURL Host User Sync Override",
   148  			givenConfig:      config.Configuration{ExternalURL: "http://host.com", UserSync: config.UserSync{ExternalURL: "http://hostoverride.com", RedirectURL: "{{.ExternalURL}}/{{.SyncerKey}}/host"}},
   149  			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated},
   150  			expectedIFramesURLs: map[string]string{
   151  				"bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhostoverride.com%2Fbidder1%2Fhost",
   152  			},
   153  		},
   154  	}
   155  
   156  	for _, test := range testCases {
   157  		result, errs := BuildSyncers(&test.givenConfig, test.givenBidderInfos)
   158  
   159  		if len(test.expectedErrors) == 0 {
   160  			assert.Empty(t, errs, test.description+":err")
   161  			resultRenderedIFrameURLS := map[string]string{}
   162  			for k, v := range result {
   163  				iframeRendered, err := v.GetSync([]SyncType{SyncTypeIFrame}, macros.UserSyncPrivacy{})
   164  				if assert.NoError(t, err, test.description+"key:%s,:iframe_render", k) {
   165  					resultRenderedIFrameURLS[k] = iframeRendered.URL
   166  				}
   167  			}
   168  			assert.Equal(t, test.expectedIFramesURLs, resultRenderedIFrameURLS, test.description+":result")
   169  		} else {
   170  			errMessages := make([]string, 0, len(errs))
   171  			for _, e := range errs {
   172  				errMessages = append(errMessages, e.Error())
   173  			}
   174  			assert.ElementsMatch(t, test.expectedErrors, errMessages, test.description+":err")
   175  			assert.Empty(t, result, test.description+":result")
   176  		}
   177  	}
   178  }
   179  
   180  func TestShouldCreateSyncer(t *testing.T) {
   181  	var (
   182  		anySupports = []string{"iframe"}
   183  		anyEndpoint = &config.SyncerEndpoint{}
   184  		anyCORS     = true
   185  	)
   186  
   187  	testCases := []struct {
   188  		description string
   189  		given       config.BidderInfo
   190  		expected    bool
   191  	}{
   192  		{
   193  			description: "Enabled, No Syncer",
   194  			given:       config.BidderInfo{Disabled: false, Syncer: nil},
   195  			expected:    false,
   196  		},
   197  		{
   198  			description: "Enabled, Syncer",
   199  			given:       config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Key: "anyKey"}},
   200  			expected:    true,
   201  		},
   202  		{
   203  			description: "Enabled, Syncer - Fully Loaded",
   204  			given:       config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Key: "anyKey", Supports: anySupports, IFrame: anyEndpoint, Redirect: anyEndpoint, SupportCORS: &anyCORS}},
   205  			expected:    true,
   206  		},
   207  		{
   208  			description: "Enabled, Syncer - Only Key",
   209  			given:       config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Key: "anyKey"}},
   210  			expected:    true,
   211  		},
   212  		{
   213  			description: "Enabled, Syncer - Only Supports",
   214  			given:       config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Supports: anySupports}},
   215  			expected:    false,
   216  		},
   217  		{
   218  			description: "Enabled, Syncer - Only IFrame",
   219  			given:       config.BidderInfo{Disabled: false, Syncer: &config.Syncer{IFrame: anyEndpoint}},
   220  			expected:    true,
   221  		},
   222  		{
   223  			description: "Enabled, Syncer - Only Redirect",
   224  			given:       config.BidderInfo{Disabled: false, Syncer: &config.Syncer{Redirect: anyEndpoint}},
   225  			expected:    true,
   226  		},
   227  		{
   228  			description: "Enabled, Syncer - Only SupportCORS",
   229  			given:       config.BidderInfo{Disabled: false, Syncer: &config.Syncer{SupportCORS: &anyCORS}},
   230  			expected:    true,
   231  		},
   232  		{
   233  			description: "Disabled, No Syncer",
   234  			given:       config.BidderInfo{Disabled: true, Syncer: nil},
   235  			expected:    false,
   236  		},
   237  		{
   238  			description: "Disabled, Syncer",
   239  			given:       config.BidderInfo{Disabled: true, Syncer: &config.Syncer{Key: "anyKey"}},
   240  			expected:    false,
   241  		},
   242  		{
   243  			description: "Disabled, Syncer - Fully Loaded",
   244  			given:       config.BidderInfo{Disabled: true, Syncer: &config.Syncer{Key: "anyKey", Supports: anySupports, IFrame: anyEndpoint, Redirect: anyEndpoint, SupportCORS: &anyCORS}},
   245  			expected:    false,
   246  		},
   247  		{
   248  			description: "Disabled, Syncer - Only Key",
   249  			given:       config.BidderInfo{Disabled: true, Syncer: &config.Syncer{Key: "anyKey"}},
   250  			expected:    false,
   251  		},
   252  		{
   253  			description: "Disabled, Syncer - Only Supports",
   254  			given:       config.BidderInfo{Disabled: true, Syncer: &config.Syncer{Supports: anySupports}},
   255  			expected:    false,
   256  		},
   257  		{
   258  			description: "Disabled, Syncer - Only IFrame",
   259  			given:       config.BidderInfo{Disabled: true, Syncer: &config.Syncer{IFrame: anyEndpoint}},
   260  			expected:    false,
   261  		},
   262  		{
   263  			description: "Disabled, Syncer - Only Redirect",
   264  			given:       config.BidderInfo{Disabled: true, Syncer: &config.Syncer{Redirect: anyEndpoint}},
   265  			expected:    false,
   266  		},
   267  		{
   268  			description: "Disabled, Syncer - Only SupportCORS",
   269  			given:       config.BidderInfo{Disabled: true, Syncer: &config.Syncer{SupportCORS: &anyCORS}},
   270  			expected:    false,
   271  		},
   272  	}
   273  
   274  	for _, test := range testCases {
   275  		result := shouldCreateSyncer(test.given)
   276  		assert.Equal(t, test.expected, result, test.description)
   277  	}
   278  }
   279  
   280  func TestChooseSyncerConfig(t *testing.T) {
   281  	var (
   282  		bidderAPopulated = namedSyncerConfig{name: "bidderA", cfg: config.Syncer{Key: "a", IFrame: &config.SyncerEndpoint{URL: "anyURL"}}}
   283  		bidderAEmpty     = namedSyncerConfig{name: "bidderA", cfg: config.Syncer{}}
   284  		bidderBPopulated = namedSyncerConfig{name: "bidderB", cfg: config.Syncer{Key: "a", IFrame: &config.SyncerEndpoint{URL: "anyURL"}}}
   285  		bidderBEmpty     = namedSyncerConfig{name: "bidderB", cfg: config.Syncer{}}
   286  	)
   287  
   288  	testCases := []struct {
   289  		description    string
   290  		given          []namedSyncerConfig
   291  		expectedConfig namedSyncerConfig
   292  		expectedError  string
   293  	}{
   294  		{
   295  			description:    "One",
   296  			given:          []namedSyncerConfig{bidderAPopulated},
   297  			expectedConfig: bidderAPopulated,
   298  		},
   299  		{
   300  			description:    "Many - Same Key - Unique Configs",
   301  			given:          []namedSyncerConfig{bidderAEmpty, bidderBPopulated},
   302  			expectedConfig: bidderBPopulated,
   303  		},
   304  		{
   305  			description:   "Many - Same Key - Multiple Configs",
   306  			given:         []namedSyncerConfig{bidderAPopulated, bidderBPopulated},
   307  			expectedError: "bidders bidderA, bidderB define endpoints (iframe and/or redirect) for the same syncer key, but only one bidder is permitted to define endpoints",
   308  		},
   309  		{
   310  			description:   "Many - Same Key - No Configs",
   311  			given:         []namedSyncerConfig{bidderAEmpty, bidderBEmpty},
   312  			expectedError: "bidders bidderA, bidderB share the same syncer key, but none define endpoints (iframe and/or redirect)",
   313  		},
   314  		{
   315  			description:    "Many - Same Key - Unique Configs",
   316  			given:          []namedSyncerConfig{bidderAEmpty, bidderBPopulated},
   317  			expectedConfig: bidderBPopulated,
   318  		},
   319  	}
   320  
   321  	for _, test := range testCases {
   322  		result, err := chooseSyncerConfig(test.given)
   323  
   324  		if test.expectedError == "" {
   325  			assert.NoError(t, err, test.description+":err")
   326  			assert.Equal(t, test.expectedConfig, result, test.description+":result")
   327  		} else {
   328  			assert.EqualError(t, err, test.expectedError, test.description+":err")
   329  			assert.Empty(t, result, test.description+":result")
   330  		}
   331  	}
   332  }