chore: cmd test cleanup (#1289)

All tests which are directory-sensitive now also:
- reset viper
- use t.Cleanup
- use t.TempDir
- use a clean XDG_CONFIG_HOME
- specify explict name when creating
- moves helpers to root_test
This commit is contained in:
Luke Kingland 2022-10-03 12:03:28 -10:00 committed by GitHub
parent dc14ec7753
commit c9861ec24e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 168 additions and 198 deletions

View File

@ -6,7 +6,6 @@ import (
fn "knative.dev/kn-plugin-func" fn "knative.dev/kn-plugin-func"
"knative.dev/kn-plugin-func/mock" "knative.dev/kn-plugin-func/mock"
. "knative.dev/kn-plugin-func/testing"
) )
// TestBuild_ImageFlag ensures that the image flag is used when specified. // TestBuild_ImageFlag ensures that the image flag is used when specified.
@ -15,9 +14,7 @@ func TestBuild_ImageFlag(t *testing.T) {
args = []string{"--image", "docker.io/tigerteam/foo"} args = []string{"--image", "docker.io/tigerteam/foo"}
builder = mock.NewBuilder() builder = mock.NewBuilder()
) )
root := fromTempDirectory(t)
root, cleanup := Mktemp(t)
defer cleanup()
if err := fn.New().Create(fn.Function{Runtime: "go", Root: root, Registry: TestRegistry}); err != nil { if err := fn.New().Create(fn.Function{Runtime: "go", Root: root, Registry: TestRegistry}); err != nil {
t.Fatal(err) t.Fatal(err)
@ -48,9 +45,7 @@ func TestBuild_ImageFlag(t *testing.T) {
// provided (or exist on the function already), and the client has not been // provided (or exist on the function already), and the client has not been
// instantiated with a default registry, an ErrRegistryRequired is received. // instantiated with a default registry, an ErrRegistryRequired is received.
func TestBuild_RegistryOrImageRequired(t *testing.T) { func TestBuild_RegistryOrImageRequired(t *testing.T) {
t.Helper() root := fromTempDirectory(t)
root, rm := Mktemp(t)
defer rm()
if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil { if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil {
t.Fatal(err) t.Fatal(err)
@ -106,8 +101,7 @@ func TestBuild_BuilderValidated(t *testing.T) {
// - Push not triggered after an unsuccessful build // - Push not triggered after an unsuccessful build
// - Push can be disabled // - Push can be disabled
func TestBuild_Push(t *testing.T) { func TestBuild_Push(t *testing.T) {
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
f := fn.Function{ f := fn.Function{
Root: root, Root: root,

View File

@ -14,7 +14,6 @@ import (
) )
func TestListEnvs(t *testing.T) { func TestListEnvs(t *testing.T) {
mock := newMockLoaderSaver() mock := newMockLoaderSaver()
foo := "foo" foo := "foo"
bar := "bar" bar := "bar"

View File

@ -5,17 +5,16 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
. "knative.dev/kn-plugin-func/testing"
"knative.dev/kn-plugin-func/utils" "knative.dev/kn-plugin-func/utils"
) )
// TestCreate_Execute ensures that an invocation of create with minimal settings // TestCreate_Execute ensures that an invocation of create with minimal settings
// and valid input completes without error; degenerate case. // and valid input completes without error; degenerate case.
func TestCreate_Execute(t *testing.T) { func TestCreate_Execute(t *testing.T) {
defer Fromtemp(t)() _ = fromTempDirectory(t)
cmd := NewCreateCmd(NewClient) cmd := NewCreateCmd(NewClient)
cmd.SetArgs([]string{"--language", "go"}) cmd.SetArgs([]string{"--language", "go", "myfunc"})
if err := cmd.Execute(); err != nil { if err := cmd.Execute(); err != nil {
t.Fatal(err) t.Fatal(err)
@ -25,10 +24,10 @@ func TestCreate_Execute(t *testing.T) {
// TestCreate_NoRuntime ensures that an invocation of create must be // TestCreate_NoRuntime ensures that an invocation of create must be
// done with a runtime. // done with a runtime.
func TestCreate_NoRuntime(t *testing.T) { func TestCreate_NoRuntime(t *testing.T) {
defer Fromtemp(t)() _ = fromTempDirectory(t)
cmd := NewCreateCmd(NewClient) cmd := NewCreateCmd(NewClient)
cmd.SetArgs([]string{}) // Do not use test command args cmd.SetArgs([]string{"myfunc"}) // Do not use test command args
err := cmd.Execute() err := cmd.Execute()
var e ErrNoRuntime var e ErrNoRuntime
@ -40,10 +39,10 @@ func TestCreate_NoRuntime(t *testing.T) {
// TestCreate_WithNoRuntime ensures that an invocation of create must be // TestCreate_WithNoRuntime ensures that an invocation of create must be
// done with one of the valid runtimes only. // done with one of the valid runtimes only.
func TestCreate_WithInvalidRuntime(t *testing.T) { func TestCreate_WithInvalidRuntime(t *testing.T) {
defer Fromtemp(t)() _ = fromTempDirectory(t)
cmd := NewCreateCmd(NewClient) cmd := NewCreateCmd(NewClient)
cmd.SetArgs([]string{"--language", "invalid"}) cmd.SetArgs([]string{"--language", "invalid", "myfunc"})
err := cmd.Execute() err := cmd.Execute()
var e ErrInvalidRuntime var e ErrInvalidRuntime
@ -55,10 +54,10 @@ func TestCreate_WithInvalidRuntime(t *testing.T) {
// TestCreate_InvalidTemplate ensures that an invocation of create must be // TestCreate_InvalidTemplate ensures that an invocation of create must be
// done with one of the valid templates only. // done with one of the valid templates only.
func TestCreate_InvalidTemplate(t *testing.T) { func TestCreate_InvalidTemplate(t *testing.T) {
defer Fromtemp(t)() _ = fromTempDirectory(t)
cmd := NewCreateCmd(NewClient) cmd := NewCreateCmd(NewClient)
cmd.SetArgs([]string{"--language", "go", "--template", "invalid"}) cmd.SetArgs([]string{"--language", "go", "--template", "invalid", "myfunc"})
err := cmd.Execute() err := cmd.Execute()
var e ErrInvalidTemplate var e ErrInvalidTemplate
@ -70,7 +69,7 @@ func TestCreate_InvalidTemplate(t *testing.T) {
// TestCreate_ValidatesName ensures that the create command only accepts // TestCreate_ValidatesName ensures that the create command only accepts
// DNS-1123 labels for function name. // DNS-1123 labels for function name.
func TestCreate_ValidatesName(t *testing.T) { func TestCreate_ValidatesName(t *testing.T) {
defer Fromtemp(t)() _ = fromTempDirectory(t)
// Execute the command with a function name containing invalid characters and // Execute the command with a function name containing invalid characters and
// confirm the expected error is returned // confirm the expected error is returned
@ -87,9 +86,10 @@ func TestCreate_ValidatesName(t *testing.T) {
// the expected repositories path, respecting the setting for XDG_CONFIG_PATH // the expected repositories path, respecting the setting for XDG_CONFIG_PATH
// when deriving the default // when deriving the default
func TestCreateConfig_RepositoriesPath(t *testing.T) { func TestCreateConfig_RepositoriesPath(t *testing.T) {
defer Fromtemp(t)() _ = fromTempDirectory(t)
// Update XDG_CONFIG_HOME to point to some arbitrary location. // Update XDG_CONFIG_HOME to point to some arbitrary location which we know
// The above call to fromTempDirectory also updates, but value is not returned.
xdgConfigHome := t.TempDir() xdgConfigHome := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", xdgConfigHome) t.Setenv("XDG_CONFIG_HOME", xdgConfigHome)
@ -97,7 +97,7 @@ func TestCreateConfig_RepositoriesPath(t *testing.T) {
expected := filepath.Join(xdgConfigHome, "func", "repositories") expected := filepath.Join(xdgConfigHome, "func", "repositories")
cmd := NewCreateCmd(NewClient) cmd := NewCreateCmd(NewClient)
cmd.SetArgs([]string{}) // Do not use test command args cmd.SetArgs([]string{"myfunc"}) // Do not use test command args
cfg, err := newCreateConfig(cmd, []string{}, NewClient) cfg, err := newCreateConfig(cmd, []string{}, NewClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -111,19 +111,12 @@ func TestCreateConfig_RepositoriesPath(t *testing.T) {
// TestCreate_ConfigOptional ensures that the system can be used without // TestCreate_ConfigOptional ensures that the system can be used without
// any additional configuration being required. // any additional configuration being required.
func TestCreate_ConfigOptional(t *testing.T) { func TestCreate_ConfigOptional(t *testing.T) {
// Empty Home _ = fromTempDirectory(t)
// the func directory, and other static assets will be created here
// if they do not exist. t.Setenv("XDG_CONFIG_HOME", t.TempDir())
home, rm := Mktemp(t)
defer rm()
t.Setenv("XDG_CONFIG_HOME", home)
// Immediately using "create" in a new empty directory should not fail;
// even when this home directory is devoid of config files.
_, rm2 := Mktemp(t)
defer rm2()
cmd := NewCreateCmd(NewClient) cmd := NewCreateCmd(NewClient)
cmd.SetArgs([]string{"--language=go"}) cmd.SetArgs([]string{"--language=go", "myfunc"})
if err := cmd.Execute(); err != nil { if err := cmd.Execute(); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -6,7 +6,6 @@ import (
fn "knative.dev/kn-plugin-func" fn "knative.dev/kn-plugin-func"
"knative.dev/kn-plugin-func/mock" "knative.dev/kn-plugin-func/mock"
. "knative.dev/kn-plugin-func/testing"
) )
// TestDelete_ByName ensures that running delete specifying the name of the // TestDelete_ByName ensures that running delete specifying the name of the
@ -46,8 +45,7 @@ func TestDelete_ByName(t *testing.T) {
// TestDelete_ByProject ensures that running delete with a valid project as its // TestDelete_ByProject ensures that running delete with a valid project as its
// context invokes remove and with the correct name (reads name from func.yaml) // context invokes remove and with the correct name (reads name from func.yaml)
func TestDelete_ByProject(t *testing.T) { func TestDelete_ByProject(t *testing.T) {
// from within a new temporary directory _ = fromTempDirectory(t)
defer Fromtemp(t)()
// Write a func.yaml config which specifies a name // Write a func.yaml config which specifies a name
funcYaml := `name: bar funcYaml := `name: bar

View File

@ -14,7 +14,6 @@ import (
fn "knative.dev/kn-plugin-func" fn "knative.dev/kn-plugin-func"
"knative.dev/kn-plugin-func/builders" "knative.dev/kn-plugin-func/builders"
"knative.dev/kn-plugin-func/mock" "knative.dev/kn-plugin-func/mock"
. "knative.dev/kn-plugin-func/testing"
) )
const TestRegistry = "example.com/alice" const TestRegistry = "example.com/alice"
@ -24,8 +23,7 @@ type commandConstructor func(ClientFactory) *cobra.Command
// TestDeploy_Default ensures that running deploy on a valid default Function // TestDeploy_Default ensures that running deploy on a valid default Function
// (only required options populated; all else default) completes successfully. // (only required options populated; all else default) completes successfully.
func TestDeploy_Default(t *testing.T) { func TestDeploy_Default(t *testing.T) {
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
// A Function with the minimum required values for deployment populated. // A Function with the minimum required values for deployment populated.
f := fn.Function{ f := fn.Function{
@ -57,8 +55,7 @@ func TestDeploy_RegistryOrImageRequired(t *testing.T) {
func testRegistryOrImageRequired(cmdFn commandConstructor, t *testing.T) { func testRegistryOrImageRequired(cmdFn commandConstructor, t *testing.T) {
t.Helper() t.Helper()
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil { if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil {
t.Fatal(err) t.Fatal(err)
@ -95,8 +92,7 @@ func TestDeploy_ImageAndRegistry(t *testing.T) {
func testImageAndRegistry(cmdFn commandConstructor, t *testing.T) { func testImageAndRegistry(cmdFn commandConstructor, t *testing.T) {
t.Helper() t.Helper()
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil { if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil {
t.Fatal(err) t.Fatal(err)
@ -171,8 +167,7 @@ func TestDeploy_InvalidRegistry(t *testing.T) {
func testInvalidRegistry(cmdFn commandConstructor, t *testing.T) { func testInvalidRegistry(cmdFn commandConstructor, t *testing.T) {
t.Helper() t.Helper()
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
f := fn.Function{ f := fn.Function{
Root: root, Root: root,
@ -204,8 +199,7 @@ func TestDeploy_RegistryLoads(t *testing.T) {
func testRegistryLoads(cmdFn commandConstructor, t *testing.T) { func testRegistryLoads(cmdFn commandConstructor, t *testing.T) {
t.Helper() t.Helper()
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
f := fn.Function{ f := fn.Function{
Root: root, Root: root,
@ -246,11 +240,9 @@ func TestDeploy_BuilderPersists(t *testing.T) {
} }
func testBuilderPersists(cmdFn commandConstructor, t *testing.T) { func testBuilderPersists(cmdFn commandConstructor, t *testing.T) {
t.Setenv("KUBECONFIG", fmt.Sprintf("%s/testdata/kubeconfig_deploy_namespace", cwd()))
t.Helper() t.Helper()
root, rm := Mktemp(t) t.Setenv("KUBECONFIG", fmt.Sprintf("%s/testdata/kubeconfig_deploy_namespace", cwd()))
defer rm() root := fromTempDirectory(t)
if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil { if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil {
t.Fatal(err) t.Fatal(err)
@ -340,8 +332,7 @@ func TestDeploy_BuilderValidated(t *testing.T) {
func testBuilderValidated(cmdFn commandConstructor, t *testing.T) { func testBuilderValidated(cmdFn commandConstructor, t *testing.T) {
t.Helper() t.Helper()
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil { if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil {
t.Fatal(err) t.Fatal(err)
@ -406,8 +397,7 @@ func TestDeploy_RemoteBuildURLPermutations(t *testing.T) {
// returns a single test function for one possible permutation of the flags. // returns a single test function for one possible permutation of the flags.
newTestFn := func(remote, build, url string) func(t *testing.T) { newTestFn := func(remote, build, url string) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
// Create a new Function in the temp directory // Create a new Function in the temp directory
if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil { if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil {
@ -580,8 +570,7 @@ func Test_ImageWithDigestErrors(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// Move into a new temp directory // Move into a new temp directory
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
// Create a new Function in the temp directory // Create a new Function in the temp directory
if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil { if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil {
@ -677,8 +666,7 @@ func Test_namespace(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
t.Helper() t.Helper()
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
// if running with an active kubeconfig // if running with an active kubeconfig
if test.context { if test.context {
@ -736,8 +724,7 @@ Test_namespaceCheck cases were refactored into:
// TestDeploy_GitArgsPersist ensures that the git flags, if provided, are // TestDeploy_GitArgsPersist ensures that the git flags, if provided, are
// persisted to the Function for subsequent deployments. // persisted to the Function for subsequent deployments.
func TestDeploy_GitArgsPersist(t *testing.T) { func TestDeploy_GitArgsPersist(t *testing.T) {
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
var ( var (
url = "https://example.com/user/repo" url = "https://example.com/user/repo"
@ -778,8 +765,7 @@ func TestDeploy_GitArgsPersist(t *testing.T) {
// TestDeploy_GitArgsUsed ensures that any git values provided as flags are used // TestDeploy_GitArgsUsed ensures that any git values provided as flags are used
// when invoking a remote deployment. // when invoking a remote deployment.
func TestDeploy_GitArgsUsed(t *testing.T) { func TestDeploy_GitArgsUsed(t *testing.T) {
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
var ( var (
url = "https://example.com/user/repo" url = "https://example.com/user/repo"
@ -821,8 +807,7 @@ func TestDeploy_GitArgsUsed(t *testing.T) {
// TestDeploy_GitURLBranch ensures that a --git-url which specifies the branch // TestDeploy_GitURLBranch ensures that a --git-url which specifies the branch
// in the URL is equivalent to providing --git-branch // in the URL is equivalent to providing --git-branch
func TestDeploy_GitURLBranch(t *testing.T) { func TestDeploy_GitURLBranch(t *testing.T) {
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil { if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil {
t.Fatal(err) t.Fatal(err)
@ -861,12 +846,8 @@ func TestDeploy_GitURLBranch(t *testing.T) {
// TestDeploy_NamespaceDefaults ensures that when not specified, a users's // TestDeploy_NamespaceDefaults ensures that when not specified, a users's
// active kubernetes context is used for the namespace if available. // active kubernetes context is used for the namespace if available.
func TestDeploy_NamespaceDefaults(t *testing.T) { func TestDeploy_NamespaceDefaults(t *testing.T) {
// Set kube context to test context
t.Setenv("KUBECONFIG", filepath.Join(cwd(), "testdata", "kubeconfig_deploy_namespace")) t.Setenv("KUBECONFIG", filepath.Join(cwd(), "testdata", "kubeconfig_deploy_namespace"))
root := fromTempDirectory(t)
// from a temp directory
root, rm := Mktemp(t)
defer rm()
// Create a new function // Create a new function
if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil { if err := fn.New().Create(fn.Function{Runtime: "go", Root: root}); err != nil {
@ -913,8 +894,7 @@ func TestDeploy_NamespaceDefaults(t *testing.T) {
// Also implicitly checks that the --namespace flag takes precidence over // Also implicitly checks that the --namespace flag takes precidence over
// the namespace of a previously deployed Function. // the namespace of a previously deployed Function.
func TestDeploy_NamespaceUpdateWarning(t *testing.T) { func TestDeploy_NamespaceUpdateWarning(t *testing.T) {
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
// Create a Function which appears to have been deployed to 'myns' // Create a Function which appears to have been deployed to 'myns'
f := fn.Function{ f := fn.Function{
@ -975,10 +955,7 @@ func TestDeploy_NamespaceUpdateWarning(t *testing.T) {
func TestDeploy_NamespaceRedeployWarning(t *testing.T) { func TestDeploy_NamespaceRedeployWarning(t *testing.T) {
// Change profile to one whose current profile is 'test-ns-deploy' // Change profile to one whose current profile is 'test-ns-deploy'
t.Setenv("KUBECONFIG", filepath.Join(cwd(), "testdata", "kubeconfig_deploy_namespace")) t.Setenv("KUBECONFIG", filepath.Join(cwd(), "testdata", "kubeconfig_deploy_namespace"))
root := fromTempDirectory(t)
// From within a temp directory
root, rm := Mktemp(t)
defer rm()
// Create a Function which appears to have been deployed to 'myns' // Create a Function which appears to have been deployed to 'myns'
f := fn.Function{ f := fn.Function{

View File

@ -12,13 +12,11 @@ import (
fn "knative.dev/kn-plugin-func" fn "knative.dev/kn-plugin-func"
"knative.dev/kn-plugin-func/mock" "knative.dev/kn-plugin-func/mock"
. "knative.dev/kn-plugin-func/testing"
) )
// TestInvoke command executes the invocation path. // TestInvoke command executes the invocation path.
func TestInvoke(t *testing.T) { func TestInvoke(t *testing.T) {
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
var invoked int32 var invoked int32
@ -78,8 +76,7 @@ func TestInvoke(t *testing.T) {
// TestInvoke_Namespace ensures that invocation uses the Function's namespace // TestInvoke_Namespace ensures that invocation uses the Function's namespace
// despite the currently active. // despite the currently active.
func TestInvoke_Namespace(t *testing.T) { func TestInvoke_Namespace(t *testing.T) {
root, rm := Mktemp(t) root := fromTempDirectory(t)
defer rm()
// Create a Function in a non-active namespace // Create a Function in a non-active namespace
f := fn.Function{Runtime: "go", Root: root, Deploy: fn.DeploySpec{Namespace: "ns"}} f := fn.Function{Runtime: "go", Root: root, Deploy: fn.DeploySpec{Namespace: "ns"}}

View File

@ -10,7 +10,8 @@ import (
// all supported languages is to print a plain text list of all the builtin // all supported languages is to print a plain text list of all the builtin
// language runtimes. // language runtimes.
func TestLanguages_Default(t *testing.T) { func TestLanguages_Default(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir()) // ignore user-added _ = fromTempDirectory(t)
buf := piped(t) // gather output buf := piped(t) // gather output
cmd := NewLanguagesCmd(NewClientFactory(func() *fn.Client { cmd := NewLanguagesCmd(NewClientFactory(func() *fn.Client {
return fn.New() return fn.New()
@ -35,7 +36,8 @@ typescript`
// TestLanguages_JSON ensures that listing languages in --json format returns // TestLanguages_JSON ensures that listing languages in --json format returns
// builtin languages as a JSON array. // builtin languages as a JSON array.
func TestLanguages_JSON(t *testing.T) { func TestLanguages_JSON(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir()) // ignore user-added _ = fromTempDirectory(t)
buf := piped(t) // gather output buf := piped(t) // gather output
cmd := NewLanguagesCmd(NewClientFactory(func() *fn.Client { cmd := NewLanguagesCmd(NewClientFactory(func() *fn.Client {
return fn.New() return fn.New()

View File

@ -1,9 +1,6 @@
package cmd package cmd
import ( import (
"io"
"os"
"strings"
"testing" "testing"
. "knative.dev/kn-plugin-func/testing" . "knative.dev/kn-plugin-func/testing"
@ -13,7 +10,8 @@ import (
// set of repositories by name for builtin repositories, by explicitly // set of repositories by name for builtin repositories, by explicitly
// setting the repositories' path to a new path which includes no others. // setting the repositories' path to a new path which includes no others.
func TestRepository_List(t *testing.T) { func TestRepository_List(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir()) _ = fromTempDirectory(t)
cmd := NewRepositoryListCmd(NewClient) cmd := NewRepositoryListCmd(NewClient)
cmd.SetArgs([]string{}) // Do not use test command args cmd.SetArgs([]string{}) // Do not use test command args
@ -35,7 +33,9 @@ func TestRepository_List(t *testing.T) {
// arguments, respects the repositories' path flag, and the expected name is echoed // arguments, respects the repositories' path flag, and the expected name is echoed
// upon subsequent 'list'. // upon subsequent 'list'.
func TestRepository_Add(t *testing.T) { func TestRepository_Add(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir()) url := ServeRepo("repository.git", t)
_ = fromTempDirectory(t)
var ( var (
add = NewRepositoryAddCmd(NewClient) add = NewRepositoryAddCmd(NewClient)
list = NewRepositoryListCmd(NewClient) list = NewRepositoryListCmd(NewClient)
@ -48,7 +48,7 @@ func TestRepository_Add(t *testing.T) {
// add [flags] <old> <new> // add [flags] <old> <new>
add.SetArgs([]string{ add.SetArgs([]string{
"newrepo", "newrepo",
TestRepoURI("repository", t), url,
}) })
// Parse flags and args, performing action // Parse flags and args, performing action
@ -73,7 +73,9 @@ func TestRepository_Add(t *testing.T) {
// positional arguments, respects the repositories' path flag, and the name is // positional arguments, respects the repositories' path flag, and the name is
// reflected as having been renamed upon subsequent 'list'. // reflected as having been renamed upon subsequent 'list'.
func TestRepository_Rename(t *testing.T) { func TestRepository_Rename(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir()) url := ServeRepo("repository.git", t)
_ = fromTempDirectory(t)
var ( var (
add = NewRepositoryAddCmd(NewClient) add = NewRepositoryAddCmd(NewClient)
rename = NewRepositoryRenameCmd(NewClient) rename = NewRepositoryRenameCmd(NewClient)
@ -86,7 +88,7 @@ func TestRepository_Rename(t *testing.T) {
list.SetArgs([]string{}) list.SetArgs([]string{})
// add a repo which will be renamed // add a repo which will be renamed
add.SetArgs([]string{"newrepo", TestRepoURI("repository", t)}) add.SetArgs([]string{"newrepo", url})
if err := add.Execute(); err != nil { if err := add.Execute(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -119,7 +121,9 @@ func TestRepository_Rename(t *testing.T) {
// its argument, respects the repositories' flag, and the entry is removed upon // its argument, respects the repositories' flag, and the entry is removed upon
// subsequent 'list'. // subsequent 'list'.
func TestRepository_Remove(t *testing.T) { func TestRepository_Remove(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir()) url := ServeRepo("repository.git", t)
_ = fromTempDirectory(t)
var ( var (
add = NewRepositoryAddCmd(NewClient) add = NewRepositoryAddCmd(NewClient)
remove = NewRepositoryRemoveCmd(NewClient) remove = NewRepositoryRemoveCmd(NewClient)
@ -132,7 +136,7 @@ func TestRepository_Remove(t *testing.T) {
list.SetArgs([]string{}) list.SetArgs([]string{})
// add a repo which will be removed // add a repo which will be removed
add.SetArgs([]string{"newrepo", TestRepoURI("repository", t)}) add.SetArgs([]string{"newrepo", url})
if err := add.Execute(); err != nil { if err := add.Execute(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -159,42 +163,3 @@ func TestRepository_Remove(t *testing.T) {
t.Fatalf("expected:\n'%v'\ngot:\n'%v'\n", expect, output) t.Fatalf("expected:\n'%v'\ngot:\n'%v'\n", expect, output)
} }
} }
// Helpers
// -------
// pipe the output of stdout to a buffer whose value is returned
// from the returned function. Call pipe() to start piping output
// to the buffer, call the returned function to access the data in
// the buffer.
func piped(t *testing.T) func() string {
t.Helper()
var (
o = os.Stdout
c = make(chan error, 1)
b strings.Builder
)
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
os.Stdout = w
go func() {
_, err := io.Copy(&b, r)
r.Close()
c <- err
}()
return func() string {
os.Stdout = o
w.Close()
err := <-c
if err != nil {
t.Fatal(err)
}
return strings.TrimSpace(b.String())
}
}

View File

@ -3,6 +3,8 @@ package cmd
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"os"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@ -37,9 +39,10 @@ func TestRoot_PersistentFlags(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
defer Fromtemp(t)() // from a temp dir, deferred cleanup _ = fromTempDirectory(t)
cmd := NewCreateCmd(NewClient) // Create a new function
cmd.SetArgs([]string{"--language", "go"}) // providing language (the only required param) cmd := NewCreateCmd(NewClient) // Create a function
cmd.SetArgs([]string{"--language", "go", "myfunc"}) // providing language
if err := cmd.Execute(); err != nil { // fail on any errors if err := cmd.Execute(); err != nil { // fail on any errors
t.Fatal(err) t.Fatal(err)
} }
@ -252,3 +255,55 @@ func TestVerbose(t *testing.T) {
}) })
} }
} }
// Helpers
// -------
// pipe the output of stdout to a buffer whose value is returned
// from the returned function. Call pipe() to start piping output
// to the buffer, call the returned function to access the data in
// the buffer.
func piped(t *testing.T) func() string {
t.Helper()
var (
o = os.Stdout
c = make(chan error, 1)
b strings.Builder
)
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
os.Stdout = w
go func() {
_, err := io.Copy(&b, r)
r.Close()
c <- err
}()
return func() string {
os.Stdout = o
w.Close()
err := <-c
if err != nil {
t.Fatal(err)
}
return strings.TrimSpace(b.String())
}
}
// fromTempDirectory is a cli-specific test helper which
// - creates a temp directory and changes to it as the new working directory
// - creates a temp directory and uses it for XDG_CONFIG_HOME
// - removes temp directories on cleanup
// - resets viper on cleanum (the reason this is "cli-specific")
func fromTempDirectory(t *testing.T) string {
t.Helper()
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
d, done := Mktemp(t) // creates and CDs to 'tmp'
t.Cleanup(func() { done(); viper.Reset() })
return d
}

View File

@ -8,7 +8,6 @@ import (
fn "knative.dev/kn-plugin-func" fn "knative.dev/kn-plugin-func"
"knative.dev/kn-plugin-func/mock" "knative.dev/kn-plugin-func/mock"
. "knative.dev/kn-plugin-func/testing"
) )
func TestRun_Run(t *testing.T) { func TestRun_Run(t *testing.T) {
@ -100,7 +99,7 @@ created: 2009-11-10 23:00:00`,
for _, tt := range tests { for _, tt := range tests {
// run as a sub-test // run as a sub-test
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
defer Fromtemp(t)() _ = fromTempDirectory(t)
runner := mock.NewRunner() runner := mock.NewRunner()
if tt.runError != nil { if tt.runError != nil {

View File

@ -12,7 +12,8 @@ import (
// TestTemplates_Default ensures that the default behavior is listing all // TestTemplates_Default ensures that the default behavior is listing all
// templates for all language runtimes. // templates for all language runtimes.
func TestTemplates_Default(t *testing.T) { func TestTemplates_Default(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir()) // ignore user-added _ = fromTempDirectory(t)
buf := piped(t) // gather output buf := piped(t) // gather output
cmd := NewTemplatesCmd(NewClientFactory(func() *fn.Client { cmd := NewTemplatesCmd(NewClientFactory(func() *fn.Client {
return fn.New() return fn.New()
@ -45,7 +46,8 @@ typescript http`
// TestTemplates_JSON ensures that listing templates respects the --json // TestTemplates_JSON ensures that listing templates respects the --json
// output format. // output format.
func TestTemplates_JSON(t *testing.T) { func TestTemplates_JSON(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir()) // ignore user-added _ = fromTempDirectory(t)
buf := piped(t) // gather output buf := piped(t) // gather output
cmd := NewTemplatesCmd(NewClientFactory(func() *fn.Client { cmd := NewTemplatesCmd(NewClientFactory(func() *fn.Client {
return fn.New() return fn.New()
@ -104,7 +106,8 @@ func TestTemplates_JSON(t *testing.T) {
// TestTemplates_ByLanguage ensures that the output is correctly filtered // TestTemplates_ByLanguage ensures that the output is correctly filtered
// by language runtime when provided. // by language runtime when provided.
func TestTemplates_ByLanguage(t *testing.T) { func TestTemplates_ByLanguage(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir()) // ignore user-added _ = fromTempDirectory(t)
cmd := NewTemplatesCmd(NewClientFactory(func() *fn.Client { cmd := NewTemplatesCmd(NewClientFactory(func() *fn.Client {
return fn.New() return fn.New()
})) }))
@ -144,7 +147,8 @@ http`
} }
func TestTemplates_ErrTemplateRepoDoesNotExist(t *testing.T) { func TestTemplates_ErrTemplateRepoDoesNotExist(t *testing.T) {
defer t.Setenv("XDG_CONFIG_HOME", t.TempDir()) _ = fromTempDirectory(t)
cmd := NewTemplatesCmd(NewClientFactory(func() *fn.Client { cmd := NewTemplatesCmd(NewClientFactory(func() *fn.Client {
return fn.New() return fn.New()
})) }))
@ -155,7 +159,8 @@ func TestTemplates_ErrTemplateRepoDoesNotExist(t *testing.T) {
} }
func TestTemplates_WrongRepositoryUrl(t *testing.T) { func TestTemplates_WrongRepositoryUrl(t *testing.T) {
defer t.Setenv("XDG_CONFIG_HOME", t.TempDir()) _ = fromTempDirectory(t)
cmd := NewTemplatesCmd(NewClientFactory(func() *fn.Client { cmd := NewTemplatesCmd(NewClientFactory(func() *fn.Client {
return fn.New() return fn.New()
})) }))

View File

@ -18,7 +18,7 @@ import (
// test. Others do eist with specific test requirements that are mutually // test. Others do eist with specific test requirements that are mutually
// exclusive, such as manifest differences, and are specified inline to their // exclusive, such as manifest differences, and are specified inline to their
// requisite test. // requisite test.
const RepositoriesTestRepo = "repository" const RepositoriesTestRepo = "repository.git"
// TestRepositories_List ensures the base case of listing // TestRepositories_List ensures the base case of listing
// repositories without error in the default scenario of builtin only. // repositories without error in the default scenario of builtin only.
@ -69,7 +69,7 @@ func TestRepositories_Get(t *testing.T) {
// TestRepositories_All ensures repos are returned from // TestRepositories_All ensures repos are returned from
// .All accessor. Tests both builtin and buitlin+extensible cases. // .All accessor. Tests both builtin and buitlin+extensible cases.
func TestRepositories_All(t *testing.T) { func TestRepositories_All(t *testing.T) {
uri := TestRepoURI(RepositoriesTestRepo, t) uri := ServeRepo(RepositoriesTestRepo, t)
root, rm := Mktemp(t) root, rm := Mktemp(t)
defer rm() defer rm()
@ -99,14 +99,14 @@ func TestRepositories_All(t *testing.T) {
// Assert it now includes both builtin and extended // Assert it now includes both builtin and extended
if len(repositories) != 2 || if len(repositories) != 2 ||
repositories[0].Name != fn.DefaultRepositoryName || repositories[0].Name != fn.DefaultRepositoryName ||
repositories[1].Name != RepositoriesTestRepo { repositories[1].Name != "repository" {
t.Fatal("Repositories list does not pass shallow repository membership check") t.Fatal("Repositories list does not pass shallow repository membership check")
} }
} }
// TestRepositories_Add checks basic adding of a repository by URI. // TestRepositories_Add checks basic adding of a repository by URI.
func TestRepositories_Add(t *testing.T) { func TestRepositories_Add(t *testing.T) {
uri := TestRepoURI(RepositoriesTestRepo, t) // ./testdata/$RepositoriesTestRepo.git uri := ServeRepo(RepositoriesTestRepo, t) // ./testdata/$RepositoriesTestRepo.git
root, rm := Mktemp(t) // create and cd to a temp dir, returning path. root, rm := Mktemp(t) // create and cd to a temp dir, returning path.
defer rm() defer rm()
@ -144,7 +144,7 @@ func TestRepositories_AddDeafultName(t *testing.T) {
// repo meant to exemplify the simplest use case: a repo with no metadata // repo meant to exemplify the simplest use case: a repo with no metadata
// that simply contains templates, grouped by runtime. It therefore does // that simply contains templates, grouped by runtime. It therefore does
// not have a manifest and the default name will therefore be the repo name // not have a manifest and the default name will therefore be the repo name
uri := TestRepoURI(RepositoriesTestRepo, t) // ./testdata/$RepositoriesTestRepo.git uri := ServeRepo(RepositoriesTestRepo, t) // ./testdata/$RepositoriesTestRepo.git
root, rm := Mktemp(t) root, rm := Mktemp(t)
defer rm() defer rm()
@ -156,7 +156,7 @@ func TestRepositories_AddDeafultName(t *testing.T) {
} }
// The name returned should be the repo name // The name returned should be the repo name
if name != RepositoriesTestRepo { if name != "repository" {
t.Fatalf("expected returned name '%v', got '%v'", RepositoriesTestRepo, name) t.Fatalf("expected returned name '%v', got '%v'", RepositoriesTestRepo, name)
} }
@ -165,7 +165,7 @@ func TestRepositories_AddDeafultName(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
expected := []string{"default", RepositoriesTestRepo} expected := []string{"default", "repository"}
if diff := cmp.Diff(expected, rr); diff != "" { if diff := cmp.Diff(expected, rr); diff != "" {
t.Error("Repositories differed (-want, +got):", diff) t.Error("Repositories differed (-want, +got):", diff)
} }
@ -179,7 +179,7 @@ func TestRepositories_AddWithManifest(t *testing.T) {
// defines a custom language pack and makes full use of the manifest.yaml. // defines a custom language pack and makes full use of the manifest.yaml.
// The manifest.yaml is included which specifies things like custom templates // The manifest.yaml is included which specifies things like custom templates
// location and (appropos to this test) a default name/ // location and (appropos to this test) a default name/
uri := TestRepoURI("repository-a", t) // ./testdata/repository-a.git uri := ServeRepo("repository-a", t) // ./testdata/repository-a.git
root, rm := Mktemp(t) root, rm := Mktemp(t)
defer rm() defer rm()
@ -210,7 +210,7 @@ func TestRepositories_AddWithManifest(t *testing.T) {
// TestRepositories_AddExistingErrors ensures that adding a repository that // TestRepositories_AddExistingErrors ensures that adding a repository that
// already exists yields an error. // already exists yields an error.
func TestRepositories_AddExistingErrors(t *testing.T) { func TestRepositories_AddExistingErrors(t *testing.T) {
uri := TestRepoURI(RepositoriesTestRepo, t) uri := ServeRepo(RepositoriesTestRepo, t)
root, rm := Mktemp(t) // create and cd to a temp dir, returning path. root, rm := Mktemp(t) // create and cd to a temp dir, returning path.
defer rm() defer rm()
@ -244,7 +244,7 @@ func TestRepositories_AddExistingErrors(t *testing.T) {
// TestRepositories_Rename ensures renaming a repository succeeds. // TestRepositories_Rename ensures renaming a repository succeeds.
func TestRepositories_Rename(t *testing.T) { func TestRepositories_Rename(t *testing.T) {
uri := TestRepoURI(RepositoriesTestRepo, t) uri := ServeRepo(RepositoriesTestRepo, t)
root, rm := Mktemp(t) // create and cd to a temp dir, returning path. root, rm := Mktemp(t) // create and cd to a temp dir, returning path.
defer rm() defer rm()
@ -278,7 +278,7 @@ func TestRepositories_Rename(t *testing.T) {
// TestRepositories_Remove ensures that removing a repository by name // TestRepositories_Remove ensures that removing a repository by name
// removes it from the list and FS. // removes it from the list and FS.
func TestRepositories_Remove(t *testing.T) { func TestRepositories_Remove(t *testing.T) {
uri := TestRepoURI(RepositoriesTestRepo, t) // ./testdata/repository.git uri := ServeRepo(RepositoriesTestRepo, t) // ./testdata/repository.git
root, rm := Mktemp(t) // create and cd to a temp dir root, rm := Mktemp(t) // create and cd to a temp dir
defer rm() defer rm()
@ -313,7 +313,7 @@ func TestRepositories_Remove(t *testing.T) {
// TestRepositories_URL ensures that a repository populates its URL member // TestRepositories_URL ensures that a repository populates its URL member
// from the git repository's origin url (if it is a git repo and exists) // from the git repository's origin url (if it is a git repo and exists)
func TestRepositories_URL(t *testing.T) { func TestRepositories_URL(t *testing.T) {
uri := TestRepoURI(RepositoriesTestRepo, t) uri := ServeRepo(RepositoriesTestRepo, t)
root, rm := Mktemp(t) root, rm := Mktemp(t)
defer rm() defer rm()

View File

@ -163,7 +163,7 @@ func TestTemplates_Remote(t *testing.T) {
root := "testdata/testTemplatesRemote" root := "testdata/testTemplatesRemote"
defer Using(t, root)() defer Using(t, root)()
url := TestRepoURI(RepositoriesTestRepo, t) url := ServeRepo(RepositoriesTestRepo, t)
// Create a client which explicitly specifies the Git repo at URL // Create a client which explicitly specifies the Git repo at URL
// rather than relying on the default internally builtin template repo // rather than relying on the default internally builtin template repo
@ -328,7 +328,7 @@ func TestTemplates_ModeRemote(t *testing.T) {
root := "testdata/testTemplates_ModeRemote" root := "testdata/testTemplates_ModeRemote"
defer Using(t, root)() defer Using(t, root)()
url := TestRepoURI(RepositoriesTestRepo, t) url := ServeRepo(RepositoriesTestRepo, t)
client := fn.New( client := fn.New(
fn.WithRegistry(TestRegistry), fn.WithRegistry(TestRegistry),

View File

@ -82,11 +82,10 @@ func Within(t *testing.T, root string) func() {
// errors encountererd fail the current test. // errors encountererd fail the current test.
func Mktemp(t *testing.T) (string, func()) { func Mktemp(t *testing.T) (string, func()) {
t.Helper() t.Helper()
tmp := tempdir(t) tmp := t.TempDir()
owd := pwd(t) owd := pwd(t)
cd(t, tmp) cd(t, tmp)
return tmp, func() { return tmp, func() {
os.RemoveAll(tmp)
cd(t, owd) cd(t, owd)
} }
} }
@ -97,19 +96,6 @@ func Fromtemp(t *testing.T) func() {
return done return done
} }
// tempdir creates a new temporary directory and returns its path.
// errors fail the current test.
func tempdir(t *testing.T) string {
// NOTE: Not using t.TempDir() because it is sometimes helpful during
// debugging to skip running the returned deferred cleanup function
// and manually inspect the contents of the test's temp directory.
d, err := os.MkdirTemp("", "dir")
if err != nil {
t.Fatal(err)
}
return d
}
// pwd prints the current working directory. // pwd prints the current working directory.
// errors fail the test. // errors fail the test.
func pwd(t *testing.T) string { func pwd(t *testing.T) string {
@ -129,20 +115,20 @@ func cd(t *testing.T, dir string) {
} }
} }
// TestRepoURI starts serving HTTP git server with GIT_PROJECT_ROOT=$(pwd)/testdata // ServeRepositry [name] from ./testdata/[name] returning URL at which
// and returns URL for project named `name` under the git root. // the named repository is available.
// // Must be called before any helpers which change test working directory
// For example TestRepoURI("my-repo", t) returns string that could look like: // such as fromTempDirectory(t)
// http://localhost:4242/my-repo.git func ServeRepo(name string, t *testing.T) string {
func TestRepoURI(name string, t *testing.T) string {
t.Helper() t.Helper()
wd, err := os.Getwd() var (
if err != nil { path = filepath.Join("./testdata", name)
t.Fatal(err) dir = filepath.Dir(path)
} abs, _ = filepath.Abs(dir)
gitRoot := filepath.Join(wd, "testdata") repo = filepath.Base(path)
hostPort := RunGitServer(t, gitRoot) url = RunGitServer(abs, t)
return fmt.Sprintf(`http://%s/%s.git`, hostPort, name) )
return fmt.Sprintf("%v/%v", url, repo)
} }
// WithExecutable creates an executable of the given name and source in a temp // WithExecutable creates an executable of the given name and source in a temp
@ -186,13 +172,13 @@ go.exe run GO_SCRIPT_PATH %*
} }
} }
// RunGitServer starts serving git HTTP server and returns its address including port // RunGitServer starts serving git HTTP server and returns its address
func RunGitServer(t *testing.T, gitRoot string) (hostPort string) { func RunGitServer(root string, t *testing.T) (url string) {
l, err := net.Listen("tcp", "localhost:0") l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
hostPort = l.Addr().String() url = l.Addr().String()
cmd := exec.Command("git", "--exec-path") cmd := exec.Command("git", "--exec-path")
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
@ -203,7 +189,7 @@ func RunGitServer(t *testing.T, gitRoot string) (hostPort string) {
server := &http.Server{ server := &http.Server{
Handler: &cgi.Handler{ Handler: &cgi.Handler{
Path: filepath.Join(strings.Trim(string(out), "\n"), "git-http-backend"), Path: filepath.Join(strings.Trim(string(out), "\n"), "git-http-backend"),
Env: []string{"GIT_HTTP_EXPORT_ALL=true", fmt.Sprintf("GIT_PROJECT_ROOT=%s", gitRoot)}, Env: []string{"GIT_HTTP_EXPORT_ALL=true", fmt.Sprintf("GIT_PROJECT_ROOT=%s", root)},
}, },
} }
@ -218,7 +204,7 @@ func RunGitServer(t *testing.T, gitRoot string) (hostPort string) {
server.Close() server.Close()
}) })
return hostPort return "http://" + url
} }
// Cwd returns the current working directory or panic if unable to determine. // Cwd returns the current working directory or panic if unable to determine.