Skip to content

Commit

Permalink
fix: make tests backwards compatible with nerdctl v1 (#212)
Browse files Browse the repository at this point in the history
Signed-off-by: Austin Vazquez <[email protected]>
  • Loading branch information
austinvazquez authored Jan 4, 2025
1 parent 877cb84 commit 9f5e817
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 5 deletions.
10 changes: 10 additions & 0 deletions option/modifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,13 @@ func WithNoEnvironmentVariablePassthrough() Modifier {
delete(o.features, environmentVariablePassthrough)
})
}

// WithNerdctlVersion denotes the underlying nerdctl version.
//
// This is useful for tests whose expectations change based on
// the underlying nerdctl version.
func WithNerdctlVersion(version string) Modifier {
return newFuncModifier(func(o *Option) {
o.features[nerdctlVersion] = version
})
}
25 changes: 25 additions & 0 deletions option/nerdctl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package option

import "regexp"

const (
nerdctl1xx = "1.x.x"
nerdctl2xx = "2.x.x"
defaultNerdctlVersion = nerdctl2xx
)

var (
nerdctl1xxRegex = regexp.MustCompile(`^1\.[x0-9]+\.[x0-9]+`)
nerdctl2xxRegex = regexp.MustCompile(`^2\.[x0-9]+\.[x0-9]+`)
)

func isNerdctl1xx(version string) bool {
return nerdctl1xxRegex.MatchString(version)
}

func isNerdctl2xx(version string) bool {
return nerdctl2xxRegex.MatchString(version)
}
41 changes: 37 additions & 4 deletions option/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type feature int

const (
environmentVariablePassthrough feature = iota
nerdctlVersion feature = iota
)

