How to use name method of dummy Package

Best Testkube code snippet using dummy.name

access_test.go

Source:access_test.go Github

copy

Full Screen

...36 cfg config.AccessToken37 token ntokend.TokenProvider38 }39 type test struct {40 name string41 args args42 checkFunc func(got, want AccessService) error43 want AccessService44 wantErr error45 }46 dummyTokenProvider := func() (string, error) { return "", nil }47 tests := []test{48 func() test {49 args := args{50 cfg: config.AccessToken{51 Enable: true,52 Expiry: "5s",53 AthenzURL: "dummy",54 PrincipalAuthHeader: "dummyAuthHeader",55 RefreshPeriod: "1s",56 },57 token: dummyTokenProvider,58 }59 return test{60 name: "NewAccessService return correct",61 args: args,62 checkFunc: func(got, want AccessService) error {63 gotS := got.(*accessService)64 wantS := want.(*accessService)65 if !reflect.DeepEqual(gotS.cfg, wantS.cfg) ||66 reflect.ValueOf(gotS.token).Pointer() != reflect.ValueOf(wantS.token).Pointer() ||67 !reflect.DeepEqual(gotS.athenzURL, wantS.athenzURL) ||68 !reflect.DeepEqual(gotS.athenzPrincipleHeader, wantS.athenzPrincipleHeader) ||69 //!reflect.DeepEqual(gotS.tokenCache, wantS.tokenCache) ||70 !reflect.DeepEqual(gotS.expiry, wantS.expiry) {71 return fmt.Errorf("got: %+v, want: %+v", got, want)72 }73 return nil74 },75 want: &accessService{76 cfg: args.cfg,77 token: args.token,78 athenzURL: args.cfg.AthenzURL,79 athenzPrincipleHeader: args.cfg.PrincipalAuthHeader,80 tokenCache: gache.New(),81 expiry: func() time.Duration {82 dur, _ := time.ParseDuration(args.cfg.Expiry)83 return dur84 }(),85 },86 }87 }(),88 func() test {89 args := args{90 cfg: config.AccessToken{91 Enable: true,92 AthenzURL: "dummy",93 PrincipalAuthHeader: "dummyAuthHeader",94 },95 token: dummyTokenProvider,96 }97 return test{98 name: "NewAccessService default values",99 args: args,100 checkFunc: func(got, want AccessService) error {101 gotS := got.(*accessService)102 wantS := want.(*accessService)103 if !reflect.DeepEqual(gotS.cfg, wantS.cfg) ||104 reflect.ValueOf(gotS.token).Pointer() != reflect.ValueOf(wantS.token).Pointer() ||105 !reflect.DeepEqual(gotS.athenzURL, wantS.athenzURL) ||106 !reflect.DeepEqual(gotS.athenzPrincipleHeader, wantS.athenzPrincipleHeader) ||107 //!reflect.DeepEqual(gotS.tokenCache, wantS.tokenCache) ||108 !reflect.DeepEqual(gotS.expiry, wantS.expiry) ||109 !reflect.DeepEqual(gotS.refreshPeriod, wantS.refreshPeriod) ||110 !reflect.DeepEqual(gotS.errRetryMaxCount, wantS.errRetryMaxCount) ||111 !reflect.DeepEqual(gotS.errRetryInterval, wantS.errRetryInterval) {112 return fmt.Errorf("got: %+v, want: %+v", got, want)113 }114 return nil115 },116 want: &accessService{117 cfg: args.cfg,118 token: args.token,119 athenzURL: args.cfg.AthenzURL,120 athenzPrincipleHeader: args.cfg.PrincipalAuthHeader,121 tokenCache: gache.New(),122 expiry: 0,123 errRetryInterval: defaultErrRetryInterval,124 errRetryMaxCount: defaultErrRetryMaxCount,125 refreshPeriod: defaultRefreshPeriod,126 },127 }128 }(),129 func() test {130 args := args{131 cfg: config.AccessToken{132 Enable: true,133 Expiry: "1x",134 },135 }136 return test{137 name: "NewAccessService return error with Expiry of invalid format",138 args: args,139 wantErr: errors.Wrap(ErrInvalidSetting, "Expiry: time: unknown unit x in duration 1x"),140 }141 }(),142 func() test {143 args := args{144 cfg: config.AccessToken{145 Enable: true,146 RefreshPeriod: "1x",147 },148 }149 return test{150 name: "NewAccessService return error with RefreshPeriod of invalid format",151 args: args,152 wantErr: errors.Wrap(ErrInvalidSetting, "RefreshPeriod: time: unknown unit x in duration 1x"),153 }154 }(),155 func() test {156 args := args{157 cfg: config.AccessToken{158 Enable: true,159 Retry: config.Retry{160 Delay: "1x",161 },162 },163 }164 return test{165 name: "NewAccessService return error with ErrRetryInterval of invalid format",166 args: args,167 wantErr: errors.Wrap(ErrInvalidSetting, "ErrRetryInterval: time: unknown unit x in duration 1x"),168 }169 }(),170 func() test {171 args := args{172 cfg: config.AccessToken{173 Enable: true,174 Retry: config.Retry{175 Attempts: -1,176 },177 },178 }179 return test{180 name: "NewAccessService return error with ErrRetryMaxCount < 0",181 args: args,182 wantErr: errors.Wrap(ErrInvalidSetting, "ErrRetryMaxCount < 0"),183 }184 }(),185 func() test {186 args := args{187 cfg: config.AccessToken{188 Enable: true,189 AthenzURL: "dummy",190 PrincipalAuthHeader: "dummyAuthHeader",191 RefreshPeriod: "60s",192 Expiry: "1s",193 },194 token: dummyTokenProvider,195 }196 return test{197 name: "NewAccessService return error when refresh period > token expiry",198 args: args,199 wantErr: errors.Wrap(ErrInvalidSetting, "refresh period > token expiry time"),200 }201 }(),202 func() test {203 cnt := 10204 args := args{205 cfg: config.AccessToken{206 Enable: true,207 AthenzURL: "dummy",208 PrincipalAuthHeader: "dummyAuthHeader",209 Retry: config.Retry{210 Attempts: cnt,211 },212 },213 token: dummyTokenProvider,214 }215 return test{216 name: "NewAccessService specific ErrRetryMaxCount",217 args: args,218 checkFunc: func(got, want AccessService) error {219 gotS := got.(*accessService)220 wantS := want.(*accessService)221 if !reflect.DeepEqual(gotS.cfg, wantS.cfg) ||222 reflect.ValueOf(gotS.token).Pointer() != reflect.ValueOf(wantS.token).Pointer() ||223 !reflect.DeepEqual(gotS.athenzURL, wantS.athenzURL) ||224 !reflect.DeepEqual(gotS.athenzPrincipleHeader, wantS.athenzPrincipleHeader) ||225 //!reflect.DeepEqual(gotS.tokenCache, wantS.tokenCache) ||226 !reflect.DeepEqual(gotS.expiry, wantS.expiry) ||227 !reflect.DeepEqual(gotS.refreshPeriod, wantS.refreshPeriod) ||228 !reflect.DeepEqual(gotS.errRetryMaxCount, wantS.errRetryMaxCount) ||229 !reflect.DeepEqual(gotS.errRetryInterval, wantS.errRetryInterval) {230 return fmt.Errorf("got: %+v, want: %+v", got, want)231 }232 return nil233 },234 want: &accessService{235 cfg: args.cfg,236 token: args.token,237 athenzURL: args.cfg.AthenzURL,238 athenzPrincipleHeader: args.cfg.PrincipalAuthHeader,239 tokenCache: gache.New(),240 expiry: 0,241 errRetryInterval: defaultErrRetryInterval,242 errRetryMaxCount: cnt,243 refreshPeriod: defaultRefreshPeriod,244 },245 }246 }(),247 func() test {248 args := args{249 cfg: config.AccessToken{250 Enable: true,251 AthenzURL: "dummy",252 PrincipalAuthHeader: "dummyAuthHeader",253 AthenzCAPath: "../test/data/dummyCa.pem",254 },255 token: dummyTokenProvider,256 }257 cp, err := NewX509CertPool(args.cfg.AthenzCAPath)258 if err != nil {259 panic(err)260 }261 return test{262 name: "NewAccessService contains valid Athenz rootCA",263 args: args,264 checkFunc: func(got, want AccessService) error {265 gotS := got.(*accessService)266 wantS := want.(*accessService)267 if !reflect.DeepEqual(gotS.cfg, wantS.cfg) ||268 reflect.ValueOf(gotS.token).Pointer() != reflect.ValueOf(wantS.token).Pointer() ||269 !reflect.DeepEqual(gotS.athenzURL, wantS.athenzURL) ||270 !reflect.DeepEqual(gotS.athenzPrincipleHeader, wantS.athenzPrincipleHeader) ||271 //!reflect.DeepEqual(gotS.tokenCache, wantS.tokenCache) ||272 !reflect.DeepEqual(gotS.expiry, wantS.expiry) ||273 !reflect.DeepEqual(gotS.rootCAs, wantS.rootCAs) ||274 !reflect.DeepEqual(gotS.refreshPeriod, wantS.refreshPeriod) ||275 !reflect.DeepEqual(gotS.errRetryMaxCount, wantS.errRetryMaxCount) ||276 !reflect.DeepEqual(gotS.errRetryInterval, wantS.errRetryInterval) {277 return fmt.Errorf("got: %+v, want: %+v", got, want)278 }279 return nil280 },281 want: &accessService{282 cfg: args.cfg,283 token: args.token,284 athenzURL: args.cfg.AthenzURL,285 athenzPrincipleHeader: args.cfg.PrincipalAuthHeader,286 tokenCache: gache.New(),287 expiry: 0,288 rootCAs: cp,289 errRetryInterval: defaultErrRetryInterval,290 errRetryMaxCount: defaultErrRetryMaxCount,291 refreshPeriod: defaultRefreshPeriod,292 },293 }294 }(),295 func() test {296 args := args{297 cfg: config.AccessToken{298 Enable: true,299 AthenzURL: "dummy",300 PrincipalAuthHeader: "dummyAuthHeader",301 CertPath: "../test/data/dummyClient.crt",302 CertKeyPath: "../test/data/dummyClient.key",303 },304 token: nil,305 }306 return test{307 name: "NewAccessService contains valid client certificate",308 args: args,309 checkFunc: func(got, want AccessService) error {310 gotS := got.(*accessService)311 wantS := want.(*accessService)312 if !reflect.DeepEqual(gotS.cfg, wantS.cfg) ||313 // reflect.ValueOf(gotS.token).Pointer() != reflect.ValueOf(wantS.token).Pointer() ||314 !reflect.DeepEqual(gotS.athenzURL, wantS.athenzURL) ||315 !reflect.DeepEqual(gotS.athenzPrincipleHeader, wantS.athenzPrincipleHeader) ||316 //!reflect.DeepEqual(gotS.tokenCache, wantS.tokenCache) ||317 !reflect.DeepEqual(gotS.expiry, wantS.expiry) ||318 !reflect.DeepEqual(gotS.certPath, wantS.certPath) ||319 !reflect.DeepEqual(gotS.certKeyPath, wantS.certKeyPath) ||320 !reflect.DeepEqual(gotS.refreshPeriod, wantS.refreshPeriod) ||321 !reflect.DeepEqual(gotS.errRetryMaxCount, wantS.errRetryMaxCount) ||322 !reflect.DeepEqual(gotS.errRetryInterval, wantS.errRetryInterval) {323 return fmt.Errorf("got: %+v, want: %+v", got, want)324 }325 return nil326 },327 want: &accessService{328 cfg: args.cfg,329 token: args.token,330 athenzURL: args.cfg.AthenzURL,331 athenzPrincipleHeader: args.cfg.PrincipalAuthHeader,332 tokenCache: gache.New(),333 expiry: 0,334 certPath: "../test/data/dummyClient.crt",335 certKeyPath: "../test/data/dummyClient.key",336 errRetryInterval: defaultErrRetryInterval,337 errRetryMaxCount: defaultErrRetryMaxCount,338 refreshPeriod: defaultRefreshPeriod,339 },340 }341 }(),342 func() test {343 args := args{344 cfg: config.AccessToken{345 Enable: true,346 AthenzURL: "dummy",347 PrincipalAuthHeader: "dummyAuthHeader",348 CertPath: "../test/data/dummyClient.crt",349 CertKeyPath: "../test/data/dummyClient.key",350 },351 token: dummyTokenProvider,352 }353 return test{354 name: "NewAccessService that ntokend takes priority over client certificate",355 args: args,356 checkFunc: func(got, want AccessService) error {357 gotS := got.(*accessService)358 wantS := want.(*accessService)359 if !reflect.DeepEqual(gotS.cfg, wantS.cfg) ||360 // reflect.ValueOf(gotS.token).Pointer() != reflect.ValueOf(wantS.token).Pointer() ||361 !reflect.DeepEqual(gotS.athenzURL, wantS.athenzURL) ||362 !reflect.DeepEqual(gotS.athenzPrincipleHeader, wantS.athenzPrincipleHeader) ||363 //!reflect.DeepEqual(gotS.tokenCache, wantS.tokenCache) ||364 !reflect.DeepEqual(gotS.expiry, wantS.expiry) ||365 !reflect.DeepEqual(gotS.certPath, wantS.certPath) ||366 !reflect.DeepEqual(gotS.certKeyPath, wantS.certKeyPath) ||367 !reflect.DeepEqual(gotS.refreshPeriod, wantS.refreshPeriod) ||368 !reflect.DeepEqual(gotS.errRetryMaxCount, wantS.errRetryMaxCount) ||369 !reflect.DeepEqual(gotS.errRetryInterval, wantS.errRetryInterval) {370 return fmt.Errorf("got: %+v, want: %+v", got, want)371 }372 // check client certificate in TLS client config373 gotClient := gotS.httpClient.Load().(*http.Client)374 if gotClient.Transport.(*http.Transport).TLSClientConfig.Certificates != nil {375 return errors.New("Unexpected client certificate is set.")376 }377 return nil378 },379 want: &accessService{380 cfg: args.cfg,381 token: args.token,382 athenzURL: args.cfg.AthenzURL,383 athenzPrincipleHeader: args.cfg.PrincipalAuthHeader,384 tokenCache: gache.New(),385 expiry: 0,386 certPath: "",387 certKeyPath: "",388 errRetryInterval: defaultErrRetryInterval,389 errRetryMaxCount: defaultErrRetryMaxCount,390 refreshPeriod: defaultRefreshPeriod,391 },392 }393 }(),394 {395 name: "NewAccessService disabled",396 args: args{397 cfg: config.AccessToken{},398 },399 wantErr: ErrDisabled,400 },401 {402 name: "NewAccessService no credentials",403 args: args{404 cfg: config.AccessToken{405 Enable: true,406 CertPath: "",407 },408 },409 wantErr: errors.Wrap(ErrInvalidSetting, "Neither NToken nor client certificate is set."),410 },411 {412 name: "NewAccessService with non-existing Athenz rootCA",413 args: args{414 cfg: config.AccessToken{415 Enable: true,416 AthenzCAPath: "../test/data/non_exist.pem",417 },418 token: dummyTokenProvider,419 },420 wantErr: errors.Wrap(ErrInvalidSetting, "Athenz CA not exist"),421 },422 {423 name: "NewAccessService with invalid Athenz rootCA",424 args: args{425 cfg: config.AccessToken{426 Enable: true,427 AthenzCAPath: "../test/data/invalid_dummyCa.pem",428 },429 token: dummyTokenProvider,430 },431 wantErr: errors.Wrap(ErrInvalidSetting, "Certification Failed"),432 },433 {434 name: "NewAccessService with non-existing client certificate",435 args: args{436 cfg: config.AccessToken{437 Enable: true,438 CertPath: "../test/data/non_exist.pem",439 },440 token: nil,441 },442 wantErr: errors.Wrap(ErrInvalidSetting, "client certificate not found"),443 },444 {445 name: "NewAccessService with non-existing client certificate key",446 args: args{447 cfg: config.AccessToken{448 Enable: true,449 CertPath: "../test/data/dummyClient.crt",450 CertKeyPath: "../test/data/non_exist.key",451 },452 token: nil,453 },454 wantErr: errors.Wrap(ErrInvalidSetting, "client certificate key not found"),455 },456 }457 for _, tt := range tests {458 t.Run(tt.name, func(t *testing.T) {459 got, err := NewAccessService(tt.args.cfg, tt.args.token)460 if tt.wantErr == nil && err != nil {461 t.Errorf("failed to instantiate, err: %v", err)462 return463 } else if tt.wantErr != nil {464 if tt.wantErr.Error() != err.Error() {465 t.Errorf("error not the same, want: %v, got: %v", tt.wantErr, err)466 }467 }468 if tt.checkFunc != nil {469 if err := tt.checkFunc(got, tt.want); err != nil {470 t.Errorf("NewAccessService() err: %v", err)471 }472 }473 })474 }475}476func Test_accessService_StartAccessUpdater(t *testing.T) {477 type fields struct {478 cfg config.AccessToken479 token ntokend.TokenProvider480 athenzURL string481 athenzPrincipleHeader string482 tokenCache gache.Gache483 expiry time.Duration484 httpClient atomic.Value485 refreshPeriod time.Duration486 errRetryMaxCount int487 errRetryInterval time.Duration488 }489 type args struct {490 ctx context.Context491 }492 type test struct {493 name string494 fields fields495 args args496 checkFunc func(AccessService, <-chan error) error497 afterFunc func() error498 }499 tests := []test{500 func() test {501 dummyExpTime := int64(1)502 dummyToken := "dummyToken"503 dummyAccessToken := &AccessTokenResponse{504 AccessToken: dummyToken,505 ExpiresIn: dummyExpTime,506 }507 tokenCache := gache.New()508 tokenCache.SetWithExpire("dummyDomain;dummyRole", &accessCacheData{509 token: dummyAccessToken,510 }, time.Minute)511 // create dummy server to mock the updateAccessToken512 dummyToken2 := `{"access_token":"dummyToken2","token_type":"Bearer","expires_in":1000,"scope":"dummyDomain2:dummyRole2"}"`513 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {514 fmt.Fprint(w, dummyToken2)515 })516 dummyServer := httptest.NewTLSServer(sampleHandler)517 var httpClient atomic.Value518 httpClient.Store(dummyServer.Client())519 return test{520 name: "StartAccessUpdater can update cache periodically",521 fields: fields{522 httpClient: httpClient,523 tokenCache: tokenCache,524 expiry: time.Second,525 athenzURL: dummyServer.URL,526 athenzPrincipleHeader: "Athenz-Principal",527 token: func() (string, error) {528 return "dummy N-token", nil529 },530 refreshPeriod: time.Second,531 errRetryMaxCount: 5,532 errRetryInterval: time.Second,533 },534 args: args{535 ctx: context.Background(),536 },537 checkFunc: func(as AccessService, errs <-chan error) error {538 accessTok1, ok := tokenCache.Get("dummyDomain;dummyRole")539 if !ok {540 return fmt.Errorf("cannot get first access token")541 }542 time.Sleep(time.Second * 2)543 accessTok2, ok := tokenCache.Get("dummyDomain;dummyRole")544 if !ok {545 return fmt.Errorf("cannot get second access token")546 }547 if reflect.DeepEqual(accessTok1, accessTok2) {548 return fmt.Errorf("Token did not updated, access token 1: %v, access token 2: %v", accessTok1, accessTok2)549 }550 return nil551 },552 afterFunc: func() error {553 dummyServer.Close()554 tokenCache.Stop()555 return nil556 },557 }558 }(),559 func() test {560 dummyTok := "newToken"561 i := 0562 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {563 if i < 3 {564 w.WriteHeader(http.StatusInternalServerError)565 } else {566 newToken := `{"access_token":"newToken","token_type":"Bearer","expires_in":1000,"scope":"dummyDomain:dummyRole"}"`567 fmt.Fprint(w, newToken)568 }569 i++570 })571 dummyServer := httptest.NewTLSServer(sampleHandler)572 tokenCache := gache.New()573 data := &accessCacheData{574 token: nil,575 domain: "dummyDomain",576 role: "dummyRole",577 proxyForPrincipal: "dummyProxy",578 expiresIn: 60,579 }580 tokenCache.SetWithExpire("dummyDomain;dummyRole;dummyProxy", data, time.Minute)581 ctx, cancel := context.WithCancel(context.Background())582 var httpClient atomic.Value583 httpClient.Store(dummyServer.Client())584 return test{585 name: "RefreshAccessTokenCache success with retry",586 fields: fields{587 httpClient: httpClient,588 tokenCache: tokenCache,589 token: func() (string, error) {590 return "dummyToken", nil591 },592 athenzURL: dummyServer.URL,593 athenzPrincipleHeader: "Athenz-Principal",594 errRetryMaxCount: 9,595 refreshPeriod: time.Millisecond * 100,596 errRetryInterval: time.Millisecond,597 expiry: time.Millisecond * 200,598 },599 args: args{600 ctx: ctx,601 },602 checkFunc: func(as AccessService, echan <-chan error) error {603 time.Sleep(time.Second)604 cancel()605 errs := make([]error, 0, 3)606 for err := range echan {607 if err != context.Canceled {608 errs = append(errs, err)609 }610 }611 // check the length612 if len(errs) != 3 {613 return errors.Errorf("len(err) = %v, errors: %v", len(errs), errs)614 }615 // check errors616 for _, err := range errs {617 if err.Error() != errors.Wrap(ErrAccessTokenRequestFailed, "error update access token").Error() {618 return errors.Errorf("Unexpected error: %v, want: %v", err, errors.Wrap(ErrAccessTokenRequestFailed, "error update access token"))619 }620 }621 tok, ok := tokenCache.Get("dummyDomain;dummyRole;dummyProxy")622 if !ok {623 return errors.New("token does not set to the cache")624 }625 if tok.(*accessCacheData).token.AccessToken != dummyTok {626 return errors.New("invalid token set on the cache")627 }628 return nil629 },630 }631 }(),632 func() test {633 dummyTok := "newToken"634 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {635 w.WriteHeader(http.StatusInternalServerError)636 })637 dummyServer := httptest.NewTLSServer(sampleHandler)638 tokenCache := gache.New()639 data := &accessCacheData{640 token: &AccessTokenResponse{641 AccessToken: dummyTok,642 ExpiresIn: int64(99999999),643 },644 domain: "dummyDomain",645 role: "dummyRole",646 proxyForPrincipal: "dummyProxy",647 expiresIn: 60,648 }649 tokenCache.SetWithExpire("dummyDomain;dummyRole;dummyProxy", data, time.Minute)650 ctx, cancel := context.WithCancel(context.Background())651 var httpClient atomic.Value652 httpClient.Store(dummyServer.Client())653 return test{654 name: "RefreshAccessTokenCache failed",655 fields: fields{656 httpClient: httpClient,657 tokenCache: tokenCache,658 token: func() (string, error) {659 return "dummyToken", nil660 },661 athenzURL: dummyServer.URL,662 athenzPrincipleHeader: "Athenz-Principal",663 errRetryMaxCount: 9,664 refreshPeriod: time.Millisecond * 700,665 errRetryInterval: time.Millisecond,666 expiry: time.Millisecond * 700,667 },668 args: args{669 ctx: ctx,670 },671 checkFunc: func(as AccessService, echan <-chan error) error {672 time.Sleep(time.Second)673 cancel()674 errs := make([]error, 0, 10)675 for err := range echan {676 if err != context.Canceled {677 errs = append(errs, err)678 }679 }680 // check the length681 if len(errs) != 10 {682 return errors.Errorf("len(err) = %v, errors: %v", len(errs), errs)683 }684 // check errors685 for _, err := range errs {686 if err.Error() != errors.Wrap(ErrAccessTokenRequestFailed, "error update access token").Error() {687 return errors.Errorf("Unexpected error: %v, want: %v", err, errors.Wrap(ErrAccessTokenRequestFailed, "error update access token"))688 }689 }690 // the cache will not be deleted even the fetch is failed691 tok, ok := tokenCache.Get("dummyDomain;dummyRole;dummyProxy")692 if !ok {693 return errors.New("token does not set to the cache")694 }695 if tok.(*accessCacheData).token.AccessToken != dummyTok {696 return errors.New("invalid token set on the cache")697 }698 return nil699 },700 }701 }(),702 }703 for _, tt := range tests {704 t.Run(tt.name, func(t *testing.T) {705 if tt.afterFunc != nil {706 defer func() {707 err := tt.afterFunc()708 if err != nil {709 t.Errorf("Failed afterFunc %v", err)710 }711 }()712 }713 r := &accessService{714 cfg: tt.fields.cfg,715 token: tt.fields.token,716 athenzURL: tt.fields.athenzURL,717 athenzPrincipleHeader: tt.fields.athenzPrincipleHeader,718 tokenCache: tt.fields.tokenCache,719 expiry: tt.fields.expiry,720 httpClient: tt.fields.httpClient,721 refreshPeriod: tt.fields.refreshPeriod,722 errRetryMaxCount: tt.fields.errRetryMaxCount,723 errRetryInterval: tt.fields.errRetryInterval,724 }725 got := r.StartAccessUpdater(tt.args.ctx)726 if err := tt.checkFunc(r, got); err != nil {727 t.Errorf("accessService.StartAccessUpdater(), error: %v", err)728 }729 })730 }731}732func Test_accessService_GetAccessProvider(t *testing.T) {733 tests := []struct {734 name string735 }{736 {737 name: "provider exactly return",738 },739 }740 for _, tt := range tests {741 t.Run(tt.name, func(t *testing.T) {742 token := func() (string, error) { return "", nil }743 r, err := NewAccessService(config.AccessToken{744 Enable: true,745 }, token)746 if err != nil {747 t.Error(err.Error())748 return749 }750 if got := r.GetAccessProvider(); got == nil {751 t.Error("provider is nil")752 return753 }754 })755 }756}757func Test_accessService_getAccessToken(t *testing.T) {758 type fields struct {759 cfg config.AccessToken760 token ntokend.TokenProvider761 athenzURL string762 athenzPrincipleHeader string763 tokenCache gache.Gache764 expiry time.Duration765 httpClient atomic.Value766 }767 type args struct {768 ctx context.Context769 domain string770 role string771 proxyForPrincipal string772 expiresIn int64773 }774 type test struct {775 name string776 fields fields777 args args778 afterFunc func() error779 want *AccessTokenResponse780 wantErr error781 }782 tests := []test{783 func() test {784 dummyTok := "dummyToken"785 dummyExpTime := int64(999999999)786 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime)787 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {788 fmt.Fprint(w, dummyToken)789 })790 dummyServer := httptest.NewTLSServer(sampleHandler)791 var httpClient atomic.Value792 httpClient.Store(dummyServer.Client())793 return test{794 name: "getAccessToken returns correct",795 fields: fields{796 httpClient: httpClient,797 tokenCache: gache.New(),798 token: func() (string, error) {799 return dummyToken, nil800 },801 athenzURL: dummyServer.URL,802 athenzPrincipleHeader: "Athenz-Principal",803 },804 args: args{805 ctx: context.Background(),806 domain: "dummyDomain",807 role: "dummyRole",808 proxyForPrincipal: "dummyProxy",809 expiresIn: 1,810 },811 afterFunc: func() error {812 dummyServer.Close()813 return nil814 },815 want: &AccessTokenResponse{816 AccessToken: dummyTok,817 TokenType: "Bearer",818 Scope: "dummyDomain:dummyRole",819 ExpiresIn: dummyExpTime,820 },821 }822 }(),823 func() test {824 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {825 w.WriteHeader(http.StatusInternalServerError)826 })827 dummyServer := httptest.NewTLSServer(sampleHandler)828 var httpClient atomic.Value829 httpClient.Store(dummyServer.Client())830 return test{831 name: "getAccessToken returns error",832 fields: fields{833 httpClient: httpClient,834 tokenCache: gache.New(),835 token: func() (string, error) {836 return "", nil837 },838 athenzURL: dummyServer.URL,839 athenzPrincipleHeader: "Athenz-Principal",840 },841 args: args{842 ctx: context.Background(),843 domain: "dummyDomain",844 role: "dummyRole",845 proxyForPrincipal: "dummyProxy",846 expiresIn: 1,847 },848 afterFunc: func() error {849 dummyServer.Close()850 return nil851 },852 wantErr: ErrAccessTokenRequestFailed,853 }854 }(),855 func() test {856 dummyTok := "dummyToken"857 dummyExpTime := int64(999999999)858 dummyAccessToken := &AccessTokenResponse{859 AccessToken: dummyTok,860 TokenType: "Bearer",861 Scope: "dummyDomain:dummyRole",862 ExpiresIn: dummyExpTime,863 }864 gac := gache.New()865 gac.Set("dummyDomain;dummyRole;dummyProxy", &accessCacheData{866 token: dummyAccessToken,867 })868 return test{869 name: "getAccessToken return from cache",870 fields: fields{871 tokenCache: gac,872 },873 args: args{874 ctx: context.Background(),875 domain: "dummyDomain",876 role: "dummyRole",877 proxyForPrincipal: "dummyProxy",878 },879 want: dummyAccessToken,880 }881 }(),882 }883 for _, tt := range tests {884 t.Run(tt.name, func(t *testing.T) {885 if tt.afterFunc != nil {886 defer func() {887 err := tt.afterFunc()888 if err != nil {889 t.Errorf("Failed afterFunc %v", err)890 }891 }()892 }893 a := &accessService{894 cfg: tt.fields.cfg,895 token: tt.fields.token,896 athenzURL: tt.fields.athenzURL,897 athenzPrincipleHeader: tt.fields.athenzPrincipleHeader,898 tokenCache: tt.fields.tokenCache,899 expiry: tt.fields.expiry,900 httpClient: tt.fields.httpClient,901 }902 got, err := a.getAccessToken(tt.args.ctx, tt.args.domain, tt.args.role, tt.args.proxyForPrincipal, tt.args.expiresIn)903 if tt.wantErr == nil && err != nil {904 t.Errorf("failed to instantiate, err: %v", err)905 return906 } else if tt.wantErr != nil {907 if tt.wantErr.Error() != err.Error() {908 t.Errorf("error not the same, want: %v, got: %v", tt.wantErr, err)909 }910 }911 if !reflect.DeepEqual(got, tt.want) {912 t.Errorf("accessService.getAccessToken() = %v, want %v", got, tt.want)913 }914 })915 }916}917func Test_accessService_RefreshAccessTokenCache(t *testing.T) {918 type fields struct {919 cfg config.AccessToken920 token ntokend.TokenProvider921 athenzURL string922 athenzPrincipleHeader string923 tokenCache gache.Gache924 expiry time.Duration925 httpClient atomic.Value926 refreshPeriod time.Duration927 errRetryMaxCount int928 errRetryInterval time.Duration929 }930 type args struct {931 ctx context.Context932 }933 type test struct {934 name string935 fields fields936 args args937 checkFunc func(<-chan error) error938 afterFunc func() error939 }940 tests := []test{941 func() test {942 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {943 newToken := `{"access_token":"newToken","token_type":"Bearer","expires_in":99999,"scope":"dummyDomain:dummyRole"}"`944 fmt.Fprint(w, newToken)945 })946 dummyServer := httptest.NewTLSServer(sampleHandler)947 tokenCache := gache.New()948 data := &accessCacheData{949 token: nil,950 domain: "dummyDomain",951 role: "dummyRole",952 proxyForPrincipal: "",953 expiresIn: 60,954 }955 tokenCache.SetWithExpire("dummyDomain;dummyRole", data, time.Minute)956 var httpClient atomic.Value957 httpClient.Store(dummyServer.Client())958 return test{959 name: "Refresh access token cache success",960 fields: fields{961 token: func() (string, error) {962 return "dummyNToken", nil963 },964 athenzURL: dummyServer.URL,965 athenzPrincipleHeader: "dummy",966 tokenCache: tokenCache,967 expiry: time.Minute,968 httpClient: httpClient,969 refreshPeriod: time.Second,970 errRetryMaxCount: 5,971 errRetryInterval: time.Second,972 },973 args: args{974 ctx: context.Background(),975 },976 checkFunc: func(errChan <-chan error) error {977 for e := range errChan {978 return e979 }980 newCache, ok := tokenCache.Get("dummyDomain;dummyRole")981 if !ok {982 return errors.New("cannot get new token")983 }984 tok := newCache.(*accessCacheData).token985 if tok == nil {986 return errors.New("updated token is nil")987 }988 if tok.AccessToken != "newToken" {989 return errors.New("new token not updated")990 }991 return nil992 },993 }994 }(),995 func() test {996 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {997 newToken := `{"access_token":"newToken","token_type":"Bearer","expires_in":99999,"scope":"dummyDomain:dummyRole"}"`998 fmt.Fprint(w, newToken)999 })1000 dummyServer := httptest.NewTLSServer(sampleHandler)1001 tokenCache := gache.New()1002 data := &accessCacheData{1003 token: nil,1004 domain: "dummyDomain",1005 role: "dummyRole",1006 proxyForPrincipal: "",1007 expiresIn: 60,1008 }1009 tokenCache.SetWithExpire("dummyDomain;dummyRole", data, time.Minute)1010 data1 := &accessCacheData{1011 token: nil,1012 domain: "dummyDomain1",1013 role: "dummyRole1",1014 proxyForPrincipal: "",1015 expiresIn: 60,1016 }1017 tokenCache.SetWithExpire("dummyDomain1;dummyRole1", data1, time.Minute)1018 var httpClient atomic.Value1019 httpClient.Store(dummyServer.Client())1020 return test{1021 name: "Refresh multiple access token cache success",1022 fields: fields{1023 token: func() (string, error) {1024 return "dummyNToken", nil1025 },1026 athenzURL: dummyServer.URL,1027 athenzPrincipleHeader: "dummy",1028 tokenCache: tokenCache,1029 expiry: time.Minute,1030 httpClient: httpClient,1031 refreshPeriod: time.Second,1032 errRetryMaxCount: 5,1033 errRetryInterval: time.Second,1034 },1035 args: args{1036 ctx: context.Background(),1037 },1038 checkFunc: func(errChan <-chan error) error {1039 for e := range errChan {1040 return e1041 }1042 checkCache := func(k string) error {1043 newCache, ok := tokenCache.Get(k)1044 if !ok {1045 return errors.New("cannot get new token")1046 }1047 tok := newCache.(*accessCacheData).token1048 if tok == nil {1049 return errors.New("updated token is nil")1050 }1051 if tok.AccessToken != "newToken" {1052 return errors.New("new token not updated")1053 }1054 return nil1055 }1056 if err := checkCache("dummyDomain;dummyRole"); err != nil {1057 return err1058 }1059 if err := checkCache("dummyDomain1;dummyRole1"); err != nil {1060 return err1061 }1062 return nil1063 },1064 }1065 }(),1066 func() test {1067 dummyTok := "newToken"1068 i := 01069 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1070 if i < 3 {1071 w.WriteHeader(http.StatusInternalServerError)1072 } else {1073 newToken := `{"access_token":"newToken","token_type":"Bearer","expires_in":99999,"scope":"dummyDomain:dummyRole"}"`1074 fmt.Fprint(w, newToken)1075 }1076 i++1077 })1078 dummyServer := httptest.NewTLSServer(sampleHandler)1079 tokenCache := gache.New()1080 data := &accessCacheData{1081 token: nil,1082 domain: "dummyDomain",1083 role: "dummyRole",1084 proxyForPrincipal: "dummyProxy",1085 expiresIn: 60,1086 }1087 tokenCache.SetWithExpire("dummyDomain;dummyRole;dummyProxy", data, time.Minute)1088 var httpClient atomic.Value1089 httpClient.Store(dummyServer.Client())1090 return test{1091 name: "RefreshAccessTokenCache success with retry",1092 fields: fields{1093 httpClient: httpClient,1094 tokenCache: tokenCache,1095 token: func() (string, error) {1096 return "dummyToken", nil1097 },1098 athenzURL: dummyServer.URL,1099 athenzPrincipleHeader: "Athenz-Principal",1100 errRetryMaxCount: 10,1101 },1102 args: args{1103 ctx: context.Background(),1104 },1105 checkFunc: func(echan <-chan error) error {1106 errs := make([]error, 0, 3)1107 for err := range echan {1108 errs = append(errs, err)1109 }1110 // check the length1111 if len(errs) != 3 {1112 return errors.Errorf("len(err) = %v, errors: %v", len(errs), errs)1113 }1114 // check errors1115 for _, err := range errs {1116 if err != ErrAccessTokenRequestFailed {1117 return errors.Errorf("Unexpected error: %v", err)1118 }1119 }1120 tok, ok := tokenCache.Get("dummyDomain;dummyRole;dummyProxy")1121 if !ok {1122 return errors.New("token does not set to the cache")1123 }1124 if tok.(*accessCacheData).token.AccessToken != dummyTok {1125 return errors.New("invalid token set on the cache")1126 }1127 return nil1128 },1129 afterFunc: func() error {1130 dummyServer.Close()1131 return nil1132 },1133 }1134 }(),1135 func() test {1136 dummyTok := "newToken"1137 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1138 w.WriteHeader(http.StatusInternalServerError)1139 })1140 dummyServer := httptest.NewTLSServer(sampleHandler)1141 tokenCache := gache.New()1142 data := &accessCacheData{1143 token: &AccessTokenResponse{1144 AccessToken: dummyTok,1145 TokenType: "Bearer",1146 Scope: "dummyDomain:dummyRole",1147 ExpiresIn: int64(99999999),1148 },1149 domain: "dummyDomain",1150 role: "dummyRole",1151 proxyForPrincipal: "dummyProxy",1152 expiresIn: 60,1153 }1154 tokenCache.SetWithExpire("dummyDomain;dummyRole;dummyProxy", data, time.Minute)1155 var httpClient atomic.Value1156 httpClient.Store(dummyServer.Client())1157 return test{1158 name: "RefreshAccessTokenCache failed",1159 fields: fields{1160 httpClient: httpClient,1161 tokenCache: tokenCache,1162 token: func() (string, error) {1163 return "dummyToken", nil1164 },1165 athenzURL: dummyServer.URL,1166 athenzPrincipleHeader: "Athenz-Principal",1167 errRetryMaxCount: 9,1168 },1169 args: args{1170 ctx: context.Background(),1171 },1172 checkFunc: func(echan <-chan error) error {1173 errs := make([]error, 0, 10)1174 for err := range echan {1175 errs = append(errs, err)1176 }1177 // check the length1178 if len(errs) != 10 {1179 return errors.Errorf("len(err) = %v, errors: %v", len(errs), errs)1180 }1181 // check errors1182 for _, err := range errs {1183 if err != ErrAccessTokenRequestFailed {1184 return errors.Errorf("Unexpected error: %v", err)1185 }1186 }1187 // the cache will not be deleted even the fetch is failed1188 tok, ok := tokenCache.Get("dummyDomain;dummyRole;dummyProxy")1189 if !ok {1190 return errors.New("token does not set to the cache")1191 }1192 if tok.(*accessCacheData).token.AccessToken != dummyTok {1193 return errors.New("invalid token set on the cache")1194 }1195 return nil1196 },1197 afterFunc: func() error {1198 dummyServer.Close()1199 return nil1200 },1201 }1202 }(),1203 }1204 for _, tt := range tests {1205 t.Run(tt.name, func(t *testing.T) {1206 if tt.afterFunc != nil {1207 defer func() {1208 err := tt.afterFunc()1209 if err != nil {1210 t.Errorf("Failed afterFunc %v", err)1211 }1212 }()1213 }1214 a := &accessService{1215 cfg: tt.fields.cfg,1216 token: tt.fields.token,1217 athenzURL: tt.fields.athenzURL,1218 athenzPrincipleHeader: tt.fields.athenzPrincipleHeader,1219 tokenCache: tt.fields.tokenCache,1220 expiry: tt.fields.expiry,1221 httpClient: tt.fields.httpClient,1222 refreshPeriod: tt.fields.refreshPeriod,1223 errRetryMaxCount: tt.fields.errRetryMaxCount,1224 errRetryInterval: tt.fields.errRetryInterval,1225 }1226 got := a.RefreshAccessTokenCache(tt.args.ctx)1227 if err := tt.checkFunc(got); err != nil {1228 t.Errorf("accessService.RefreshAccessTokenCache() error: %s", err)1229 }1230 })1231 }1232}1233func Test_accessService_updateAccessTokenWithRetry(t *testing.T) {1234 type fields struct {1235 cfg config.AccessToken1236 token ntokend.TokenProvider1237 athenzURL string1238 athenzPrincipleHeader string1239 tokenCache gache.Gache1240 expiry time.Duration1241 httpClient atomic.Value1242 refreshPeriod time.Duration1243 errRetryMaxCount int1244 errRetryInterval time.Duration1245 }1246 type args struct {1247 ctx context.Context1248 domain string1249 role string1250 proxyForPrincipal string1251 expiresIn int641252 }1253 type test struct {1254 name string1255 fields fields1256 args args1257 checkFunc func(<-chan error) error1258 afterFunc func() error1259 }1260 tests := []test{1261 func() test {1262 dummyTok := "dummyToken"1263 dummyExpTime := int64(999999999)1264 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime)1265 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1266 fmt.Fprint(w, dummyToken)1267 })1268 dummyServer := httptest.NewTLSServer(sampleHandler)1269 tokenCache := gache.New()1270 var httpClient atomic.Value1271 httpClient.Store(dummyServer.Client())1272 return test{1273 name: "updateAccessTokenWithRetry success",1274 fields: fields{1275 httpClient: httpClient,1276 tokenCache: tokenCache,1277 token: func() (string, error) {1278 return dummyToken, nil1279 },1280 athenzURL: dummyServer.URL,1281 athenzPrincipleHeader: "Athenz-Principal",1282 errRetryMaxCount: 9,1283 },1284 args: args{1285 ctx: context.Background(),1286 domain: "dummyDomain",1287 role: "dummyRole",1288 proxyForPrincipal: "dummyProxy",1289 },1290 checkFunc: func(echan <-chan error) error {1291 for err := range echan {1292 return errors.Wrap(err, "Unexpected error occurred")1293 }1294 tok, ok := tokenCache.Get("dummyDomain;dummyRole;dummyProxy")1295 if !ok {1296 return errors.New("token is not set to the cache")1297 }1298 if tok.(*accessCacheData).token.AccessToken != dummyTok {1299 return errors.New("invalid token set on the cache")1300 }1301 return nil1302 },1303 afterFunc: func() error {1304 dummyServer.Close()1305 return nil1306 },1307 }1308 }(),1309 func() test {1310 dummyTok := "dummyToken"1311 dummyExpTime := int64(999999999)1312 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime)1313 i := 01314 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1315 if i < 3 {1316 w.WriteHeader(http.StatusInternalServerError)1317 } else {1318 fmt.Fprint(w, dummyToken)1319 }1320 i++1321 })1322 dummyServer := httptest.NewTLSServer(sampleHandler)1323 tokenCache := gache.New()1324 var httpClient atomic.Value1325 httpClient.Store(dummyServer.Client())1326 return test{1327 name: "updateAccessTokenWithRetry success with retry",1328 fields: fields{1329 httpClient: httpClient,1330 tokenCache: tokenCache,1331 token: func() (string, error) {1332 return dummyToken, nil1333 },1334 athenzURL: dummyServer.URL,1335 athenzPrincipleHeader: "Athenz-Principal",1336 errRetryMaxCount: 9,1337 },1338 args: args{1339 ctx: context.Background(),1340 domain: "dummyDomain",1341 role: "dummyRole",1342 proxyForPrincipal: "dummyProxy",1343 },1344 checkFunc: func(echan <-chan error) error {1345 errs := make([]error, 0, 3)1346 for err := range echan {1347 errs = append(errs, err)1348 }1349 // check the length1350 if len(errs) != 3 {1351 return errors.Errorf("len(err) = %v", len(errs))1352 }1353 // check errors1354 for _, err := range errs {1355 if err != ErrAccessTokenRequestFailed {1356 return errors.Errorf("Unexpected error: %v", err)1357 }1358 }1359 tok, ok := tokenCache.Get("dummyDomain;dummyRole;dummyProxy")1360 if !ok {1361 return errors.New("token is not set to the cache")1362 }1363 if tok.(*accessCacheData).token.AccessToken != dummyTok {1364 return errors.New("invalid token set on the cache")1365 }1366 return nil1367 },1368 afterFunc: func() error {1369 dummyServer.Close()1370 return nil1371 },1372 }1373 }(),1374 func() test {1375 dummyToken := "tok"1376 // create a dummy server that returns a dummy token1377 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1378 w.WriteHeader(http.StatusInternalServerError)1379 })1380 dummyServer := httptest.NewTLSServer(sampleHandler)1381 tokenCache := gache.New()1382 var httpClient atomic.Value1383 httpClient.Store(dummyServer.Client())1384 return test{1385 name: "updateAccessTokenWithRetry returns error",1386 fields: fields{1387 httpClient: httpClient,1388 tokenCache: tokenCache,1389 token: func() (string, error) {1390 return dummyToken, nil1391 },1392 athenzURL: dummyServer.URL,1393 athenzPrincipleHeader: "Athenz-Principal",1394 errRetryMaxCount: 9,1395 },1396 args: args{1397 ctx: context.Background(),1398 domain: "dummyDomain",1399 role: "dummyRole",1400 proxyForPrincipal: "dummyProxy",1401 },1402 checkFunc: func(echan <-chan error) error {1403 errs := make([]error, 0, 10)1404 for err := range echan {1405 errs = append(errs, err)1406 }1407 // check the length1408 if len(errs) != 10 {1409 return errors.Errorf("len(err) = %v", len(errs))1410 }1411 // check errors1412 for _, err := range errs {1413 if err != ErrAccessTokenRequestFailed {1414 return errors.Errorf("Unexpected error: %v", err)1415 }1416 }1417 return nil1418 },1419 afterFunc: func() error {1420 dummyServer.Close()1421 return nil1422 },1423 }1424 }(),1425 }1426 for _, tt := range tests {1427 t.Run(tt.name, func(t *testing.T) {1428 defer func() {1429 err := tt.afterFunc()1430 if err != nil {1431 t.Errorf("Failed afterFunc %v", err)1432 }1433 }()1434 a := &accessService{1435 cfg: tt.fields.cfg,1436 token: tt.fields.token,1437 athenzURL: tt.fields.athenzURL,1438 athenzPrincipleHeader: tt.fields.athenzPrincipleHeader,1439 tokenCache: tt.fields.tokenCache,1440 expiry: tt.fields.expiry,1441 httpClient: tt.fields.httpClient,1442 refreshPeriod: tt.fields.refreshPeriod,1443 errRetryMaxCount: tt.fields.errRetryMaxCount,1444 errRetryInterval: tt.fields.errRetryInterval,1445 }1446 got := a.updateAccessTokenWithRetry(tt.args.ctx, tt.args.domain, tt.args.role, tt.args.proxyForPrincipal, tt.args.expiresIn)1447 if err := tt.checkFunc(got); err != nil {1448 t.Errorf("accessService.updateAccessTokenWithRetry(). error: %v", err)1449 }1450 })1451 }1452}1453func Test_accessService_updateAccessToken(t *testing.T) {1454 type fields struct {1455 cfg config.AccessToken1456 token ntokend.TokenProvider1457 athenzURL string1458 athenzPrincipleHeader string1459 tokenCache gache.Gache1460 expiry time.Duration1461 httpClient atomic.Value1462 }1463 type args struct {1464 ctx context.Context1465 domain string1466 role string1467 proxyForPrincipal string1468 expiresIn int641469 }1470 type test struct {1471 name string1472 fields fields1473 args args1474 checkFunc func(got, want *AccessTokenResponse) error1475 afterFunc func() error1476 want *AccessTokenResponse1477 wantErr error1478 }1479 tests := []test{1480 func() test {1481 dummyTok := "dummyToken"1482 dummyExpTime := int64(999999999)1483 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime)1484 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1485 fmt.Fprint(w, dummyToken)1486 })1487 dummyServer := httptest.NewTLSServer(sampleHandler)1488 var httpClient atomic.Value1489 httpClient.Store(dummyServer.Client())1490 return test{1491 name: "updateAccessToken returns correct",1492 fields: fields{1493 httpClient: httpClient,1494 tokenCache: gache.New(),1495 token: func() (string, error) {1496 return dummyToken, nil1497 },1498 athenzURL: dummyServer.URL,1499 athenzPrincipleHeader: "Athenz-Principal",1500 },1501 args: args{1502 ctx: context.Background(),1503 domain: "dummyDomain",1504 role: "dummyRole",1505 proxyForPrincipal: "dummyProxy",1506 expiresIn: 1,1507 },1508 afterFunc: func() error {1509 dummyServer.Close()1510 return nil1511 },1512 want: &AccessTokenResponse{1513 AccessToken: dummyTok,1514 TokenType: "Bearer",1515 Scope: "dummyDomain:dummyRole",1516 ExpiresIn: dummyExpTime,1517 },1518 }1519 }(),1520 func() test {1521 dummyErr := fmt.Errorf("Dummy error")1522 return test{1523 name: "updateAccessToken token returns error",1524 fields: fields{1525 httpClient: atomic.Value{},1526 tokenCache: gache.New(),1527 token: func() (string, error) {1528 return "", dummyErr1529 },1530 athenzPrincipleHeader: "Athenz-Principal",1531 },1532 args: args{1533 ctx: context.Background(),1534 domain: "dummyDomain",1535 role: "dummyRole",1536 proxyForPrincipal: "dummyProxy",1537 expiresIn: 1,1538 },1539 wantErr: dummyErr,1540 }1541 }(),1542 func() test {1543 dummyTok := "dummyToken"1544 dummyExpTime := int64(999999999)1545 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime)1546 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1547 fmt.Fprint(w, dummyToken)1548 })1549 dummyServer := httptest.NewTLSServer(sampleHandler)1550 var httpClient atomic.Value1551 httpClient.Store(dummyServer.Client())1552 return test{1553 name: "updateAccessToken new request returns error",1554 fields: fields{1555 httpClient: httpClient,1556 tokenCache: gache.New(),1557 token: func() (string, error) {1558 return "", nil1559 },1560 athenzURL: "127.0.0.1:9876",1561 athenzPrincipleHeader: "Athenz-Principal",1562 },1563 args: args{1564 ctx: context.Background(),1565 domain: "dummyDomain",1566 role: "dummyRole",1567 proxyForPrincipal: "dummyProxy",1568 expiresIn: 1,1569 },1570 wantErr: fmt.Errorf(`Post "https://127.0.0.1:9876/oauth2/token": dial tcp 127.0.0.1:9876: connect: connection refused`),1571 }1572 }(),1573 func() test {1574 dummyTok := "dummyToken"1575 dummyExpTime := int64(999999999)1576 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime)1577 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1578 w.WriteHeader(http.StatusInternalServerError)1579 })1580 dummyServer := httptest.NewTLSServer(sampleHandler)1581 var httpClient atomic.Value1582 httpClient.Store(dummyServer.Client())1583 return test{1584 name: "updateAccessToken get token error",1585 fields: fields{1586 httpClient: httpClient,1587 tokenCache: gache.New(),1588 token: func() (string, error) {1589 return dummyToken, nil1590 },1591 athenzURL: dummyServer.URL,1592 athenzPrincipleHeader: "Athenz-Principal",1593 },1594 args: args{1595 ctx: context.Background(),1596 domain: "dummyDomain",1597 role: "dummyRole",1598 proxyForPrincipal: "dummyProxy",1599 expiresIn: 1,1600 },1601 afterFunc: func() error {1602 dummyServer.Close()1603 return nil1604 },1605 wantErr: ErrAccessTokenRequestFailed,1606 }1607 }(),1608 func() test {1609 dummyToken := "dummyToken"1610 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1611 fmt.Fprint(w, dummyToken)1612 })1613 dummyServer := httptest.NewTLSServer(sampleHandler)1614 var httpClient atomic.Value1615 httpClient.Store(dummyServer.Client())1616 return test{1617 name: "updateAccessToken decode token error",1618 fields: fields{1619 httpClient: httpClient,1620 tokenCache: gache.New(),1621 token: func() (string, error) {1622 return dummyToken, nil1623 },1624 athenzURL: dummyServer.URL,1625 athenzPrincipleHeader: "Athenz-Principal",1626 },1627 args: args{1628 ctx: context.Background(),1629 domain: "dummyDomain",1630 role: "dummyRole",1631 proxyForPrincipal: "dummyProxy",1632 expiresIn: 1,1633 },1634 afterFunc: func() error {1635 dummyServer.Close()1636 return nil1637 },1638 wantErr: fmt.Errorf("invalid character 'd' looking for beginning of value"),1639 }1640 }(),1641 func() test {1642 dummyTok := "dummyToken"1643 dummyExpTime := fastime.Now().Add(time.Hour).UTC()1644 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime.Unix())1645 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1646 fmt.Fprint(w, dummyToken)1647 })1648 dummyServer := httptest.NewTLSServer(sampleHandler)1649 tokenCache := gache.New()1650 var httpClient atomic.Value1651 httpClient.Store(dummyServer.Client())1652 return test{1653 name: "updateAccessToken set token in cache",1654 fields: fields{1655 httpClient: httpClient,1656 tokenCache: tokenCache,1657 token: func() (string, error) {1658 return dummyToken, nil1659 },1660 athenzURL: dummyServer.URL,1661 athenzPrincipleHeader: "Athenz-Principal",1662 },1663 args: args{1664 ctx: context.Background(),1665 domain: "dummyDomain",1666 role: "dummyRole",1667 proxyForPrincipal: "dummyProxy",1668 expiresIn: 1,1669 },1670 checkFunc: func(got, want *AccessTokenResponse) error {1671 c, exp, ok := tokenCache.GetWithExpire("dummyDomain;dummyRole;dummyProxy")1672 if !ok {1673 return fmt.Errorf("element cannot found in cache")1674 }1675 if c.(*accessCacheData).token.AccessToken != dummyTok {1676 return fmt.Errorf("token not matched, got: %v, want: %v", c, dummyTok)1677 }1678 if math.Abs(time.Unix(0, exp).Sub(dummyExpTime).Seconds()) > (time.Minute+(time.Second*3)).Seconds()*3 {1679 return errors.Errorf("cache expiry not match with policy expires, got: %d", exp)1680 }1681 return nil1682 },1683 afterFunc: func() error {1684 dummyServer.Close()1685 return nil1686 },1687 }1688 }(),1689 func() test {1690 dummyTok := "dummyToken"1691 dummyExpTime := fastime.Now().Add(time.Hour).UTC()1692 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime.Unix())1693 // create a dummy server that returns a dummy token1694 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1695 fmt.Fprint(w, dummyToken)1696 })1697 dummyServer := httptest.NewTLSServer(sampleHandler)1698 tokenCache := gache.New()1699 // set another dummy token and see if it is updated1700 dummyTok2 := "dummyToken2"1701 dummyToken2 := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok2, dummyExpTime.Unix())1702 dummyAccessToke2 := &AccessTokenResponse{1703 AccessToken: dummyToken2,1704 TokenType: "Bearer",1705 Scope: "dummyDomain:dummyRole",1706 ExpiresIn: dummyExpTime.UnixNano() / int64(time.Second),1707 }1708 tokenCache.SetWithExpire("dummyDomain;dummyRole", &accessCacheData{1709 token: dummyAccessToke2,1710 }, time.Second)1711 var httpClient atomic.Value1712 httpClient.Store(dummyServer.Client())1713 return test{1714 name: "updateAccessToken update token in cache",1715 fields: fields{1716 httpClient: httpClient,1717 tokenCache: tokenCache,1718 token: func() (string, error) {1719 return dummyToken, nil1720 },1721 athenzURL: dummyServer.URL,1722 athenzPrincipleHeader: "Athenz-Principal",1723 },1724 args: args{1725 ctx: context.Background(),1726 domain: "dummyDomain",1727 role: "dummyRole",1728 proxyForPrincipal: "dummyProxy",1729 expiresIn: 1,1730 },1731 checkFunc: func(got, want *AccessTokenResponse) error {1732 tok, exp, ok := tokenCache.GetWithExpire("dummyDomain;dummyRole;dummyProxy")1733 if !ok {1734 return fmt.Errorf("element cannot found in cache")1735 }1736 if tok.(*accessCacheData).token.AccessToken != dummyTok {1737 return fmt.Errorf("Token not updated")1738 }1739 if math.Abs(time.Unix(0, exp).Sub(dummyExpTime).Seconds()) > (time.Minute+(time.Second*3)).Seconds()*3 {1740 return errors.Errorf("cache expiry not match with policy expires, got: %d", exp)1741 }1742 return nil1743 },1744 afterFunc: func() error {1745 dummyServer.Close()1746 return nil1747 },1748 }1749 }(),1750 }1751 for _, tt := range tests {1752 if tt.afterFunc != nil {1753 defer func() {1754 err := tt.afterFunc()1755 if err != nil {1756 t.Errorf("Failed afterFunc %v", err)1757 }1758 }()1759 }1760 t.Run(tt.name, func(t *testing.T) {1761 a := &accessService{1762 cfg: tt.fields.cfg,1763 token: tt.fields.token,1764 athenzURL: tt.fields.athenzURL,1765 athenzPrincipleHeader: tt.fields.athenzPrincipleHeader,1766 tokenCache: tt.fields.tokenCache,1767 expiry: tt.fields.expiry,1768 httpClient: tt.fields.httpClient,1769 }1770 got, err := a.updateAccessToken(tt.args.ctx, tt.args.domain, tt.args.role, tt.args.proxyForPrincipal, tt.args.expiresIn)1771 if tt.wantErr == nil && err != nil {1772 t.Errorf("failed to instantiate, err: %v", err)1773 return1774 } else if tt.wantErr != nil {1775 if tt.wantErr.Error() != err.Error() {1776 t.Errorf("error not the same, want: %v, got: %v", tt.wantErr, err)1777 }1778 }1779 if tt.checkFunc != nil {1780 if err := tt.checkFunc(got, tt.want); err != nil {1781 t.Errorf("accessService.updateAccessToken() = %v", err)1782 }1783 } else {1784 if !reflect.DeepEqual(got, tt.want) {1785 t.Errorf("accessService.updateAccessToken() = %v, want %v", got, tt.want)1786 }1787 }1788 })1789 }1790}1791func Test_accessService_fetchAccessToken(t *testing.T) {1792 type fields struct {1793 cfg config.AccessToken1794 token ntokend.TokenProvider1795 athenzURL string1796 athenzPrincipleHeader string1797 tokenCache gache.Gache1798 expiry time.Duration1799 httpClient atomic.Value1800 rootCAs *x509.CertPool1801 certPath string1802 certKeyPath string1803 refreshPeriod time.Duration1804 errRetryMaxCount int1805 errRetryInterval time.Duration1806 }1807 type args struct {1808 ctx context.Context1809 domain string1810 role string1811 proxyForPrincipal string1812 expiry int641813 }1814 type test struct {1815 name string1816 fields fields1817 args args1818 want *AccessTokenResponse1819 wantErr error1820 afterFunc func() error1821 }1822 tests := []test{1823 {1824 name: "fetch access token error, invalid TLS",1825 fields: fields{1826 httpClient: atomic.Value{},1827 certPath: "../test/data/invalid_dummyServer.crt",1828 certKeyPath: "../test/data/invalid_dummyServer.key",1829 },1830 args: args{1831 ctx: context.Background(),1832 domain: "dummyDomain",1833 role: "dummyRole",1834 proxyForPrincipal: "dummyProxy",1835 expiry: 3600,1836 },1837 wantErr: errors.New("tls: failed to find any PEM data in certificate input"),1838 },1839 {1840 name: "fetch access token error, no credentials",1841 fields: fields{1842 httpClient: atomic.Value{},1843 },1844 args: args{1845 ctx: context.Background(),1846 domain: "dummyDomain",1847 role: "dummyRole",1848 proxyForPrincipal: "dummyProxy",1849 expiry: 3600,1850 },1851 wantErr: ErrNoCredentials,1852 },1853 func() test {1854 dummyTok := "dummyToken"1855 dummyExpTime := int64(999999999)1856 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime)1857 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1858 if r.TLS.PeerCertificates == nil || r.TLS.PeerCertificates[0].Subject.CommonName != "athenz.test.syncer" {1859 w.WriteHeader(http.StatusInternalServerError)1860 return1861 }1862 fmt.Fprint(w, dummyToken)1863 w.WriteHeader(http.StatusOK)1864 })1865 dummyServer := httptest.NewUnstartedServer(sampleHandler)1866 serverTLSCfg, err := NewTLSConfig(config.TLS{1867 CertPath: "../test/data/dummyServer.crt",1868 KeyPath: "../test/data/dummyServer.key",1869 CAPath: "../test/data/dummyClient.crt",1870 })1871 if err != nil {1872 panic(err)1873 }1874 clientCACp, err := NewX509CertPool("../test/data/dummyServer.crt")1875 if err != nil {1876 panic(err)1877 }1878 dummyServer.TLS = serverTLSCfg1879 dummyServer.StartTLS()1880 var httpClient atomic.Value1881 httpClient.Store(dummyServer.Client())1882 return test{1883 name: "fetch access token success with client certificate",1884 fields: fields{1885 athenzURL: dummyServer.URL,1886 athenzPrincipleHeader: "dummy-header",1887 httpClient: httpClient,1888 rootCAs: clientCACp,1889 certPath: "../test/data/dummyClient.crt",1890 certKeyPath: "../test/data/dummyClient.key",1891 },1892 args: args{1893 ctx: context.Background(),1894 domain: "dummyDomain",1895 role: "dummyRole",1896 proxyForPrincipal: "dummyProxy",1897 expiry: 3600,1898 },1899 want: &AccessTokenResponse{1900 AccessToken: dummyTok,1901 TokenType: "Bearer",1902 Scope: "dummyDomain:dummyRole",1903 ExpiresIn: dummyExpTime,1904 },1905 afterFunc: func() error {1906 dummyServer.Close()1907 return nil1908 },1909 }1910 }(),1911 func() test {1912 dummyTok := "dummyToken"1913 dummyExpTime := int64(999999999)1914 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime)1915 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1916 fmt.Fprint(w, dummyToken)1917 w.WriteHeader(http.StatusOK)1918 })1919 dummyServer := httptest.NewTLSServer(sampleHandler)1920 var httpClient atomic.Value1921 httpClient.Store(dummyServer.Client())1922 return test{1923 name: "fetch access token success with ntoken",1924 fields: fields{1925 token: func() (string, error) {1926 return "dummyNtoken", nil1927 },1928 athenzURL: dummyServer.URL,1929 athenzPrincipleHeader: "dummy-header",1930 httpClient: httpClient,1931 },1932 args: args{1933 ctx: context.Background(),1934 domain: "dummyDomain",1935 role: "dummyRole",1936 proxyForPrincipal: "dummyProxy",1937 expiry: 3600,1938 },1939 want: &AccessTokenResponse{1940 AccessToken: dummyTok,1941 TokenType: "Bearer",1942 Scope: "dummyDomain:dummyRole",1943 ExpiresIn: dummyExpTime,1944 },1945 afterFunc: func() error {1946 dummyServer.Close()1947 return nil1948 },1949 }1950 }(),1951 func() test {1952 dummyTok := "dummyToken"1953 dummyExpTime := int64(999999999)1954 dummyToken := fmt.Sprintf(`{"access_token":"%v","token_type":"Bearer","expires_in":%v,"scope":"dummyDomain:dummyRole"}"`, dummyTok, dummyExpTime)1955 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1956 fmt.Fprint(w, dummyToken)1957 w.WriteHeader(http.StatusOK)1958 })1959 dummyServer := httptest.NewTLSServer(sampleHandler)1960 dummyErr := errors.New("dummy error")1961 var httpClient atomic.Value1962 httpClient.Store(dummyServer.Client())1963 return test{1964 name: "N-token provider return error",1965 fields: fields{1966 token: func() (string, error) {1967 return "", dummyErr1968 },1969 athenzURL: dummyServer.URL,1970 athenzPrincipleHeader: "dummy-header",1971 httpClient: httpClient,1972 },1973 args: args{1974 ctx: context.Background(),1975 domain: "dummyDomain",1976 role: "dummyRole",1977 proxyForPrincipal: "dummyProxy",1978 expiry: 3600,1979 },1980 wantErr: dummyErr,1981 afterFunc: func() error {1982 dummyServer.Close()1983 return nil1984 },1985 }1986 }(),1987 func() test {1988 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {1989 w.WriteHeader(http.StatusInternalServerError)1990 })1991 dummyServer := httptest.NewTLSServer(sampleHandler)1992 var httpClient atomic.Value1993 httpClient.Store(dummyServer.Client())1994 return test{1995 name: "Athenz server return error",1996 fields: fields{1997 token: func() (string, error) {1998 return "dummyNToken", nil1999 },2000 athenzURL: dummyServer.URL,2001 athenzPrincipleHeader: "dummy-header",2002 httpClient: httpClient,2003 },2004 args: args{2005 ctx: context.Background(),2006 domain: "dummyDomain",2007 role: "dummyRole",2008 proxyForPrincipal: "dummyProxy",2009 expiry: 3600,2010 },2011 wantErr: ErrAccessTokenRequestFailed,2012 afterFunc: func() error {2013 dummyServer.Close()2014 return nil2015 },2016 }2017 }(),2018 func() test {2019 dummyTok := "dummyToken"2020 var sampleHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {2021 fmt.Fprint(w, dummyTok)2022 w.WriteHeader(http.StatusOK)2023 })2024 dummyServer := httptest.NewTLSServer(sampleHandler)2025 var httpClient atomic.Value2026 httpClient.Store(dummyServer.Client())2027 return test{2028 name: "Athenz server return invalid access token",2029 fields: fields{2030 token: func() (string, error) {2031 return "dummyNToken", nil2032 },2033 athenzURL: dummyServer.URL,2034 athenzPrincipleHeader: "dummy-header",2035 httpClient: httpClient,2036 },2037 args: args{2038 ctx: context.Background(),2039 domain: "dummyDomain",2040 role: "dummyRole",2041 proxyForPrincipal: "dummyProxy",2042 expiry: 3600,2043 },2044 wantErr: errors.New("invalid character 'd' looking for beginning of value"),2045 afterFunc: func() error {2046 dummyServer.Close()2047 return nil2048 },2049 }2050 }(),2051 }2052 for _, tt := range tests {2053 t.Run(tt.name, func(t *testing.T) {2054 if tt.afterFunc != nil {2055 defer func() {2056 err := tt.afterFunc()2057 if err != nil {2058 t.Errorf("Failed afterFunc %v", err)2059 }2060 }()2061 }2062 a := &accessService{2063 cfg: tt.fields.cfg,2064 token: tt.fields.token,2065 athenzURL: tt.fields.athenzURL,2066 athenzPrincipleHeader: tt.fields.athenzPrincipleHeader,2067 tokenCache: tt.fields.tokenCache,2068 expiry: tt.fields.expiry,2069 httpClient: tt.fields.httpClient,2070 rootCAs: tt.fields.rootCAs,2071 certPath: tt.fields.certPath,2072 certKeyPath: tt.fields.certKeyPath,2073 refreshPeriod: tt.fields.refreshPeriod,2074 errRetryMaxCount: tt.fields.errRetryMaxCount,2075 errRetryInterval: tt.fields.errRetryInterval,2076 }2077 got, err := a.fetchAccessToken(tt.args.ctx, tt.args.domain, tt.args.role, tt.args.proxyForPrincipal, tt.args.expiry)2078 if err != nil {2079 if tt.wantErr == nil {2080 t.Errorf("accessService.fetchAccessToken() error = %v, wantErr %v", err, tt.wantErr)2081 return2082 }2083 if err.Error() != tt.wantErr.Error() {2084 t.Errorf("accessService.fetchAccessToken() error = %v, wantErr %v", err, tt.wantErr)2085 return2086 }2087 } else if tt.wantErr != nil {2088 t.Errorf("accessService.fetchAccessToken() error = %v, wantErr %v", err, tt.wantErr)2089 return2090 }2091 if !reflect.DeepEqual(got, tt.want) {2092 t.Errorf("accessService.fetchAccessToken() = %v, want %v", got, tt.want)2093 }2094 })2095 }2096}2097func Test_createScope(t *testing.T) {2098 type args struct {2099 domain string2100 role string2101 }2102 tests := []struct {2103 name string2104 args args2105 want string2106 }{2107 {2108 name: "createScope single role",2109 args: args{2110 domain: "dummyDomain",2111 role: "dummyRole",2112 },2113 want: "dummyDomain:role.dummyRole",2114 },2115 {2116 name: "createScope multi role",2117 args: args{2118 domain: "dummyDomain",2119 role: "dummyRole1,dummyRole2",2120 },2121 want: "dummyDomain:role.dummyRole1 dummyDomain:role.dummyRole2",2122 },2123 {2124 name: "createScope empty role",2125 args: args{2126 domain: "dummyDomain",2127 },2128 want: "dummyDomain:domain",2129 },2130 {2131 name: "createScope empty domain",2132 args: args{2133 role: "dummyRole1,dummyRole2",2134 },2135 want: ":role.dummyRole1 :role.dummyRole2",2136 },2137 }2138 for _, tt := range tests {2139 t.Run(tt.name, func(t *testing.T) {2140 if got := createScope(tt.args.domain, tt.args.role); got != tt.want {2141 t.Errorf("createScope() = %v, want %v", got, tt.want)2142 }2143 })2144 }2145}2146func Test_accessService_getCache(t *testing.T) {2147 type fields struct {2148 cfg config.AccessToken2149 token ntokend.TokenProvider2150 athenzURL string2151 athenzPrincipleHeader string2152 tokenCache gache.Gache2153 expiry time.Duration2154 }2155 type args struct {2156 domain string2157 role string2158 principal string2159 }2160 type test struct {2161 name string2162 fields fields2163 args args2164 want *AccessTokenResponse2165 want1 bool2166 }2167 tests := []test{2168 func() test {2169 return test{2170 name: "getCache return not ok (cache not exist)",2171 fields: fields{2172 tokenCache: gache.New(),2173 },2174 args: args{2175 domain: "dummyDomain",2176 role: "dummyRole",2177 principal: "principal",2178 },2179 want: nil,2180 want1: false,2181 }2182 }(),2183 func() test {2184 dummyTok := "dummyToken"2185 dummyExpTime := int64(999999999)2186 dummyToken := fmt.Sprintf(`{"token":"%v", "expiryTime": %v}`, dummyTok, dummyExpTime)2187 accessToken := &AccessTokenResponse{2188 AccessToken: dummyToken,2189 TokenType: "Bearer",2190 Scope: "dummyDomain:dummyRole",2191 ExpiresIn: dummyExpTime,2192 }2193 tokenCache := gache.New()2194 tokenCache.Set("dummyDomain;dummyRole;principal", &accessCacheData{2195 token: accessToken,2196 })2197 return test{2198 name: "getCache return cache value",2199 fields: fields{2200 tokenCache: tokenCache,2201 },2202 args: args{2203 domain: "dummyDomain",2204 role: "dummyRole",2205 principal: "principal",2206 },2207 want: accessToken,2208 want1: true,2209 }2210 }(),2211 }2212 for _, tt := range tests {2213 t.Run(tt.name, func(t *testing.T) {2214 a := &accessService{2215 cfg: tt.fields.cfg,2216 token: tt.fields.token,2217 athenzURL: tt.fields.athenzURL,2218 athenzPrincipleHeader: tt.fields.athenzPrincipleHeader,2219 tokenCache: tt.fields.tokenCache,2220 expiry: tt.fields.expiry,2221 }2222 got, got1 := a.getCache(tt.args.domain, tt.args.role, tt.args.principal)2223 if !reflect.DeepEqual(got, tt.want) {2224 t.Errorf("accessService.getCache() got = %v, want %v", got, tt.want)2225 }2226 if got1 != tt.want1 {2227 t.Errorf("accessService.getCache() got1 = %v, want %v", got1, tt.want1)2228 }2229 })2230 }2231}2232func Test_accessService_createPostAccessTokenRequest(t *testing.T) {2233 type fields struct {2234 cfg config.AccessToken2235 token ntokend.TokenProvider2236 athenzURL string2237 athenzPrincipleHeader string2238 tokenCache gache.Gache2239 expiry time.Duration2240 }2241 type args struct {2242 scope string2243 proxyForPrincipal string2244 expiry int642245 token string2246 }2247 tests := []struct {2248 name string2249 fields fields2250 args args2251 want *http.Request2252 wantErr error2253 }{2254 {2255 name: "createPostAccessTokenRequest correct",2256 args: args{2257 scope: "dummyDomain:dummyRole",2258 proxyForPrincipal: "dummyProxyForPrincipal",2259 expiry: 1,2260 token: "dummyToken",2261 },2262 fields: fields{2263 athenzURL: "dummyAthenzURL",2264 athenzPrincipleHeader: "dummyHeader",2265 },2266 want: func() *http.Request {2267 q := url.Values{}2268 q.Add("grant_type", "client_credentials")2269 q.Add("scope", "dummyDomain:dummyRole")2270 q.Add("proxy_for_principal", "dummyProxyForPrincipal")2271 q.Add("expires_in", "1")2272 r, _ := http.NewRequest(http.MethodPost, "https://dummyAthenzURL/oauth2/token", strings.NewReader(q.Encode()))2273 r.Header.Set("Content-Type", "application/x-www-form-urlencoded")2274 return r2275 }(),2276 },2277 {2278 name: "createPostAccessTokenRequest correct null expiry use default",2279 args: args{2280 scope: "dummyDomain:dummyRole",2281 proxyForPrincipal: "dummyProxyForPrincipal",2282 token: "dummyToken",2283 },2284 fields: fields{2285 athenzURL: "dummyAthenzURL",2286 athenzPrincipleHeader: "dummyHeader",2287 expiry: time.Minute,2288 },2289 want: func() *http.Request {2290 q := url.Values{}2291 q.Add("grant_type", "client_credentials")2292 q.Add("scope", "dummyDomain:dummyRole")2293 q.Add("proxy_for_principal", "dummyProxyForPrincipal")2294 q.Add("expires_in", "60")2295 r, _ := http.NewRequest(http.MethodPost, "https://dummyAthenzURL/oauth2/token", strings.NewReader(q.Encode()))2296 r.Header.Set("Content-Type", "application/x-www-form-urlencoded")2297 return r2298 }(),2299 },2300 {2301 name: "createPostAccessTokenRequest correct null proxyForPrincipal",2302 args: args{2303 scope: "dummyDomain:dummyRole",2304 expiry: 1,2305 token: "dummyToken",2306 },2307 fields: fields{2308 athenzURL: "dummyAthenzURL",2309 athenzPrincipleHeader: "dummyHeader",2310 },2311 want: func() *http.Request {2312 q := url.Values{}2313 q.Add("grant_type", "client_credentials")2314 q.Add("scope", "dummyDomain:dummyRole")2315 q.Add("expires_in", "1")2316 r, _ := http.NewRequest(http.MethodPost, "https://dummyAthenzURL/oauth2/token", strings.NewReader(q.Encode()))2317 r.Header.Set("Content-Type", "application/x-www-form-urlencoded")2318 return r2319 }(),2320 },2321 }2322 for _, tt := range tests {2323 t.Run(tt.name, func(t *testing.T) {2324 a := &accessService{2325 cfg: tt.fields.cfg,2326 token: tt.fields.token,2327 athenzURL: tt.fields.athenzURL,2328 athenzPrincipleHeader: tt.fields.athenzPrincipleHeader,2329 tokenCache: tt.fields.tokenCache,2330 expiry: tt.fields.expiry,2331 }2332 got, err := a.createPostAccessTokenRequest(tt.args.scope, tt.args.proxyForPrincipal, tt.args.expiry)2333 gotBody, readErr := ioutil.ReadAll(got.Body)2334 if readErr != nil {2335 t.Errorf("createPostAccessTokenRequest() err: %v", err)2336 }2337 wantBody, readErr := ioutil.ReadAll(tt.want.Body)...

Full Screen

Full Screen

evalctx.go

Source:evalctx.go Github

copy

Full Screen

...69// HasPrivilege is part of the tree.EvalDatabase interface.70func (so *DummySequenceOperators) HasPrivilege(71 ctx context.Context,72 specifier tree.HasPrivilegeSpecifier,73 user security.SQLUsername,74 kind privilege.Kind,75 withGrantOpt bool,76) (bool, error) {77 return false, errors.WithStack(errEvalPlanner)78}79// IncrementSequence is part of the tree.SequenceOperators interface.80func (so *DummySequenceOperators) IncrementSequence(81 ctx context.Context, seqName *tree.TableName,82) (int64, error) {83 return 0, errors.WithStack(errSequenceOperators)84}85// IncrementSequenceByID is part of the tree.SequenceOperators interface.86func (so *DummySequenceOperators) IncrementSequenceByID(87 ctx context.Context, seqID int64,88) (int64, error) {89 return 0, errors.WithStack(errSequenceOperators)90}91// GetLatestValueInSessionForSequence implements the tree.SequenceOperators92// interface.93func (so *DummySequenceOperators) GetLatestValueInSessionForSequence(94 ctx context.Context, seqName *tree.TableName,95) (int64, error) {96 return 0, errors.WithStack(errSequenceOperators)97}98// GetLatestValueInSessionForSequenceByID implements the tree.SequenceOperators99// interface.100func (so *DummySequenceOperators) GetLatestValueInSessionForSequenceByID(101 ctx context.Context, seqID int64,102) (int64, error) {103 return 0, errors.WithStack(errSequenceOperators)104}105// SetSequenceValue implements the tree.SequenceOperators interface.106func (so *DummySequenceOperators) SetSequenceValue(107 ctx context.Context, seqName *tree.TableName, newVal int64, isCalled bool,108) error {109 return errors.WithStack(errSequenceOperators)110}111// SetSequenceValueByID implements the tree.SequenceOperators interface.112func (so *DummySequenceOperators) SetSequenceValueByID(113 ctx context.Context, seqID int64, newVal int64, isCalled bool,114) error {115 return errors.WithStack(errSequenceOperators)116}117// DummyRegionOperator implements the tree.RegionOperator interface by118// returning errors.119type DummyRegionOperator struct{}120var _ tree.RegionOperator = &DummyRegionOperator{}121var errRegionOperator = unimplemented.NewWithIssue(42508,122 "cannot evaluate scalar expressions containing region operations in this context")123// CurrentDatabaseRegionConfig is part of the tree.EvalDatabase interface.124func (so *DummyRegionOperator) CurrentDatabaseRegionConfig(125 _ context.Context,126) (tree.DatabaseRegionConfig, error) {127 return nil, errors.WithStack(errRegionOperator)128}129// ValidateAllMultiRegionZoneConfigsInCurrentDatabase is part of the tree.EvalDatabase interface.130func (so *DummyRegionOperator) ValidateAllMultiRegionZoneConfigsInCurrentDatabase(131 _ context.Context,132) error {133 return errors.WithStack(errRegionOperator)134}135// ResetMultiRegionZoneConfigsForTable is part of the tree.EvalDatabase136// interface.137func (so *DummyRegionOperator) ResetMultiRegionZoneConfigsForTable(138 _ context.Context, id int64,139) error {140 return errors.WithStack(errRegionOperator)141}142// ResetMultiRegionZoneConfigsForDatabase is part of the tree.EvalDatabase143// interface.144func (so *DummyRegionOperator) ResetMultiRegionZoneConfigsForDatabase(145 _ context.Context, id int64,146) error {147 return errors.WithStack(errRegionOperator)148}149// DummyEvalPlanner implements the tree.EvalPlanner interface by returning150// errors.151type DummyEvalPlanner struct{}152// ResolveOIDFromString is part of the EvalPlanner interface.153func (ep *DummyEvalPlanner) ResolveOIDFromString(154 ctx context.Context, resultType *types.T, toResolve *tree.DString,155) (*tree.DOid, error) {156 return nil, errors.WithStack(errEvalPlanner)157}158// ResolveOIDFromOID is part of the EvalPlanner interface.159func (ep *DummyEvalPlanner) ResolveOIDFromOID(160 ctx context.Context, resultType *types.T, toResolve *tree.DOid,161) (*tree.DOid, error) {162 return nil, errors.WithStack(errEvalPlanner)163}164// UnsafeUpsertDescriptor is part of the EvalPlanner interface.165func (ep *DummyEvalPlanner) UnsafeUpsertDescriptor(166 ctx context.Context, descID int64, encodedDescriptor []byte, force bool,167) error {168 return errors.WithStack(errEvalPlanner)169}170// GetImmutableTableInterfaceByID is part of the EvalPlanner interface.171func (ep *DummyEvalPlanner) GetImmutableTableInterfaceByID(172 ctx context.Context, id int,173) (interface{}, error) {174 return nil, errors.WithStack(errEvalPlanner)175}176// UnsafeDeleteDescriptor is part of the EvalPlanner interface.177func (ep *DummyEvalPlanner) UnsafeDeleteDescriptor(178 ctx context.Context, descID int64, force bool,179) error {180 return errors.WithStack(errEvalPlanner)181}182// ForceDeleteTableData is part of the EvalPlanner interface.183func (ep *DummyEvalPlanner) ForceDeleteTableData(ctx context.Context, descID int64) error {184 return errors.WithStack(errEvalPlanner)185}186// UnsafeUpsertNamespaceEntry is part of the EvalPlanner interface.187func (ep *DummyEvalPlanner) UnsafeUpsertNamespaceEntry(188 ctx context.Context, parentID, parentSchemaID int64, name string, descID int64, force bool,189) error {190 return errors.WithStack(errEvalPlanner)191}192// UnsafeDeleteNamespaceEntry is part of the EvalPlanner interface.193func (ep *DummyEvalPlanner) UnsafeDeleteNamespaceEntry(194 ctx context.Context, parentID, parentSchemaID int64, name string, descID int64, force bool,195) error {196 return errors.WithStack(errEvalPlanner)197}198// MemberOfWithAdminOption is part of the EvalPlanner interface.199func (ep *DummyEvalPlanner) MemberOfWithAdminOption(200 ctx context.Context, member security.SQLUsername,201) (map[security.SQLUsername]bool, error) {202 return nil, errors.WithStack(errEvalPlanner)203}204// ExternalReadFile is part of the EvalPlanner interface.205func (*DummyEvalPlanner) ExternalReadFile(ctx context.Context, uri string) ([]byte, error) {206 return nil, errors.WithStack(errEvalPlanner)207}208// ExternalWriteFile is part of the EvalPlanner interface.209func (*DummyEvalPlanner) ExternalWriteFile(ctx context.Context, uri string, content []byte) error {210 return errors.WithStack(errEvalPlanner)211}212var _ tree.EvalPlanner = &DummyEvalPlanner{}213var errEvalPlanner = pgerror.New(pgcode.ScalarOperationCannotRunWithoutFullSessionContext,214 "cannot evaluate scalar expressions using table lookups in this context")215// CurrentDatabaseRegionConfig is part of the tree.EvalDatabase interface.216func (ep *DummyEvalPlanner) CurrentDatabaseRegionConfig(217 _ context.Context,218) (tree.DatabaseRegionConfig, error) {219 return nil, errors.WithStack(errEvalPlanner)220}221// ResetMultiRegionZoneConfigsForTable is part of the tree.EvalDatabase222// interface.223func (ep *DummyEvalPlanner) ResetMultiRegionZoneConfigsForTable(_ context.Context, _ int64) error {224 return errors.WithStack(errEvalPlanner)225}226// ResetMultiRegionZoneConfigsForDatabase is part of the tree.EvalDatabase227// interface.228func (ep *DummyEvalPlanner) ResetMultiRegionZoneConfigsForDatabase(229 _ context.Context, _ int64,230) error {231 return errors.WithStack(errEvalPlanner)232}233// ValidateAllMultiRegionZoneConfigsInCurrentDatabase is part of the tree.EvalDatabase interface.234func (ep *DummyEvalPlanner) ValidateAllMultiRegionZoneConfigsInCurrentDatabase(235 _ context.Context,236) error {237 return errors.WithStack(errEvalPlanner)238}239// ParseQualifiedTableName is part of the tree.EvalDatabase interface.240func (ep *DummyEvalPlanner) ParseQualifiedTableName(sql string) (*tree.TableName, error) {241 return parser.ParseQualifiedTableName(sql)242}243// SchemaExists is part of the tree.EvalDatabase interface.244func (ep *DummyEvalPlanner) SchemaExists(ctx context.Context, dbName, scName string) (bool, error) {245 return false, errors.WithStack(errEvalPlanner)246}247// IsTableVisible is part of the tree.EvalDatabase interface.248func (ep *DummyEvalPlanner) IsTableVisible(249 ctx context.Context, curDB string, searchPath sessiondata.SearchPath, tableID oid.Oid,250) (bool, bool, error) {251 return false, false, errors.WithStack(errEvalPlanner)252}253// IsTypeVisible is part of the tree.EvalDatabase interface.254func (ep *DummyEvalPlanner) IsTypeVisible(255 ctx context.Context, curDB string, searchPath sessiondata.SearchPath, typeID oid.Oid,256) (bool, bool, error) {257 return false, false, errors.WithStack(errEvalPlanner)258}259// HasPrivilege is part of the tree.EvalDatabase interface.260func (ep *DummyEvalPlanner) HasPrivilege(261 ctx context.Context,262 specifier tree.HasPrivilegeSpecifier,263 user security.SQLUsername,264 kind privilege.Kind,265 withGrantOpt bool,266) (bool, error) {267 return false, errors.WithStack(errEvalPlanner)268}269// ResolveTableName is part of the tree.EvalDatabase interface.270func (ep *DummyEvalPlanner) ResolveTableName(271 ctx context.Context, tn *tree.TableName,272) (tree.ID, error) {273 return 0, errors.WithStack(errEvalPlanner)274}275// GetTypeFromValidSQLSyntax is part of the tree.EvalPlanner interface.276func (ep *DummyEvalPlanner) GetTypeFromValidSQLSyntax(sql string) (*types.T, error) {277 return nil, errors.WithStack(errEvalPlanner)278}279// EvalSubquery is part of the tree.EvalPlanner interface.280func (ep *DummyEvalPlanner) EvalSubquery(expr *tree.Subquery) (tree.Datum, error) {281 return nil, errors.WithStack(errEvalPlanner)282}283// ResolveTypeByOID implements the tree.TypeReferenceResolver interface.284func (ep *DummyEvalPlanner) ResolveTypeByOID(_ context.Context, _ oid.Oid) (*types.T, error) {285 return nil, errors.WithStack(errEvalPlanner)286}287// ResolveType implements the tree.TypeReferenceResolver interface.288func (ep *DummyEvalPlanner) ResolveType(289 _ context.Context, _ *tree.UnresolvedObjectName,290) (*types.T, error) {291 return nil, errors.WithStack(errEvalPlanner)292}293// DummyPrivilegedAccessor implements the tree.PrivilegedAccessor interface by returning errors.294type DummyPrivilegedAccessor struct{}295var _ tree.PrivilegedAccessor = &DummyPrivilegedAccessor{}296var errEvalPrivileged = pgerror.New(pgcode.ScalarOperationCannotRunWithoutFullSessionContext,297 "cannot evaluate privileged expressions in this context")298// LookupNamespaceID is part of the tree.PrivilegedAccessor interface.299func (ep *DummyPrivilegedAccessor) LookupNamespaceID(300 ctx context.Context, parentID int64, name string,301) (tree.DInt, bool, error) {302 return 0, false, errors.WithStack(errEvalPrivileged)303}304// LookupZoneConfigByNamespaceID is part of the tree.PrivilegedAccessor interface.305func (ep *DummyPrivilegedAccessor) LookupZoneConfigByNamespaceID(306 ctx context.Context, id int64,307) (tree.DBytes, bool, error) {308 return "", false, errors.WithStack(errEvalPrivileged)309}310// DummySessionAccessor implements the tree.EvalSessionAccessor interface by returning errors.311type DummySessionAccessor struct{}312var _ tree.EvalSessionAccessor = &DummySessionAccessor{}313var errEvalSessionVar = pgerror.New(pgcode.ScalarOperationCannotRunWithoutFullSessionContext,314 "cannot evaluate scalar expressions that access session variables in this context")...

Full Screen

Full Screen

name

Using AI Code Generation

copy

Full Screen

1func main() {2 d := dummy{}3 d.name()4}5func main() {6 d := dummy{}7 d.name()8}9func main() {10 d := dummy{}11 d.name()12}13func main() {14 d := dummy{}15 d.name()16}17func main() {18 d := dummy{}19 d.name()20}21func main() {22 d := dummy{}23 d.name()24}25func main() {26 d := dummy{}27 d.name()28}29func main() {30 d := dummy{}31 d.name()32}33func main() {34 d := dummy{}35 d.name()36}37func main() {38 d := dummy{}39 d.name()40}41func main() {42 d := dummy{}43 d.name()44}45func main() {46 d := dummy{}47 d.name()48}49func main() {50 d := dummy{}51 d.name()52}53func main() {54 d := dummy{}55 d.name()56}57func main() {58 d := dummy{}59 d.name()60}61func main() {62 d := dummy{}63 d.name()64}

Full Screen

Full Screen

name

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 d := dummy{}4 fmt.Println(d.name())5}6type dummy struct{}7func (d dummy) name() string {8}9func (d dummy) Name() string {10}

Full Screen

Full Screen

name

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 d.name()4}5import "fmt"6type dummy struct{}7func (d dummy) name() {8 fmt.Println("dummy")9}10import "fmt"11type dummy struct{}12func (d dummy) name() {13 fmt.Println("dummy")14}15import "fmt"16func main() {17 d.name()18}19import "fmt"20type dummy struct{}21func (d dummy) name() {22 fmt.Println("dummy")23}24import "fmt"25type dummy struct{}26func (d dummy) name() {27 fmt.Println("dummy")28}29import "fmt"30func main() {31 d.name()32}33import "fmt"34type dummy struct{}35func (d dummy) name() {36 fmt.Println("dummy")37}38import "fmt"39type dummy struct{}40func (d dummy) name() {41 fmt.Println("dummy")42}43import "fmt"44func main() {45 d.name()46}47import "fmt"48type dummy struct{}49func (d dummy) name() {50 fmt.Println("dummy")51}52import "fmt"53type dummy struct{}54func (d dummy) name() {55 fmt.Println("dummy")56}57import "fmt"58func main() {59 d.name()60}

Full Screen

Full Screen

name

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 d.name()4}5import "fmt"6type dummy struct{}7func (d dummy) name() {8 fmt.Println("dummy")9}10./2.go:6: d.name undefined (type dummy has no field or method name)11type Dummy struct{}

Full Screen

Full Screen

name

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 d := dummy{}4 d.name()5}6type DerivedClass struct {7}8import (9type name struct {10}11type person struct {12}13func main() {14 p := person{}15 fmt.Println("First Name is:", p.firstname)16 fmt.Println("Last Name is:", p.lastname)17 fmt.Println("Age is:", p.age)18}19type DerivedClass struct {20}21import (22type name struct {23}24type person struct {25}26func main() {27 p := person{}28 fmt.Println("First Name is:", p.firstname)29 fmt.Println("Last Name is:", p.lastname)30 fmt.Println("Age is:", p.age)31}

Full Screen

Full Screen

name

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 fmt.Println(d.name)4}5import "fmt"6type dummy struct {7}8func main() {9 d := dummy{"test"}10 fmt.Println(d.name)11}12import "fmt"13type dummy struct {14}15func main() {16 d := dummy{name: "test"}17 fmt.Println(d.name)18}19import "fmt"20type dummy struct {21}22func main() {23 d := &dummy{name: "test"}24 fmt.Println(d.name)25}26import "fmt"27type dummy struct {28}29func main() {30 d := new(dummy)31 fmt.Println(d.name)32}33import "fmt"34type dummy struct {35}36func main() {37 d := &dummy{}38 fmt.Println(d.name)39}

Full Screen

Full Screen

name

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 d := new(dummy)4 d.name()5}6import (7func main() {8 d := new(dummy)9 d.namePointer()10}11import (12func main() {13 d := new(dummy)14 d.namePointerWithPointer()15}16import (17func main() {18 d := new(dummy)19 d.namePointerWithPointer()20}21import (

Full Screen

Full Screen

name

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 d := dummy{"Hello"}4 fmt.Println(d.name())5}6import "fmt"7func main() {8 d := dummy{"Hello"}9 fmt.Println(d.name())10 d.change()11 fmt.Println(d.name())12}13type dummy struct {14}15func (d dummy) name() string {16}17func (d *dummy) change() {18}19import "fmt"20func main() {21 d := dummy{"Hello"}22 fmt.Println(d.name())23 d.change()24 fmt.Println(d.name())25}26type dummy struct {27}28func (d dummy) name() string {29}30func (d *dummy) change() {31}32import "fmt"33func main() {34 d := dummy{"Hello"}35 fmt.Println(d.name())36 d.change()37 fmt.Println(d.name())38}39type dummy struct {40}41func (d dummy) name() string {42}43func (d *dummy) change() {

Full Screen

Full Screen

name

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 fmt.Println(d.name)4}5import "fmt"6type person struct {7}8type dummy struct {9}10func main() {11 fmt.Println(d.name)12}13import "fmt"14type person struct {15}16type dummy struct {17}18func main() {19 fmt.Println(d.person.name)20}21import "fmt"22type person struct {23}24type dummy struct {25}26func main() {27 fmt.Println(d.name)28}29import "fmt"30type person struct {31}32type dummy struct {33}34func main() {35 fmt.Println(d.person.name)36}37import "fmt"38type person struct {39}40type dummy struct {41}42func main() {43 fmt.Println(d.person.name)44 fmt.Println((*d.person).name)45}

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Testkube automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful