github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/registry/remote/referrers_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 remote
    17  
    18  import (
    19  	"reflect"
    20  	"testing"
    21  
    22  	"github.com/opencontainers/go-digest"
    23  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    24  )
    25  
    26  func Test_buildReferrersTag(t *testing.T) {
    27  	tests := []struct {
    28  		name string
    29  		desc ocispec.Descriptor
    30  		want string
    31  	}{
    32  		{
    33  			name: "zero digest",
    34  			desc: ocispec.Descriptor{
    35  				Digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000",
    36  			},
    37  			want: "sha256-0000000000000000000000000000000000000000000000000000000000000000",
    38  		},
    39  		{
    40  			name: "sha256",
    41  			desc: ocispec.Descriptor{
    42  				Digest: "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
    43  			},
    44  			want: "sha256-9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
    45  		},
    46  		{
    47  			name: "sha512",
    48  			desc: ocispec.Descriptor{
    49  				Digest: "sha512:ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
    50  			},
    51  			want: "sha512-ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
    52  		},
    53  	}
    54  	for _, tt := range tests {
    55  		t.Run(tt.name, func(t *testing.T) {
    56  			if got := buildReferrersTag(tt.desc); got != tt.want {
    57  				t.Errorf("getReferrersTag() = %v, want %v", got, tt.want)
    58  			}
    59  		})
    60  	}
    61  }
    62  
    63  func Test_isReferrersFilterApplied(t *testing.T) {
    64  	tests := []struct {
    65  		name        string
    66  		annotations map[string]string
    67  		requested   string
    68  		want        bool
    69  	}{
    70  		{
    71  			name:        "single filter applied, specified filter matches",
    72  			annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: "artifactType"},
    73  			requested:   "artifactType",
    74  			want:        true,
    75  		},
    76  		{
    77  			name:        "single filter applied, specified filter does not match",
    78  			annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: "foo"},
    79  			requested:   "artifactType",
    80  			want:        false,
    81  		},
    82  		{
    83  			name:        "multiple filters applied, specified filter matches",
    84  			annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: "foo,artifactType"},
    85  			requested:   "artifactType",
    86  			want:        true,
    87  		},
    88  		{
    89  			name:        "multiple filters applied, specified filter does not match",
    90  			annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: "foo,bar"},
    91  			requested:   "artifactType",
    92  			want:        false,
    93  		},
    94  		{
    95  			name:        "single filter applied, specified filter empty",
    96  			annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: "foo"},
    97  			requested:   "",
    98  			want:        false,
    99  		},
   100  		{
   101  			name:        "no filter applied",
   102  			annotations: map[string]string{},
   103  			requested:   "artifactType",
   104  			want:        false,
   105  		},
   106  		{
   107  			name:        "empty filter applied",
   108  			annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: ""},
   109  			requested:   "artifactType",
   110  			want:        false,
   111  		},
   112  		{
   113  			name:        "no filter applied, specified filter empty",
   114  			annotations: map[string]string{},
   115  			requested:   "",
   116  			want:        false,
   117  		},
   118  	}
   119  	for _, tt := range tests {
   120  		t.Run(tt.name, func(t *testing.T) {
   121  			if got := isReferrersFilterApplied(tt.annotations, tt.requested); got != tt.want {
   122  				t.Errorf("isReferrersFilterApplied() = %v, want %v", got, tt.want)
   123  			}
   124  		})
   125  	}
   126  }
   127  
   128  func Test_filterReferrers(t *testing.T) {
   129  	refs := []ocispec.Descriptor{
   130  		{
   131  			MediaType:    ocispec.MediaTypeArtifactManifest,
   132  			Size:         1,
   133  			Digest:       digest.FromString("1"),
   134  			ArtifactType: "application/vnd.test",
   135  		},
   136  		{
   137  			MediaType:    ocispec.MediaTypeArtifactManifest,
   138  			Size:         2,
   139  			Digest:       digest.FromString("2"),
   140  			ArtifactType: "application/vnd.foo",
   141  		},
   142  		{
   143  			MediaType:    ocispec.MediaTypeArtifactManifest,
   144  			Size:         3,
   145  			Digest:       digest.FromString("3"),
   146  			ArtifactType: "application/vnd.bar",
   147  		},
   148  		{
   149  			MediaType:    ocispec.MediaTypeArtifactManifest,
   150  			Size:         4,
   151  			Digest:       digest.FromString("4"),
   152  			ArtifactType: "application/vnd.test",
   153  		},
   154  		{
   155  			MediaType:    ocispec.MediaTypeArtifactManifest,
   156  			Size:         5,
   157  			Digest:       digest.FromString("5"),
   158  			ArtifactType: "application/vnd.baz",
   159  		},
   160  	}
   161  	got := filterReferrers(refs, "application/vnd.test")
   162  	want := []ocispec.Descriptor{
   163  		{
   164  			MediaType:    ocispec.MediaTypeArtifactManifest,
   165  			Size:         1,
   166  			Digest:       digest.FromString("1"),
   167  			ArtifactType: "application/vnd.test",
   168  		},
   169  		{
   170  			MediaType:    ocispec.MediaTypeArtifactManifest,
   171  			Size:         4,
   172  			Digest:       digest.FromString("4"),
   173  			ArtifactType: "application/vnd.test",
   174  		},
   175  	}
   176  	if !reflect.DeepEqual(got, want) {
   177  		t.Errorf("filterReferrers() = %v, want %v", got, want)
   178  	}
   179  }
   180  
   181  func Test_filterReferrers_allMatch(t *testing.T) {
   182  	refs := []ocispec.Descriptor{
   183  		{
   184  			MediaType:    ocispec.MediaTypeArtifactManifest,
   185  			Size:         1,
   186  			Digest:       digest.FromString("1"),
   187  			ArtifactType: "application/vnd.test",
   188  		},
   189  		{
   190  			MediaType:    ocispec.MediaTypeArtifactManifest,
   191  			Size:         4,
   192  			Digest:       digest.FromString("2"),
   193  			ArtifactType: "application/vnd.test",
   194  		},
   195  		{
   196  			MediaType:    ocispec.MediaTypeArtifactManifest,
   197  			Size:         5,
   198  			Digest:       digest.FromString("3"),
   199  			ArtifactType: "application/vnd.test",
   200  		},
   201  	}
   202  	got := filterReferrers(refs, "application/vnd.test")
   203  	if !reflect.DeepEqual(got, refs) {
   204  		t.Errorf("filterReferrers() = %v, want %v", got, refs)
   205  	}
   206  }
   207  
   208  func Test_applyReferrerChanges(t *testing.T) {
   209  	descs := []ocispec.Descriptor{
   210  		{
   211  			MediaType:    ocispec.MediaTypeDescriptor,
   212  			Digest:       "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
   213  			Size:         3,
   214  			ArtifactType: "foo",
   215  			Annotations:  map[string]string{"name": "foo"},
   216  		},
   217  		{
   218  			MediaType:    ocispec.MediaTypeDescriptor,
   219  			Digest:       "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
   220  			Size:         3,
   221  			ArtifactType: "bar",
   222  			Annotations:  map[string]string{"name": "bar"},
   223  		},
   224  		{
   225  			MediaType:    ocispec.MediaTypeDescriptor,
   226  			Digest:       "sha256:baa5a0964d3320fbc0c6a922140453c8513ea24ab8fd0577034804a967248096",
   227  			Size:         3,
   228  			ArtifactType: "baz",
   229  			Annotations:  map[string]string{"name": "baz"},
   230  		},
   231  		{
   232  			MediaType:    ocispec.MediaTypeDescriptor,
   233  			Digest:       "sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
   234  			Size:         5,
   235  			ArtifactType: "hello",
   236  			Annotations:  map[string]string{"name": "hello"},
   237  		},
   238  		{
   239  			MediaType:    ocispec.MediaTypeDescriptor,
   240  			Digest:       "sha256:82e35a63ceba37e9646434c5dd412ea577147f1e4a41ccde1614253187e3dbf9",
   241  			Size:         7,
   242  			ArtifactType: "goodbye",
   243  			Annotations:  map[string]string{"name": "goodbye"},
   244  		},
   245  	}
   246  
   247  	tests := []struct {
   248  		name            string
   249  		referrers       []ocispec.Descriptor
   250  		referrerChanges []referrerChange
   251  		want            []ocispec.Descriptor
   252  		wantErr         error
   253  	}{
   254  		{
   255  			name:      "add to an empty list",
   256  			referrers: []ocispec.Descriptor{},
   257  			referrerChanges: []referrerChange{
   258  				{descs[0], referrerOperationAdd}, // add new
   259  				{descs[1], referrerOperationAdd}, // add new
   260  				{descs[2], referrerOperationAdd}, // add new
   261  			},
   262  			want: []ocispec.Descriptor{
   263  				descs[0],
   264  				descs[1],
   265  				descs[2],
   266  			},
   267  			wantErr: nil,
   268  		},
   269  		{
   270  			name: "add to a non-empty list",
   271  			referrers: []ocispec.Descriptor{
   272  				descs[0],
   273  				descs[1],
   274  			},
   275  			referrerChanges: []referrerChange{
   276  				{descs[2], referrerOperationAdd}, // add new
   277  				{descs[1], referrerOperationAdd}, // add existing
   278  				{descs[1], referrerOperationAdd}, // add duplicate existing
   279  				{descs[3], referrerOperationAdd}, // add new
   280  				{descs[2], referrerOperationAdd}, // add duplicate new
   281  			},
   282  			want: []ocispec.Descriptor{
   283  				descs[0],
   284  				descs[1],
   285  				descs[2],
   286  				descs[3],
   287  			},
   288  			wantErr: nil,
   289  		},
   290  		{
   291  			name: "partially remove",
   292  			referrers: []ocispec.Descriptor{
   293  				descs[0],
   294  				descs[1],
   295  				descs[2],
   296  			},
   297  			referrerChanges: []referrerChange{
   298  				{descs[2], referrerOperationRemove}, // remove existing
   299  				{descs[1], referrerOperationRemove}, // remove existing
   300  				{descs[3], referrerOperationRemove}, // remove non-existing
   301  				{descs[2], referrerOperationRemove}, // remove duplicate existing
   302  				{descs[4], referrerOperationRemove}, // remove non-existing
   303  			},
   304  			want: []ocispec.Descriptor{
   305  				descs[0],
   306  			},
   307  			wantErr: nil,
   308  		},
   309  		{
   310  			name: "remove all",
   311  			referrers: []ocispec.Descriptor{
   312  				descs[0],
   313  				descs[1],
   314  				descs[2],
   315  			},
   316  			referrerChanges: []referrerChange{
   317  				{descs[2], referrerOperationRemove}, // remove existing
   318  				{descs[0], referrerOperationRemove}, // remove existing
   319  				{descs[1], referrerOperationRemove}, // remove existing
   320  			},
   321  			want:    []ocispec.Descriptor{},
   322  			wantErr: nil,
   323  		},
   324  		{
   325  			name: "add a new one and remove it",
   326  			referrers: []ocispec.Descriptor{
   327  				descs[0],
   328  				descs[1],
   329  				descs[2],
   330  			},
   331  			referrerChanges: []referrerChange{
   332  				{descs[1], referrerOperationAdd},    // add existing
   333  				{descs[3], referrerOperationAdd},    // add new
   334  				{descs[3], referrerOperationAdd},    // add duplicate new
   335  				{descs[3], referrerOperationRemove}, // remove new
   336  				{descs[4], referrerOperationAdd},    // add new
   337  			},
   338  			want: []ocispec.Descriptor{
   339  				descs[0],
   340  				descs[1],
   341  				descs[2],
   342  				descs[4],
   343  			},
   344  			wantErr: nil,
   345  		},
   346  		{
   347  			name: "remove a new one and add it back",
   348  			referrers: []ocispec.Descriptor{
   349  				descs[0],
   350  				descs[1],
   351  				descs[2],
   352  			},
   353  			referrerChanges: []referrerChange{
   354  				{descs[1], referrerOperationAdd},    // add existing
   355  				{descs[3], referrerOperationAdd},    // add new
   356  				{descs[3], referrerOperationRemove}, // remove new,
   357  				{descs[3], referrerOperationAdd},    // add new back
   358  				{descs[4], referrerOperationAdd},    // add new
   359  			},
   360  			want: []ocispec.Descriptor{
   361  				descs[0],
   362  				descs[1],
   363  				descs[2],
   364  				descs[3],
   365  				descs[4],
   366  			},
   367  			wantErr: nil,
   368  		},
   369  		{
   370  			name: "remove an existing one and add it back",
   371  			referrers: []ocispec.Descriptor{
   372  				descs[0],
   373  				descs[1],
   374  				descs[2],
   375  			},
   376  			referrerChanges: []referrerChange{
   377  				{descs[2], referrerOperationRemove}, // remove existing
   378  				{descs[3], referrerOperationAdd},    // add new
   379  				{descs[2], referrerOperationAdd},    // add existing back
   380  			},
   381  			want: []ocispec.Descriptor{
   382  				descs[0],
   383  				descs[1],
   384  				descs[3],
   385  				descs[2],
   386  			},
   387  			wantErr: nil,
   388  		},
   389  		{
   390  			name: "list containing duplicate entries",
   391  			referrers: []ocispec.Descriptor{
   392  				descs[0],
   393  				descs[1],
   394  				descs[0], // duplicate
   395  				descs[2],
   396  				descs[3],
   397  				descs[1], // duplicate
   398  			},
   399  			referrerChanges: []referrerChange{
   400  				{descs[2], referrerOperationAdd},    // add new
   401  				{descs[2], referrerOperationAdd},    // add duplicate new
   402  				{descs[3], referrerOperationRemove}, // remove existing
   403  			},
   404  			want: []ocispec.Descriptor{
   405  				descs[0],
   406  				descs[1],
   407  				descs[2],
   408  			},
   409  			wantErr: nil,
   410  		},
   411  		{
   412  			name: "list containing bad entries",
   413  			referrers: []ocispec.Descriptor{
   414  				descs[0],
   415  				{},
   416  				descs[1],
   417  			},
   418  			referrerChanges: []referrerChange{
   419  				{descs[2], referrerOperationAdd},    // add new
   420  				{descs[1], referrerOperationRemove}, // remove existing
   421  			},
   422  			want: []ocispec.Descriptor{
   423  				descs[0],
   424  				descs[2],
   425  			},
   426  			wantErr: nil,
   427  		},
   428  		{
   429  			name: "no update: same order",
   430  			referrers: []ocispec.Descriptor{
   431  				descs[0],
   432  				descs[1],
   433  				descs[2],
   434  			},
   435  			referrerChanges: []referrerChange{
   436  				{descs[3], referrerOperationAdd},    // add new
   437  				{descs[2], referrerOperationRemove}, // remove existing
   438  				{descs[4], referrerOperationAdd},    // add new
   439  				{descs[4], referrerOperationRemove}, // remove new
   440  				{descs[2], referrerOperationAdd},    // add existing back
   441  				{descs[3], referrerOperationRemove}, // remove new
   442  			},
   443  			want:    nil,
   444  			wantErr: errNoReferrerUpdate,
   445  		},
   446  		{
   447  			name: "no update: different order",
   448  			referrers: []ocispec.Descriptor{
   449  				descs[0],
   450  				descs[1],
   451  				descs[2],
   452  			},
   453  			referrerChanges: []referrerChange{
   454  				{descs[2], referrerOperationRemove}, // remove existing
   455  				{descs[0], referrerOperationRemove}, // remove existing
   456  				{descs[0], referrerOperationAdd},    // add existing back
   457  				{descs[2], referrerOperationAdd},    // add existing back
   458  			},
   459  			want:    nil,
   460  			wantErr: errNoReferrerUpdate, // internal result: 2, 1, 0
   461  		},
   462  		{
   463  			name: "no update: list containing duplicate entries",
   464  			referrers: []ocispec.Descriptor{
   465  				descs[0],
   466  				descs[1],
   467  				descs[0], // duplicate
   468  				descs[2],
   469  				descs[1], // duplicate
   470  			},
   471  			referrerChanges: []referrerChange{
   472  				{descs[2], referrerOperationRemove}, // remove existing
   473  				{descs[0], referrerOperationRemove}, // remove existing
   474  				{descs[0], referrerOperationAdd},    // add existing back
   475  				{descs[2], referrerOperationAdd},    // add existing back
   476  			},
   477  			want: []ocispec.Descriptor{
   478  				descs[1],
   479  				descs[0],
   480  				descs[2],
   481  			},
   482  			wantErr: nil,
   483  		},
   484  	}
   485  	for _, tt := range tests {
   486  		t.Run(tt.name, func(t *testing.T) {
   487  			got, err := applyReferrerChanges(tt.referrers, tt.referrerChanges)
   488  			if err != tt.wantErr {
   489  				t.Errorf("applyReferrerChanges() error = %v, wantErr %v", err, tt.wantErr)
   490  			}
   491  			if !reflect.DeepEqual(got, tt.want) {
   492  				t.Errorf("applyReferrerChanges() = %v, want %v", got, tt.want)
   493  			}
   494  		})
   495  	}
   496  }
   497  
   498  func Test_removeEmptyDescriptors(t *testing.T) {
   499  	descs := []ocispec.Descriptor{
   500  		{
   501  			MediaType:    ocispec.MediaTypeDescriptor,
   502  			Digest:       "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
   503  			Size:         3,
   504  			ArtifactType: "foo",
   505  			Annotations:  map[string]string{"name": "foo"},
   506  		},
   507  		{
   508  			MediaType:    ocispec.MediaTypeDescriptor,
   509  			Digest:       "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
   510  			Size:         3,
   511  			ArtifactType: "bar",
   512  			Annotations:  map[string]string{"name": "bar"},
   513  		},
   514  		{
   515  			MediaType:    ocispec.MediaTypeDescriptor,
   516  			Digest:       "sha256:baa5a0964d3320fbc0c6a922140453c8513ea24ab8fd0577034804a967248096",
   517  			Size:         3,
   518  			ArtifactType: "baz",
   519  			Annotations:  map[string]string{"name": "baz"},
   520  		},
   521  	}
   522  	tests := []struct {
   523  		name  string
   524  		descs []ocispec.Descriptor
   525  		hint  int
   526  		want  []ocispec.Descriptor
   527  	}{
   528  		{
   529  			name:  "empty list",
   530  			descs: []ocispec.Descriptor{},
   531  			hint:  0,
   532  			want:  []ocispec.Descriptor{},
   533  		},
   534  		{
   535  			name:  "all non-empty",
   536  			descs: descs,
   537  			hint:  len(descs),
   538  			want:  descs,
   539  		},
   540  		{
   541  			name: "all empty",
   542  			descs: []ocispec.Descriptor{
   543  				{},
   544  				{},
   545  				{},
   546  			},
   547  			hint: 0,
   548  			want: []ocispec.Descriptor{},
   549  		},
   550  		{
   551  			name: "empty rear",
   552  			descs: []ocispec.Descriptor{
   553  				descs[0],
   554  				{},
   555  				descs[2],
   556  				{},
   557  				{},
   558  			},
   559  			hint: 2,
   560  			want: []ocispec.Descriptor{
   561  				descs[0],
   562  				descs[2],
   563  			},
   564  		},
   565  		{
   566  			name: "empty head",
   567  			descs: []ocispec.Descriptor{
   568  				{},
   569  				descs[0],
   570  				descs[1],
   571  				{},
   572  				{},
   573  				descs[2],
   574  			},
   575  			hint: 3,
   576  			want: []ocispec.Descriptor{
   577  				descs[0],
   578  				descs[1],
   579  				descs[2],
   580  			},
   581  		},
   582  	}
   583  	for _, tt := range tests {
   584  		t.Run(tt.name, func(t *testing.T) {
   585  			if got := removeEmptyDescriptors(tt.descs, tt.hint); !reflect.DeepEqual(got, tt.want) {
   586  				t.Errorf("removeEmptyDescriptors() = %v, want %v", got, tt.want)
   587  			}
   588  		})
   589  	}
   590  }