diff --git a/internal/graph/graph_test.go b/internal/graph/graph_test.go index 2a15c5bba..15efd94f9 100644 --- a/internal/graph/graph_test.go +++ b/internal/graph/graph_test.go @@ -16,75 +16,19 @@ limitations under the License. package graph import ( - "bytes" "context" - "encoding/json" - "github.com/opencontainers/go-digest" - "oras.land/oras-go/v2/content/memory" "reflect" "testing" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras-go/v2/content" "oras.land/oras/internal/docker" + "oras.land/oras/internal/testutils" ) -type contentFetcher struct { - content.Fetcher -} - -func newTestFetcher(t *testing.T) (subject, config, ociImage, dockerImage, index ocispec.Descriptor, fetcher content.Fetcher) { - var blobs [][]byte - ctx := context.Background() - memoryStorage := memory.New() - appendBlob := func(mediaType string, blob []byte) ocispec.Descriptor { - blobs = append(blobs, blob) - desc := ocispec.Descriptor{ - MediaType: mediaType, - Digest: digest.FromBytes(blob), - Size: int64(len(blob)), - } - if err := memoryStorage.Push(ctx, desc, bytes.NewReader(blob)); err != nil { - t.Errorf("Error pushing %v\n", err) - } - return desc - } - generateImage := func(subject *ocispec.Descriptor, mediaType string, config ocispec.Descriptor, layers ...ocispec.Descriptor) ocispec.Descriptor { - manifest := ocispec.Manifest{ - MediaType: mediaType, - Subject: subject, - Config: config, - Layers: layers, - } - manifestJSON, err := json.Marshal(manifest) - if err != nil { - t.Fatal(err) - } - return appendBlob(mediaType, manifestJSON) - } - generateIndex := func(manifests ...ocispec.Descriptor) ocispec.Descriptor { - index := ocispec.Index{ - Manifests: manifests, - } - indexJSON, err := json.Marshal(index) - if err != nil { - t.Fatal(err) - } - return appendBlob(ocispec.MediaTypeImageIndex, indexJSON) - } - - subject = appendBlob(ocispec.MediaTypeImageLayer, []byte("blob")) - imageType := "test.image" - config = appendBlob(imageType, []byte("config content")) - ociImage = generateImage(&subject, ocispec.MediaTypeImageManifest, config) - dockerImage = generateImage(&subject, docker.MediaTypeManifest, config) - index = generateIndex(subject) - - return subject, config, ociImage, dockerImage, index, &contentFetcher{Fetcher: memoryStorage} -} - func TestSuccessors(t *testing.T) { - subject, config, ociImage, dockerImage, index, fetcher := newTestFetcher(t) + mockFetcher := testutils.NewMockFetcher(t) + fetcher := mockFetcher.Fetcher ctx := context.Background() type args struct { ctx context.Context @@ -101,9 +45,9 @@ func TestSuccessors(t *testing.T) { }{ {"should failed to get non-existent OCI image", args{ctx, fetcher, ocispec.Descriptor{MediaType: ocispec.MediaTypeImageManifest}}, nil, nil, nil, true}, {"should failed to get non-existent docker image", args{ctx, fetcher, ocispec.Descriptor{MediaType: docker.MediaTypeManifest}}, nil, nil, nil, true}, - {"should get success of a docker image", args{ctx, fetcher, dockerImage}, nil, &subject, &config, false}, - {"should get success of an OCI image", args{ctx, fetcher, ociImage}, nil, &subject, &config, false}, - {"should get success of an index", args{ctx, fetcher, index}, []ocispec.Descriptor{subject}, nil, nil, false}, + {"should get success of a docker image", args{ctx, fetcher, mockFetcher.DockerImage}, nil, &mockFetcher.Subject, &mockFetcher.Config, false}, + {"should get success of an OCI image", args{ctx, fetcher, mockFetcher.OciImage}, nil, &mockFetcher.Subject, &mockFetcher.Config, false}, + {"should get success of an index", args{ctx, fetcher, mockFetcher.Index}, []ocispec.Descriptor{mockFetcher.Subject}, nil, nil, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/testutils/fetcher.go b/internal/testutils/fetcher.go new file mode 100644 index 000000000..a0f8b636d --- /dev/null +++ b/internal/testutils/fetcher.go @@ -0,0 +1,101 @@ +/* +Copyright The ORAS Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testutils + +import ( + "bytes" + "context" + "encoding/json" + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "oras.land/oras-go/v2/content" + "oras.land/oras-go/v2/content/memory" + "oras.land/oras/internal/docker" + "testing" +) + +type MockFetcher struct { + t *testing.T + store *memory.Store + Fetcher content.Fetcher + Subject ocispec.Descriptor + Config ocispec.Descriptor + OciImage ocispec.Descriptor + DockerImage ocispec.Descriptor + Index ocispec.Descriptor +} + +// NewMockFetcher creates a MockFetcher and populates it. +func NewMockFetcher(t *testing.T) (mockFetcher MockFetcher) { + mockFetcher = MockFetcher{store: memory.New(), t: t} + mockFetcher.Subject = mockFetcher.PushBlob(ocispec.MediaTypeImageLayer, []byte("blob")) + imageType := "test.image" + mockFetcher.Config = mockFetcher.PushBlob(imageType, []byte("config content")) + mockFetcher.OciImage = mockFetcher.PushOCIImage(&mockFetcher.Subject, mockFetcher.Config) + mockFetcher.DockerImage = mockFetcher.PushDockerImage(&mockFetcher.Subject, mockFetcher.Config) + mockFetcher.Index = mockFetcher.PushIndex(mockFetcher.Subject) + mockFetcher.Fetcher = mockFetcher.store + return mockFetcher +} + +// PushBlob pushes a blob to the memory store. +func (mf *MockFetcher) PushBlob(mediaType string, blob []byte) ocispec.Descriptor { + desc := ocispec.Descriptor{ + MediaType: mediaType, + Digest: digest.FromBytes(blob), + Size: int64(len(blob)), + } + if err := mf.store.Push(context.Background(), desc, bytes.NewReader(blob)); err != nil { + mf.t.Fatal(err) + } + return desc +} + +func (mf *MockFetcher) pushImage(subject *ocispec.Descriptor, mediaType string, config ocispec.Descriptor, layers ...ocispec.Descriptor) ocispec.Descriptor { + manifest := ocispec.Manifest{ + MediaType: mediaType, + Subject: subject, + Config: config, + Layers: layers, + } + manifestJSON, err := json.Marshal(manifest) + if err != nil { + mf.t.Fatal(err) + } + return mf.PushBlob(mediaType, manifestJSON) +} + +// PushOCIImage pushes the given subject, config and layers as a OCI image. +func (mf *MockFetcher) PushOCIImage(subject *ocispec.Descriptor, config ocispec.Descriptor, layers ...ocispec.Descriptor) ocispec.Descriptor { + return mf.pushImage(subject, ocispec.MediaTypeImageManifest, config, layers...) +} + +// PushDockerImage pushes the given subject, config and layers as a Docker image. +func (mf *MockFetcher) PushDockerImage(subject *ocispec.Descriptor, config ocispec.Descriptor, layers ...ocispec.Descriptor) ocispec.Descriptor { + return mf.pushImage(subject, docker.MediaTypeManifest, config, layers...) +} + +// PushIndex pushes the manifests as an index. +func (mf *MockFetcher) PushIndex(manifests ...ocispec.Descriptor) ocispec.Descriptor { + index := ocispec.Index{ + Manifests: manifests, + } + indexJSON, err := json.Marshal(index) + if err != nil { + mf.t.Fatal(err) + } + return mf.PushBlob(ocispec.MediaTypeImageIndex, indexJSON) +}