github.com/prebid/prebid-server/v2@v2.18.0/config/config_test.go (about)

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"net"
     7  	"os"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/prebid/go-gdpr/consentconstants"
    13  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    14  	"github.com/prebid/prebid-server/v2/util/ptrutil"
    15  	"github.com/spf13/viper"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  var bidderInfos = BidderInfos{
    20  	"bidder1": BidderInfo{
    21  		Endpoint:   "http://bidder1.com",
    22  		Maintainer: &MaintainerInfo{Email: "maintainer@bidder1.com"},
    23  		Capabilities: &CapabilitiesInfo{
    24  			App: &PlatformInfo{
    25  				MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner},
    26  			},
    27  		},
    28  	},
    29  	"bidder2": BidderInfo{
    30  		Endpoint:   "http://bidder2.com",
    31  		Maintainer: &MaintainerInfo{Email: "maintainer@bidder2.com"},
    32  		Capabilities: &CapabilitiesInfo{
    33  			App: &PlatformInfo{
    34  				MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner},
    35  			},
    36  		},
    37  	},
    38  }
    39  
    40  func TestExternalCacheURLValidate(t *testing.T) {
    41  	testCases := []struct {
    42  		desc      string
    43  		data      ExternalCache
    44  		expErrors int
    45  	}{
    46  		{
    47  			desc:      "With http://",
    48  			data:      ExternalCache{Host: "http://www.google.com", Path: "/path/v1"},
    49  			expErrors: 1,
    50  		},
    51  		{
    52  			desc:      "Without http://",
    53  			data:      ExternalCache{Host: "www.google.com", Path: "/path/v1"},
    54  			expErrors: 0,
    55  		},
    56  		{
    57  			desc:      "No scheme but '//' prefix",
    58  			data:      ExternalCache{Host: "//www.google.com", Path: "/path/v1"},
    59  			expErrors: 1,
    60  		},
    61  		{
    62  			desc:      "// appears twice",
    63  			data:      ExternalCache{Host: "//www.google.com//", Path: "path/v1"},
    64  			expErrors: 1,
    65  		},
    66  		{
    67  			desc:      "Host has an only // value",
    68  			data:      ExternalCache{Host: "//", Path: "path/v1"},
    69  			expErrors: 1,
    70  		},
    71  		{
    72  			desc:      "only scheme host, valid path",
    73  			data:      ExternalCache{Host: "http://", Path: "/path/v1"},
    74  			expErrors: 1,
    75  		},
    76  		{
    77  			desc:      "No host, path only",
    78  			data:      ExternalCache{Host: "", Path: "path/v1"},
    79  			expErrors: 1,
    80  		},
    81  		{
    82  			desc:      "No host, nor path",
    83  			data:      ExternalCache{Host: "", Path: ""},
    84  			expErrors: 0,
    85  		},
    86  		{
    87  			desc:      "Invalid http at the end",
    88  			data:      ExternalCache{Host: "www.google.com", Path: "http://"},
    89  			expErrors: 1,
    90  		},
    91  		{
    92  			desc:      "Host has an unknown scheme",
    93  			data:      ExternalCache{Host: "unknownscheme://host", Path: "/path/v1"},
    94  			expErrors: 1,
    95  		},
    96  		{
    97  			desc:      "Wrong colon side in scheme",
    98  			data:      ExternalCache{Host: "http//:www.appnexus.com", Path: "/path/v1"},
    99  			expErrors: 1,
   100  		},
   101  		{
   102  			desc:      "Missing '/' in scheme",
   103  			data:      ExternalCache{Host: "http:/www.appnexus.com", Path: "/path/v1"},
   104  			expErrors: 1,
   105  		},
   106  		{
   107  			desc:      "host with scheme, no path",
   108  			data:      ExternalCache{Host: "http://www.appnexus.com", Path: ""},
   109  			expErrors: 1,
   110  		},
   111  		{
   112  			desc:      "scheme, no host nor path",
   113  			data:      ExternalCache{Host: "http://", Path: ""},
   114  			expErrors: 1,
   115  		},
   116  		{
   117  			desc:      "Scheme Invalid",
   118  			data:      ExternalCache{Scheme: "invalid", Host: "www.google.com", Path: "/path/v1"},
   119  			expErrors: 1,
   120  		},
   121  		{
   122  			desc:      "Scheme HTTP",
   123  			data:      ExternalCache{Scheme: "http", Host: "www.google.com", Path: "/path/v1"},
   124  			expErrors: 0,
   125  		},
   126  		{
   127  			desc:      "Scheme HTTPS",
   128  			data:      ExternalCache{Scheme: "https", Host: "www.google.com", Path: "/path/v1"},
   129  			expErrors: 0,
   130  		},
   131  		{
   132  			desc:      "Host with port",
   133  			data:      ExternalCache{Scheme: "https", Host: "localhost:2424", Path: "/path/v1"},
   134  			expErrors: 0,
   135  		},
   136  	}
   137  	for _, test := range testCases {
   138  		errs := test.data.validate([]error{})
   139  
   140  		assert.Equal(t, test.expErrors, len(errs), "Test case threw unexpected number of errors. Desc: %s errMsg = %v \n", test.desc, errs)
   141  	}
   142  }
   143  
   144  func TestDefaults(t *testing.T) {
   145  	cfg, _ := newDefaultConfig(t)
   146  
   147  	cmpInts(t, "port", 8000, cfg.Port)
   148  	cmpInts(t, "admin_port", 6060, cfg.AdminPort)
   149  	cmpInts(t, "auction_timeouts_ms.max", 0, int(cfg.AuctionTimeouts.Max))
   150  	cmpInts(t, "max_request_size", 1024*256, int(cfg.MaxRequestSize))
   151  	cmpInts(t, "host_cookie.ttl_days", 90, int(cfg.HostCookie.TTL))
   152  	cmpInts(t, "host_cookie.max_cookie_size_bytes", 0, cfg.HostCookie.MaxCookieSizeBytes)
   153  	cmpInts(t, "currency_converter.fetch_interval_seconds", 1800, cfg.CurrencyConverter.FetchIntervalSeconds)
   154  	cmpStrings(t, "currency_converter.fetch_url", "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json", cfg.CurrencyConverter.FetchURL)
   155  	cmpBools(t, "account_required", false, cfg.AccountRequired)
   156  	cmpInts(t, "metrics.influxdb.collection_rate_seconds", 20, cfg.Metrics.Influxdb.MetricSendInterval)
   157  	cmpBools(t, "account_adapter_details", false, cfg.Metrics.Disabled.AccountAdapterDetails)
   158  	cmpBools(t, "account_debug", true, cfg.Metrics.Disabled.AccountDebug)
   159  	cmpBools(t, "account_stored_responses", true, cfg.Metrics.Disabled.AccountStoredResponses)
   160  	cmpBools(t, "adapter_connections_metrics", true, cfg.Metrics.Disabled.AdapterConnectionMetrics)
   161  	cmpBools(t, "adapter_buyeruid_scrubbed", true, cfg.Metrics.Disabled.AdapterBuyerUIDScrubbed)
   162  	cmpBools(t, "adapter_gdpr_request_blocked", false, cfg.Metrics.Disabled.AdapterGDPRRequestBlocked)
   163  	cmpStrings(t, "certificates_file", "", cfg.PemCertsFile)
   164  	cmpInts(t, "stored_requests_timeout_ms", 50, cfg.StoredRequestsTimeout)
   165  	cmpBools(t, "stored_requests.filesystem.enabled", false, cfg.StoredRequests.Files.Enabled)
   166  	cmpStrings(t, "stored_requests.filesystem.directorypath", "./stored_requests/data/by_id", cfg.StoredRequests.Files.Path)
   167  	cmpBools(t, "auto_gen_source_tid", true, cfg.AutoGenSourceTID)
   168  	cmpBools(t, "generate_bid_id", false, cfg.GenerateBidID)
   169  	cmpStrings(t, "experiment.adscert.mode", "off", cfg.Experiment.AdCerts.Mode)
   170  	cmpStrings(t, "experiment.adscert.inprocess.origin", "", cfg.Experiment.AdCerts.InProcess.Origin)
   171  	cmpStrings(t, "experiment.adscert.inprocess.key", "", cfg.Experiment.AdCerts.InProcess.PrivateKey)
   172  	cmpInts(t, "experiment.adscert.inprocess.domain_check_interval_seconds", 30, cfg.Experiment.AdCerts.InProcess.DNSCheckIntervalInSeconds)
   173  	cmpInts(t, "experiment.adscert.inprocess.domain_renewal_interval_seconds", 30, cfg.Experiment.AdCerts.InProcess.DNSRenewalIntervalInSeconds)
   174  	cmpStrings(t, "experiment.adscert.remote.url", "", cfg.Experiment.AdCerts.Remote.Url)
   175  	cmpInts(t, "experiment.adscert.remote.signing_timeout_ms", 5, cfg.Experiment.AdCerts.Remote.SigningTimeoutMs)
   176  	cmpNils(t, "host_schain_node", cfg.HostSChainNode)
   177  	cmpStrings(t, "datacenter", "", cfg.DataCenter)
   178  
   179  	//Assert the price floor default values
   180  	cmpBools(t, "price_floors.enabled", false, cfg.PriceFloors.Enabled)
   181  	cmpInts(t, "price_floors.fetcher.worker", 20, cfg.PriceFloors.Fetcher.Worker)
   182  	cmpInts(t, "price_floors.fetcher.capacity", 20000, cfg.PriceFloors.Fetcher.Capacity)
   183  	cmpInts(t, "price_floors.fetcher.cache_size_mb", 64, cfg.PriceFloors.Fetcher.CacheSize)
   184  	cmpInts(t, "price_floors.fetcher.http_client.max_connections_per_host", 0, cfg.PriceFloors.Fetcher.HttpClient.MaxConnsPerHost)
   185  	cmpInts(t, "price_floors.fetcher.http_client.max_idle_connections", 40, cfg.PriceFloors.Fetcher.HttpClient.MaxIdleConns)
   186  	cmpInts(t, "price_floors.fetcher.http_client.max_idle_connections_per_host", 2, cfg.PriceFloors.Fetcher.HttpClient.MaxIdleConnsPerHost)
   187  	cmpInts(t, "price_floors.fetcher.http_client.idle_connection_timeout_seconds", 60, cfg.PriceFloors.Fetcher.HttpClient.IdleConnTimeout)
   188  	cmpInts(t, "price_floors.fetcher.max_retries", 10, cfg.PriceFloors.Fetcher.MaxRetries)
   189  
   190  	// Assert compression related defaults
   191  	cmpBools(t, "compression.request.enable_gzip", false, cfg.Compression.Request.GZIP)
   192  	cmpBools(t, "compression.response.enable_gzip", false, cfg.Compression.Response.GZIP)
   193  
   194  	cmpBools(t, "account_defaults.price_floors.enabled", false, cfg.AccountDefaults.PriceFloors.Enabled)
   195  	cmpInts(t, "account_defaults.price_floors.enforce_floors_rate", 100, cfg.AccountDefaults.PriceFloors.EnforceFloorsRate)
   196  	cmpBools(t, "account_defaults.price_floors.adjust_for_bid_adjustment", true, cfg.AccountDefaults.PriceFloors.AdjustForBidAdjustment)
   197  	cmpBools(t, "account_defaults.price_floors.enforce_deal_floors", false, cfg.AccountDefaults.PriceFloors.EnforceDealFloors)
   198  	cmpBools(t, "account_defaults.price_floors.use_dynamic_data", false, cfg.AccountDefaults.PriceFloors.UseDynamicData)
   199  	cmpInts(t, "account_defaults.price_floors.max_rules", 100, cfg.AccountDefaults.PriceFloors.MaxRule)
   200  	cmpInts(t, "account_defaults.price_floors.max_schema_dims", 3, cfg.AccountDefaults.PriceFloors.MaxSchemaDims)
   201  	cmpBools(t, "account_defaults.price_floors.fetch.enabled", false, cfg.AccountDefaults.PriceFloors.Fetcher.Enabled)
   202  	cmpStrings(t, "account_defaults.price_floors.fetch.url", "", cfg.AccountDefaults.PriceFloors.Fetcher.URL)
   203  	cmpInts(t, "account_defaults.price_floors.fetch.timeout_ms", 3000, cfg.AccountDefaults.PriceFloors.Fetcher.Timeout)
   204  	cmpInts(t, "account_defaults.price_floors.fetch.max_file_size_kb", 100, cfg.AccountDefaults.PriceFloors.Fetcher.MaxFileSizeKB)
   205  	cmpInts(t, "account_defaults.price_floors.fetch.max_rules", 1000, cfg.AccountDefaults.PriceFloors.Fetcher.MaxRules)
   206  	cmpInts(t, "account_defaults.price_floors.fetch.period_sec", 3600, cfg.AccountDefaults.PriceFloors.Fetcher.Period)
   207  	cmpInts(t, "account_defaults.price_floors.fetch.max_age_sec", 86400, cfg.AccountDefaults.PriceFloors.Fetcher.MaxAge)
   208  	cmpInts(t, "account_defaults.price_floors.fetch.max_schema_dims", 0, cfg.AccountDefaults.PriceFloors.Fetcher.MaxSchemaDims)
   209  	cmpStrings(t, "account_defaults.privacy.topicsdomain", "", cfg.AccountDefaults.Privacy.PrivacySandbox.TopicsDomain)
   210  	cmpBools(t, "account_defaults.privacy.privacysandbox.cookiedeprecation.enabled", false, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.Enabled)
   211  	cmpInts(t, "account_defaults.privacy.privacysandbox.cookiedeprecation.ttl_sec", 604800, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)
   212  
   213  	cmpBools(t, "account_defaults.events.enabled", false, cfg.AccountDefaults.Events.Enabled)
   214  
   215  	cmpBools(t, "hooks.enabled", false, cfg.Hooks.Enabled)
   216  	cmpStrings(t, "validations.banner_creative_max_size", "skip", cfg.Validations.BannerCreativeMaxSize)
   217  	cmpStrings(t, "validations.secure_markup", "skip", cfg.Validations.SecureMarkup)
   218  	cmpInts(t, "validations.max_creative_width", 0, int(cfg.Validations.MaxCreativeWidth))
   219  	cmpInts(t, "validations.max_creative_height", 0, int(cfg.Validations.MaxCreativeHeight))
   220  	cmpBools(t, "account_modules_metrics", false, cfg.Metrics.Disabled.AccountModulesMetrics)
   221  
   222  	cmpBools(t, "tmax_adjustments.enabled", false, cfg.TmaxAdjustments.Enabled)
   223  	cmpUnsignedInts(t, "tmax_adjustments.bidder_response_duration_min_ms", 0, cfg.TmaxAdjustments.BidderResponseDurationMin)
   224  	cmpUnsignedInts(t, "tmax_adjustments.bidder_network_latency_buffer_ms", 0, cfg.TmaxAdjustments.BidderNetworkLatencyBuffer)
   225  	cmpUnsignedInts(t, "tmax_adjustments.pbs_response_preparation_duration_ms", 0, cfg.TmaxAdjustments.PBSResponsePreparationDuration)
   226  
   227  	cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 56, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits)
   228  	cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 24, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits)
   229  
   230  	//Assert purpose VendorExceptionMap hash tables were built correctly
   231  	cmpBools(t, "analytics.agma.enabled", false, cfg.Analytics.Agma.Enabled)
   232  	cmpStrings(t, "analytics.agma.endpoint.timeout", "2s", cfg.Analytics.Agma.Endpoint.Timeout)
   233  	cmpBools(t, "analytics.agma.endpoint.gzip", false, cfg.Analytics.Agma.Endpoint.Gzip)
   234  	cmpStrings(t, "analytics.agma.endppoint.url", "https://go.pbs.agma-analytics.de/v1/prebid-server", cfg.Analytics.Agma.Endpoint.Url)
   235  	cmpStrings(t, "analytics.agma.buffers.size", "2MB", cfg.Analytics.Agma.Buffers.BufferSize)
   236  	cmpInts(t, "analytics.agma.buffers.count", 100, cfg.Analytics.Agma.Buffers.EventCount)
   237  	cmpStrings(t, "analytics.agma.buffers.timeout", "15m", cfg.Analytics.Agma.Buffers.Timeout)
   238  	cmpInts(t, "analytics.agma.accounts", 0, len(cfg.Analytics.Agma.Accounts))
   239  	expectedTCF2 := TCF2{
   240  		Enabled: true,
   241  		Purpose1: TCF2Purpose{
   242  			EnforceAlgo:        TCF2EnforceAlgoFull,
   243  			EnforceAlgoID:      TCF2FullEnforcement,
   244  			EnforcePurpose:     true,
   245  			EnforceVendors:     true,
   246  			VendorExceptions:   []string{},
   247  			VendorExceptionMap: map[string]struct{}{},
   248  		},
   249  		Purpose2: TCF2Purpose{
   250  			EnforceAlgo:        TCF2EnforceAlgoFull,
   251  			EnforceAlgoID:      TCF2FullEnforcement,
   252  			EnforcePurpose:     true,
   253  			EnforceVendors:     true,
   254  			VendorExceptions:   []string{},
   255  			VendorExceptionMap: map[string]struct{}{},
   256  		},
   257  		Purpose3: TCF2Purpose{
   258  			EnforceAlgo:        TCF2EnforceAlgoFull,
   259  			EnforceAlgoID:      TCF2FullEnforcement,
   260  			EnforcePurpose:     true,
   261  			EnforceVendors:     true,
   262  			VendorExceptions:   []string{},
   263  			VendorExceptionMap: map[string]struct{}{},
   264  		},
   265  		Purpose4: TCF2Purpose{
   266  			EnforceAlgo:        TCF2EnforceAlgoFull,
   267  			EnforceAlgoID:      TCF2FullEnforcement,
   268  			EnforcePurpose:     true,
   269  			EnforceVendors:     true,
   270  			VendorExceptions:   []string{},
   271  			VendorExceptionMap: map[string]struct{}{},
   272  		},
   273  		Purpose5: TCF2Purpose{
   274  			EnforceAlgo:        TCF2EnforceAlgoFull,
   275  			EnforceAlgoID:      TCF2FullEnforcement,
   276  			EnforcePurpose:     true,
   277  			EnforceVendors:     true,
   278  			VendorExceptions:   []string{},
   279  			VendorExceptionMap: map[string]struct{}{},
   280  		},
   281  		Purpose6: TCF2Purpose{
   282  			EnforceAlgo:        TCF2EnforceAlgoFull,
   283  			EnforceAlgoID:      TCF2FullEnforcement,
   284  			EnforcePurpose:     true,
   285  			EnforceVendors:     true,
   286  			VendorExceptions:   []string{},
   287  			VendorExceptionMap: map[string]struct{}{},
   288  		},
   289  		Purpose7: TCF2Purpose{
   290  			EnforceAlgo:        TCF2EnforceAlgoFull,
   291  			EnforceAlgoID:      TCF2FullEnforcement,
   292  			EnforcePurpose:     true,
   293  			EnforceVendors:     true,
   294  			VendorExceptions:   []string{},
   295  			VendorExceptionMap: map[string]struct{}{},
   296  		},
   297  		Purpose8: TCF2Purpose{
   298  			EnforceAlgo:        TCF2EnforceAlgoFull,
   299  			EnforceAlgoID:      TCF2FullEnforcement,
   300  			EnforcePurpose:     true,
   301  			EnforceVendors:     true,
   302  			VendorExceptions:   []string{},
   303  			VendorExceptionMap: map[string]struct{}{},
   304  		},
   305  		Purpose9: TCF2Purpose{
   306  			EnforceAlgo:        TCF2EnforceAlgoFull,
   307  			EnforceAlgoID:      TCF2FullEnforcement,
   308  			EnforcePurpose:     true,
   309  			EnforceVendors:     true,
   310  			VendorExceptions:   []string{},
   311  			VendorExceptionMap: map[string]struct{}{},
   312  		},
   313  		Purpose10: TCF2Purpose{
   314  			EnforceAlgo:        TCF2EnforceAlgoFull,
   315  			EnforceAlgoID:      TCF2FullEnforcement,
   316  			EnforcePurpose:     true,
   317  			EnforceVendors:     true,
   318  			VendorExceptions:   []string{},
   319  			VendorExceptionMap: map[string]struct{}{},
   320  		},
   321  		SpecialFeature1: TCF2SpecialFeature{
   322  			Enforce:            true,
   323  			VendorExceptions:   []openrtb_ext.BidderName{},
   324  			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
   325  		},
   326  		PurposeOneTreatment: TCF2PurposeOneTreatment{
   327  			Enabled:       true,
   328  			AccessAllowed: true,
   329  		},
   330  	}
   331  	expectedTCF2.PurposeConfigs = map[consentconstants.Purpose]*TCF2Purpose{
   332  		1:  &expectedTCF2.Purpose1,
   333  		2:  &expectedTCF2.Purpose2,
   334  		3:  &expectedTCF2.Purpose3,
   335  		4:  &expectedTCF2.Purpose4,
   336  		5:  &expectedTCF2.Purpose5,
   337  		6:  &expectedTCF2.Purpose6,
   338  		7:  &expectedTCF2.Purpose7,
   339  		8:  &expectedTCF2.Purpose8,
   340  		9:  &expectedTCF2.Purpose9,
   341  		10: &expectedTCF2.Purpose10,
   342  	}
   343  	assert.Equal(t, expectedTCF2, cfg.GDPR.TCF2, "gdpr.tcf2")
   344  }
   345  
   346  // When adding a new field, make sure the indentations are spaces not tabs otherwise read config may fail to parse the new field value.
   347  var fullConfig = []byte(`
   348  gdpr:
   349    host_vendor_id: 15
   350    default_value: "1"
   351    non_standard_publishers: ["pub1", "pub2"]
   352    eea_countries: ["eea1", "eea2"]
   353    tcf2:
   354      purpose1:
   355        enforce_vendors: false
   356        vendor_exceptions: ["foo1a", "foo1b"]
   357      purpose2:
   358        enforce_algo: "full"
   359        enforce_purpose: false
   360        enforce_vendors: false
   361        vendor_exceptions: ["foo2"]
   362      purpose3:
   363        enforce_algo: "basic"
   364        enforce_vendors: false
   365        vendor_exceptions: ["foo3"]
   366      purpose4:
   367        enforce_vendors: false
   368        vendor_exceptions: ["foo4"]
   369      purpose5:
   370        enforce_vendors: false
   371        vendor_exceptions: ["foo5"]
   372      purpose6:
   373        enforce_vendors: false
   374        vendor_exceptions: ["foo6"]
   375      purpose7:
   376        enforce_vendors: false
   377        vendor_exceptions: ["foo7"]
   378      purpose8:
   379        enforce_vendors: false
   380        vendor_exceptions: ["foo8"]
   381      purpose9:
   382        enforce_vendors: false
   383        vendor_exceptions: ["foo9"]
   384      purpose10:
   385        enforce_vendors: false
   386        vendor_exceptions: ["foo10"]
   387      special_feature1:
   388        vendor_exceptions: ["fooSP1"]
   389  ccpa:
   390    enforce: true
   391  lmt:
   392    enforce: true
   393  host_cookie:
   394    cookie_name: userid
   395    family: prebid
   396    domain: cookies.prebid.org
   397    opt_out_url: http://prebid.org/optout
   398    opt_in_url: http://prebid.org/optin
   399    max_cookie_size_bytes: 32768
   400  external_url: http://prebid-server.prebid.org/
   401  host: prebid-server.prebid.org
   402  port: 1234
   403  admin_port: 5678
   404  stored_requests_timeout_ms: 75
   405  compression:
   406      request:
   407          enable_gzip: true
   408      response:
   409          enable_gzip: false
   410  garbage_collector_threshold: 1
   411  datacenter: "1"
   412  auction_timeouts_ms:
   413    max: 123
   414    default: 50
   415  cache:
   416    scheme: http
   417    host: prebidcache.net
   418    query: uuid=%PBS_CACHE_UUID%
   419  external_cache:
   420    scheme: https
   421    host: www.externalprebidcache.net
   422    path: /endpoints/cache
   423  http_client:
   424    max_connections_per_host: 10
   425    max_idle_connections: 500
   426    max_idle_connections_per_host: 20
   427    idle_connection_timeout_seconds: 30
   428  http_client_cache:
   429    max_connections_per_host: 5
   430    max_idle_connections: 1
   431    max_idle_connections_per_host: 2
   432    idle_connection_timeout_seconds: 3
   433  currency_converter:
   434    fetch_url: https://currency.prebid.org
   435    fetch_interval_seconds: 1800
   436  recaptcha_secret: asdfasdfasdfasdf
   437  metrics:
   438    influxdb:
   439      host: upstream:8232
   440      database: metricsdb
   441      measurement: anyMeasurement
   442      username: admin
   443      password: admin1324
   444      align_timestamps: true
   445      metric_send_interval: 30
   446    disabled_metrics:
   447      account_adapter_details: true
   448      account_debug: false
   449      account_stored_responses: false
   450      adapter_connections_metrics: true
   451      adapter_buyeruid_scrubbed: false
   452      adapter_gdpr_request_blocked: true
   453      account_modules_metrics: true
   454  blacklisted_apps: ["spamAppID","sketchy-app-id"]
   455  account_required: true
   456  auto_gen_source_tid: false
   457  certificates_file: /etc/ssl/cert.pem
   458  request_validation:
   459      ipv4_private_networks: ["1.1.1.0/24"]
   460      ipv6_private_networks: ["1111::/16", "2222::/16"]
   461  generate_bid_id: true
   462  host_schain_node:
   463      asi: "pbshostcompany.com"
   464      sid: "00001"
   465      rid: "BidRequest"
   466      hp: 1
   467  validations:
   468      banner_creative_max_size: "skip"
   469      secure_markup: "skip"
   470      max_creative_width: 0
   471      max_creative_height: 0
   472  experiment:
   473      adscert:
   474          mode: inprocess
   475          inprocess:
   476              origin: "http://test.com"
   477              key: "ABC123"
   478              domain_check_interval_seconds: 40
   479              domain_renewal_interval_seconds : 60
   480          remote:
   481              url: ""
   482              signing_timeout_ms: 10
   483  hooks:
   484      enabled: true
   485  price_floors:
   486      enabled: true
   487      fetcher:
   488        worker: 20
   489        capacity: 20000
   490        cache_size_mb: 8
   491        http_client:
   492          max_connections_per_host: 5
   493          max_idle_connections: 1
   494          max_idle_connections_per_host: 2
   495          idle_connection_timeout_seconds: 10
   496        max_retries: 5
   497  account_defaults:
   498      events:
   499          enabled: true
   500      price_floors:
   501          enabled: true
   502          enforce_floors_rate: 50
   503          adjust_for_bid_adjustment: false
   504          enforce_deal_floors: true
   505          use_dynamic_data: true
   506          max_rules: 120
   507          max_schema_dims: 5
   508          fetch:
   509            enabled: true
   510            url: http://test.com/floors
   511            timeout_ms: 500
   512            max_file_size_kb: 200
   513            max_rules: 500
   514            period_sec: 2000
   515            max_age_sec: 6000
   516            max_schema_dims: 10
   517      bidadjustments:
   518          mediatype:
   519              '*':
   520                  '*':
   521                      '*':
   522                          - adjtype: multiplier
   523                            value: 1.01
   524                            currency: USD
   525              video-instream:
   526                  bidder:
   527                      deal_id:
   528                          - adjtype: cpm
   529                            value: 1.02
   530                            currency: EUR
   531      privacy:
   532          ipv6:
   533              anon_keep_bits: 50
   534          ipv4:
   535              anon_keep_bits: 20
   536          dsa:
   537              default: "{\"dsarequired\":3,\"pubrender\":1,\"datatopub\":2,\"transparency\":[{\"domain\":\"domain.com\",\"dsaparams\":[1]}]}"
   538              gdpr_only: true
   539          privacysandbox:
   540              topicsdomain: "test.com"
   541              cookiedeprecation:
   542                  enabled: true
   543                  ttl_sec: 86400
   544  tmax_adjustments:
   545    enabled: true
   546    bidder_response_duration_min_ms: 700
   547    bidder_network_latency_buffer_ms: 100
   548    pbs_response_preparation_duration_ms: 100
   549  analytics:
   550    agma:
   551      enabled: true
   552      endpoint:
   553        url: "http://test.com"
   554        timeout: "5s"
   555        gzip: false
   556      buffers:
   557        size: 10MB
   558        count: 111
   559        timeout: 5m
   560      accounts:
   561      - code: agma-code
   562        publisher_id: publisher-id
   563        site_app_id: site-or-app-id
   564  `)
   565  
   566  func cmpStrings(t *testing.T, key, expected, actual string) {
   567  	t.Helper()
   568  	assert.Equal(t, expected, actual, "%s: %s != %s", key, expected, actual)
   569  }
   570  
   571  func cmpInts(t *testing.T, key string, expected, actual int) {
   572  	t.Helper()
   573  	assert.Equal(t, expected, actual, "%s: %d != %d", key, expected, actual)
   574  }
   575  
   576  func cmpUnsignedInts(t *testing.T, key string, expected, actual uint) {
   577  	t.Helper()
   578  	assert.Equal(t, expected, actual, "%s: %d != %d", key, expected, actual)
   579  }
   580  
   581  func cmpInt8s(t *testing.T, key string, expected, actual *int8) {
   582  	t.Helper()
   583  	assert.Equal(t, expected, actual, "%s: %d != %d", key, expected, actual)
   584  }
   585  
   586  func cmpBools(t *testing.T, key string, expected, actual bool) {
   587  	t.Helper()
   588  	assert.Equal(t, expected, actual, "%s: %t != %t", key, expected, actual)
   589  }
   590  
   591  func cmpNils(t *testing.T, key string, a interface{}) {
   592  	t.Helper()
   593  	assert.Nilf(t, a, "%s: %t != nil", key, a)
   594  }
   595  
   596  func TestFullConfig(t *testing.T) {
   597  	int8One := int8(1)
   598  
   599  	v := viper.New()
   600  	SetupViper(v, "", bidderInfos)
   601  	v.SetConfigType("yaml")
   602  	v.ReadConfig(bytes.NewBuffer(fullConfig))
   603  	cfg, err := New(v, bidderInfos, mockNormalizeBidderName)
   604  	assert.NoError(t, err, "Setting up config should work but it doesn't")
   605  	cmpStrings(t, "cookie domain", "cookies.prebid.org", cfg.HostCookie.Domain)
   606  	cmpStrings(t, "cookie name", "userid", cfg.HostCookie.CookieName)
   607  	cmpStrings(t, "cookie family", "prebid", cfg.HostCookie.Family)
   608  	cmpStrings(t, "opt out", "http://prebid.org/optout", cfg.HostCookie.OptOutURL)
   609  	cmpStrings(t, "opt in", "http://prebid.org/optin", cfg.HostCookie.OptInURL)
   610  	cmpStrings(t, "external url", "http://prebid-server.prebid.org/", cfg.ExternalURL)
   611  	cmpStrings(t, "host", "prebid-server.prebid.org", cfg.Host)
   612  	cmpInts(t, "port", 1234, cfg.Port)
   613  	cmpInts(t, "admin_port", 5678, cfg.AdminPort)
   614  	cmpInts(t, "garbage_collector_threshold", 1, cfg.GarbageCollectorThreshold)
   615  	cmpInts(t, "auction_timeouts_ms.default", 50, int(cfg.AuctionTimeouts.Default))
   616  	cmpInts(t, "auction_timeouts_ms.max", 123, int(cfg.AuctionTimeouts.Max))
   617  	cmpInts(t, "stored_request_timeout_ms", 75, cfg.StoredRequestsTimeout)
   618  	cmpStrings(t, "cache.scheme", "http", cfg.CacheURL.Scheme)
   619  	cmpStrings(t, "cache.host", "prebidcache.net", cfg.CacheURL.Host)
   620  	cmpStrings(t, "cache.query", "uuid=%PBS_CACHE_UUID%", cfg.CacheURL.Query)
   621  	cmpStrings(t, "external_cache.scheme", "https", cfg.ExtCacheURL.Scheme)
   622  	cmpStrings(t, "external_cache.host", "www.externalprebidcache.net", cfg.ExtCacheURL.Host)
   623  	cmpStrings(t, "external_cache.path", "/endpoints/cache", cfg.ExtCacheURL.Path)
   624  	cmpInts(t, "http_client.max_connections_per_host", 10, cfg.Client.MaxConnsPerHost)
   625  	cmpInts(t, "http_client.max_idle_connections", 500, cfg.Client.MaxIdleConns)
   626  	cmpInts(t, "http_client.max_idle_connections_per_host", 20, cfg.Client.MaxIdleConnsPerHost)
   627  	cmpInts(t, "http_client.idle_connection_timeout_seconds", 30, cfg.Client.IdleConnTimeout)
   628  	cmpInts(t, "http_client_cache.max_connections_per_host", 5, cfg.CacheClient.MaxConnsPerHost)
   629  	cmpInts(t, "http_client_cache.max_idle_connections", 1, cfg.CacheClient.MaxIdleConns)
   630  	cmpInts(t, "http_client_cache.max_idle_connections_per_host", 2, cfg.CacheClient.MaxIdleConnsPerHost)
   631  	cmpInts(t, "http_client_cache.idle_connection_timeout_seconds", 3, cfg.CacheClient.IdleConnTimeout)
   632  	cmpInts(t, "gdpr.host_vendor_id", 15, cfg.GDPR.HostVendorID)
   633  	cmpStrings(t, "gdpr.default_value", "1", cfg.GDPR.DefaultValue)
   634  	cmpStrings(t, "host_schain_node.asi", "pbshostcompany.com", cfg.HostSChainNode.ASI)
   635  	cmpStrings(t, "host_schain_node.sid", "00001", cfg.HostSChainNode.SID)
   636  	cmpStrings(t, "host_schain_node.rid", "BidRequest", cfg.HostSChainNode.RID)
   637  	cmpInt8s(t, "host_schain_node.hp", &int8One, cfg.HostSChainNode.HP)
   638  	cmpStrings(t, "datacenter", "1", cfg.DataCenter)
   639  	cmpStrings(t, "validations.banner_creative_max_size", "skip", cfg.Validations.BannerCreativeMaxSize)
   640  	cmpStrings(t, "validations.secure_markup", "skip", cfg.Validations.SecureMarkup)
   641  	cmpInts(t, "validations.max_creative_width", 0, int(cfg.Validations.MaxCreativeWidth))
   642  	cmpInts(t, "validations.max_creative_height", 0, int(cfg.Validations.MaxCreativeHeight))
   643  	cmpBools(t, "tmax_adjustments.enabled", true, cfg.TmaxAdjustments.Enabled)
   644  	cmpUnsignedInts(t, "tmax_adjustments.bidder_response_duration_min_ms", 700, cfg.TmaxAdjustments.BidderResponseDurationMin)
   645  	cmpUnsignedInts(t, "tmax_adjustments.bidder_network_latency_buffer_ms", 100, cfg.TmaxAdjustments.BidderNetworkLatencyBuffer)
   646  	cmpUnsignedInts(t, "tmax_adjustments.pbs_response_preparation_duration_ms", 100, cfg.TmaxAdjustments.PBSResponsePreparationDuration)
   647  
   648  	//Assert the price floor values
   649  	cmpBools(t, "price_floors.enabled", true, cfg.PriceFloors.Enabled)
   650  	cmpInts(t, "price_floors.fetcher.worker", 20, cfg.PriceFloors.Fetcher.Worker)
   651  	cmpInts(t, "price_floors.fetcher.capacity", 20000, cfg.PriceFloors.Fetcher.Capacity)
   652  	cmpInts(t, "price_floors.fetcher.cache_size_mb", 8, cfg.PriceFloors.Fetcher.CacheSize)
   653  	cmpInts(t, "price_floors.fetcher.http_client.max_connections_per_host", 5, cfg.PriceFloors.Fetcher.HttpClient.MaxConnsPerHost)
   654  	cmpInts(t, "price_floors.fetcher.http_client.max_idle_connections", 1, cfg.PriceFloors.Fetcher.HttpClient.MaxIdleConns)
   655  	cmpInts(t, "price_floors.fetcher.http_client.max_idle_connections_per_host", 2, cfg.PriceFloors.Fetcher.HttpClient.MaxIdleConnsPerHost)
   656  	cmpInts(t, "price_floors.fetcher.http_client.idle_connection_timeout_seconds", 10, cfg.PriceFloors.Fetcher.HttpClient.IdleConnTimeout)
   657  	cmpInts(t, "price_floors.fetcher.max_retries", 5, cfg.PriceFloors.Fetcher.MaxRetries)
   658  	cmpBools(t, "account_defaults.price_floors.enabled", true, cfg.AccountDefaults.PriceFloors.Enabled)
   659  	cmpInts(t, "account_defaults.price_floors.enforce_floors_rate", 50, cfg.AccountDefaults.PriceFloors.EnforceFloorsRate)
   660  	cmpBools(t, "account_defaults.price_floors.adjust_for_bid_adjustment", false, cfg.AccountDefaults.PriceFloors.AdjustForBidAdjustment)
   661  	cmpBools(t, "account_defaults.price_floors.enforce_deal_floors", true, cfg.AccountDefaults.PriceFloors.EnforceDealFloors)
   662  	cmpBools(t, "account_defaults.price_floors.use_dynamic_data", true, cfg.AccountDefaults.PriceFloors.UseDynamicData)
   663  	cmpInts(t, "account_defaults.price_floors.max_rules", 120, cfg.AccountDefaults.PriceFloors.MaxRule)
   664  	cmpInts(t, "account_defaults.price_floors.max_schema_dims", 5, cfg.AccountDefaults.PriceFloors.MaxSchemaDims)
   665  	cmpBools(t, "account_defaults.price_floors.fetch.enabled", true, cfg.AccountDefaults.PriceFloors.Fetcher.Enabled)
   666  	cmpStrings(t, "account_defaults.price_floors.fetch.url", "http://test.com/floors", cfg.AccountDefaults.PriceFloors.Fetcher.URL)
   667  	cmpInts(t, "account_defaults.price_floors.fetch.timeout_ms", 500, cfg.AccountDefaults.PriceFloors.Fetcher.Timeout)
   668  	cmpInts(t, "account_defaults.price_floors.fetch.max_file_size_kb", 200, cfg.AccountDefaults.PriceFloors.Fetcher.MaxFileSizeKB)
   669  	cmpInts(t, "account_defaults.price_floors.fetch.max_rules", 500, cfg.AccountDefaults.PriceFloors.Fetcher.MaxRules)
   670  	cmpInts(t, "account_defaults.price_floors.fetch.period_sec", 2000, cfg.AccountDefaults.PriceFloors.Fetcher.Period)
   671  	cmpInts(t, "account_defaults.price_floors.fetch.max_age_sec", 6000, cfg.AccountDefaults.PriceFloors.Fetcher.MaxAge)
   672  	cmpInts(t, "account_defaults.price_floors.fetch.max_schema_dims", 10, cfg.AccountDefaults.PriceFloors.Fetcher.MaxSchemaDims)
   673  
   674  	// Assert the DSA was correctly unmarshalled and DefaultUnpacked was built correctly
   675  	expectedDSA := AccountDSA{
   676  		Default: "{\"dsarequired\":3,\"pubrender\":1,\"datatopub\":2,\"transparency\":[{\"domain\":\"domain.com\",\"dsaparams\":[1]}]}",
   677  		DefaultUnpacked: &openrtb_ext.ExtRegsDSA{
   678  			Required:  ptrutil.ToPtr[int8](3),
   679  			PubRender: ptrutil.ToPtr[int8](1),
   680  			DataToPub: ptrutil.ToPtr[int8](2),
   681  			Transparency: []openrtb_ext.ExtBidDSATransparency{
   682  				{
   683  					Domain: "domain.com",
   684  					Params: []int{1},
   685  				},
   686  			},
   687  		},
   688  		GDPROnly: true,
   689  	}
   690  	assert.Equal(t, &expectedDSA, cfg.AccountDefaults.Privacy.DSA)
   691  
   692  	cmpBools(t, "account_defaults.events.enabled", true, cfg.AccountDefaults.Events.Enabled)
   693  
   694  	cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 50, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits)
   695  	cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 20, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits)
   696  
   697  	cmpStrings(t, "account_defaults.privacy.topicsdomain", "test.com", cfg.AccountDefaults.Privacy.PrivacySandbox.TopicsDomain)
   698  	cmpBools(t, "account_defaults.privacy.cookiedeprecation.enabled", true, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.Enabled)
   699  	cmpInts(t, "account_defaults.privacy.cookiedeprecation.ttl_sec", 86400, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)
   700  
   701  	// Assert compression related defaults
   702  	cmpBools(t, "compression.request.enable_gzip", true, cfg.Compression.Request.GZIP)
   703  	cmpBools(t, "compression.response.enable_gzip", false, cfg.Compression.Response.GZIP)
   704  
   705  	//Assert the NonStandardPublishers was correctly unmarshalled
   706  	assert.Equal(t, []string{"pub1", "pub2"}, cfg.GDPR.NonStandardPublishers, "gdpr.non_standard_publishers")
   707  	assert.Equal(t, map[string]struct{}{"pub1": {}, "pub2": {}}, cfg.GDPR.NonStandardPublisherMap, "gdpr.non_standard_publishers Hash Map")
   708  
   709  	// Assert EEA Countries was correctly unmarshalled and the EEACountriesMap built correctly.
   710  	assert.Equal(t, []string{"eea1", "eea2"}, cfg.GDPR.EEACountries, "gdpr.eea_countries")
   711  	assert.Equal(t, map[string]struct{}{"eea1": {}, "eea2": {}}, cfg.GDPR.EEACountriesMap, "gdpr.eea_countries Hash Map")
   712  
   713  	cmpBools(t, "ccpa.enforce", true, cfg.CCPA.Enforce)
   714  	cmpBools(t, "lmt.enforce", true, cfg.LMT.Enforce)
   715  
   716  	//Assert the NonStandardPublishers was correctly unmarshalled
   717  	cmpStrings(t, "blacklisted_apps", "spamAppID", cfg.BlacklistedApps[0])
   718  	cmpStrings(t, "blacklisted_apps", "sketchy-app-id", cfg.BlacklistedApps[1])
   719  
   720  	//Assert the BlacklistedAppMap hash table was built correctly
   721  	for i := 0; i < len(cfg.BlacklistedApps); i++ {
   722  		cmpBools(t, "cfg.BlacklistedAppMap", true, cfg.BlacklistedAppMap[cfg.BlacklistedApps[i]])
   723  	}
   724  
   725  	//Assert purpose VendorExceptionMap hash tables were built correctly
   726  	expectedTCF2 := TCF2{
   727  		Enabled: true,
   728  		Purpose1: TCF2Purpose{
   729  			EnforceAlgo:        TCF2EnforceAlgoFull,
   730  			EnforceAlgoID:      TCF2FullEnforcement,
   731  			EnforcePurpose:     true,
   732  			EnforceVendors:     false,
   733  			VendorExceptions:   []string{"foo1a", "foo1b"},
   734  			VendorExceptionMap: map[string]struct{}{"foo1a": {}, "foo1b": {}},
   735  		},
   736  		Purpose2: TCF2Purpose{
   737  			EnforceAlgo:        TCF2EnforceAlgoFull,
   738  			EnforceAlgoID:      TCF2FullEnforcement,
   739  			EnforcePurpose:     false,
   740  			EnforceVendors:     false,
   741  			VendorExceptions:   []string{"foo2"},
   742  			VendorExceptionMap: map[string]struct{}{"foo2": {}},
   743  		},
   744  		Purpose3: TCF2Purpose{
   745  			EnforceAlgo:        TCF2EnforceAlgoBasic,
   746  			EnforceAlgoID:      TCF2BasicEnforcement,
   747  			EnforcePurpose:     true,
   748  			EnforceVendors:     false,
   749  			VendorExceptions:   []string{"foo3"},
   750  			VendorExceptionMap: map[string]struct{}{"foo3": {}},
   751  		},
   752  		Purpose4: TCF2Purpose{
   753  			EnforceAlgo:        TCF2EnforceAlgoFull,
   754  			EnforceAlgoID:      TCF2FullEnforcement,
   755  			EnforcePurpose:     true,
   756  			EnforceVendors:     false,
   757  			VendorExceptions:   []string{"foo4"},
   758  			VendorExceptionMap: map[string]struct{}{"foo4": {}},
   759  		},
   760  		Purpose5: TCF2Purpose{
   761  			EnforceAlgo:        TCF2EnforceAlgoFull,
   762  			EnforceAlgoID:      TCF2FullEnforcement,
   763  			EnforcePurpose:     true,
   764  			EnforceVendors:     false,
   765  			VendorExceptions:   []string{"foo5"},
   766  			VendorExceptionMap: map[string]struct{}{"foo5": {}},
   767  		},
   768  		Purpose6: TCF2Purpose{
   769  			EnforceAlgo:        TCF2EnforceAlgoFull,
   770  			EnforceAlgoID:      TCF2FullEnforcement,
   771  			EnforcePurpose:     true,
   772  			EnforceVendors:     false,
   773  			VendorExceptions:   []string{"foo6"},
   774  			VendorExceptionMap: map[string]struct{}{"foo6": {}},
   775  		},
   776  		Purpose7: TCF2Purpose{
   777  			EnforceAlgo:        TCF2EnforceAlgoFull,
   778  			EnforceAlgoID:      TCF2FullEnforcement,
   779  			EnforcePurpose:     true,
   780  			EnforceVendors:     false,
   781  			VendorExceptions:   []string{"foo7"},
   782  			VendorExceptionMap: map[string]struct{}{"foo7": {}},
   783  		},
   784  		Purpose8: TCF2Purpose{
   785  			EnforceAlgo:        TCF2EnforceAlgoFull,
   786  			EnforceAlgoID:      TCF2FullEnforcement,
   787  			EnforcePurpose:     true,
   788  			EnforceVendors:     false,
   789  			VendorExceptions:   []string{"foo8"},
   790  			VendorExceptionMap: map[string]struct{}{"foo8": {}},
   791  		},
   792  		Purpose9: TCF2Purpose{
   793  			EnforceAlgo:        TCF2EnforceAlgoFull,
   794  			EnforceAlgoID:      TCF2FullEnforcement,
   795  			EnforcePurpose:     true,
   796  			EnforceVendors:     false,
   797  			VendorExceptions:   []string{"foo9"},
   798  			VendorExceptionMap: map[string]struct{}{"foo9": {}},
   799  		},
   800  		Purpose10: TCF2Purpose{
   801  			EnforceAlgo:        TCF2EnforceAlgoFull,
   802  			EnforceAlgoID:      TCF2FullEnforcement,
   803  			EnforcePurpose:     true,
   804  			EnforceVendors:     false,
   805  			VendorExceptions:   []string{"foo10"},
   806  			VendorExceptionMap: map[string]struct{}{"foo10": {}},
   807  		},
   808  		SpecialFeature1: TCF2SpecialFeature{
   809  			Enforce:            true, // true by default
   810  			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("fooSP1")},
   811  			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("fooSP1"): {}},
   812  		},
   813  		PurposeOneTreatment: TCF2PurposeOneTreatment{
   814  			Enabled:       true, // true by default
   815  			AccessAllowed: true, // true by default
   816  		},
   817  	}
   818  	expectedTCF2.PurposeConfigs = map[consentconstants.Purpose]*TCF2Purpose{
   819  		1:  &expectedTCF2.Purpose1,
   820  		2:  &expectedTCF2.Purpose2,
   821  		3:  &expectedTCF2.Purpose3,
   822  		4:  &expectedTCF2.Purpose4,
   823  		5:  &expectedTCF2.Purpose5,
   824  		6:  &expectedTCF2.Purpose6,
   825  		7:  &expectedTCF2.Purpose7,
   826  		8:  &expectedTCF2.Purpose8,
   827  		9:  &expectedTCF2.Purpose9,
   828  		10: &expectedTCF2.Purpose10,
   829  	}
   830  
   831  	expectedBidAdjustments := &openrtb_ext.ExtRequestPrebidBidAdjustments{
   832  		MediaType: openrtb_ext.MediaType{
   833  			WildCard: map[openrtb_ext.BidderName]openrtb_ext.AdjustmentsByDealID{
   834  				"*": {
   835  					"*": []openrtb_ext.Adjustment{{Type: "multiplier", Value: 1.01, Currency: "USD"}},
   836  				},
   837  			},
   838  			VideoInstream: map[openrtb_ext.BidderName]openrtb_ext.AdjustmentsByDealID{
   839  				"bidder": {
   840  					"deal_id": []openrtb_ext.Adjustment{{Type: "cpm", Value: 1.02, Currency: "EUR"}},
   841  				},
   842  			},
   843  		},
   844  	}
   845  	assert.Equal(t, expectedTCF2, cfg.GDPR.TCF2, "gdpr.tcf2")
   846  	assert.Equal(t, expectedBidAdjustments, cfg.AccountDefaults.BidAdjustments)
   847  
   848  	cmpStrings(t, "currency_converter.fetch_url", "https://currency.prebid.org", cfg.CurrencyConverter.FetchURL)
   849  	cmpInts(t, "currency_converter.fetch_interval_seconds", 1800, cfg.CurrencyConverter.FetchIntervalSeconds)
   850  	cmpStrings(t, "recaptcha_secret", "asdfasdfasdfasdf", cfg.RecaptchaSecret)
   851  	cmpStrings(t, "metrics.influxdb.host", "upstream:8232", cfg.Metrics.Influxdb.Host)
   852  	cmpStrings(t, "metrics.influxdb.database", "metricsdb", cfg.Metrics.Influxdb.Database)
   853  	cmpStrings(t, "metrics.influxdb.measurement", "anyMeasurement", cfg.Metrics.Influxdb.Measurement)
   854  	cmpStrings(t, "metrics.influxdb.username", "admin", cfg.Metrics.Influxdb.Username)
   855  	cmpStrings(t, "metrics.influxdb.password", "admin1324", cfg.Metrics.Influxdb.Password)
   856  	cmpBools(t, "metrics.influxdb.align_timestamps", true, cfg.Metrics.Influxdb.AlignTimestamps)
   857  	cmpInts(t, "metrics.influxdb.metric_send_interval", 30, cfg.Metrics.Influxdb.MetricSendInterval)
   858  	cmpStrings(t, "", "http://prebidcache.net", cfg.CacheURL.GetBaseURL())
   859  	cmpStrings(t, "", "http://prebidcache.net/cache?uuid=a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11", cfg.GetCachedAssetURL("a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11"))
   860  	cmpBools(t, "account_required", true, cfg.AccountRequired)
   861  	cmpBools(t, "auto_gen_source_tid", false, cfg.AutoGenSourceTID)
   862  	cmpBools(t, "account_adapter_details", true, cfg.Metrics.Disabled.AccountAdapterDetails)
   863  	cmpBools(t, "account_debug", false, cfg.Metrics.Disabled.AccountDebug)
   864  	cmpBools(t, "account_stored_responses", false, cfg.Metrics.Disabled.AccountStoredResponses)
   865  	cmpBools(t, "adapter_connections_metrics", true, cfg.Metrics.Disabled.AdapterConnectionMetrics)
   866  	cmpBools(t, "adapter_buyeruid_scrubbed", false, cfg.Metrics.Disabled.AdapterBuyerUIDScrubbed)
   867  	cmpBools(t, "adapter_gdpr_request_blocked", true, cfg.Metrics.Disabled.AdapterGDPRRequestBlocked)
   868  	cmpStrings(t, "certificates_file", "/etc/ssl/cert.pem", cfg.PemCertsFile)
   869  	cmpStrings(t, "request_validation.ipv4_private_networks", "1.1.1.0/24", cfg.RequestValidation.IPv4PrivateNetworks[0])
   870  	cmpStrings(t, "request_validation.ipv6_private_networks", "1111::/16", cfg.RequestValidation.IPv6PrivateNetworks[0])
   871  	cmpStrings(t, "request_validation.ipv6_private_networks", "2222::/16", cfg.RequestValidation.IPv6PrivateNetworks[1])
   872  	cmpBools(t, "generate_bid_id", true, cfg.GenerateBidID)
   873  	cmpStrings(t, "debug.override_token", "", cfg.Debug.OverrideToken)
   874  	cmpStrings(t, "experiment.adscert.mode", "inprocess", cfg.Experiment.AdCerts.Mode)
   875  	cmpStrings(t, "experiment.adscert.inprocess.origin", "http://test.com", cfg.Experiment.AdCerts.InProcess.Origin)
   876  	cmpStrings(t, "experiment.adscert.inprocess.key", "ABC123", cfg.Experiment.AdCerts.InProcess.PrivateKey)
   877  	cmpInts(t, "experiment.adscert.inprocess.domain_check_interval_seconds", 40, cfg.Experiment.AdCerts.InProcess.DNSCheckIntervalInSeconds)
   878  	cmpInts(t, "experiment.adscert.inprocess.domain_renewal_interval_seconds", 60, cfg.Experiment.AdCerts.InProcess.DNSRenewalIntervalInSeconds)
   879  	cmpStrings(t, "experiment.adscert.remote.url", "", cfg.Experiment.AdCerts.Remote.Url)
   880  	cmpInts(t, "experiment.adscert.remote.signing_timeout_ms", 10, cfg.Experiment.AdCerts.Remote.SigningTimeoutMs)
   881  	cmpBools(t, "hooks.enabled", true, cfg.Hooks.Enabled)
   882  	cmpBools(t, "account_modules_metrics", true, cfg.Metrics.Disabled.AccountModulesMetrics)
   883  	cmpBools(t, "analytics.agma.enabled", true, cfg.Analytics.Agma.Enabled)
   884  	cmpStrings(t, "analytics.agma.endpoint.timeout", "5s", cfg.Analytics.Agma.Endpoint.Timeout)
   885  	cmpBools(t, "analytics.agma.endpoint.gzip", false, cfg.Analytics.Agma.Endpoint.Gzip)
   886  	cmpStrings(t, "analytics.agma.endpoint.url", "http://test.com", cfg.Analytics.Agma.Endpoint.Url)
   887  	cmpStrings(t, "analytics.agma.buffers.size", "10MB", cfg.Analytics.Agma.Buffers.BufferSize)
   888  	cmpInts(t, "analytics.agma.buffers.count", 111, cfg.Analytics.Agma.Buffers.EventCount)
   889  	cmpStrings(t, "analytics.agma.buffers.timeout", "5m", cfg.Analytics.Agma.Buffers.Timeout)
   890  	cmpStrings(t, "analytics.agma.accounts.0.publisher_id", "publisher-id", cfg.Analytics.Agma.Accounts[0].PublisherId)
   891  	cmpStrings(t, "analytics.agma.accounts.0.code", "agma-code", cfg.Analytics.Agma.Accounts[0].Code)
   892  	cmpStrings(t, "analytics.agma.accounts.0.site_app_id", "site-or-app-id", cfg.Analytics.Agma.Accounts[0].SiteAppId)
   893  }
   894  
   895  func TestValidateConfig(t *testing.T) {
   896  	cfg := Configuration{
   897  		GDPR: GDPR{
   898  			DefaultValue: "1",
   899  			TCF2: TCF2{
   900  				Purpose1:  TCF2Purpose{EnforceAlgo: TCF2EnforceAlgoBasic},
   901  				Purpose2:  TCF2Purpose{EnforceAlgo: TCF2EnforceAlgoFull},
   902  				Purpose3:  TCF2Purpose{EnforceAlgo: TCF2EnforceAlgoBasic},
   903  				Purpose4:  TCF2Purpose{EnforceAlgo: TCF2EnforceAlgoFull},
   904  				Purpose5:  TCF2Purpose{EnforceAlgo: TCF2EnforceAlgoBasic},
   905  				Purpose6:  TCF2Purpose{EnforceAlgo: TCF2EnforceAlgoFull},
   906  				Purpose7:  TCF2Purpose{EnforceAlgo: TCF2EnforceAlgoBasic},
   907  				Purpose8:  TCF2Purpose{EnforceAlgo: TCF2EnforceAlgoFull},
   908  				Purpose9:  TCF2Purpose{EnforceAlgo: TCF2EnforceAlgoBasic},
   909  				Purpose10: TCF2Purpose{EnforceAlgo: TCF2EnforceAlgoFull},
   910  			},
   911  		},
   912  		StoredRequests: StoredRequests{
   913  			Files: FileFetcherConfig{Enabled: true},
   914  			InMemoryCache: InMemoryCache{
   915  				Type: "none",
   916  			},
   917  		},
   918  		StoredRequestsTimeout: 50,
   919  		StoredVideo: StoredRequests{
   920  			Files: FileFetcherConfig{Enabled: true},
   921  			InMemoryCache: InMemoryCache{
   922  				Type: "none",
   923  			},
   924  		},
   925  		CategoryMapping: StoredRequests{
   926  			Files: FileFetcherConfig{Enabled: true},
   927  		},
   928  		Accounts: StoredRequests{
   929  			Files:         FileFetcherConfig{Enabled: true},
   930  			InMemoryCache: InMemoryCache{Type: "none"},
   931  		},
   932  		AccountDefaults: Account{
   933  			PriceFloors: AccountPriceFloors{
   934  				Fetcher: AccountFloorFetch{
   935  					Timeout: 100,
   936  					Period:  300,
   937  					MaxAge:  600,
   938  				},
   939  			},
   940  		},
   941  	}
   942  
   943  	v := viper.New()
   944  	v.Set("gdpr.default_value", "0")
   945  
   946  	resolvedStoredRequestsConfig(&cfg)
   947  	err := cfg.validate(v)
   948  	assert.Nil(t, err, "OpenRTB filesystem config should work. %v", err)
   949  }
   950  
   951  func TestMigrateConfigFromEnv(t *testing.T) {
   952  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_ENDPOINT"); ok {
   953  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_ENDPOINT", oldval)
   954  	} else {
   955  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_ENDPOINT")
   956  	}
   957  
   958  	os.Setenv("PBS_ADAPTERS_BIDDER1_ENDPOINT", "http://bidder1_override.com")
   959  	cfg, _ := newDefaultConfig(t)
   960  	cmpStrings(t, "adapters.bidder1.endpoint", "http://bidder1_override.com", cfg.BidderInfos["bidder1"].Endpoint)
   961  }
   962  
   963  func TestUserSyncFromEnv(t *testing.T) {
   964  	truePtr := true
   965  
   966  	// setup env vars for testing
   967  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_URL"); ok {
   968  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_URL", oldval)
   969  	} else {
   970  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_URL")
   971  	}
   972  
   973  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_USER_MACRO"); ok {
   974  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_USER_MACRO", oldval)
   975  	} else {
   976  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_USER_MACRO")
   977  	}
   978  
   979  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_USERSYNC_SUPPORT_CORS"); ok {
   980  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_USERSYNC_SUPPORT_CORS", oldval)
   981  	} else {
   982  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_USERSYNC_SUPPORT_CORS")
   983  	}
   984  
   985  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER2_USERSYNC_IFRAME_URL"); ok {
   986  		defer os.Setenv("PBS_ADAPTERS_BIDDER2_USERSYNC_IFRAME_URL", oldval)
   987  	} else {
   988  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER2_USERSYNC_IFRAME_URL")
   989  	}
   990  
   991  	// set new
   992  	os.Setenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_URL", "http://some.url/sync?redirect={{.RedirectURL}}")
   993  	os.Setenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_USER_MACRO", "[UID]")
   994  	os.Setenv("PBS_ADAPTERS_BIDDER1_USERSYNC_SUPPORT_CORS", "true")
   995  	os.Setenv("PBS_ADAPTERS_BIDDER2_USERSYNC_IFRAME_URL", "http://somedifferent.url/sync?redirect={{.RedirectURL}}")
   996  
   997  	cfg, _ := newDefaultConfig(t)
   998  
   999  	assert.Equal(t, "http://some.url/sync?redirect={{.RedirectURL}}", cfg.BidderInfos["bidder1"].Syncer.Redirect.URL)
  1000  	assert.Equal(t, "[UID]", cfg.BidderInfos["bidder1"].Syncer.Redirect.UserMacro)
  1001  	assert.Nil(t, cfg.BidderInfos["bidder1"].Syncer.IFrame)
  1002  	assert.Equal(t, &truePtr, cfg.BidderInfos["bidder1"].Syncer.SupportCORS)
  1003  
  1004  	assert.Equal(t, "http://somedifferent.url/sync?redirect={{.RedirectURL}}", cfg.BidderInfos["bidder2"].Syncer.IFrame.URL)
  1005  	assert.Nil(t, cfg.BidderInfos["bidder2"].Syncer.Redirect)
  1006  	assert.Nil(t, cfg.BidderInfos["bidder2"].Syncer.SupportCORS)
  1007  }
  1008  
  1009  func TestBidderInfoFromEnv(t *testing.T) {
  1010  	// setup env vars for testing
  1011  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_DISABLED"); ok {
  1012  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_DISABLED", oldval)
  1013  	} else {
  1014  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_DISABLED")
  1015  	}
  1016  
  1017  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_ENDPOINT"); ok {
  1018  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_ENDPOINT", oldval)
  1019  	} else {
  1020  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_ENDPOINT")
  1021  	}
  1022  
  1023  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_EXTRA_INFO"); ok {
  1024  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_EXTRA_INFO", oldval)
  1025  	} else {
  1026  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_EXTRA_INFO")
  1027  	}
  1028  
  1029  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_DEBUG_ALLOW"); ok {
  1030  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_DEBUG_ALLOW", oldval)
  1031  	} else {
  1032  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_DEBUG_ALLOW")
  1033  	}
  1034  
  1035  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_GVLVENDORID"); ok {
  1036  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_GVLVENDORID", oldval)
  1037  	} else {
  1038  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_GVLVENDORID")
  1039  	}
  1040  
  1041  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_EXPERIMENT_ADSCERT_ENABLED"); ok {
  1042  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_EXPERIMENT_ADSCERT_ENABLED", oldval)
  1043  	} else {
  1044  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_EXPERIMENT_ADSCERT_ENABLED")
  1045  	}
  1046  
  1047  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_XAPI_USERNAME"); ok {
  1048  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_XAPI_USERNAME", oldval)
  1049  	} else {
  1050  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_XAPI_USERNAME")
  1051  	}
  1052  
  1053  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_URL"); ok {
  1054  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_URL", oldval)
  1055  	} else {
  1056  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_URL")
  1057  	}
  1058  	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_OPENRTB_VERSION"); ok {
  1059  		defer os.Setenv("PBS_ADAPTERS_BIDDER1_OPENRTB_VERSION", oldval)
  1060  	} else {
  1061  		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_OPENRTB_VERSION")
  1062  	}
  1063  
  1064  	// set new
  1065  	os.Setenv("PBS_ADAPTERS_BIDDER1_DISABLED", "true")
  1066  	os.Setenv("PBS_ADAPTERS_BIDDER1_ENDPOINT", "http://some.url/override")
  1067  	os.Setenv("PBS_ADAPTERS_BIDDER1_EXTRA_INFO", `{"extrainfo": true}`)
  1068  	os.Setenv("PBS_ADAPTERS_BIDDER1_DEBUG_ALLOW", "true")
  1069  	os.Setenv("PBS_ADAPTERS_BIDDER1_GVLVENDORID", "42")
  1070  	os.Setenv("PBS_ADAPTERS_BIDDER1_EXPERIMENT_ADSCERT_ENABLED", "true")
  1071  	os.Setenv("PBS_ADAPTERS_BIDDER1_XAPI_USERNAME", "username_override")
  1072  	os.Setenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_URL", "http://some.url/sync?redirect={{.RedirectURL}}")
  1073  	os.Setenv("PBS_ADAPTERS_BIDDER1_OPENRTB_VERSION", "2.6")
  1074  
  1075  	cfg, _ := newDefaultConfig(t)
  1076  
  1077  	assert.Equal(t, true, cfg.BidderInfos["bidder1"].Disabled)
  1078  	assert.Equal(t, "http://some.url/override", cfg.BidderInfos["bidder1"].Endpoint)
  1079  	assert.Equal(t, `{"extrainfo": true}`, cfg.BidderInfos["bidder1"].ExtraAdapterInfo)
  1080  
  1081  	assert.Equal(t, true, cfg.BidderInfos["bidder1"].Debug.Allow)
  1082  	assert.Equal(t, uint16(42), cfg.BidderInfos["bidder1"].GVLVendorID)
  1083  
  1084  	assert.Equal(t, true, cfg.BidderInfos["bidder1"].Experiment.AdsCert.Enabled)
  1085  	assert.Equal(t, "username_override", cfg.BidderInfos["bidder1"].XAPI.Username)
  1086  
  1087  	assert.Equal(t, "2.6", cfg.BidderInfos["bidder1"].OpenRTB.Version)
  1088  }
  1089  
  1090  func TestIsConfigInfoPresent(t *testing.T) {
  1091  	configPrefix1Field2Only := []byte(`
  1092        prefix1:
  1093          field2: "value2"
  1094      `)
  1095  	configPrefix1Field4Only := []byte(`
  1096        prefix1:
  1097          field4: "value4"
  1098        `)
  1099  	configPrefix1Field2AndField3 := []byte(`
  1100        prefix1:
  1101          field2: "value2"
  1102          field3: "value3"
  1103        `)
  1104  
  1105  	tests := []struct {
  1106  		description string
  1107  		config      []byte
  1108  		keyPrefix   string
  1109  		fields      []string
  1110  		wantResult  bool
  1111  	}{
  1112  		{
  1113  			description: "config is nil",
  1114  			config:      nil,
  1115  			keyPrefix:   "prefix1",
  1116  			fields:      []string{"field1", "field2", "field3"},
  1117  			wantResult:  false,
  1118  		},
  1119  		{
  1120  			description: "config is empty",
  1121  			config:      []byte{},
  1122  			keyPrefix:   "prefix1",
  1123  			fields:      []string{"field1", "field2", "field3"},
  1124  			wantResult:  false,
  1125  		},
  1126  		{
  1127  			description: "present - one field exists in config",
  1128  			config:      configPrefix1Field2Only,
  1129  			keyPrefix:   "prefix1",
  1130  			fields:      []string{"field1", "field2", "field3"},
  1131  			wantResult:  true,
  1132  		},
  1133  		{
  1134  			description: "present - many fields exist in config",
  1135  			config:      configPrefix1Field2AndField3,
  1136  			keyPrefix:   "prefix1",
  1137  			fields:      []string{"field1", "field2", "field3"},
  1138  			wantResult:  true,
  1139  		},
  1140  		{
  1141  			description: "not present - field not found",
  1142  			config:      configPrefix1Field4Only,
  1143  			keyPrefix:   "prefix1",
  1144  			fields:      []string{"field1", "field2", "field3"},
  1145  			wantResult:  false,
  1146  		},
  1147  		{
  1148  			description: "not present - field exists but with a different prefix",
  1149  			config:      configPrefix1Field2Only,
  1150  			keyPrefix:   "prefix2",
  1151  			fields:      []string{"field1", "field2", "field3"},
  1152  			wantResult:  false,
  1153  		},
  1154  		{
  1155  			description: "not present - fields is nil",
  1156  			config:      configPrefix1Field2Only,
  1157  			keyPrefix:   "prefix1",
  1158  			fields:      nil,
  1159  			wantResult:  false,
  1160  		},
  1161  		{
  1162  			description: "not present - fields is empty",
  1163  			config:      configPrefix1Field2Only,
  1164  			keyPrefix:   "prefix1",
  1165  			fields:      []string{},
  1166  			wantResult:  false,
  1167  		},
  1168  	}
  1169  
  1170  	for _, tt := range tests {
  1171  		v := viper.New()
  1172  		v.SetConfigType("yaml")
  1173  		v.ReadConfig(bytes.NewBuffer(tt.config))
  1174  
  1175  		result := isConfigInfoPresent(v, tt.keyPrefix, tt.fields)
  1176  		assert.Equal(t, tt.wantResult, result, tt.description)
  1177  	}
  1178  }
  1179  
  1180  func TestNegativeOrZeroStoredRequestsTimeout(t *testing.T) {
  1181  	cfg, v := newDefaultConfig(t)
  1182  
  1183  	cfg.StoredRequestsTimeout = -1
  1184  	assertOneError(t, cfg.validate(v), "cfg.stored_requests_timeout_ms must be > 0. Got -1")
  1185  
  1186  	cfg.StoredRequestsTimeout = 0
  1187  	assertOneError(t, cfg.validate(v), "cfg.stored_requests_timeout_ms must be > 0. Got 0")
  1188  }
  1189  
  1190  func TestNegativeRequestSize(t *testing.T) {
  1191  	cfg, v := newDefaultConfig(t)
  1192  	cfg.MaxRequestSize = -1
  1193  	assertOneError(t, cfg.validate(v), "cfg.max_request_size must be >= 0. Got -1")
  1194  }
  1195  
  1196  func TestNegativePrometheusTimeout(t *testing.T) {
  1197  	cfg, v := newDefaultConfig(t)
  1198  	cfg.Metrics.Prometheus.Port = 8001
  1199  	cfg.Metrics.Prometheus.TimeoutMillisRaw = 0
  1200  	assertOneError(t, cfg.validate(v), "metrics.prometheus.timeout_ms must be positive if metrics.prometheus.port is defined. Got timeout=0 and port=8001")
  1201  }
  1202  
  1203  func TestInvalidHostVendorID(t *testing.T) {
  1204  	tests := []struct {
  1205  		description  string
  1206  		vendorID     int
  1207  		wantErrorMsg string
  1208  	}{
  1209  		{
  1210  			description:  "Negative GDPR.HostVendorID",
  1211  			vendorID:     -1,
  1212  			wantErrorMsg: "gdpr.host_vendor_id must be in the range [0, 65535]. Got -1",
  1213  		},
  1214  		{
  1215  			description:  "Overflowed GDPR.HostVendorID",
  1216  			vendorID:     (0xffff) + 1,
  1217  			wantErrorMsg: "gdpr.host_vendor_id must be in the range [0, 65535]. Got 65536",
  1218  		},
  1219  	}
  1220  
  1221  	for _, tt := range tests {
  1222  		cfg, v := newDefaultConfig(t)
  1223  		cfg.GDPR.HostVendorID = tt.vendorID
  1224  		errs := cfg.validate(v)
  1225  
  1226  		assert.Equal(t, 1, len(errs), tt.description)
  1227  		assert.EqualError(t, errs[0], tt.wantErrorMsg, tt.description)
  1228  	}
  1229  }
  1230  
  1231  func TestInvalidAMPException(t *testing.T) {
  1232  	cfg, v := newDefaultConfig(t)
  1233  	cfg.GDPR.AMPException = true
  1234  	assertOneError(t, cfg.validate(v), "gdpr.amp_exception has been discontinued and must be removed from your config. If you need to disable GDPR for AMP, you may do so per-account (gdpr.integration_enabled.amp) or at the host level for the default account (account_defaults.gdpr.integration_enabled.amp)")
  1235  }
  1236  
  1237  func TestInvalidGDPRDefaultValue(t *testing.T) {
  1238  	cfg, v := newDefaultConfig(t)
  1239  	cfg.GDPR.DefaultValue = "2"
  1240  	assertOneError(t, cfg.validate(v), "gdpr.default_value must be 0 or 1")
  1241  }
  1242  
  1243  func TestMissingGDPRDefaultValue(t *testing.T) {
  1244  	v := viper.New()
  1245  
  1246  	cfg, _ := newDefaultConfig(t)
  1247  	assertOneError(t, cfg.validate(v), "gdpr.default_value is required and must be specified")
  1248  }
  1249  
  1250  func TestInvalidEnforceAlgo(t *testing.T) {
  1251  	cfg, v := newDefaultConfig(t)
  1252  	cfg.GDPR.TCF2.Purpose1.EnforceAlgo = ""
  1253  	cfg.GDPR.TCF2.Purpose2.EnforceAlgo = TCF2EnforceAlgoFull
  1254  	cfg.GDPR.TCF2.Purpose3.EnforceAlgo = TCF2EnforceAlgoBasic
  1255  	cfg.GDPR.TCF2.Purpose4.EnforceAlgo = TCF2EnforceAlgoFull
  1256  	cfg.GDPR.TCF2.Purpose5.EnforceAlgo = "invalid1"
  1257  	cfg.GDPR.TCF2.Purpose6.EnforceAlgo = "invalid2"
  1258  	cfg.GDPR.TCF2.Purpose7.EnforceAlgo = TCF2EnforceAlgoFull
  1259  	cfg.GDPR.TCF2.Purpose8.EnforceAlgo = TCF2EnforceAlgoBasic
  1260  	cfg.GDPR.TCF2.Purpose9.EnforceAlgo = TCF2EnforceAlgoFull
  1261  	cfg.GDPR.TCF2.Purpose10.EnforceAlgo = "invalid3"
  1262  
  1263  	errs := cfg.validate(v)
  1264  
  1265  	expectedErrs := []error{
  1266  		errors.New("gdpr.tcf2.purpose1.enforce_algo must be \"basic\" or \"full\". Got "),
  1267  		errors.New("gdpr.tcf2.purpose5.enforce_algo must be \"basic\" or \"full\". Got invalid1"),
  1268  		errors.New("gdpr.tcf2.purpose6.enforce_algo must be \"basic\" or \"full\". Got invalid2"),
  1269  		errors.New("gdpr.tcf2.purpose10.enforce_algo must be \"basic\" or \"full\". Got invalid3"),
  1270  	}
  1271  	assert.ElementsMatch(t, errs, expectedErrs, "gdpr.tcf2.purposeX.enforce_algo should prevent invalid values but it doesn't")
  1272  }
  1273  
  1274  func TestNegativeCurrencyConverterFetchInterval(t *testing.T) {
  1275  	v := viper.New()
  1276  	v.Set("gdpr.default_value", "0")
  1277  
  1278  	cfg := Configuration{
  1279  		CurrencyConverter: CurrencyConverter{
  1280  			FetchIntervalSeconds: -1,
  1281  		},
  1282  	}
  1283  	err := cfg.validate(v)
  1284  	assert.NotNil(t, err, "cfg.currency_converter.fetch_interval_seconds should prevent negative values, but it doesn't")
  1285  }
  1286  
  1287  func TestOverflowedCurrencyConverterFetchInterval(t *testing.T) {
  1288  	v := viper.New()
  1289  	v.Set("gdpr.default_value", "0")
  1290  
  1291  	cfg := Configuration{
  1292  		CurrencyConverter: CurrencyConverter{
  1293  			FetchIntervalSeconds: (0xffff) + 1,
  1294  		},
  1295  	}
  1296  	err := cfg.validate(v)
  1297  	assert.NotNil(t, err, "cfg.currency_converter.fetch_interval_seconds prevent values over %d, but it doesn't", 0xffff)
  1298  }
  1299  
  1300  func TestLimitTimeout(t *testing.T) {
  1301  	doTimeoutTest(t, 10, 15, 10, 0)
  1302  	doTimeoutTest(t, 10, 0, 10, 0)
  1303  	doTimeoutTest(t, 5, 5, 10, 0)
  1304  	doTimeoutTest(t, 15, 15, 0, 0)
  1305  	doTimeoutTest(t, 15, 0, 20, 15)
  1306  }
  1307  
  1308  func TestCookieSizeError(t *testing.T) {
  1309  	testCases := []struct {
  1310  		description string
  1311  		cookieSize  int
  1312  		expectError bool
  1313  	}{
  1314  		{"MIN_COOKIE_SIZE_BYTES + 1", MIN_COOKIE_SIZE_BYTES + 1, false},
  1315  		{"MIN_COOKIE_SIZE_BYTES", MIN_COOKIE_SIZE_BYTES, false},
  1316  		{"MIN_COOKIE_SIZE_BYTES - 1", MIN_COOKIE_SIZE_BYTES - 1, true},
  1317  		{"Zero", 0, false},
  1318  		{"Negative", -100, true},
  1319  	}
  1320  
  1321  	for _, test := range testCases {
  1322  		resultErr := isValidCookieSize(test.cookieSize)
  1323  
  1324  		if test.expectError {
  1325  			assert.Error(t, resultErr, test.description)
  1326  		} else {
  1327  			assert.NoError(t, resultErr, test.description)
  1328  		}
  1329  	}
  1330  }
  1331  
  1332  func TestNewCallsRequestValidation(t *testing.T) {
  1333  	testCases := []struct {
  1334  		description       string
  1335  		privateIPNetworks string
  1336  		expectedError     string
  1337  		expectedIPs       []net.IPNet
  1338  	}{
  1339  		{
  1340  			description:       "Valid",
  1341  			privateIPNetworks: `["1.1.1.0/24"]`,
  1342  			expectedIPs:       []net.IPNet{{IP: net.IP{1, 1, 1, 0}, Mask: net.CIDRMask(24, 32)}},
  1343  		},
  1344  		{
  1345  			description:       "Invalid",
  1346  			privateIPNetworks: `["1"]`,
  1347  			expectedError:     "Invalid private IPv4 networks: '1'",
  1348  		},
  1349  	}
  1350  
  1351  	for _, test := range testCases {
  1352  		v := viper.New()
  1353  		SetupViper(v, "", bidderInfos)
  1354  		v.Set("gdpr.default_value", "0")
  1355  		v.SetConfigType("yaml")
  1356  		v.ReadConfig(bytes.NewBuffer([]byte(
  1357  			`request_validation:
  1358      ipv4_private_networks: ` + test.privateIPNetworks)))
  1359  
  1360  		result, resultErr := New(v, bidderInfos, mockNormalizeBidderName)
  1361  
  1362  		if test.expectedError == "" {
  1363  			assert.NoError(t, resultErr, test.description+":err")
  1364  			assert.ElementsMatch(t, test.expectedIPs, result.RequestValidation.IPv4PrivateNetworksParsed, test.description+":parsed")
  1365  		} else {
  1366  			assert.Error(t, resultErr, test.description+":err")
  1367  		}
  1368  	}
  1369  }
  1370  
  1371  func TestValidateDebug(t *testing.T) {
  1372  	cfg, v := newDefaultConfig(t)
  1373  	cfg.Debug.TimeoutNotification.SamplingRate = 1.1
  1374  
  1375  	err := cfg.validate(v)
  1376  	assert.NotNil(t, err, "cfg.debug.timeout_notification.sampling_rate should not be allowed to be greater than 1.0, but it was allowed")
  1377  }
  1378  
  1379  func TestValidateAccountsConfigRestrictions(t *testing.T) {
  1380  	cfg, v := newDefaultConfig(t)
  1381  	cfg.Accounts.Files.Enabled = true
  1382  	cfg.Accounts.HTTP.Endpoint = "http://localhost"
  1383  	cfg.Accounts.Database.ConnectionInfo.Database = "accounts"
  1384  
  1385  	errs := cfg.validate(v)
  1386  	assert.Len(t, errs, 1)
  1387  	assert.Contains(t, errs, errors.New("accounts.database: retrieving accounts via database not available, use accounts.files"))
  1388  }
  1389  
  1390  func newDefaultConfig(t *testing.T) (*Configuration, *viper.Viper) {
  1391  	v := viper.New()
  1392  	SetupViper(v, "", bidderInfos)
  1393  	v.Set("gdpr.default_value", "0")
  1394  	v.SetConfigType("yaml")
  1395  	cfg, err := New(v, bidderInfos, mockNormalizeBidderName)
  1396  	assert.NoError(t, err, "Setting up config should work but it doesn't")
  1397  	return cfg, v
  1398  }
  1399  
  1400  func assertOneError(t *testing.T, errs []error, message string) {
  1401  	if !assert.Len(t, errs, 1) {
  1402  		return
  1403  	}
  1404  	assert.EqualError(t, errs[0], message)
  1405  }
  1406  
  1407  func doTimeoutTest(t *testing.T, expected int, requested int, max uint64, def uint64) {
  1408  	t.Helper()
  1409  	cfg := AuctionTimeouts{
  1410  		Default: def,
  1411  		Max:     max,
  1412  	}
  1413  	expectedDuration := time.Duration(expected) * time.Millisecond
  1414  	limited := cfg.LimitAuctionTimeout(time.Duration(requested) * time.Millisecond)
  1415  	assert.Equal(t, limited, expectedDuration, "Expected %dms timeout, got %dms", expectedDuration, limited/time.Millisecond)
  1416  }
  1417  
  1418  func TestSpecialFeature1VendorExceptionMap(t *testing.T) {
  1419  	baseConfig := []byte(`
  1420      gdpr:
  1421        default_value: 0
  1422        tcf2:
  1423          special_feature1:
  1424            vendor_exceptions: `)
  1425  
  1426  	tests := []struct {
  1427  		description             string
  1428  		configVendorExceptions  []byte
  1429  		wantVendorExceptions    []openrtb_ext.BidderName
  1430  		wantVendorExceptionsMap map[openrtb_ext.BidderName]struct{}
  1431  	}{
  1432  		{
  1433  			description:             "nil vendor exceptions",
  1434  			configVendorExceptions:  []byte(`null`),
  1435  			wantVendorExceptions:    []openrtb_ext.BidderName{},
  1436  			wantVendorExceptionsMap: map[openrtb_ext.BidderName]struct{}{},
  1437  		},
  1438  		{
  1439  			description:             "no vendor exceptions",
  1440  			configVendorExceptions:  []byte(`[]`),
  1441  			wantVendorExceptions:    []openrtb_ext.BidderName{},
  1442  			wantVendorExceptionsMap: map[openrtb_ext.BidderName]struct{}{},
  1443  		},
  1444  		{
  1445  			description:             "one vendor exception",
  1446  			configVendorExceptions:  []byte(`["vendor1"]`),
  1447  			wantVendorExceptions:    []openrtb_ext.BidderName{openrtb_ext.BidderName("vendor1")},
  1448  			wantVendorExceptionsMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("vendor1"): {}},
  1449  		},
  1450  		{
  1451  			description:             "many exceptions",
  1452  			configVendorExceptions:  []byte(`["vendor1","vendor2"]`),
  1453  			wantVendorExceptions:    []openrtb_ext.BidderName{openrtb_ext.BidderName("vendor1"), openrtb_ext.BidderName("vendor2")},
  1454  			wantVendorExceptionsMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("vendor1"): {}, openrtb_ext.BidderName("vendor2"): {}},
  1455  		},
  1456  	}
  1457  
  1458  	for _, tt := range tests {
  1459  		config := append(baseConfig, tt.configVendorExceptions...)
  1460  
  1461  		v := viper.New()
  1462  		SetupViper(v, "", bidderInfos)
  1463  		v.SetConfigType("yaml")
  1464  		v.ReadConfig(bytes.NewBuffer(config))
  1465  		cfg, err := New(v, bidderInfos, mockNormalizeBidderName)
  1466  		assert.NoError(t, err, "Setting up config error", tt.description)
  1467  
  1468  		assert.Equal(t, tt.wantVendorExceptions, cfg.GDPR.TCF2.SpecialFeature1.VendorExceptions, tt.description)
  1469  		assert.Equal(t, tt.wantVendorExceptionsMap, cfg.GDPR.TCF2.SpecialFeature1.VendorExceptionMap, tt.description)
  1470  	}
  1471  }
  1472  
  1473  func TestSetConfigBidderInfoNillableFields(t *testing.T) {
  1474  	falseValue := false
  1475  	trueValue := true
  1476  
  1477  	bidder1ConfigFalses := []byte(`
  1478      adapters:
  1479        bidder1:
  1480          disabled: false
  1481          modifyingVastXmlAllowed: false`)
  1482  	bidder1ConfigTrues := []byte(`
  1483      adapters:
  1484        bidder1:
  1485          disabled: true
  1486          modifyingVastXmlAllowed: true`)
  1487  	bidder1ConfigNils := []byte(`
  1488      adapters:
  1489        bidder1:
  1490          disabled: null
  1491          modifyingVastXmlAllowed: null`)
  1492  	bidder1Bidder2ConfigMixed := []byte(`
  1493      adapters:
  1494        bidder1:
  1495          disabled: true
  1496          modifyingVastXmlAllowed: false
  1497        bidder2:
  1498          disabled: false
  1499          modifyingVastXmlAllowed: true`)
  1500  
  1501  	tests := []struct {
  1502  		name        string
  1503  		rawConfig   []byte
  1504  		bidderInfos BidderInfos
  1505  		expected    nillableFieldBidderInfos
  1506  		expectError bool
  1507  	}{
  1508  		{
  1509  			name:     "viper and bidder infos are nil",
  1510  			expected: nil,
  1511  		},
  1512  		{
  1513  			name:        "viper is nil",
  1514  			bidderInfos: map[string]BidderInfo{},
  1515  			expected:    nil,
  1516  		},
  1517  		{
  1518  			name:      "bidder infos is nil",
  1519  			rawConfig: []byte{},
  1520  			expected:  nil,
  1521  		},
  1522  		{
  1523  			name:        "bidder infos is empty",
  1524  			bidderInfos: map[string]BidderInfo{},
  1525  			expected:    nil,
  1526  		},
  1527  		{
  1528  			name: "one: bidder info has nillable fields as false, viper has as nil",
  1529  			bidderInfos: map[string]BidderInfo{
  1530  				"bidder1": {Disabled: false, ModifyingVastXmlAllowed: false},
  1531  			},
  1532  			rawConfig: bidder1ConfigNils,
  1533  			expected: nillableFieldBidderInfos{
  1534  				"bidder1": nillableFieldBidderInfo{
  1535  					nillableFields: bidderInfoNillableFields{
  1536  						Disabled:                nil,
  1537  						ModifyingVastXmlAllowed: nil,
  1538  					},
  1539  					bidderInfo: BidderInfo{Disabled: false, ModifyingVastXmlAllowed: false},
  1540  				},
  1541  			},
  1542  		},
  1543  		{
  1544  			name: "one: bidder info has nillable fields as false, viper has as false",
  1545  			bidderInfos: map[string]BidderInfo{
  1546  				"bidder1": {Disabled: false, ModifyingVastXmlAllowed: false},
  1547  			},
  1548  			rawConfig: bidder1ConfigFalses,
  1549  			expected: nillableFieldBidderInfos{
  1550  				"bidder1": nillableFieldBidderInfo{
  1551  					nillableFields: bidderInfoNillableFields{
  1552  						Disabled:                &falseValue,
  1553  						ModifyingVastXmlAllowed: &falseValue,
  1554  					},
  1555  					bidderInfo: BidderInfo{Disabled: false, ModifyingVastXmlAllowed: false},
  1556  				},
  1557  			},
  1558  		},
  1559  		{
  1560  			name: "one: bidder info has nillable fields as false, viper has as true",
  1561  			bidderInfos: map[string]BidderInfo{
  1562  				"bidder1": {Disabled: false, ModifyingVastXmlAllowed: false},
  1563  			},
  1564  			rawConfig: bidder1ConfigTrues,
  1565  			expected: nillableFieldBidderInfos{
  1566  				"bidder1": nillableFieldBidderInfo{
  1567  					nillableFields: bidderInfoNillableFields{
  1568  						Disabled:                &trueValue,
  1569  						ModifyingVastXmlAllowed: &trueValue,
  1570  					},
  1571  					bidderInfo: BidderInfo{Disabled: false, ModifyingVastXmlAllowed: false},
  1572  				},
  1573  			},
  1574  		},
  1575  		{
  1576  			name: "many with extra info: bidder infos have nillable fields as false and true, viper has as true and false",
  1577  			bidderInfos: map[string]BidderInfo{
  1578  				"bidder1": {Disabled: false, ModifyingVastXmlAllowed: true, Endpoint: "endpoint a"},
  1579  				"bidder2": {Disabled: true, ModifyingVastXmlAllowed: false, Endpoint: "endpoint b"},
  1580  			},
  1581  			rawConfig: bidder1Bidder2ConfigMixed,
  1582  			expected: nillableFieldBidderInfos{
  1583  				"bidder1": nillableFieldBidderInfo{
  1584  					nillableFields: bidderInfoNillableFields{
  1585  						Disabled:                &trueValue,
  1586  						ModifyingVastXmlAllowed: &falseValue,
  1587  					},
  1588  					bidderInfo: BidderInfo{Disabled: false, ModifyingVastXmlAllowed: true, Endpoint: "endpoint a"},
  1589  				},
  1590  				"bidder2": nillableFieldBidderInfo{
  1591  					nillableFields: bidderInfoNillableFields{
  1592  						Disabled:                &falseValue,
  1593  						ModifyingVastXmlAllowed: &trueValue,
  1594  					},
  1595  					bidderInfo: BidderInfo{Disabled: true, ModifyingVastXmlAllowed: false, Endpoint: "endpoint b"},
  1596  				},
  1597  			},
  1598  		},
  1599  	}
  1600  
  1601  	for _, tt := range tests {
  1602  		t.Run(tt.name, func(t *testing.T) {
  1603  			v := viper.New()
  1604  			v.SetConfigType("yaml")
  1605  			for bidderName := range tt.bidderInfos {
  1606  				setBidderDefaults(v, strings.ToLower(bidderName))
  1607  			}
  1608  			v.ReadConfig(bytes.NewBuffer(tt.rawConfig))
  1609  
  1610  			result, err := setConfigBidderInfoNillableFields(v, tt.bidderInfos)
  1611  
  1612  			assert.Equal(t, tt.expected, result)
  1613  			if tt.expectError {
  1614  				assert.NotNil(t, err)
  1615  			} else {
  1616  				assert.Nil(t, err)
  1617  			}
  1618  		})
  1619  	}
  1620  }
  1621  
  1622  func TestTCF2PurposeEnforced(t *testing.T) {
  1623  	tests := []struct {
  1624  		description          string
  1625  		givePurposeConfigNil bool
  1626  		givePurpose1Enforced bool
  1627  		givePurpose2Enforced bool
  1628  		givePurpose          consentconstants.Purpose
  1629  		wantEnforced         bool
  1630  	}{
  1631  		{
  1632  			description:          "Purpose config is nil",
  1633  			givePurposeConfigNil: true,
  1634  			givePurpose:          1,
  1635  			wantEnforced:         false,
  1636  		},
  1637  		{
  1638  			description:          "Purpose 1 Enforced set to true",
  1639  			givePurpose1Enforced: true,
  1640  			givePurpose:          1,
  1641  			wantEnforced:         true,
  1642  		},
  1643  		{
  1644  			description:          "Purpose 1 Enforced set to false",
  1645  			givePurpose1Enforced: false,
  1646  			givePurpose:          1,
  1647  			wantEnforced:         false,
  1648  		},
  1649  		{
  1650  			description:          "Purpose 2 Enforced set to true",
  1651  			givePurpose2Enforced: true,
  1652  			givePurpose:          2,
  1653  			wantEnforced:         true,
  1654  		},
  1655  	}
  1656  
  1657  	for _, tt := range tests {
  1658  		tcf2 := TCF2{}
  1659  
  1660  		if !tt.givePurposeConfigNil {
  1661  			tcf2.PurposeConfigs = map[consentconstants.Purpose]*TCF2Purpose{
  1662  				1: {
  1663  					EnforcePurpose: tt.givePurpose1Enforced,
  1664  				},
  1665  				2: {
  1666  					EnforcePurpose: tt.givePurpose2Enforced,
  1667  				},
  1668  			}
  1669  		}
  1670  
  1671  		value := tcf2.PurposeEnforced(tt.givePurpose)
  1672  
  1673  		assert.Equal(t, tt.wantEnforced, value, tt.description)
  1674  	}
  1675  }
  1676  
  1677  func TestTCF2PurposeEnforcementAlgo(t *testing.T) {
  1678  	tests := []struct {
  1679  		description          string
  1680  		givePurposeConfigNil bool
  1681  		givePurpose1Algo     TCF2EnforcementAlgo
  1682  		givePurpose2Algo     TCF2EnforcementAlgo
  1683  		givePurpose          consentconstants.Purpose
  1684  		wantAlgo             TCF2EnforcementAlgo
  1685  	}{
  1686  		{
  1687  			description:          "Purpose config is nil",
  1688  			givePurposeConfigNil: true,
  1689  			givePurpose:          1,
  1690  			wantAlgo:             TCF2FullEnforcement,
  1691  		},
  1692  		{
  1693  			description:      "Purpose 1 enforcement algo set to basic",
  1694  			givePurpose1Algo: TCF2BasicEnforcement,
  1695  			givePurpose:      1,
  1696  			wantAlgo:         TCF2BasicEnforcement,
  1697  		},
  1698  		{
  1699  			description:      "Purpose 1 enforcement algo set to full",
  1700  			givePurpose1Algo: TCF2FullEnforcement,
  1701  			givePurpose:      1,
  1702  			wantAlgo:         TCF2FullEnforcement,
  1703  		},
  1704  		{
  1705  			description:      "Purpose 2 Enforcement algo set to basic",
  1706  			givePurpose2Algo: TCF2BasicEnforcement,
  1707  			givePurpose:      2,
  1708  			wantAlgo:         TCF2BasicEnforcement,
  1709  		},
  1710  	}
  1711  
  1712  	for _, tt := range tests {
  1713  		tcf2 := TCF2{}
  1714  
  1715  		if !tt.givePurposeConfigNil {
  1716  			tcf2.PurposeConfigs = map[consentconstants.Purpose]*TCF2Purpose{
  1717  				1: {
  1718  					EnforceAlgoID: tt.givePurpose1Algo,
  1719  				},
  1720  				2: {
  1721  					EnforceAlgoID: tt.givePurpose2Algo,
  1722  				},
  1723  			}
  1724  		}
  1725  
  1726  		value := tcf2.PurposeEnforcementAlgo(tt.givePurpose)
  1727  
  1728  		assert.Equal(t, tt.wantAlgo, value, tt.description)
  1729  	}
  1730  }
  1731  
  1732  func TestTCF2PurposeEnforcingVendors(t *testing.T) {
  1733  	tests := []struct {
  1734  		description           string
  1735  		givePurposeConfigNil  bool
  1736  		givePurpose1Enforcing bool
  1737  		givePurpose2Enforcing bool
  1738  		givePurpose           consentconstants.Purpose
  1739  		wantEnforcing         bool
  1740  	}{
  1741  		{
  1742  			description:          "Purpose config is nil",
  1743  			givePurposeConfigNil: true,
  1744  			givePurpose:          1,
  1745  			wantEnforcing:        false,
  1746  		},
  1747  		{
  1748  			description:           "Purpose 1 Enforcing set to true",
  1749  			givePurpose1Enforcing: true,
  1750  			givePurpose:           1,
  1751  			wantEnforcing:         true,
  1752  		},
  1753  		{
  1754  			description:           "Purpose 1 Enforcing set to false",
  1755  			givePurpose1Enforcing: false,
  1756  			givePurpose:           1,
  1757  			wantEnforcing:         false,
  1758  		},
  1759  		{
  1760  			description:           "Purpose 2 Enforcing set to true",
  1761  			givePurpose2Enforcing: true,
  1762  			givePurpose:           2,
  1763  			wantEnforcing:         true,
  1764  		},
  1765  	}
  1766  
  1767  	for _, tt := range tests {
  1768  		tcf2 := TCF2{}
  1769  
  1770  		if !tt.givePurposeConfigNil {
  1771  			tcf2.PurposeConfigs = map[consentconstants.Purpose]*TCF2Purpose{
  1772  				1: {
  1773  					EnforceVendors: tt.givePurpose1Enforcing,
  1774  				},
  1775  				2: {
  1776  					EnforceVendors: tt.givePurpose2Enforcing,
  1777  				},
  1778  			}
  1779  		}
  1780  
  1781  		value := tcf2.PurposeEnforcingVendors(tt.givePurpose)
  1782  
  1783  		assert.Equal(t, tt.wantEnforcing, value, tt.description)
  1784  	}
  1785  }
  1786  
  1787  func TestTCF2PurposeVendorExceptions(t *testing.T) {
  1788  	tests := []struct {
  1789  		description              string
  1790  		givePurposeConfigNil     bool
  1791  		givePurpose1ExceptionMap map[string]struct{}
  1792  		givePurpose2ExceptionMap map[string]struct{}
  1793  		givePurpose              consentconstants.Purpose
  1794  		wantExceptionMap         map[string]struct{}
  1795  	}{
  1796  		{
  1797  			description:          "Purpose config is nil",
  1798  			givePurposeConfigNil: true,
  1799  			givePurpose:          1,
  1800  			wantExceptionMap:     map[string]struct{}{},
  1801  		},
  1802  		{
  1803  			description:      "Nil - exception map not defined for purpose",
  1804  			givePurpose:      1,
  1805  			wantExceptionMap: map[string]struct{}{},
  1806  		},
  1807  		{
  1808  			description:              "Empty - exception map empty for purpose",
  1809  			givePurpose:              1,
  1810  			givePurpose1ExceptionMap: map[string]struct{}{},
  1811  			wantExceptionMap:         map[string]struct{}{},
  1812  		},
  1813  		{
  1814  			description:              "Nonempty - exception map with multiple entries for purpose",
  1815  			givePurpose:              1,
  1816  			givePurpose1ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
  1817  			wantExceptionMap:         map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
  1818  		},
  1819  		{
  1820  			description:              "Nonempty - exception map with multiple entries for different purpose",
  1821  			givePurpose:              2,
  1822  			givePurpose1ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
  1823  			givePurpose2ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}},
  1824  			wantExceptionMap:         map[string]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}},
  1825  		},
  1826  	}
  1827  
  1828  	for _, tt := range tests {
  1829  		tcf2 := TCF2{}
  1830  
  1831  		if !tt.givePurposeConfigNil {
  1832  			tcf2.PurposeConfigs = map[consentconstants.Purpose]*TCF2Purpose{
  1833  				1: {
  1834  					VendorExceptionMap: tt.givePurpose1ExceptionMap,
  1835  				},
  1836  				2: {
  1837  					VendorExceptionMap: tt.givePurpose2ExceptionMap,
  1838  				},
  1839  			}
  1840  		}
  1841  
  1842  		value := tcf2.PurposeVendorExceptions(tt.givePurpose)
  1843  
  1844  		assert.Equal(t, tt.wantExceptionMap, value, tt.description)
  1845  	}
  1846  }
  1847  
  1848  func TestTCF2FeatureOneVendorException(t *testing.T) {
  1849  	tests := []struct {
  1850  		description           string
  1851  		giveExceptionMap      map[openrtb_ext.BidderName]struct{}
  1852  		giveBidder            openrtb_ext.BidderName
  1853  		wantIsVendorException bool
  1854  	}{
  1855  		{
  1856  			description:           "Nil - exception map not defined",
  1857  			giveBidder:            "appnexus",
  1858  			wantIsVendorException: false,
  1859  		},
  1860  		{
  1861  			description:           "Empty - exception map empty",
  1862  			giveExceptionMap:      map[openrtb_ext.BidderName]struct{}{},
  1863  			giveBidder:            "appnexus",
  1864  			wantIsVendorException: false,
  1865  		},
  1866  		{
  1867  			description:           "One - bidder found in exception map containing one entry",
  1868  			giveExceptionMap:      map[openrtb_ext.BidderName]struct{}{"appnexus": {}},
  1869  			giveBidder:            "appnexus",
  1870  			wantIsVendorException: true,
  1871  		},
  1872  		{
  1873  			description:           "Many - bidder found in exception map containing multiple entries",
  1874  			giveExceptionMap:      map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
  1875  			giveBidder:            "appnexus",
  1876  			wantIsVendorException: true,
  1877  		},
  1878  		{
  1879  			description:           "Many - bidder not found in exception map containing multiple entries",
  1880  			giveExceptionMap:      map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}},
  1881  			giveBidder:            "openx",
  1882  			wantIsVendorException: false,
  1883  		},
  1884  	}
  1885  
  1886  	for _, tt := range tests {
  1887  		tcf2 := TCF2{
  1888  			SpecialFeature1: TCF2SpecialFeature{
  1889  				VendorExceptionMap: tt.giveExceptionMap,
  1890  			},
  1891  		}
  1892  
  1893  		value := tcf2.FeatureOneVendorException(tt.giveBidder)
  1894  
  1895  		assert.Equal(t, tt.wantIsVendorException, value, tt.description)
  1896  	}
  1897  }
  1898  
  1899  func TestUnpackDSADefault(t *testing.T) {
  1900  	tests := []struct {
  1901  		name      string
  1902  		giveDSA   *AccountDSA
  1903  		wantError bool
  1904  	}{
  1905  		{
  1906  			name:      "nil",
  1907  			giveDSA:   nil,
  1908  			wantError: false,
  1909  		},
  1910  		{
  1911  			name: "empty",
  1912  			giveDSA: &AccountDSA{
  1913  				Default: "",
  1914  			},
  1915  			wantError: false,
  1916  		},
  1917  		{
  1918  			name: "empty_json",
  1919  			giveDSA: &AccountDSA{
  1920  				Default: "{}",
  1921  			},
  1922  			wantError: false,
  1923  		},
  1924  		{
  1925  			name: "well_formed",
  1926  			giveDSA: &AccountDSA{
  1927  				Default: "{\"dsarequired\":3,\"pubrender\":1,\"datatopub\":2,\"transparency\":[{\"domain\":\"domain.com\",\"dsaparams\":[1]}]}",
  1928  			},
  1929  			wantError: false,
  1930  		},
  1931  		{
  1932  			name: "well_formed_with_extra_fields",
  1933  			giveDSA: &AccountDSA{
  1934  				Default: "{\"unmappedkey\":\"unmappedvalue\",\"dsarequired\":3,\"pubrender\":1,\"datatopub\":2,\"transparency\":[{\"domain\":\"domain.com\",\"dsaparams\":[1]}]}",
  1935  			},
  1936  			wantError: false,
  1937  		},
  1938  		{
  1939  			name: "invalid_type",
  1940  			giveDSA: &AccountDSA{
  1941  				Default: "{\"dsarequired\":\"invalid\",\"pubrender\":1,\"datatopub\":2,\"transparency\":[{\"domain\":\"domain.com\",\"dsaparams\":[1]}]}",
  1942  			},
  1943  			wantError: true,
  1944  		},
  1945  		{
  1946  			name: "invalid_malformed_missing_colon",
  1947  			giveDSA: &AccountDSA{
  1948  				Default: "{\"dsarequired\"3,\"pubrender\":1,\"datatopub\":2,\"transparency\":[{\"domain\":\"domain.com\",\"dsaparams\":[1]}]}",
  1949  			},
  1950  			wantError: true,
  1951  		},
  1952  	}
  1953  
  1954  	for _, tt := range tests {
  1955  		t.Run(tt.name, func(t *testing.T) {
  1956  			err := UnpackDSADefault(tt.giveDSA)
  1957  			if tt.wantError {
  1958  				assert.Error(t, err)
  1959  			} else {
  1960  				assert.NoError(t, err)
  1961  			}
  1962  		})
  1963  	}
  1964  }