github.com/prebid/prebid-server@v0.275.0/account/account_test.go (about) 1 package account 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "testing" 8 9 "github.com/buger/jsonparser" 10 "github.com/prebid/prebid-server/config" 11 "github.com/prebid/prebid-server/errortypes" 12 "github.com/prebid/prebid-server/metrics" 13 "github.com/prebid/prebid-server/openrtb_ext" 14 "github.com/prebid/prebid-server/stored_requests" 15 "github.com/prebid/prebid-server/util/iputil" 16 "github.com/prebid/prebid-server/util/ptrutil" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/mock" 19 ) 20 21 var mockAccountData = map[string]json.RawMessage{ 22 "valid_acct": json.RawMessage(`{"disabled":false}`), 23 "invalid_acct_ipv6_ipv4": json.RawMessage(`{"disabled":false, "privacy": {"ipv6": {"anon_keep_bits": -32}, "ipv4": {"anon_keep_bits": -16}}}`), 24 "disabled_acct": json.RawMessage(`{"disabled":true}`), 25 "malformed_acct": json.RawMessage(`{"disabled":"invalid type"}`), 26 "gdpr_channel_enabled_acct": json.RawMessage(`{"disabled":false,"gdpr":{"channel_enabled":{"amp":true}}}`), 27 "ccpa_channel_enabled_acct": json.RawMessage(`{"disabled":false,"ccpa":{"channel_enabled":{"amp":true}}}`), 28 "gdpr_channel_enabled_deprecated_purpose_acct": json.RawMessage(`{"disabled":false,"gdpr":{"purpose1":{"enforce_purpose":"full"}, "channel_enabled":{"amp":true}}}`), 29 "gdpr_deprecated_purpose1": json.RawMessage(`{"disabled":false,"gdpr":{"purpose1":{"enforce_purpose":"full"}}}`), 30 "gdpr_deprecated_purpose2": json.RawMessage(`{"disabled":false,"gdpr":{"purpose2":{"enforce_purpose":"full"}}}`), 31 "gdpr_deprecated_purpose3": json.RawMessage(`{"disabled":false,"gdpr":{"purpose3":{"enforce_purpose":"full"}}}`), 32 "gdpr_deprecated_purpose4": json.RawMessage(`{"disabled":false,"gdpr":{"purpose4":{"enforce_purpose":"full"}}}`), 33 "gdpr_deprecated_purpose5": json.RawMessage(`{"disabled":false,"gdpr":{"purpose5":{"enforce_purpose":"full"}}}`), 34 "gdpr_deprecated_purpose6": json.RawMessage(`{"disabled":false,"gdpr":{"purpose6":{"enforce_purpose":"full"}}}`), 35 "gdpr_deprecated_purpose7": json.RawMessage(`{"disabled":false,"gdpr":{"purpose7":{"enforce_purpose":"full"}}}`), 36 "gdpr_deprecated_purpose8": json.RawMessage(`{"disabled":false,"gdpr":{"purpose8":{"enforce_purpose":"full"}}}`), 37 "gdpr_deprecated_purpose9": json.RawMessage(`{"disabled":false,"gdpr":{"purpose9":{"enforce_purpose":"full"}}}`), 38 "gdpr_deprecated_purpose10": json.RawMessage(`{"disabled":false,"gdpr":{"purpose10":{"enforce_purpose":"full"}}}`), 39 } 40 41 type mockAccountFetcher struct { 42 } 43 44 func (af mockAccountFetcher) FetchAccount(ctx context.Context, accountDefaultsJSON json.RawMessage, accountID string) (json.RawMessage, []error) { 45 if account, ok := mockAccountData[accountID]; ok { 46 return account, nil 47 } 48 return nil, []error{stored_requests.NotFoundError{ID: accountID, DataType: "Account"}} 49 } 50 51 func TestGetAccount(t *testing.T) { 52 unknown := metrics.PublisherUnknown 53 testCases := []struct { 54 accountID string 55 // account_required 56 required bool 57 // account_defaults.disabled 58 disabled bool 59 // checkDefaultIP indicates IPv6 and IPv6 should be set to default values 60 checkDefaultIP bool 61 // expected error, or nil if account should be found 62 err error 63 }{ 64 // Blacklisted account is always rejected even in permissive setup 65 {accountID: "bad_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}}, 66 67 // empty pubID 68 {accountID: unknown, required: false, disabled: false, err: nil}, 69 {accountID: unknown, required: true, disabled: false, err: &errortypes.AcctRequired{}}, 70 {accountID: unknown, required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, 71 {accountID: unknown, required: true, disabled: true, err: &errortypes.AcctRequired{}}, 72 73 // pubID given but is not a valid host account (does not exist) 74 {accountID: "doesnt_exist_acct", required: false, disabled: false, err: nil}, 75 {accountID: "doesnt_exist_acct", required: true, disabled: false, err: nil}, 76 {accountID: "doesnt_exist_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, 77 {accountID: "doesnt_exist_acct", required: true, disabled: true, err: &errortypes.AcctRequired{}}, 78 79 // pubID given and matches a valid host account with Disabled: false 80 {accountID: "valid_acct", required: false, disabled: false, err: nil}, 81 {accountID: "valid_acct", required: true, disabled: false, err: nil}, 82 {accountID: "valid_acct", required: false, disabled: true, err: nil}, 83 {accountID: "valid_acct", required: true, disabled: true, err: nil}, 84 85 {accountID: "invalid_acct_ipv6_ipv4", required: true, disabled: false, err: nil, checkDefaultIP: true}, 86 87 // pubID given and matches a host account explicitly disabled (Disabled: true on account json) 88 {accountID: "disabled_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}}, 89 {accountID: "disabled_acct", required: true, disabled: false, err: &errortypes.BlacklistedAcct{}}, 90 {accountID: "disabled_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, 91 {accountID: "disabled_acct", required: true, disabled: true, err: &errortypes.BlacklistedAcct{}}, 92 93 // pubID given and matches a host account with Disabled: false and GDPR purpose data to convert 94 {accountID: "gdpr_deprecated_purpose1", required: false, disabled: false, err: nil}, 95 {accountID: "gdpr_deprecated_purpose1", required: true, disabled: false, err: nil}, 96 {accountID: "gdpr_deprecated_purpose1", required: false, disabled: true, err: nil}, 97 {accountID: "gdpr_deprecated_purpose1", required: true, disabled: true, err: nil}, 98 99 // pubID given and matches a host account that has a malformed config 100 {accountID: "malformed_acct", required: false, disabled: false, err: &errortypes.MalformedAcct{}}, 101 {accountID: "malformed_acct", required: true, disabled: false, err: &errortypes.MalformedAcct{}}, 102 {accountID: "malformed_acct", required: false, disabled: true, err: &errortypes.MalformedAcct{}}, 103 {accountID: "malformed_acct", required: true, disabled: true, err: &errortypes.MalformedAcct{}}, 104 105 // account not provided (does not exist) 106 {accountID: "", required: false, disabled: false, err: nil}, 107 {accountID: "", required: true, disabled: false, err: nil}, 108 {accountID: "", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, 109 {accountID: "", required: true, disabled: true, err: &errortypes.AcctRequired{}}, 110 } 111 112 for _, test := range testCases { 113 description := fmt.Sprintf(`ID=%s/required=%t/disabled=%t`, test.accountID, test.required, test.disabled) 114 t.Run(description, func(t *testing.T) { 115 cfg := &config.Configuration{ 116 BlacklistedAcctMap: map[string]bool{"bad_acct": true}, 117 AccountRequired: test.required, 118 AccountDefaults: config.Account{Disabled: test.disabled}, 119 } 120 fetcher := &mockAccountFetcher{} 121 assert.NoError(t, cfg.MarshalAccountDefaults()) 122 123 metrics := &metrics.MetricsEngineMock{} 124 metrics.Mock.On("RecordAccountGDPRPurposeWarning", mock.Anything, mock.Anything).Return() 125 metrics.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return() 126 127 account, errors := GetAccount(context.Background(), cfg, fetcher, test.accountID, metrics) 128 129 if test.err == nil { 130 assert.Empty(t, errors) 131 assert.Equal(t, test.accountID, account.ID, "account.ID must match requested ID") 132 assert.Equal(t, false, account.Disabled, "returned account must not be disabled") 133 } else { 134 assert.NotEmpty(t, errors, "expected errors but got success") 135 assert.Nil(t, account, "return account must be nil on error") 136 assert.IsType(t, test.err, errors[0], "error is of unexpected type") 137 } 138 if test.checkDefaultIP { 139 assert.Equal(t, account.Privacy.IPv6Config.AnonKeepBits, iputil.IPv6DefaultMaskingBitSize, "ipv6 should be set to default value") 140 assert.Equal(t, account.Privacy.IPv4Config.AnonKeepBits, iputil.IPv4DefaultMaskingBitSize, "ipv4 should be set to default value") 141 } 142 }) 143 } 144 } 145 146 func TestSetDerivedConfig(t *testing.T) { 147 tests := []struct { 148 description string 149 purpose1VendorExceptions []openrtb_ext.BidderName 150 feature1VendorExceptions []openrtb_ext.BidderName 151 basicEnforcementVendors []string 152 enforceAlgo string 153 wantEnforceAlgoID config.TCF2EnforcementAlgo 154 }{ 155 { 156 description: "Nil purpose 1 vendor exceptions", 157 purpose1VendorExceptions: nil, 158 }, 159 { 160 description: "One purpose 1 vendor exception", 161 purpose1VendorExceptions: []openrtb_ext.BidderName{"appnexus"}, 162 }, 163 { 164 description: "Multiple purpose 1 vendor exceptions", 165 purpose1VendorExceptions: []openrtb_ext.BidderName{"appnexus", "rubicon"}, 166 }, 167 { 168 description: "Nil feature 1 vendor exceptions", 169 feature1VendorExceptions: nil, 170 }, 171 { 172 description: "One feature 1 vendor exception", 173 feature1VendorExceptions: []openrtb_ext.BidderName{"appnexus"}, 174 }, 175 { 176 description: "Multiple feature 1 vendor exceptions", 177 feature1VendorExceptions: []openrtb_ext.BidderName{"appnexus", "rubicon"}, 178 }, 179 { 180 description: "Nil basic enforcement vendors", 181 basicEnforcementVendors: nil, 182 }, 183 { 184 description: "One basic enforcement vendor", 185 basicEnforcementVendors: []string{"appnexus"}, 186 }, 187 { 188 description: "Multiple basic enforcement vendors", 189 basicEnforcementVendors: []string{"appnexus", "rubicon"}, 190 }, 191 { 192 description: "Basic Enforcement algorithm", 193 enforceAlgo: config.TCF2EnforceAlgoBasic, 194 wantEnforceAlgoID: config.TCF2BasicEnforcement, 195 }, 196 { 197 description: "Full Enforcement algorithm", 198 enforceAlgo: config.TCF2EnforceAlgoFull, 199 wantEnforceAlgoID: config.TCF2FullEnforcement, 200 }, 201 } 202 203 for _, tt := range tests { 204 account := config.Account{ 205 GDPR: config.AccountGDPR{ 206 Purpose1: config.AccountGDPRPurpose{ 207 VendorExceptions: tt.purpose1VendorExceptions, 208 EnforceAlgo: tt.enforceAlgo, 209 }, 210 SpecialFeature1: config.AccountGDPRSpecialFeature{ 211 VendorExceptions: tt.feature1VendorExceptions, 212 }, 213 BasicEnforcementVendors: tt.basicEnforcementVendors, 214 }, 215 } 216 217 setDerivedConfig(&account) 218 219 purpose1ExceptionMapKeys := make([]openrtb_ext.BidderName, 0) 220 for k := range account.GDPR.Purpose1.VendorExceptionMap { 221 purpose1ExceptionMapKeys = append(purpose1ExceptionMapKeys, k) 222 } 223 224 feature1ExceptionMapKeys := make([]openrtb_ext.BidderName, 0) 225 for k := range account.GDPR.SpecialFeature1.VendorExceptionMap { 226 feature1ExceptionMapKeys = append(feature1ExceptionMapKeys, k) 227 } 228 229 basicEnforcementMapKeys := make([]string, 0) 230 for k := range account.GDPR.BasicEnforcementVendorsMap { 231 basicEnforcementMapKeys = append(basicEnforcementMapKeys, k) 232 } 233 234 assert.ElementsMatch(t, purpose1ExceptionMapKeys, tt.purpose1VendorExceptions, tt.description) 235 assert.ElementsMatch(t, feature1ExceptionMapKeys, tt.feature1VendorExceptions, tt.description) 236 assert.ElementsMatch(t, basicEnforcementMapKeys, tt.basicEnforcementVendors, tt.description) 237 238 assert.Equal(t, account.GDPR.Purpose1.EnforceAlgoID, tt.wantEnforceAlgoID, tt.description) 239 } 240 } 241 242 func TestConvertGDPREnforcePurposeFields(t *testing.T) { 243 enforcePurposeNo := `{"enforce_purpose":"no"}` 244 enforcePurposeNoMapped := `{"enforce_algo":"full", "enforce_purpose":false}` 245 enforcePurposeFull := `{"enforce_purpose":"full"}` 246 enforcePurposeFullMapped := `{"enforce_algo":"full", "enforce_purpose":true}` 247 248 tests := []struct { 249 description string 250 giveConfig []byte 251 wantConfig []byte 252 wantErr error 253 wantPurposeFields []string 254 }{ 255 { 256 description: "config is nil", 257 giveConfig: nil, 258 wantConfig: nil, 259 wantErr: nil, 260 wantPurposeFields: nil, 261 }, 262 { 263 description: "config is empty - no gdpr key", 264 giveConfig: []byte(``), 265 wantConfig: []byte(``), 266 wantErr: nil, 267 wantPurposeFields: nil, 268 }, 269 { 270 description: "gdpr present but empty", 271 giveConfig: []byte(`{"gdpr": {}}`), 272 wantConfig: []byte(`{"gdpr": {}}`), 273 wantErr: nil, 274 wantPurposeFields: nil, 275 }, 276 { 277 description: "gdpr present but invalid", 278 giveConfig: []byte(`{"gdpr": {`), 279 wantConfig: nil, 280 wantErr: jsonparser.MalformedJsonError, 281 wantPurposeFields: nil, 282 }, 283 { 284 description: "gdpr.purpose1 present but empty", 285 giveConfig: []byte(`{"gdpr":{"purpose1":{}}}`), 286 wantConfig: []byte(`{"gdpr":{"purpose1":{}}}`), 287 wantErr: nil, 288 wantPurposeFields: nil, 289 }, 290 { 291 description: "gdpr.purpose1.enforce_purpose is set to bool", 292 giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":true}}}`), 293 wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":true}}}`), 294 wantErr: nil, 295 wantPurposeFields: nil, 296 }, 297 { 298 description: "gdpr.purpose1.enforce_purpose is set to string full", 299 giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":"full"}}}`), 300 wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":true}}}`), 301 wantErr: nil, 302 wantPurposeFields: []string{"purpose1"}, 303 }, 304 { 305 description: "gdpr.purpose1.enforce_purpose is set to string no", 306 giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":"no"}}}`), 307 wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":false}}}`), 308 wantErr: nil, 309 wantPurposeFields: []string{"purpose1"}, 310 }, 311 { 312 description: "gdpr.purpose1.enforce_purpose is set to string no and other fields are untouched during conversion", 313 giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":"no", "enforce_vendors":true}}}`), 314 wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":false, "enforce_vendors":true}}}`), 315 wantErr: nil, 316 wantPurposeFields: []string{"purpose1"}, 317 }, 318 { 319 description: "gdpr.purpose1.enforce_purpose is set but invalid", 320 giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":}}}`), 321 wantConfig: nil, 322 wantErr: jsonparser.MalformedJsonError, 323 wantPurposeFields: nil, 324 }, 325 { 326 description: "gdpr.purpose1.enforce_algo is set", 327 giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full"}}}`), 328 wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full"}}}`), 329 wantErr: nil, 330 wantPurposeFields: nil, 331 }, 332 { 333 description: "gdpr.purpose1.enforce_purpose is set to string and enforce_algo is set", 334 giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":"full"}}}`), 335 wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":"full"}}}`), 336 wantErr: nil, 337 wantPurposeFields: []string{"purpose1"}, 338 }, 339 { 340 description: "gdpr.purpose1.enforce_purpose is set to string and enforce_algo is set but invalid", 341 giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":, "enforce_purpose":"full"}}}`), 342 wantConfig: nil, 343 wantErr: jsonparser.MalformedJsonError, 344 wantPurposeFields: []string{"purpose1"}, 345 }, 346 { 347 description: "gdpr.purpose{1-10}.enforce_purpose are set to strings no and full alternating", 348 giveConfig: []byte(`{"gdpr":{` + 349 `"purpose1":` + enforcePurposeNo + 350 `,"purpose2":` + enforcePurposeFull + 351 `,"purpose3":` + enforcePurposeNo + 352 `,"purpose4":` + enforcePurposeFull + 353 `,"purpose5":` + enforcePurposeNo + 354 `,"purpose6":` + enforcePurposeFull + 355 `,"purpose7":` + enforcePurposeNo + 356 `,"purpose8":` + enforcePurposeFull + 357 `,"purpose9":` + enforcePurposeNo + 358 `,"purpose10":` + enforcePurposeFull + 359 `}}`), 360 wantConfig: []byte(`{"gdpr":{` + 361 `"purpose1":` + enforcePurposeNoMapped + 362 `,"purpose2":` + enforcePurposeFullMapped + 363 `,"purpose3":` + enforcePurposeNoMapped + 364 `,"purpose4":` + enforcePurposeFullMapped + 365 `,"purpose5":` + enforcePurposeNoMapped + 366 `,"purpose6":` + enforcePurposeFullMapped + 367 `,"purpose7":` + enforcePurposeNoMapped + 368 `,"purpose8":` + enforcePurposeFullMapped + 369 `,"purpose9":` + enforcePurposeNoMapped + 370 `,"purpose10":` + enforcePurposeFullMapped + 371 `}}`), 372 wantErr: nil, 373 wantPurposeFields: []string{"purpose1", "purpose2", "purpose3", "purpose4", "purpose5", "purpose6", "purpose7", "purpose8", "purpose9", "purpose10"}, 374 }, 375 } 376 377 for _, tt := range tests { 378 metricsMock := &metrics.MetricsEngineMock{} 379 metricsMock.Mock.On("RecordAccountGDPRPurposeWarning", mock.Anything, mock.Anything).Return() 380 metricsMock.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return() 381 382 newConfig, err, deprecatedPurposeFields := ConvertGDPREnforcePurposeFields(tt.giveConfig) 383 if tt.wantErr != nil { 384 assert.Error(t, err, tt.description) 385 } 386 387 if len(tt.wantConfig) == 0 { 388 assert.Equal(t, tt.wantConfig, newConfig, tt.description) 389 } else { 390 assert.JSONEq(t, string(tt.wantConfig), string(newConfig), tt.description) 391 } 392 assert.Equal(t, tt.wantPurposeFields, deprecatedPurposeFields, tt.description) 393 } 394 } 395 396 func TestGdprCcpaChannelEnabledMetrics(t *testing.T) { 397 cfg := &config.Configuration{} 398 fetcher := &mockAccountFetcher{} 399 assert.NoError(t, cfg.MarshalAccountDefaults()) 400 401 testCases := []struct { 402 name string 403 givenAccountID string 404 givenMetric string 405 expectedMetricCount int 406 }{ 407 { 408 name: "ChannelEnabledGDPR", 409 givenAccountID: "gdpr_channel_enabled_acct", 410 givenMetric: "RecordAccountGDPRChannelEnabledWarning", 411 expectedMetricCount: 1, 412 }, 413 { 414 name: "ChannelEnabledCCPA", 415 givenAccountID: "ccpa_channel_enabled_acct", 416 givenMetric: "RecordAccountCCPAChannelEnabledWarning", 417 expectedMetricCount: 1, 418 }, 419 { 420 name: "NotChannelEnabledCCPA", 421 givenAccountID: "valid_acct", 422 givenMetric: "RecordAccountCCPAChannelEnabledWarning", 423 expectedMetricCount: 0, 424 }, 425 { 426 name: "NotChannelEnabledGDPR", 427 givenAccountID: "valid_acct", 428 givenMetric: "RecordAccountGDPRChannelEnabledWarning", 429 expectedMetricCount: 0, 430 }, 431 } 432 433 for _, test := range testCases { 434 t.Run(test.name, func(t *testing.T) { 435 metrics := &metrics.MetricsEngineMock{} 436 metrics.Mock.On(test.givenMetric, mock.Anything, mock.Anything).Return() 437 metrics.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return() 438 439 _, _ = GetAccount(context.Background(), cfg, fetcher, test.givenAccountID, metrics) 440 441 metrics.AssertNumberOfCalls(t, test.givenMetric, test.expectedMetricCount) 442 }) 443 } 444 } 445 446 func TestGdprPurposeWarningMetrics(t *testing.T) { 447 cfg := &config.Configuration{} 448 fetcher := &mockAccountFetcher{} 449 assert.NoError(t, cfg.MarshalAccountDefaults()) 450 451 testCases := []struct { 452 name string 453 givenMetric string 454 givenAccountID string 455 givenConfig []byte 456 expectedMetricCount int 457 }{ 458 { 459 name: "Purpose1MetricCalled", 460 givenAccountID: "gdpr_deprecated_purpose1", 461 expectedMetricCount: 1, 462 }, 463 { 464 name: "Purpose2MetricCalled", 465 givenAccountID: "gdpr_deprecated_purpose2", 466 expectedMetricCount: 1, 467 }, 468 { 469 name: "Purpose3MetricCalled", 470 givenAccountID: "gdpr_deprecated_purpose3", 471 expectedMetricCount: 1, 472 }, 473 { 474 name: "Purpose4MetricCalled", 475 givenAccountID: "gdpr_deprecated_purpose4", 476 expectedMetricCount: 1, 477 }, 478 { 479 name: "Purpose5MetricCalled", 480 givenAccountID: "gdpr_deprecated_purpose5", 481 expectedMetricCount: 1, 482 }, 483 { 484 name: "Purpose6MetricCalled", 485 givenAccountID: "gdpr_deprecated_purpose6", 486 expectedMetricCount: 1, 487 }, 488 { 489 name: "Purpose7MetricCalled", 490 givenAccountID: "gdpr_deprecated_purpose7", 491 expectedMetricCount: 1, 492 }, 493 { 494 name: "Purpose8MetricCalled", 495 givenAccountID: "gdpr_deprecated_purpose8", 496 expectedMetricCount: 1, 497 }, 498 { 499 name: "Purpose9MetricCalled", 500 givenAccountID: "gdpr_deprecated_purpose9", 501 expectedMetricCount: 1, 502 }, 503 { 504 name: "Purpose10MetricCalled", 505 givenAccountID: "gdpr_deprecated_purpose10", 506 expectedMetricCount: 1, 507 }, 508 { 509 name: "PurposeMetricNotCalled", 510 givenAccountID: "valid_acct", 511 expectedMetricCount: 0, 512 }, 513 } 514 515 for _, test := range testCases { 516 t.Run(test.name, func(t *testing.T) { 517 metrics := &metrics.MetricsEngineMock{} 518 metrics.Mock.On("RecordAccountGDPRPurposeWarning", mock.Anything, mock.Anything).Return() 519 metrics.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return() 520 521 _, _ = GetAccount(context.Background(), cfg, fetcher, test.givenAccountID, metrics) 522 523 metrics.AssertNumberOfCalls(t, "RecordAccountGDPRPurposeWarning", test.expectedMetricCount) 524 }) 525 526 } 527 } 528 529 func TestAccountUpgradeStatusGetAccount(t *testing.T) { 530 cfg := &config.Configuration{} 531 fetcher := &mockAccountFetcher{} 532 assert.NoError(t, cfg.MarshalAccountDefaults()) 533 534 testCases := []struct { 535 name string 536 givenAccountIDs []string 537 givenMetrics []string 538 expectedMetricCount int 539 }{ 540 { 541 name: "MultipleDeprecatedConfigs", 542 givenAccountIDs: []string{"gdpr_channel_enabled_deprecated_purpose_acct"}, 543 givenMetrics: []string{"RecordAccountGDPRChannelEnabledWarning", "RecordAccountGDPRPurposeWarning"}, 544 expectedMetricCount: 1, 545 }, 546 { 547 name: "ZeroDeprecatedConfigs", 548 givenAccountIDs: []string{"valid_acct"}, 549 givenMetrics: []string{}, 550 expectedMetricCount: 0, 551 }, 552 { 553 name: "OneDeprecatedConfigPurpose", 554 givenAccountIDs: []string{"gdpr_deprecated_purpose1"}, 555 givenMetrics: []string{"RecordAccountGDPRPurposeWarning"}, 556 expectedMetricCount: 1, 557 }, 558 { 559 name: "OneDeprecatedConfigGDPRChannelEnabled", 560 givenAccountIDs: []string{"gdpr_channel_enabled_acct"}, 561 givenMetrics: []string{"RecordAccountGDPRChannelEnabledWarning"}, 562 expectedMetricCount: 1, 563 }, 564 { 565 name: "OneDeprecatedConfigCCPAChannelEnabled", 566 givenAccountIDs: []string{"ccpa_channel_enabled_acct"}, 567 givenMetrics: []string{"RecordAccountCCPAChannelEnabledWarning"}, 568 expectedMetricCount: 1, 569 }, 570 { 571 name: "MultipleAccountsWithDeprecatedConfigs", 572 givenAccountIDs: []string{"gdpr_channel_enabled_acct", "gdpr_deprecated_purpose1"}, 573 givenMetrics: []string{"RecordAccountGDPRChannelEnabledWarning", "RecordAccountGDPRPurposeWarning"}, 574 expectedMetricCount: 2, 575 }, 576 } 577 578 for _, test := range testCases { 579 t.Run(test.name, func(t *testing.T) { 580 metrics := &metrics.MetricsEngineMock{} 581 for _, metric := range test.givenMetrics { 582 metrics.Mock.On(metric, mock.Anything, mock.Anything).Return() 583 } 584 metrics.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return() 585 586 for _, accountID := range test.givenAccountIDs { 587 _, _ = GetAccount(context.Background(), cfg, fetcher, accountID, metrics) 588 } 589 metrics.AssertNumberOfCalls(t, "RecordAccountUpgradeStatus", test.expectedMetricCount) 590 }) 591 } 592 } 593 594 func TestDeprecateEventsEnabledField(t *testing.T) { 595 testCases := []struct { 596 name string 597 account *config.Account 598 want *bool 599 }{ 600 { 601 name: "account is nil", 602 account: nil, 603 want: nil, 604 }, 605 { 606 name: "account.EventsEnabled is nil, account.Events.Enabled is nil", 607 account: &config.Account{ 608 EventsEnabled: nil, 609 Events: config.Events{ 610 Enabled: nil, 611 }, 612 }, 613 want: nil, 614 }, 615 { 616 name: "account.EventsEnabled is nil, account.Events.Enabled is non-nil", 617 account: &config.Account{ 618 EventsEnabled: nil, 619 Events: config.Events{ 620 Enabled: ptrutil.ToPtr(true), 621 }, 622 }, 623 want: ptrutil.ToPtr(true), 624 }, 625 { 626 name: "account.EventsEnabled is non-nil, account.Events.Enabled is nil", 627 account: &config.Account{ 628 EventsEnabled: ptrutil.ToPtr(true), 629 Events: config.Events{ 630 Enabled: nil, 631 }, 632 }, 633 want: ptrutil.ToPtr(true), 634 }, 635 { 636 name: "account.EventsEnabled is non-nil, account.Events.Enabled is non-nil", 637 account: &config.Account{ 638 EventsEnabled: ptrutil.ToPtr(false), 639 Events: config.Events{ 640 Enabled: ptrutil.ToPtr(true), 641 }, 642 }, 643 want: ptrutil.ToPtr(true), 644 }, 645 } 646 647 for _, test := range testCases { 648 t.Run(test.name, func(t *testing.T) { 649 deprecateEventsEnabledField(test.account) 650 if test.account != nil { 651 assert.Equal(t, test.want, test.account.Events.Enabled) 652 } 653 }) 654 } 655 }