// Option customizes how tests are run.
Expand All @@ -27,7 +28,7 @@ const (
type Option struct {
subject []string
env []string
features map[feature]bool
features map[feature]any
}

// New does some sanity checks on the arguments before initializing an Option.
Expand All @@ -43,7 +44,13 @@ func New(subject []string, modifiers ...Modifier) (*Option, error) {
return nil, errors.New("missing subject")
}

o := &Option{subject: subject, features: map[feature]bool{environmentVariablePassthrough: true}}
o := &Option{
subject: subject,
features: map[feature]any{
environmentVariablePassthrough: true,
nerdctlVersion: nerdctl2xx,
},
}
for _, modifier := range modifiers {
modifier.modify(o)
}
Expand Down Expand Up @@ -91,6 +98,32 @@ func containsEnv(envs []string, targetEnvKey string) (int, bool) {
// SupportsEnvVarPassthrough is used by tests to check if the option
// supports [feature.environmentVariablePassthrough].
func (o *Option) SupportsEnvVarPassthrough() bool {
support, ok := o.features[environmentVariablePassthrough]
return ok && support
if value, exists := o.features[environmentVariablePassthrough]; exists {
if boolValue, ok := value.(bool); ok {
return boolValue
}
}
return false
}

// IsNerdctlV1 is used by tests to check if the option supports [feature.nerdctlVersion] == nerdctl1xx.
func (o *Option) IsNerdctlV1() bool {
return o.isNerdctlVersion(isNerdctl1xx)
}

// IsNerdctlV2 is used by tests to check if the option supports [feature.nerdctlVersion] == nerdctl2xx.
func (o *Option) IsNerdctlV2() bool {
return o.isNerdctlVersion(isNerdctl2xx)
}

func (o *Option) isNerdctlVersion(cmp func(string) bool) bool {
var version string

if value, exists := o.features[nerdctlVersion]; !exists {
version = defaultNerdctlVersion
} else if value, ok := value.(string); ok {
version = value
}

return cmp(version)
}
116 changes: 116 additions & 0 deletions option/option_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package option

import "testing"

func TestSupportsEnvVarPassthrough(t *testing.T) {
t.Parallel()

tests := []struct {
name string
mods []Modifier
assert func(*testing.T, *Option)
}{
{
name: "IsEnvVarPassthroughByDefault",
mods: []Modifier{},
assert: func(t *testing.T, uut *Option) {
if !uut.SupportsEnvVarPassthrough() {
t.Fatal("expected SupportsEnvVarPassthrough to be true")
}
},
},
{
name: "IsNotEnvVarPassthrough",
mods: []Modifier{
WithNoEnvironmentVariablePassthrough(),
},
assert: func(t *testing.T, uut *Option) {
if uut.SupportsEnvVarPassthrough() {
t.Fatal("expected SupportsEnvVarPassthrough to be false")
}
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()

uut, err := New([]string{"nerdctl"}, test.mods...)
if err != nil {
t.Fatal(err)
}

test.assert(t, uut)
})
}
}

func TestNerdctlVersion(t *testing.T) {
t.Parallel()

tests := []struct {
name string
mods []Modifier
assert func(*testing.T, *Option)
}{
{
name: "IsNerdctlV2ByDefault",
mods: []Modifier{},
assert: func(t *testing.T, uut *Option) {
if !uut.IsNerdctlV2() {
t.Fatal("expected IsNerdctlV2 to be true")
}
},
},
{
name: "IsNerdctlV1",
mods: []Modifier{
WithNerdctlVersion("1.7.7"),
},
assert: func(t *testing.T, uut *Option) {
if !uut.IsNerdctlV1() {
t.Fatal("expected IsNerdctlV1 to be true")
}
},
},
{
name: "IsNerdctlV2",
mods: []Modifier{
WithNerdctlVersion("2.0.2"),
},
assert: func(t *testing.T, uut *Option) {
if !uut.IsNerdctlV2() {
t.Fatal("expected IsNerdctlV2 to be true")
}
},
},
{
name: "IsPatchedNerdctlV2",
mods: []Modifier{
WithNerdctlVersion("2.0.2.m"),
},
assert: func(t *testing.T, uut *Option) {
if !uut.IsNerdctlV2() {
t.Fatal("expected IsNerdctlV2 to be true")
}
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()

uut, err := New([]string{"nerdctl"}, test.mods...)
if err != nil {
t.Fatal(err)
}

test.assert(t, uut)
})
}
}
10 changes: 9 additions & 1 deletion tests/builder_prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@ func BuilderPrune(o *option.Option) {
command.Run(o, "build", "--output=type=docker", buildContext)
// There is no interface to validate the current builder cache size.
// To validate in Buildkit, run `buildctl du -v`.
command.Run(o, "builder", "prune", "-f")
args := []string{"builder", "prune"}

// TODO(macedonv): remove after nerdctlv2 is supported across all platforms.
if o.IsNerdctlV2() {
// Do not prompt for user response during automated testing.
args = append(args, "--force")
}

command.Run(o, args...)
})
})
}
13 changes: 13 additions & 0 deletions tests/volume_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,20 @@ func VolumeCreate(o *option.Option) {
gomega.Expect(tag1).Should(gomega.Equal("tag1"))
})

ginkgo.It("should not create a volume if the volume with the same name exists", func() {
// TODO(macedonv): remove entire test after nerdctl v2 is supported on all platforms.
if o.IsNerdctlV2() {
ginkgo.Skip("Behavior is not supported on nerdctl v2")
}
command.Run(o, "volume", "create", testVolumeName)
command.RunWithoutSuccessfulExit(o, "volume", "create", testVolumeName)
})

ginkgo.It("should warn volume already exists if a volume with the same name exists", func() {
// TODO(macedonv): remove check when nerdctl v2 is supported on all platforms.
if o.IsNerdctlV1() {
ginkgo.Skip("Behavior is not supported on nerdctl v1")
}
command.Run(o, "volume", "create", testVolumeName)
session := command.Run(o, "volume", "create", testVolumeName)
gomega.Expect(string(session.Err.Contents())).Should(gomega.ContainSubstring("already exists"))
Expand Down

0 comments on commit 9f5e817

Please sign in to comment.