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  }