Skip to content

Commit

Permalink
Rewrites trickiest cmd tests to help clarify what's happening (#151)
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Cole <adrian@tetrate.io>
  • Loading branch information
codefromthecrypt authored Apr 1, 2021
1 parent 414491e commit d1ce5a3
Show file tree
Hide file tree
Showing 34 changed files with 1,661 additions and 1,727 deletions.
27 changes: 0 additions & 27 deletions pkg/cmd/extension/build/build_suite_test.go

This file was deleted.

314 changes: 145 additions & 169 deletions pkg/cmd/extension/build/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,186 +15,162 @@
package build_test

import (
"bytes"
"fmt"
"os"
"path/filepath"
"strings"
"os/user"
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/spf13/cobra"
"github.com/stretchr/testify/require"

"github.com/tetratelabs/getenvoy/pkg/cmd"
testcontext "github.com/tetratelabs/getenvoy/pkg/test/cmd/extension"
cmd2 "github.com/tetratelabs/getenvoy/pkg/test/cmd"
cmdutil "github.com/tetratelabs/getenvoy/pkg/util/cmd"
)

var _ = Describe("getenvoy extension build", func() {
// relativeWorkspaceDir points to a usable pre-initialized workspace
const relativeWorkspaceDir = "testdata/workspace"

var dockerDir string

BeforeEach(func() {
dir, err := filepath.Abs("../../../extension/workspace/toolchain/builtin/testdata/toolchain")
Expect(err).ToNot(HaveOccurred())
dockerDir = dir
})

var pathBackup string

BeforeEach(func() {
pathBackup = os.Getenv("PATH")

// override PATH to overshadow `docker` executable during the test
path := strings.Join([]string{dockerDir, pathBackup}, string(filepath.ListSeparator))
os.Setenv("PATH", path)
})

AfterEach(func() {
os.Setenv("PATH", pathBackup)
})

var cwdBackup string

BeforeEach(func() {
cwd, err := os.Getwd()
Expect(err).ToNot(HaveOccurred())
cwdBackup = cwd
})

AfterEach(func() {
if cwdBackup != "" {
Expect(os.Chdir(cwdBackup)).To(Succeed())
}
})

testcontext.SetDefaultUser() // UID:GID == 1001:1002

var stdout *bytes.Buffer
var stderr *bytes.Buffer

BeforeEach(func() {
stdout = new(bytes.Buffer)
stderr = new(bytes.Buffer)
})

var c *cobra.Command

BeforeEach(func() {
c = cmd.NewRoot()
c.SetOut(stdout)
c.SetErr(stderr)
})

It("should validate value of --toolchain-container-image flag", func() {
By("running command")
c.SetArgs([]string{"extension", "build", "--toolchain-container-image", "?invalid value?"})
err := cmdutil.Execute(c)
Expect(err).To(HaveOccurred())

By("verifying command output")
Expect(stdout.String()).To(BeEmpty())
Expect(stderr.String()).To(Equal(`Error: "?invalid value?" is not a valid image name: invalid reference format
Run 'getenvoy extension build --help' for usage.
`))
})

It("should validate value of --toolchain-container-options flag", func() {
By("running command")
c.SetArgs([]string{"extension", "build", "--toolchain-container-options", "imbalanced ' quotes"})
err := cmdutil.Execute(c)
Expect(err).To(HaveOccurred())

By("verifying command output")
Expect(stdout.String()).To(BeEmpty())
Expect(stderr.String()).To(Equal(`Error: "imbalanced ' quotes" is not a valid command line string
Run 'getenvoy extension build --help' for usage.
`))
})

chdir := func(path string) string {
dir, err := filepath.Abs(path)
Expect(err).ToNot(HaveOccurred())

err = os.Chdir(dir)
Expect(err).ToNot(HaveOccurred())

return dir
func TestGetEnvoyExtensionBuildValidateFlag(t *testing.T) {
type testCase struct {
flag string
flagValue string
expectedErr string
}

//nolint:lll
Context("inside a workspace directory", func() {
It("should succeed", func() {
By("changing to a workspace dir")
workspaceDir := chdir("testdata/workspace")

By("running command")
c.SetArgs([]string{"extension", "build"})
err := cmdutil.Execute(c)
Expect(err).ToNot(HaveOccurred())

By("verifying command output")
Expect(stdout.String()).To(Equal(fmt.Sprintf("%s/docker run -u 1001:1002 --rm -t -v %s:/source -w /source --init getenvoy/extension-rust-builder:latest build --output-file target/getenvoy/extension.wasm\n", dockerDir, workspaceDir)))
Expect(stderr.String()).To(Equal("docker stderr\n"))
})

It("should allow to override build image and add Docker cli options", func() {
By("changing to a workspace dir")
workspaceDir := chdir("testdata/workspace")

By("running command")
c.SetArgs([]string{"extension", "build",
"--toolchain-container-image", "build/image",
"--toolchain-container-options", `-e 'VAR=VALUE' -v "/host:/container"`,
})
err := cmdutil.Execute(c)
Expect(err).ToNot(HaveOccurred())

By("verifying command output")
Expect(stdout.String()).To(Equal(fmt.Sprintf("%s/docker run -u 1001:1002 --rm -t -v %s:/source -w /source --init -e VAR=VALUE -v /host:/container build/image build --output-file target/getenvoy/extension.wasm\n", dockerDir, workspaceDir)))
Expect(stderr.String()).To(Equal("docker stderr\n"))
})

It("should properly handle Docker build failing", func() {
By("changing to a workspace dir")
workspaceDir := chdir("testdata/workspace")

By("running command")
c.SetArgs([]string{"extension", "build",
"--toolchain-container-image", "build/image",
"--toolchain-container-options", `-e EXIT_CODE=3`,
})
err := cmdutil.Execute(c)
Expect(err).To(HaveOccurred())

By("verifying command output")
Expect(stdout.String()).To(Equal(fmt.Sprintf("%s/docker run -u 1001:1002 --rm -t -v %s:/source -w /source --init -e EXIT_CODE=3 build/image build --output-file target/getenvoy/extension.wasm\n", dockerDir, workspaceDir)))
Expect(stderr.String()).To(Equal(fmt.Sprintf(`docker stderr
Error: failed to build Envoy extension using "default" toolchain: failed to execute an external command "%s/docker run -u 1001:1002 --rm -t -v %s:/source -w /source --init -e EXIT_CODE=3 build/image build --output-file target/getenvoy/extension.wasm": exit status 3
Run 'getenvoy extension build --help' for usage.
`, dockerDir, workspaceDir)))
})
})

Context("outside of a workspace directory", func() {
It("should fail", func() {
By("changing to a non-workspace dir")
dir := chdir("testdata")
tests := []testCase{
{
flag: "--toolchain-container-image",
flagValue: "?invalid value?",
expectedErr: `"?invalid value?" is not a valid image name: invalid reference format`,
},
{
flag: "--toolchain-container-options",
flagValue: "imbalanced ' quotes",
expectedErr: `"imbalanced ' quotes" is not a valid command line string`,
},
}

By("running command")
c.SetArgs([]string{"extension", "build"})
err := cmdutil.Execute(c)
Expect(err).To(HaveOccurred())
for _, test := range tests {
test := test // pin! see https://github.com/kyoh86/scopelint for why

By("verifying command output")
Expect(stdout.String()).To(BeEmpty())
Expect(stderr.String()).To(Equal(fmt.Sprintf(`Error: there is no extension directory at or above: %s
t.Run(test.flag+"="+test.flagValue, func(t *testing.T) {
// Run "getenvoy extension build" with the flags we are testing
cmd, stdout, stderr := cmd2.NewRootCommand()
cmd.SetArgs([]string{"extension", "build", test.flag, test.flagValue})
err := cmdutil.Execute(cmd)
require.EqualError(t, err, test.expectedErr, `expected an error running [%v]`, cmd)

Run 'getenvoy extension build --help' for usage.
`, dir)))
// Verify the command failed with the expected error
require.Empty(t, stdout.String(), `expected no stdout running [%v]`, cmd)
expectedStderr := fmt.Sprintf("Error: %s\n\nRun 'getenvoy extension build --help' for usage.\n", test.expectedErr)
require.Equal(t, expectedStderr, stderr.String(), `expected stderr running [%v]`, cmd)
})
}
}

func TestGetEnvoyExtensionBuildFailsOutsideWorkspaceDirectory(t *testing.T) {
// Change to a non-workspace dir
dir, revertWd := cmd2.RequireChDir(t, relativeWorkspaceDir+"/..")
defer revertWd()

// Run "getenvoy extension build"
cmd, stdout, stderr := cmd2.NewRootCommand()
cmd.SetArgs([]string{"extension", "build"})
err := cmdutil.Execute(cmd)

// Verify the command failed with the expected error
expectedErr := "there is no extension directory at or above: " + dir
require.EqualError(t, err, expectedErr, `expected an error running [%v]`, cmd)
require.Empty(t, stdout.String(), `expected no stdout running [%v]`, cmd)
expectedStderr := fmt.Sprintf("Error: %s\n\nRun 'getenvoy extension build --help' for usage.\n", expectedErr)
require.Equal(t, expectedStderr, stderr.String(), `expected stderr running [%v]`, cmd)
}

func TestGetEnvoyExtensionBuild(t *testing.T) {
// We use a fake docker command to capture the commandline that would be invoked
dockerDir, revertPath := cmd2.RequireOverridePath(t, cmd2.FakeDockerDir)
defer revertPath()

// "getenvoy extension build" must be in a valid workspace directory
workspaceDir, revertWd := cmd2.RequireChDir(t, relativeWorkspaceDir)
defer revertWd()

// Fake the current user so we can test it is used in the docker args
expectedUser := user.User{Uid: "1001", Gid: "1002"}
revertGetCurrentUser := cmd2.OverrideGetCurrentUser(&expectedUser)
defer revertGetCurrentUser()

// Run "getenvoy extension build"
cmd, stdout, stderr := cmd2.NewRootCommand()
cmd.SetArgs([]string{"extension", "build"})
err := cmdutil.Execute(cmd)

// We expect docker to run from the correct path, as the current user and mount a volume for the correct workspace.
expectedDockerExec := fmt.Sprintf("%s/docker run -u %s:%s --rm -t -v %s:/source -w /source --init getenvoy/extension-rust-builder:latest build --output-file target/getenvoy/extension.wasm",
dockerDir, expectedUser.Uid, expectedUser.Gid, workspaceDir)

// Verify the command invoked, passing the correct default commandline
require.NoError(t, err, `expected no error running [%v]`, cmd)
require.Equal(t, expectedDockerExec+"\n", stdout.String(), `expected stdout running [%v]`, cmd)
require.Equal(t, "docker stderr\n", stderr.String(), `expected stderr running [%v]`, cmd)
}

// This tests --toolchain-container flags become docker command options
func TestGetEnvoyExtensionBuildWithDockerOptions(t *testing.T) {
// We use a fake docker command to capture the commandline that would be invoked
_, revertPath := cmd2.RequireOverridePath(t, cmd2.FakeDockerDir)
defer revertPath()

// "getenvoy extension build" must be in a valid workspace directory
_, revertWd := cmd2.RequireChDir(t, relativeWorkspaceDir)
defer revertWd()

// Run "getenvoy extension build"
cmd, stdout, stderr := cmd2.NewRootCommand()
cmd.SetArgs([]string{"extension", "build",
"--toolchain-container-image", "build/image",
"--toolchain-container-options", `-e 'VAR=VALUE' -v "/host:/container"`,
})
})
err := cmdutil.Execute(cmd)

// Verify the command's stdout includes the init args. TestGetEnvoyExtensionBuild tests the rest of stdout.
require.NoError(t, err, `expected no error running [%v]`, cmd)
require.Regexp(t, ".*--init -e VAR=VALUE -v /host:/container build/image build.*", stdout.String(), `expected stdout running [%v]`, cmd)
require.Equal(t, "docker stderr\n", stderr.String(), `expected stderr running [%v]`, cmd)
}

// TestGetEnvoyExtensionBuildFail ensures build failures show useful information in stderr
func TestGetEnvoyExtensionBuildFail(t *testing.T) {
// We use a fake docker command to capture the commandline that would be invoked, and force a failure.
dockerDir, revertPath := cmd2.RequireOverridePath(t, cmd2.FakeDockerDir)
defer revertPath()

// "getenvoy extension build" must be in a valid workspace directory
workspaceDir, revertWd := cmd2.RequireChDir(t, relativeWorkspaceDir)
defer revertWd()

// Fake the current user so we can test it is used in the docker args
expectedUser := user.User{Uid: "1001", Gid: "1002"}
revertGetCurrentUser := cmd2.OverrideGetCurrentUser(&expectedUser)
defer revertGetCurrentUser()

// "-e DOCKER_EXIT_CODE=3" is a special instruction handled in the fake docker script
toolchainOptions := "-e DOCKER_EXIT_CODE=3"
// Run "getenvoy extension build"
cmd, stdout, stderr := cmd2.NewRootCommand()
cmd.SetArgs([]string{"extension", "build", "--toolchain-container-options", toolchainOptions})
err := cmdutil.Execute(cmd)

// We expect the exit instruction to have gotten to the fake docker script, along with the default options.
expectedDockerExec := fmt.Sprintf("%s/docker run -u %s:%s --rm -t -v %s:/source -w /source --init %s getenvoy/extension-rust-builder:latest build --output-file target/getenvoy/extension.wasm",
dockerDir, expectedUser.Uid, expectedUser.Gid, workspaceDir, toolchainOptions)

// Verify the command failed with the expected error.
expectedErr := fmt.Sprintf(`failed to build Envoy extension using "default" toolchain: failed to execute an external command "%s": exit status 3`, expectedDockerExec)
require.EqualError(t, err, expectedErr, `expected an error running [%v]`, cmd)

// We should see stdout because the docker script was invoked.
require.Equal(t, expectedDockerExec+"\n", stdout.String(), `expected stdout running [%v]`, cmd)

// We also expect "docker stderr" in the output for the same reason.
expectedStderr := fmt.Sprintf("docker stderr\nError: %s\n\nRun 'getenvoy extension build --help' for usage.\n", expectedErr)
require.Equal(t, expectedStderr, stderr.String(), `expected stderr running [%v]`, cmd)
}
Loading

0 comments on commit d1ce5a3

Please sign in to comment.