github.com/dtroyer-salad/og2/v2@v2.0.0-20240412154159-c47231610877/registry/remote/auth/scope_test.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package auth
    17  
    18  import (
    19  	"context"
    20  	"reflect"
    21  	"testing"
    22  
    23  	"oras.land/oras-go/v2/registry"
    24  )
    25  
    26  func TestScopeRepository(t *testing.T) {
    27  	tests := []struct {
    28  		name       string
    29  		repository string
    30  		actions    []string
    31  		want       string
    32  	}{
    33  		{
    34  			name: "empty repository",
    35  			actions: []string{
    36  				"pull",
    37  			},
    38  		},
    39  		{
    40  			name:       "nil actions",
    41  			repository: "foo",
    42  		},
    43  		{
    44  			name:       "empty actions",
    45  			repository: "foo",
    46  			actions:    []string{},
    47  		},
    48  		{
    49  			name:       "empty actions list",
    50  			repository: "foo",
    51  			actions:    []string{},
    52  		},
    53  		{
    54  			name:       "empty actions",
    55  			repository: "foo",
    56  			actions: []string{
    57  				"",
    58  			},
    59  		},
    60  		{
    61  			name:       "single action",
    62  			repository: "foo",
    63  			actions: []string{
    64  				"pull",
    65  			},
    66  			want: "repository:foo:pull",
    67  		},
    68  		{
    69  			name:       "multiple actions",
    70  			repository: "foo",
    71  			actions: []string{
    72  				"pull",
    73  				"push",
    74  			},
    75  			want: "repository:foo:pull,push",
    76  		},
    77  		{
    78  			name:       "unordered actions",
    79  			repository: "foo",
    80  			actions: []string{
    81  				"push",
    82  				"pull",
    83  			},
    84  			want: "repository:foo:pull,push",
    85  		},
    86  		{
    87  			name:       "duplicated actions",
    88  			repository: "foo",
    89  			actions: []string{
    90  				"push",
    91  				"pull",
    92  				"pull",
    93  				"delete",
    94  				"push",
    95  			},
    96  			want: "repository:foo:delete,pull,push",
    97  		},
    98  	}
    99  	for _, tt := range tests {
   100  		t.Run(tt.name, func(t *testing.T) {
   101  			if got := ScopeRepository(tt.repository, tt.actions...); got != tt.want {
   102  				t.Errorf("ScopeRepository() = %v, want %v", got, tt.want)
   103  			}
   104  		})
   105  	}
   106  }
   107  
   108  func TestWithScopeHints(t *testing.T) {
   109  	ctx := context.Background()
   110  	ref1, err := registry.ParseReference("registry.example.com/foo")
   111  	if err != nil {
   112  		t.Fatal("registry.ParseReference() error =", err)
   113  	}
   114  	ref2, err := registry.ParseReference("docker.io/foo")
   115  	if err != nil {
   116  		t.Fatal("registry.ParseReference() error =", err)
   117  	}
   118  
   119  	// with single scope
   120  	want1 := []string{
   121  		"repository:foo:pull",
   122  	}
   123  	want2 := []string{
   124  		"repository:foo:push",
   125  	}
   126  	ctx = AppendRepositoryScope(ctx, ref1, ActionPull)
   127  	ctx = AppendRepositoryScope(ctx, ref2, ActionPush)
   128  	if got := GetScopesForHost(ctx, ref1.Host()); !reflect.DeepEqual(got, want1) {
   129  		t.Errorf("GetScopesPerRegistry(WithScopeHints()) = %v, want %v", got, want1)
   130  	}
   131  	if got := GetScopesForHost(ctx, ref2.Host()); !reflect.DeepEqual(got, want2) {
   132  		t.Errorf("GetScopesPerRegistry(WithScopeHints()) = %v, want %v", got, want2)
   133  	}
   134  
   135  	// with duplicated scopes
   136  	scopes1 := []string{
   137  		ActionDelete,
   138  		ActionDelete,
   139  		ActionPull,
   140  	}
   141  	want1 = []string{
   142  		"repository:foo:delete,pull",
   143  	}
   144  	scopes2 := []string{
   145  		ActionPush,
   146  		ActionPush,
   147  		ActionDelete,
   148  	}
   149  	want2 = []string{
   150  		"repository:foo:delete,push",
   151  	}
   152  	ctx = AppendRepositoryScope(ctx, ref1, scopes1...)
   153  	ctx = AppendRepositoryScope(ctx, ref2, scopes2...)
   154  	if got := GetScopesForHost(ctx, ref1.Host()); !reflect.DeepEqual(got, want1) {
   155  		t.Errorf("GetScopesPerRegistry(WithScopeHints()) = %v, want %v", got, want1)
   156  	}
   157  	if got := GetScopesForHost(ctx, ref2.Host()); !reflect.DeepEqual(got, want2) {
   158  		t.Errorf("GetScopesPerRegistry(WithScopeHints()) = %v, want %v", got, want2)
   159  	}
   160  
   161  	// append empty scopes
   162  	ctx = AppendRepositoryScope(ctx, ref1)
   163  	ctx = AppendRepositoryScope(ctx, ref2)
   164  	if got := GetScopesForHost(ctx, ref1.Host()); !reflect.DeepEqual(got, want1) {
   165  		t.Errorf("GetScopesPerRegistry(WithScopeHints()) = %v, want %v", got, want1)
   166  	}
   167  	if got := GetScopesForHost(ctx, ref2.Host()); !reflect.DeepEqual(got, want2) {
   168  		t.Errorf("GetScopesPerRegistry(WithScopeHints()) = %v, want %v", got, want2)
   169  	}
   170  }
   171  
   172  func TestWithScopes(t *testing.T) {
   173  	ctx := context.Background()
   174  
   175  	// with single scope
   176  	want := []string{
   177  		"repository:foo:pull",
   178  	}
   179  	ctx = WithScopes(ctx, want...)
   180  	if got := GetScopes(ctx); !reflect.DeepEqual(got, want) {
   181  		t.Errorf("GetScopes(WithScopes()) = %v, want %v", got, want)
   182  	}
   183  
   184  	// overwrite scopes
   185  	want = []string{
   186  		"repository:bar:push",
   187  	}
   188  	ctx = WithScopes(ctx, want...)
   189  	if got := GetScopes(ctx); !reflect.DeepEqual(got, want) {
   190  		t.Errorf("GetScopes(WithScopes()) = %v, want %v", got, want)
   191  	}
   192  
   193  	// overwrite scopes with de-duplication
   194  	scopes := []string{
   195  		"repository:hello-world:push",
   196  		"repository:alpine:delete",
   197  		"repository:hello-world:pull",
   198  		"repository:alpine:delete",
   199  	}
   200  	want = []string{
   201  		"repository:alpine:delete",
   202  		"repository:hello-world:pull,push",
   203  	}
   204  	ctx = WithScopes(ctx, scopes...)
   205  	if got := GetScopes(ctx); !reflect.DeepEqual(got, want) {
   206  		t.Errorf("GetScopes(WithScopes()) = %v, want %v", got, want)
   207  	}
   208  
   209  	// clean scopes
   210  	want = nil
   211  	ctx = WithScopes(ctx, want...)
   212  	if got := GetScopes(ctx); !reflect.DeepEqual(got, want) {
   213  		t.Errorf("GetScopes(WithScopes()) = %v, want %v", got, want)
   214  	}
   215  }
   216  
   217  func TestAppendScopes(t *testing.T) {
   218  	ctx := context.Background()
   219  
   220  	// append single scope
   221  	want := []string{
   222  		"repository:foo:pull",
   223  	}
   224  	ctx = AppendScopes(ctx, want...)
   225  	if got := GetScopes(ctx); !reflect.DeepEqual(got, want) {
   226  		t.Errorf("GetScopes(AppendScopes()) = %v, want %v", got, want)
   227  	}
   228  
   229  	// append scopes with de-duplication
   230  	scopes := []string{
   231  		"repository:hello-world:push",
   232  		"repository:alpine:delete",
   233  		"repository:hello-world:pull",
   234  		"repository:alpine:delete",
   235  	}
   236  	want = []string{
   237  		"repository:alpine:delete",
   238  		"repository:foo:pull",
   239  		"repository:hello-world:pull,push",
   240  	}
   241  	ctx = AppendScopes(ctx, scopes...)
   242  	if got := GetScopes(ctx); !reflect.DeepEqual(got, want) {
   243  		t.Errorf("GetScopes(AppendScopes()) = %v, want %v", got, want)
   244  	}
   245  
   246  	// append empty scopes
   247  	ctx = AppendScopes(ctx)
   248  	if got := GetScopes(ctx); !reflect.DeepEqual(got, want) {
   249  		t.Errorf("GetScopes(AppendScopes()) = %v, want %v", got, want)
   250  	}
   251  }
   252  
   253  func TestWithScopesPerHost(t *testing.T) {
   254  	ctx := context.Background()
   255  	reg1 := "registry1.example.com"
   256  	reg2 := "registry2.example.com"
   257  
   258  	// with single scope
   259  	want1 := []string{
   260  		"repository:foo:pull",
   261  	}
   262  	want2 := []string{
   263  		"repository:foo:push",
   264  	}
   265  	ctx = WithScopesForHost(ctx, reg1, want1...)
   266  	ctx = WithScopesForHost(ctx, reg2, want2...)
   267  	if got := GetScopesForHost(ctx, reg1); !reflect.DeepEqual(got, want1) {
   268  		t.Errorf("GetScopesPerRegistry(WithScopesPerRegistry()) = %v, want %v", got, want1)
   269  	}
   270  	if got := GetScopesForHost(ctx, reg2); !reflect.DeepEqual(got, want2) {
   271  		t.Errorf("GetScopesPerRegistry(WithScopesPerRegistry()) = %v, want %v", got, want2)
   272  	}
   273  
   274  	// overwrite scopes
   275  	want1 = []string{
   276  		"repository:bar:push",
   277  	}
   278  	want2 = []string{
   279  		"repository:bar:pull",
   280  	}
   281  	ctx = WithScopesForHost(ctx, reg1, want1...)
   282  	ctx = WithScopesForHost(ctx, reg2, want2...)
   283  	if got := GetScopesForHost(ctx, reg1); !reflect.DeepEqual(got, want1) {
   284  		t.Errorf("GetScopesPerRegistry(WithScopesPerRegistry()) = %v, want %v", got, want1)
   285  	}
   286  	if got := GetScopesForHost(ctx, reg2); !reflect.DeepEqual(got, want2) {
   287  		t.Errorf("GetScopesPerRegistry(WithScopesPerRegistry()) = %v, want %v", got, want2)
   288  	}
   289  
   290  	// overwrite scopes with de-duplication
   291  	scopes1 := []string{
   292  		"repository:hello-world:push",
   293  		"repository:alpine:delete",
   294  		"repository:hello-world:pull",
   295  		"repository:alpine:delete",
   296  	}
   297  	want1 = []string{
   298  		"repository:alpine:delete",
   299  		"repository:hello-world:pull,push",
   300  	}
   301  	scopes2 := []string{
   302  		"repository:goodbye-world:push",
   303  		"repository:nginx:delete",
   304  		"repository:goodbye-world:pull",
   305  		"repository:nginx:delete",
   306  	}
   307  	want2 = []string{
   308  		"repository:goodbye-world:pull,push",
   309  		"repository:nginx:delete",
   310  	}
   311  	ctx = WithScopesForHost(ctx, reg1, scopes1...)
   312  	ctx = WithScopesForHost(ctx, reg2, scopes2...)
   313  	if got := GetScopesForHost(ctx, reg1); !reflect.DeepEqual(got, want1) {
   314  		t.Errorf("GetScopesPerRegistry(WithScopesPerRegistry()) = %v, want %v", got, want1)
   315  	}
   316  	if got := GetScopesForHost(ctx, reg2); !reflect.DeepEqual(got, want2) {
   317  		t.Errorf("GetScopesPerRegistry(WithScopesPerRegistry()) = %v, want %v", got, want2)
   318  	}
   319  
   320  	// clean scopes
   321  	var want []string
   322  	ctx = WithScopesForHost(ctx, reg1, want...)
   323  	ctx = WithScopesForHost(ctx, reg2, want...)
   324  	if got := GetScopesForHost(ctx, reg1); !reflect.DeepEqual(got, want) {
   325  		t.Errorf("GetScopesPerRegistry(WithScopesPerRegistry()) = %v, want %v", got, want)
   326  	}
   327  	if got := GetScopesForHost(ctx, reg2); !reflect.DeepEqual(got, want) {
   328  		t.Errorf("GetScopesPerRegistry(WithScopesPerRegistry()) = %v, want %v", got, want)
   329  	}
   330  }
   331  
   332  func TestAppendScopesPerHost(t *testing.T) {
   333  	ctx := context.Background()
   334  	reg1 := "registry1.example.com"
   335  	reg2 := "registry2.example.com"
   336  
   337  	// with single scope
   338  	want1 := []string{
   339  		"repository:foo:pull",
   340  	}
   341  	want2 := []string{
   342  		"repository:foo:push",
   343  	}
   344  	ctx = AppendScopesForHost(ctx, reg1, want1...)
   345  	ctx = AppendScopesForHost(ctx, reg2, want2...)
   346  	if got := GetScopesForHost(ctx, reg1); !reflect.DeepEqual(got, want1) {
   347  		t.Errorf("GetScopesPerRegistry(AppendScopesPerRegistry()) = %v, want %v", got, want1)
   348  	}
   349  	if got := GetScopesForHost(ctx, reg2); !reflect.DeepEqual(got, want2) {
   350  		t.Errorf("GetScopesPerRegistry(AppendScopesPerRegistry()) = %v, want %v", got, want2)
   351  	}
   352  
   353  	// append scopes with de-duplication
   354  	scopes1 := []string{
   355  		"repository:hello-world:push",
   356  		"repository:alpine:delete",
   357  		"repository:hello-world:pull",
   358  		"repository:alpine:delete",
   359  	}
   360  	want1 = []string{
   361  		"repository:alpine:delete",
   362  		"repository:foo:pull",
   363  		"repository:hello-world:pull,push",
   364  	}
   365  	scopes2 := []string{
   366  		"repository:goodbye-world:push",
   367  		"repository:nginx:delete",
   368  		"repository:goodbye-world:pull",
   369  		"repository:nginx:delete",
   370  	}
   371  	want2 = []string{
   372  		"repository:foo:push",
   373  		"repository:goodbye-world:pull,push",
   374  		"repository:nginx:delete",
   375  	}
   376  	ctx = AppendScopesForHost(ctx, reg1, scopes1...)
   377  	ctx = AppendScopesForHost(ctx, reg2, scopes2...)
   378  	if got := GetScopesForHost(ctx, reg1); !reflect.DeepEqual(got, want1) {
   379  		t.Errorf("GetScopesPerRegistry(AppendScopesPerRegistry()) = %v, want %v", got, want1)
   380  	}
   381  	if got := GetScopesForHost(ctx, reg2); !reflect.DeepEqual(got, want2) {
   382  		t.Errorf("GetScopesPerRegistry(AppendScopesPerRegistry()) = %v, want %v", got, want2)
   383  	}
   384  
   385  	// append empty scopes
   386  	ctx = AppendScopesForHost(ctx, reg1)
   387  	ctx = AppendScopesForHost(ctx, reg2)
   388  	if got := GetScopesForHost(ctx, reg1); !reflect.DeepEqual(got, want1) {
   389  		t.Errorf("GetScopesPerRegistry(AppendScopesPerRegistry()) = %v, want %v", got, want1)
   390  	}
   391  	if got := GetScopesForHost(ctx, reg2); !reflect.DeepEqual(got, want2) {
   392  		t.Errorf("GetScopesPerRegistry(AppendScopesPerRegistry()) = %v, want %v", got, want2)
   393  	}
   394  }
   395  
   396  func TestCleanScopes(t *testing.T) {
   397  	tests := []struct {
   398  		name   string
   399  		scopes []string
   400  		want   []string
   401  	}{
   402  		{
   403  			name: "nil scope",
   404  		},
   405  		{
   406  			name:   "empty scope",
   407  			scopes: []string{},
   408  		},
   409  		{
   410  			name: "single scope",
   411  			scopes: []string{
   412  				"repository:foo:pull",
   413  			},
   414  			want: []string{
   415  				"repository:foo:pull",
   416  			},
   417  		},
   418  		{
   419  			name: "single scope with unordered actions",
   420  			scopes: []string{
   421  				"repository:foo:push,pull,delete",
   422  			},
   423  			want: []string{
   424  				"repository:foo:delete,pull,push",
   425  			},
   426  		},
   427  		{
   428  			name: "single scope with duplicated actions",
   429  			scopes: []string{
   430  				"repository:foo:push,pull,push,pull,push,push,pull",
   431  			},
   432  			want: []string{
   433  				"repository:foo:pull,push",
   434  			},
   435  		},
   436  		{
   437  			name: "single scope with wild cards",
   438  			scopes: []string{
   439  				"repository:foo:pull,*,push",
   440  			},
   441  			want: []string{
   442  				"repository:foo:*",
   443  			},
   444  		},
   445  		{
   446  			name: "single scope with no actions",
   447  			scopes: []string{
   448  				"repository:foo:,",
   449  			},
   450  			want: nil,
   451  		},
   452  		{
   453  			name: "multiple scopes",
   454  			scopes: []string{
   455  				"repository:bar:push",
   456  				"repository:foo:pull",
   457  			},
   458  			want: []string{
   459  				"repository:bar:push",
   460  				"repository:foo:pull",
   461  			},
   462  		},
   463  		{
   464  			name: "multiple unordered scopes",
   465  			scopes: []string{
   466  				"repository:foo:pull",
   467  				"repository:bar:push",
   468  			},
   469  			want: []string{
   470  				"repository:bar:push",
   471  				"repository:foo:pull",
   472  			},
   473  		},
   474  		{
   475  			name: "multiple scopes with duplicates",
   476  			scopes: []string{
   477  				"repository:foo:pull",
   478  				"repository:bar:push",
   479  				"repository:foo:push",
   480  				"repository:bar:push,delete,pull",
   481  				"repository:bar:delete,pull",
   482  				"repository:foo:pull",
   483  				"registry:catalog:*",
   484  				"registry:catalog:pull",
   485  			},
   486  			want: []string{
   487  				"registry:catalog:*",
   488  				"repository:bar:delete,pull,push",
   489  				"repository:foo:pull,push",
   490  			},
   491  		},
   492  		{
   493  			name: "multiple scopes with no actions",
   494  			scopes: []string{
   495  				"repository:foo:,",
   496  				"repository:bar:,",
   497  			},
   498  			want: nil,
   499  		},
   500  		{
   501  			name: "single unknown or invalid scope",
   502  			scopes: []string{
   503  				"unknown",
   504  			},
   505  			want: []string{
   506  				"unknown",
   507  			},
   508  		},
   509  		{
   510  			name: "multiple unknown or invalid scopes",
   511  			scopes: []string{
   512  				"repository:foo:pull",
   513  				"unknown",
   514  				"invalid:scope",
   515  				"no:actions:",
   516  				"repository:foo:push",
   517  			},
   518  			want: []string{
   519  				"invalid:scope",
   520  				"repository:foo:pull,push",
   521  				"unknown",
   522  			},
   523  		},
   524  	}
   525  	for _, tt := range tests {
   526  		t.Run(tt.name, func(t *testing.T) {
   527  			if got := CleanScopes(tt.scopes); !reflect.DeepEqual(got, tt.want) {
   528  				t.Errorf("CleanScopes() = %v, want %v", got, tt.want)
   529  			}
   530  		})
   531  	}
   532  }
   533  
   534  func Test_cleanActions(t *testing.T) {
   535  	tests := []struct {
   536  		name    string
   537  		actions []string
   538  		want    []string
   539  	}{
   540  		{
   541  			name: "nil action",
   542  		},
   543  		{
   544  			name:    "empty action",
   545  			actions: []string{},
   546  		},
   547  		{
   548  			name: "single action",
   549  			actions: []string{
   550  				"pull",
   551  			},
   552  			want: []string{
   553  				"pull",
   554  			},
   555  		},
   556  		{
   557  			name: "single empty action",
   558  			actions: []string{
   559  				"",
   560  			},
   561  		},
   562  		{
   563  			name: "multiple actions",
   564  			actions: []string{
   565  				"pull",
   566  				"push",
   567  			},
   568  			want: []string{
   569  				"pull",
   570  				"push",
   571  			},
   572  		},
   573  		{
   574  			name: "multiple actions with empty action",
   575  			actions: []string{
   576  				"pull",
   577  				"",
   578  				"push",
   579  			},
   580  			want: []string{
   581  				"pull",
   582  				"push",
   583  			},
   584  		},
   585  		{
   586  			name: "multiple actions with all empty action",
   587  			actions: []string{
   588  				"",
   589  				"",
   590  				"",
   591  			},
   592  			want: nil,
   593  		},
   594  		{
   595  			name: "unordered actions",
   596  			actions: []string{
   597  				"push",
   598  				"pull",
   599  				"delete",
   600  			},
   601  			want: []string{
   602  				"delete",
   603  				"pull",
   604  				"push",
   605  			},
   606  		},
   607  		{
   608  			name: "wildcard",
   609  			actions: []string{
   610  				"*",
   611  			},
   612  			want: []string{
   613  				"*",
   614  			},
   615  		},
   616  		{
   617  			name: "wildcard at the begining",
   618  			actions: []string{
   619  				"*",
   620  				"push",
   621  				"pull",
   622  				"delete",
   623  			},
   624  			want: []string{
   625  				"*",
   626  			},
   627  		},
   628  		{
   629  			name: "wildcard in the middle",
   630  			actions: []string{
   631  				"push",
   632  				"pull",
   633  				"*",
   634  				"delete",
   635  			},
   636  			want: []string{
   637  				"*",
   638  			},
   639  		},
   640  		{
   641  			name: "wildcard at the end",
   642  			actions: []string{
   643  				"push",
   644  				"pull",
   645  				"delete",
   646  				"*",
   647  			},
   648  			want: []string{
   649  				"*",
   650  			},
   651  		},
   652  	}
   653  	for _, tt := range tests {
   654  		t.Run(tt.name, func(t *testing.T) {
   655  			if got := cleanActions(tt.actions); !reflect.DeepEqual(got, tt.want) {
   656  				t.Errorf("cleanActions() = %v, want %v", got, tt.want)
   657  			}
   658  		})
   659  	}
   660  }
   661  
   662  func Test_getAllScopesForHost(t *testing.T) {
   663  	host := "registry.example.com"
   664  	tests := []struct {
   665  		name         string
   666  		scopes       []string
   667  		globalScopes []string
   668  		want         []string
   669  	}{
   670  		{
   671  			name:   "Empty per-host scopes",
   672  			scopes: []string{},
   673  			globalScopes: []string{
   674  				"repository:hello-world:push",
   675  				"repository:alpine:delete",
   676  				"repository:hello-world:pull",
   677  				"repository:alpine:delete",
   678  			},
   679  			want: []string{
   680  				"repository:alpine:delete",
   681  				"repository:hello-world:pull,push",
   682  			},
   683  		},
   684  		{
   685  			name: "Empty global scopes",
   686  			scopes: []string{
   687  				"repository:hello-world:push",
   688  				"repository:alpine:delete",
   689  				"repository:hello-world:pull",
   690  				"repository:alpine:delete",
   691  			},
   692  			globalScopes: []string{},
   693  			want: []string{
   694  				"repository:alpine:delete",
   695  				"repository:hello-world:pull,push",
   696  			},
   697  		},
   698  		{
   699  			name: "Per-host scopes + global scopes",
   700  			scopes: []string{
   701  				"repository:hello-world:push",
   702  				"repository:alpine:delete",
   703  				"repository:hello-world:pull",
   704  				"repository:alpine:delete",
   705  			},
   706  			globalScopes: []string{
   707  				"repository:foo:pull",
   708  				"repository:hello-world:pull",
   709  				"repository:alpine:pull",
   710  			},
   711  			want: []string{
   712  				"repository:alpine:delete,pull",
   713  				"repository:foo:pull",
   714  				"repository:hello-world:pull,push",
   715  			},
   716  		},
   717  	}
   718  	for _, tt := range tests {
   719  		t.Run(tt.name, func(t *testing.T) {
   720  			ctx := context.Background()
   721  			ctx = WithScopesForHost(ctx, host, tt.scopes...)
   722  			ctx = WithScopes(ctx, tt.globalScopes...)
   723  			if got := GetAllScopesForHost(ctx, host); !reflect.DeepEqual(got, tt.want) {
   724  				t.Errorf("getAllScopesForHost() = %v, want %v", got, tt.want)
   725  			}
   726  		})
   727  	}
   728  }