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