github.com/prebid/prebid-server/v2@v2.18.0/floors/validate_test.go (about) 1 package floors 2 3 import ( 4 "errors" 5 "fmt" 6 "testing" 7 8 "github.com/prebid/prebid-server/v2/config" 9 "github.com/prebid/prebid-server/v2/openrtb_ext" 10 "github.com/stretchr/testify/assert" 11 ) 12 13 func TestValidateFloorParams(t *testing.T) { 14 15 tt := []struct { 16 name string 17 floorExt *openrtb_ext.PriceFloorRules 18 Err error 19 }{ 20 { 21 name: "Valid Skip Rate", 22 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 23 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 24 ModelVersion: "Version 1", 25 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}, Delimiter: "|"}, 26 Values: map[string]float64{ 27 "banner|300x250|www.website.com": 1.01, 28 "banner|300x250|*": 2.01, 29 "banner|300x600|www.website.com|www.test.com": 3.01, 30 "banner|300x600|*": 4.01, 31 }, Default: 0.01}, 32 }}}, 33 }, 34 { 35 name: "Invalid Skip Rate at Root level", 36 floorExt: &openrtb_ext.PriceFloorRules{SkipRate: -10}, 37 Err: errors.New("Invalid SkipRate = '-10' at ext.prebid.floors.skiprate"), 38 }, 39 { 40 name: "Invalid Skip Rate at Data level", 41 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 42 SkipRate: -10, 43 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 44 ModelVersion: "Version 1", 45 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}}, 46 Values: map[string]float64{ 47 "*|*|www.website.com": 15.01, 48 "*|*|*": 16.01, 49 }, Default: 0.01}, 50 }}}, 51 Err: errors.New("Invalid SkipRate = '-10' at ext.prebid.floors.data.skiprate"), 52 }, 53 { 54 name: "Invalid FloorMin ", 55 floorExt: &openrtb_ext.PriceFloorRules{FloorMin: -10}, 56 Err: errors.New("Invalid FloorMin = '-10', value should be >= 0"), 57 }, 58 { 59 name: "Invalid FloorSchemaVersion 2", 60 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 61 FloorsSchemaVersion: 1, 62 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 63 ModelVersion: "Version 1", 64 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}, Delimiter: "|"}, 65 Values: map[string]float64{ 66 "banner|300x250|www.website.com": 1.01, 67 "banner|300x600|*": 4.01, 68 }, Default: 0.01}, 69 }}}, 70 Err: errors.New("Invalid FloorsSchemaVersion = '1', supported version 2"), 71 }, 72 { 73 name: "Invalid FloorSchemaVersion -2", 74 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 75 FloorsSchemaVersion: -2, 76 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 77 ModelVersion: "Version 1", 78 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}, Delimiter: "|"}, 79 Values: map[string]float64{ 80 "banner|300x250|www.website.com": 1.01, 81 "banner|300x600|*": 4.01, 82 }, Default: 0.01}, 83 }}}, 84 Err: errors.New("Invalid FloorsSchemaVersion = '-2', supported version 2"), 85 }, 86 { 87 name: "Valid FloorSchemaVersion 0", 88 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 89 FloorsSchemaVersion: 0, 90 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 91 ModelVersion: "Version 1", 92 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}, Delimiter: "|"}, 93 Values: map[string]float64{ 94 "banner|300x250|www.website.com": 1.01, 95 "banner|300x600|*": 4.01, 96 }, Default: 0.01}, 97 }}}, 98 }, 99 { 100 name: "Valid FloorSchemaVersion 2", 101 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 102 FloorsSchemaVersion: 2, 103 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 104 ModelVersion: "Version 1", 105 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}, Delimiter: "|"}, 106 Values: map[string]float64{ 107 "banner|300x250|www.website.com": 1.01, 108 "banner|300x600|*": 4.01, 109 }, Default: 0.01}, 110 }}}, 111 }, 112 } 113 for _, tc := range tt { 114 t.Run(tc.name, func(t *testing.T) { 115 actErr := validateFloorParams(tc.floorExt) 116 assert.Equal(t, actErr, tc.Err, tc.name) 117 }) 118 } 119 } 120 121 func TestSelectValidFloorModelGroups(t *testing.T) { 122 123 tt := []struct { 124 name string 125 floorExt *openrtb_ext.PriceFloorRules 126 account config.Account 127 Err []error 128 }{ 129 { 130 name: "Invalid Skip Rate in model Group 1", 131 account: config.Account{ 132 PriceFloors: config.AccountPriceFloors{ 133 MaxRule: 100, 134 MaxSchemaDims: 5, 135 }, 136 }, 137 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 138 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 139 ModelWeight: getIntPtr(50), 140 SkipRate: 110, 141 ModelVersion: "Version 1", 142 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}}, 143 Values: map[string]float64{ 144 "banner|300x250|www.website.com": 1.01, 145 "banner|300x250|*": 2.01, 146 "banner|300x600|www.website.com": 3.01, 147 "*|728x90|*": 14.01, 148 "*|*|www.website.com": 15.01, 149 "*|*|*": 16.01, 150 }, Default: 0.01}, 151 { 152 ModelWeight: getIntPtr(50), 153 SkipRate: 20, 154 ModelVersion: "Version 2", 155 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}}, 156 Values: map[string]float64{ 157 "banner|300x250|www.website.com": 1.01, 158 "banner|300x250|*": 2.01, 159 "*|*|www.website.com": 15.01, 160 "*|*|*": 16.01, 161 }, Default: 0.01}, 162 }}}, 163 Err: []error{errors.New("Invalid Floor Model = 'Version 1' due to SkipRate = '110' is out of range (1-100)")}, 164 }, 165 { 166 name: "Invalid model weight Model Group 1", 167 account: config.Account{ 168 PriceFloors: config.AccountPriceFloors{ 169 MaxRule: 100, 170 MaxSchemaDims: 5, 171 }, 172 }, 173 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 174 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 175 ModelWeight: getIntPtr(-1), 176 SkipRate: 10, 177 ModelVersion: "Version 1", 178 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}}, 179 Values: map[string]float64{ 180 "banner|300x250|www.website.com": 1.01, 181 "banner|300x250|*": 2.01, 182 "*|728x90|*": 14.01, 183 "*|*|www.website.com": 15.01, 184 "*|*|*": 16.01, 185 }, Default: 0.01}, 186 { 187 ModelWeight: getIntPtr(50), 188 SkipRate: 20, 189 ModelVersion: "Version 2", 190 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}}, 191 Values: map[string]float64{ 192 "banner|300x250|www.website.com": 1.01, 193 "banner|300x250|*": 2.01, 194 "*|728x90|*": 14.01, 195 "*|*|www.website.com": 15.01, 196 "*|*|*": 16.01, 197 }, Default: 0.01}, 198 }}}, 199 Err: []error{errors.New("Invalid Floor Model = 'Version 1' due to ModelWeight = '-1' is out of range (1-100)")}, 200 }, 201 { 202 name: "Invalid Default Value", 203 account: config.Account{ 204 PriceFloors: config.AccountPriceFloors{ 205 MaxRule: 100, 206 MaxSchemaDims: 5, 207 }, 208 }, 209 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 210 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 211 ModelWeight: getIntPtr(50), 212 ModelVersion: "Version 1", 213 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}}, 214 Values: map[string]float64{ 215 "banner|300x250|www.website.com": 1.01, 216 "*|728x90|*": 14.01, 217 "*|*|www.website.com": 15.01, 218 "*|*|*": 16.01, 219 }, Default: -1.0000}, 220 }}}, 221 Err: []error{errors.New("Invalid Floor Model = 'Version 1' due to Default = '-1' is less than 0")}, 222 }, 223 { 224 name: "Invalid Number of Schema dimensions", 225 account: config.Account{ 226 PriceFloors: config.AccountPriceFloors{ 227 MaxRule: 100, 228 MaxSchemaDims: 2, 229 }, 230 }, 231 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 232 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 233 ModelVersion: "Version 1", 234 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}}, 235 Values: map[string]float64{ 236 "banner|300x250|www.website.com": 1.01, 237 "*|728x90|*": 14.01, 238 "*|*|www.website.com": 15.01, 239 "*|*|*": 16.01, 240 }}, 241 }}}, 242 Err: []error{errors.New("Invalid Floor Model = 'Version 1' due to number of schema fields = '3' are greater than limit 2")}, 243 }, 244 { 245 name: "Invalid Schema field creativeType", 246 account: config.Account{ 247 PriceFloors: config.AccountPriceFloors{ 248 MaxRule: 100, 249 MaxSchemaDims: 3, 250 }, 251 }, 252 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 253 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 254 ModelVersion: "Version 1", 255 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"creativeType", "size", "domain"}}, 256 Values: map[string]float64{ 257 "banner|300x250|www.website.com": 1.01, 258 "*|728x90|*": 14.01, 259 "*|*|www.website.com": 15.01, 260 "*|*|*": 16.01, 261 }}, 262 }}}, 263 Err: []error{errors.New("Invalid schema dimension provided = 'creativeType' in Schema Fields = '[creativeType size domain]'")}, 264 }, 265 { 266 name: "Invalid Number of rules", 267 account: config.Account{ 268 PriceFloors: config.AccountPriceFloors{ 269 MaxRule: 3, 270 MaxSchemaDims: 5, 271 }, 272 }, 273 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 274 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 275 ModelVersion: "Version 1", 276 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}}, 277 Values: map[string]float64{ 278 "banner|300x250|www.website.com": 1.01, 279 "*|728x90|*": 14.01, 280 "*|*|www.website.com": 15.01, 281 "*|*|*": 16.01, 282 }}, 283 }}}, 284 Err: []error{errors.New("Invalid Floor Model = 'Version 1' due to number of rules = '4' are greater than limit 3")}, 285 }, 286 { 287 name: "No Modelgroup present", 288 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 289 ModelGroups: []openrtb_ext.PriceFloorModelGroup{}}}, 290 Err: []error{errors.New("No model group present in floors.data")}, 291 }, 292 } 293 for _, tc := range tt { 294 t.Run(tc.name, func(t *testing.T) { 295 _, ErrList := selectValidFloorModelGroups(tc.floorExt.Data.ModelGroups, tc.account) 296 assert.Equal(t, ErrList, tc.Err, tc.name) 297 }) 298 } 299 } 300 301 func TestValidateFloorRulesAndLowerValidRuleKey(t *testing.T) { 302 303 tt := []struct { 304 name string 305 floorExt *openrtb_ext.PriceFloorRules 306 Err []error 307 expctedFloor map[string]float64 308 }{ 309 { 310 name: "Invalid floor rule banner|300x600|www.website.com|www.test.com", 311 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 312 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 313 ModelVersion: "Version 1", 314 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}, Delimiter: "|"}, 315 Values: map[string]float64{ 316 "BANNER|300x250|WWW.WEBSITE.COM": 1.01, 317 "banner|300x250|*": 2.01, 318 "banner|300x600|www.website.com|www.test.com": 3.01, 319 "banner|300x600|*": 4.01, 320 "banner|728x90|www.website.com": 5.01, 321 "banner|728x90|*": 6.01, 322 "banner|*|www.website.com": 7.01, 323 "banner|*|*": 8.01, 324 "*|300x250|www.website.com": 9.01, 325 "*|300x250|*": 10.01, 326 "*|300x600|www.website.com": 11.01, 327 "*|300x600|*": 12.01, 328 "*|728x90|www.website.com": 13.01, 329 "*|728x90|*": 14.01, 330 "*|*|www.website.com": 15.01, 331 "*|*|*": 16.01, 332 }, Default: 0.01}, 333 }}}, 334 Err: []error{errors.New("Invalid Floor Rule = 'banner|300x600|www.website.com|www.test.com' for Schema Fields = '[mediaType size domain]'")}, 335 expctedFloor: map[string]float64{ 336 "banner|300x250|www.website.com": 1.01, 337 "banner|300x250|*": 2.01, 338 "banner|300x600|*": 4.01, 339 "banner|728x90|www.website.com": 5.01, 340 "banner|728x90|*": 6.01, 341 "banner|*|www.website.com": 7.01, 342 "banner|*|*": 8.01, 343 "*|300x250|www.website.com": 9.01, 344 "*|300x250|*": 10.01, 345 "*|300x600|www.website.com": 11.01, 346 "*|300x600|*": 12.01, 347 "*|728x90|www.website.com": 13.01, 348 "*|728x90|*": 14.01, 349 "*|*|www.website.com": 15.01, 350 "*|*|*": 16.01, 351 }, 352 }, 353 { 354 name: "Invalid floor rule banner|300x600|www.website.com|www.test.com", 355 floorExt: &openrtb_ext.PriceFloorRules{Data: &openrtb_ext.PriceFloorData{ 356 ModelGroups: []openrtb_ext.PriceFloorModelGroup{{ 357 ModelVersion: "Version 1", 358 Schema: openrtb_ext.PriceFloorSchema{Fields: []string{"mediaType", "size", "domain"}, Delimiter: "|"}, 359 Values: map[string]float64{ 360 "banner|300x250|www.website.com": 1.01, 361 "banner|300x250|*": 2.01, 362 "banner|300x600|www.website.com": 3.01, 363 "banner|300x600": 4.01, 364 "banner|728x90|www.website.com": 5.01, 365 "banner|728x90|*": 6.01, 366 "banner|*|www.website.com": 7.01, 367 "banner|*|*": 8.01, 368 "*|300x250|www.website.com": 9.01, 369 "*|300x250|*": 10.01, 370 "*|300x600|www.website.com": 11.01, 371 "*|300x600|*": 12.01, 372 "*|728x90|www.website.com": 13.01, 373 "*|728x90|*": 14.01, 374 "*|*|www.website.com": 15.01, 375 "*|*|*": 16.01, 376 }, Default: 0.01}, 377 }}}, 378 Err: []error{errors.New("Invalid Floor Rule = 'banner|300x600' for Schema Fields = '[mediaType size domain]'")}, 379 expctedFloor: map[string]float64{ 380 "banner|300x250|www.website.com": 1.01, 381 "banner|300x250|*": 2.01, 382 "banner|300x600|www.website.com": 3.01, 383 "banner|728x90|www.website.com": 5.01, 384 "banner|728x90|*": 6.01, 385 "banner|*|www.website.com": 7.01, 386 "banner|*|*": 8.01, 387 "*|300x250|www.website.com": 9.01, 388 "*|300x250|*": 10.01, 389 "*|300x600|www.website.com": 11.01, 390 "*|300x600|*": 12.01, 391 "*|728x90|www.website.com": 13.01, 392 "*|728x90|*": 14.01, 393 "*|*|www.website.com": 15.01, 394 "*|*|*": 16.01, 395 }, 396 }, 397 } 398 399 for _, tc := range tt { 400 t.Run(tc.name, func(t *testing.T) { 401 ErrList := validateFloorRulesAndLowerValidRuleKey(tc.floorExt.Data.ModelGroups[0].Schema, tc.floorExt.Data.ModelGroups[0].Schema.Delimiter, tc.floorExt.Data.ModelGroups[0].Values) 402 assert.Equal(t, ErrList, tc.Err, tc.name) 403 assert.Equal(t, tc.floorExt.Data.ModelGroups[0].Values, tc.expctedFloor, tc.name) 404 }) 405 } 406 } 407 408 func TestValidateSchemaDimensions(t *testing.T) { 409 tests := []struct { 410 name string 411 fields []string 412 err error 413 }{ 414 { 415 name: "valid_fields", 416 fields: []string{"deviceType", "size"}, 417 }, 418 { 419 name: "invalid_fields", 420 fields: []string{"deviceType", "dealType"}, 421 err: fmt.Errorf("Invalid schema dimension provided = 'dealType' in Schema Fields = '[deviceType dealType]'"), 422 }, 423 } 424 for _, tt := range tests { 425 t.Run(tt.name, func(t *testing.T) { 426 err := validateSchemaDimensions(tt.fields) 427 assert.Equal(t, tt.err, err) 428 }) 429 } 430 }