github.com/prebid/prebid-server/v2@v2.18.0/exchange/price_granularity_test.go (about) 1 package exchange 2 3 import ( 4 "encoding/json" 5 "math" 6 "testing" 7 8 "github.com/prebid/openrtb/v20/openrtb2" 9 "github.com/prebid/prebid-server/v2/openrtb_ext" 10 "github.com/prebid/prebid-server/v2/util/ptrutil" 11 "github.com/stretchr/testify/assert" 12 ) 13 14 func TestGetPriceBucketString(t *testing.T) { 15 low, _ := openrtb_ext.NewPriceGranularityFromLegacyID("low") 16 medium, _ := openrtb_ext.NewPriceGranularityFromLegacyID("medium") 17 high, _ := openrtb_ext.NewPriceGranularityFromLegacyID("high") 18 auto, _ := openrtb_ext.NewPriceGranularityFromLegacyID("auto") 19 dense, _ := openrtb_ext.NewPriceGranularityFromLegacyID("dense") 20 21 custom1 := openrtb_ext.PriceGranularity{ 22 Precision: ptrutil.ToPtr(2), 23 Ranges: []openrtb_ext.GranularityRange{ 24 { 25 Min: 0.0, 26 Max: 5.0, 27 Increment: 0.03, 28 }, 29 { 30 Min: 5.0, 31 Max: 10.0, 32 Increment: 0.1, 33 }, 34 }, 35 } 36 37 custom2 := openrtb_ext.PriceGranularity{ 38 Precision: ptrutil.ToPtr(2), 39 Ranges: []openrtb_ext.GranularityRange{ 40 { 41 Min: 0.0, 42 Max: 1.5, 43 Increment: 1.0, 44 }, 45 { 46 Min: 1.5, 47 Max: 10.0, 48 Increment: 1.2, 49 }, 50 }, 51 } 52 53 // Define test cases 54 type aTest struct { 55 granularityId string 56 targetData targetData 57 expectedPriceBucket string 58 } 59 testGroups := []struct { 60 groupDesc string 61 bid openrtb2.Bid 62 testCases []aTest 63 }{ 64 { 65 groupDesc: "cpm below the max in every price bucket", 66 bid: openrtb2.Bid{Price: 1.87}, 67 testCases: []aTest{ 68 {"low", targetData{priceGranularity: low}, "1.50"}, 69 {"medium", targetData{priceGranularity: medium}, "1.80"}, 70 {"high", targetData{priceGranularity: high}, "1.87"}, 71 {"auto", targetData{priceGranularity: auto}, "1.85"}, 72 {"dense", targetData{priceGranularity: dense}, "1.87"}, 73 {"custom1", targetData{priceGranularity: custom1}, "1.86"}, 74 {"custom2", targetData{priceGranularity: custom2}, "1.50"}, 75 }, 76 }, 77 { 78 groupDesc: "cpm above the max in low price bucket", 79 bid: openrtb2.Bid{Price: 5.72}, 80 testCases: []aTest{ 81 {"low", targetData{priceGranularity: low}, "5.00"}, 82 {"medium", targetData{priceGranularity: medium}, "5.70"}, 83 {"high", targetData{priceGranularity: high}, "5.72"}, 84 {"auto", targetData{priceGranularity: auto}, "5.70"}, 85 {"dense", targetData{priceGranularity: dense}, "5.70"}, 86 {"custom1", targetData{priceGranularity: custom1}, "5.70"}, 87 {"custom2", targetData{priceGranularity: custom2}, "5.10"}, 88 }, 89 }, 90 { 91 groupDesc: "media type price granularity for bid type video", 92 bid: openrtb2.Bid{Price: 5.0, MType: openrtb2.MarkupVideo}, 93 testCases: []aTest{ 94 {"medium", targetData{priceGranularity: medium}, "5.00"}, 95 {"video-custom2", targetData{priceGranularity: medium, mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{Video: &custom2}}, "3.90"}, 96 {"banner-custom2", targetData{priceGranularity: medium, mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{Banner: &custom2}}, "5.00"}, 97 }, 98 }, 99 { 100 groupDesc: "media type price granularity for bid type banner", 101 bid: openrtb2.Bid{Price: 5.0, MType: openrtb2.MarkupBanner}, 102 testCases: []aTest{ 103 {"medium", targetData{priceGranularity: medium}, "5.00"}, 104 {"video-custom2", targetData{priceGranularity: medium, mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{Video: &custom2}}, "5.00"}, 105 {"banner-custom2", targetData{priceGranularity: medium, mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{Banner: &custom2}}, "3.90"}, 106 }, 107 }, 108 { 109 groupDesc: "media type price granularity for bid type native", 110 bid: openrtb2.Bid{Price: 5.0, MType: openrtb2.MarkupNative}, 111 testCases: []aTest{ 112 {"medium", targetData{priceGranularity: medium}, "5.00"}, 113 {"video-custom2", targetData{priceGranularity: medium, mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{Video: &custom2}}, "5.00"}, 114 {"native-custom2", targetData{priceGranularity: medium, mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{Native: &custom2}}, "3.90"}, 115 }, 116 }, 117 { 118 groupDesc: "media type price granularity set but bid type incorrect", 119 bid: openrtb2.Bid{Price: 5.0, Ext: json.RawMessage(`{`)}, 120 testCases: []aTest{ 121 {"medium", targetData{priceGranularity: medium}, "5.00"}, 122 {"video-custom2", targetData{priceGranularity: medium, mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{Video: &custom2}}, "5.00"}, 123 {"banner-custom2", targetData{priceGranularity: medium, mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{Banner: &custom2}}, "5.00"}, 124 {"native-custom2", targetData{priceGranularity: medium, mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{Native: &custom2}}, "5.00"}, 125 }, 126 }, 127 { 128 groupDesc: "cpm equal the max for custom granularity", 129 bid: openrtb2.Bid{Price: 10}, 130 testCases: []aTest{ 131 {"custom1", targetData{priceGranularity: custom1}, "10.00"}, 132 {"custom2", targetData{priceGranularity: custom2}, "9.90"}, 133 }, 134 }, 135 { 136 groupDesc: "Precision value corner cases", 137 bid: openrtb2.Bid{Price: 1.876}, 138 testCases: []aTest{ 139 { 140 "Negative precision defaults to number of digits already in CPM float", 141 targetData{priceGranularity: openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(-1), Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 0.05}}}}, 142 "1.85", 143 }, 144 { 145 "Precision value equals zero, we expect to round up to the nearest integer", 146 targetData{priceGranularity: openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(0), Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 0.05}}}}, 147 "2", 148 }, 149 { 150 "Largest precision value PBS supports 15", 151 targetData{priceGranularity: openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(15), Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 0.05}}}}, 152 "1.850000000000000", 153 }, 154 }, 155 }, 156 { 157 groupDesc: "Increment value corner cases", 158 bid: openrtb2.Bid{Price: 1.876}, 159 testCases: []aTest{ 160 { 161 "Negative increment, return empty string", 162 targetData{priceGranularity: openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: -0.05}}}}, 163 "", 164 }, 165 { 166 "Zero increment, return empty string", 167 targetData{priceGranularity: openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{{Max: 5}}}}, 168 "", 169 }, 170 { 171 "Increment value is greater than CPM itself, return zero float value", 172 targetData{priceGranularity: openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 1.877}}}}, 173 "0.00", 174 }, 175 }, 176 }, 177 { 178 groupDesc: "Negative Cpm, return empty string since it does not belong into any range", 179 bid: openrtb2.Bid{Price: -1.876}, 180 testCases: []aTest{{"low", targetData{priceGranularity: low}, ""}}, 181 }, 182 { 183 groupDesc: "Zero value Cpm, return the same, only in string format", 184 bid: openrtb2.Bid{Price: 0}, 185 testCases: []aTest{{"low", targetData{priceGranularity: low}, "0.00"}}, 186 }, 187 { 188 groupDesc: "Large Cpm, return bucket Max", 189 bid: openrtb2.Bid{Price: math.MaxFloat64}, 190 testCases: []aTest{{"low", targetData{priceGranularity: low}, "5.00"}}, 191 }, 192 } 193 194 for _, testGroup := range testGroups { 195 for i, test := range testGroup.testCases { 196 var priceBucket string 197 assert.NotPanics(t, func() { priceBucket = GetPriceBucket(testGroup.bid, test.targetData) }, "Group: %s Granularity: %d", testGroup.groupDesc, i) 198 assert.Equal(t, test.expectedPriceBucket, priceBucket, "Group: %s Granularity: %s :: Expected %s, got %s from %f", testGroup.groupDesc, test.granularityId, test.expectedPriceBucket, priceBucket, testGroup.bid.Price) 199 } 200 } 201 }