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