Skip to content

Commit

Permalink
refactor: Make mock fetcher generally available (#1455)
Browse files Browse the repository at this point in the history
Signed-off-by: Terry Howe <[email protected]>
  • Loading branch information
Terry Howe authored Aug 4, 2024
1 parent 82949a2 commit 25b8d59
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 62 deletions.
68 changes: 6 additions & 62 deletions internal/graph/graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down
101 changes: 101 additions & 0 deletions internal/testutils/fetcher.go
Original file line number Diff line number Diff line change
@@ -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)
}

0 comments on commit 25b8d59

Please sign in to comment.