Skip to content

Commit

Permalink
test: add test for oras push (#691)
Browse files Browse the repository at this point in the history
Resolves #566

Signed-off-by: Billy Zha <jinzha1@microsoft.com>
  • Loading branch information
qweeah authored Nov 16, 2022
1 parent 2329c6e commit b89736e
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 9 deletions.
16 changes: 13 additions & 3 deletions test/e2e/internal/utils/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,19 @@ func (opts *ExecOption) MatchErrKeyWords(keywords ...string) *ExecOption {
// MatchContent adds full content matching to the execution.
func (opts *ExecOption) MatchContent(content string) *ExecOption {
if !opts.shouldFail {
opts.stdout = append(opts.stdout, match.NewContentMatcher(content))
opts.stdout = append(opts.stdout, match.NewContentMatcher(content, false))
} else {
opts.stderr = append(opts.stderr, match.NewContentMatcher(content))
opts.stderr = append(opts.stderr, match.NewContentMatcher(content, false))
}
return opts
}

// MatchTrimedContent adds trimmed content matching to the execution.
func (opts *ExecOption) MatchTrimmedContent(content string) *ExecOption {
if !opts.shouldFail {
opts.stdout = append(opts.stdout, match.NewContentMatcher(content, true))
} else {
opts.stderr = append(opts.stderr, match.NewContentMatcher(content, true))
}
return opts
}
Expand All @@ -138,7 +148,7 @@ func (opts *ExecOption) Exec() *gexec.Session {
opts.text = "pass"
}
}
description := fmt.Sprintf(">> should %s: %s %s >>", opts.text, opts.binary, strings.Join(opts.args, " "))
description := fmt.Sprintf("\n>> should %s: %s %s >>", opts.text, opts.binary, strings.Join(opts.args, " "))
ginkgo.By(description)

var cmd *exec.Cmd
Expand Down
24 changes: 19 additions & 5 deletions test/e2e/internal/utils/match/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,34 @@ limitations under the License.
package match

import (
"strings"

. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
)

// contentMatcher provides whole matching of the output.
type contentMatcher string
type contentMatcher struct {
s string
trimSpace bool
}

// NewContentMatcher returns a content matcher.
func NewContentMatcher(s string) contentMatcher {
return contentMatcher(s)
func NewContentMatcher(s string, trimSpace bool) contentMatcher {
if trimSpace {
s = strings.TrimSpace(s)
}
return contentMatcher{
s: s,
trimSpace: trimSpace,
}
}

// Match matches got with s.
func (c contentMatcher) Match(got *gbytes.Buffer) {
content := got.Contents()
Expect(contentMatcher(content)).Should(Equal(c))
content := string(got.Contents())
if c.trimSpace {
content = strings.TrimSpace(content)
}
Expect(content).Should(Equal(c.s))
}
177 changes: 177 additions & 0 deletions test/e2e/suite/command/push.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
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 command

import (
"bytes"
"fmt"

. "github.com/onsi/ginkgo/v2"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras-go/v2"
. "oras.land/oras/test/e2e/internal/utils"
"oras.land/oras/test/e2e/internal/utils/match"
)

var _ = Describe("Remote registry users:", func() {
layerDescriptorTemplate := `{"mediaType":"%s","digest":"sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9","size":3,"annotations":{"org.opencontainers.image.title":"foobar/bar"}}`
tag := "e2e"
When("pushing to registy without OCI artifact support", func() {
repoPrefix := fmt.Sprintf("command/push/%d", GinkgoRandomSeed())
files := []string{
"foobar/config.json",
"foobar/bar",
}
statusKeys := []match.StateKey{
{Digest: "44136fa355b3", Name: "application/vnd.unknown.config.v1+json"},
{Digest: "fcde2b2edba5", Name: files[1]},
}
configDescriptorTemplate := `{"mediaType":"%s","digest":"sha256:46b68ac1696c3870d537f376868d9402400de28587e345264a77b65da09669be","size":13}`

It("should push files without customized media types", func() {
repo := fmt.Sprintf("%s/%s", repoPrefix, "with-mediatype")
tempDir := GinkgoT().TempDir()
if err := CopyTestData(tempDir); err != nil {
panic(err)
}

ORAS("push", Reference(Host, repo, tag), files[1], "-v").
MatchStatus(statusKeys, true, 2).
WithWorkDir(tempDir).Exec()
fetched := ORAS("manifest", "fetch", Reference(Host, repo, tag)).Exec().Out
Binary("jq", ".layers[]", "--compact-output").
MatchTrimmedContent(fmt.Sprintf(layerDescriptorTemplate, ocispec.MediaTypeImageLayer)).
WithInput(fetched).Exec()
})

It("should push files with customized media types", func() {
repo := fmt.Sprintf("%s/%s", repoPrefix, "layer-mediatype")
layerType := "layer.type"
tempDir := GinkgoT().TempDir()
if err := CopyTestData(tempDir); err != nil {
panic(err)
}

ORAS("push", Reference(Host, repo, tag), files[1]+":"+layerType, "-v").
MatchStatus(statusKeys, true, 2).
WithWorkDir(tempDir).Exec()
fetched := ORAS("manifest", "fetch", Reference(Host, repo, tag)).Exec().Out
Binary("jq", ".layers[]", "--compact-output").
MatchTrimmedContent(fmt.Sprintf(layerDescriptorTemplate, layerType)).
WithInput(fetched).Exec()
})

It("should push files with manifest exported", func() {
repo := fmt.Sprintf("%s/%s", repoPrefix, "export-manifest")
layerType := "layer.type"
tempDir := GinkgoT().TempDir()
if err := CopyTestData(tempDir); err != nil {
panic(err)
}

exportPath := "packed.json"
ORAS("push", Reference(Host, repo, tag), files[1]+":"+layerType, "-v", "--export-manifest", exportPath).
MatchStatus(statusKeys, true, 2).
WithWorkDir(tempDir).Exec()
fetched := ORAS("manifest", "fetch", Reference(Host, repo, tag)).Exec().Out
Binary("cat", exportPath).
WithWorkDir(tempDir).
MatchTrimmedContent(string(fetched.Contents())).Exec()
})

It("should push files with customized config file", func() {
repo := fmt.Sprintf("%s/%s", repoPrefix, "config")
tempDir := GinkgoT().TempDir()
if err := CopyTestData(tempDir); err != nil {
panic(err)
}

ORAS("push", Reference(Host, repo, tag), "--config", files[0], files[1], "-v").
MatchStatus([]match.StateKey{
{Digest: "46b68ac1696c", Name: oras.MediaTypeUnknownConfig},
{Digest: "fcde2b2edba5", Name: files[1]},
}, true, 2).
WithWorkDir(tempDir).Exec()
fetched := ORAS("manifest", "fetch", Reference(Host, repo, tag)).Exec().Out
Binary("jq", ".config", "--compact-output").
MatchTrimmedContent(fmt.Sprintf(configDescriptorTemplate, oras.MediaTypeUnknownConfig)).
WithInput(fetched).Exec()
})

It("should push files with customized config file and mediatype", func() {
repo := fmt.Sprintf("%s/%s", repoPrefix, "config-mediatype")
configType := "config.type"
tempDir := GinkgoT().TempDir()
if err := CopyTestData(tempDir); err != nil {
panic(err)
}

ORAS("push", Reference(Host, repo, tag), "--config", fmt.Sprintf("%s:%s", files[0], configType), files[1], "-v").
MatchStatus([]match.StateKey{
{Digest: "46b68ac1696c", Name: configType},
{Digest: "fcde2b2edba5", Name: files[1]},
}, true, 2).
WithWorkDir(tempDir).Exec()
fetched := ORAS("manifest", "fetch", Reference(Host, repo, tag)).Exec().Out
Binary("jq", ".config", "--compact-output").
MatchTrimmedContent(fmt.Sprintf(configDescriptorTemplate, configType)).
WithInput(fetched).Exec()
})

It("should push files with customized manifest annotation", func() {
repo := fmt.Sprintf("%s/%s", repoPrefix, "manifest-annotation")
key := "image-anno-key"
value := "image-anno-value"
tempDir := GinkgoT().TempDir()
if err := CopyTestData(tempDir); err != nil {
panic(err)
}

ORAS("push", Reference(Host, repo, tag), files[1], "-v", "--annotation", fmt.Sprintf("%s=%s", key, value)).
MatchStatus(statusKeys, true, 2).
WithWorkDir(tempDir).Exec()
fetched := ORAS("manifest", "fetch", Reference(Host, repo, tag)).Exec().Out

Binary("jq", `.annotations|del(.["org.opencontainers.image.created"])`, "--compact-output").
MatchTrimmedContent(fmt.Sprintf(`{"%s":"%s"}`, key, value)).
WithInput(fetched).Exec()
})

It("should push files with customized file annotation", func() {
repo := fmt.Sprintf("%s/%s", repoPrefix, "file-annotation")
tempDir := GinkgoT().TempDir()
if err := CopyTestData(tempDir); err != nil {
panic(err)
}

ORAS("push", Reference(Host, repo, tag), files[1], "-v", "--annotation-file", "foobar/annotation.json").
MatchStatus(statusKeys, true, 2).
WithWorkDir(tempDir).Exec()
fetched := ORAS("manifest", "fetch", Reference(Host, repo, tag)).Exec().Out

// see testdata\files\foobar\annotation.json
Binary("jq", `.annotations|del(.["org.opencontainers.image.created"])`, "--compact-output").
MatchTrimmedContent(fmt.Sprintf(`{"%s":"%s"}`, "hi", "manifest")).
WithInput(bytes.NewReader(fetched.Contents())).Exec()

Binary("jq", ".config.annotations", "--compact-output").
MatchTrimmedContent(fmt.Sprintf(`{"%s":"%s"}`, "hello", "config")).
WithInput(bytes.NewReader(fetched.Contents())).Exec()

Binary("jq", `.layers[0].annotations|del(.["org.opencontainers.image.title"])`, "--compact-output").
MatchTrimmedContent(fmt.Sprintf(`{"%s":"%s"}`, "foo", "bar")).
WithInput(bytes.NewReader(fetched.Contents())).Exec()
})
})
})
2 changes: 1 addition & 1 deletion test/e2e/suite/scenario/oci_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var (
)

var _ = Describe("OCI image user:", Ordered, func() {
repo := "oci-image"
repo := "scenario/oci-image"
When("pushing images and check", func() {
tag := "image"
var tempDir string
Expand Down
1 change: 1 addition & 0 deletions test/e2e/testdata/files/foobar/annotation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"$config":{"hello":"config"},"$manifest":{"hi":"manifest"},"foobar/bar":{"foo":"bar"}}

0 comments on commit b89736e

Please sign in to comment.