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 }