integration test isolation (#2894)

- Default builder and pusher set to embedded Host Builder/Pusher(oci)
- Most tests clear environment
- Environment defaults can be controlled via environment variables
- Tests which require back-compat `git` binary actively check and skip
  when running with a cleared environment (both integration and unit).
- Bugfixes for when run in tandem with E2E tests
- Ignores go-created directories in the default home path (testdata)
This commit is contained in:
Luke Kingland 2025-07-03 09:33:54 +09:00 committed by GitHub
parent a18f763b6a
commit 18a119abff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 279 additions and 135 deletions

17
.gitignore vendored
View File

@ -10,10 +10,13 @@
/hack/bin
/.artifacts
/e2e/testdata/default_home/go
/e2e/testdata/default_home/.cache
/pkg/functions/testdata/migrations/*/.gitignore
/pkg/functions/testdata/default_home/go
/pkg/functions/testdata/default_home/.cache
/pkg/functions/testdata/migrations/*/.gitignore
# Go
/templates/go/cloudevents/go.sum
# JS
node_modules
@ -25,10 +28,12 @@ __pycache__
/templates/python/cloudevents/.venv
/templates/python/http/.venv
# VSCode
.vscode
# E2E Tests
/e2e/testdata/default_home/go
/e2e/testdata/default_home/.cache
# IntelliJ
# Editors
.vscode
.idea
# Operating system temporary files

View File

@ -9,8 +9,9 @@ import (
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"reflect"
"strconv"
"testing"
"time"
@ -18,11 +19,11 @@ import (
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
"knative.dev/func/pkg/builders/buildpacks"
"knative.dev/func/pkg/builders/s2i"
"knative.dev/func/pkg/docker"
fn "knative.dev/func/pkg/functions"
"knative.dev/func/pkg/knative"
"knative.dev/func/pkg/oci"
. "knative.dev/func/pkg/testing"
"knative.dev/pkg/ptr"
)
@ -31,57 +32,60 @@ import (
//
// go test -tags integration ./...
//
// ## Cluster Required
// ## Requirements
//
// These integration tests require a properly configured cluster,
// such as that which is setup and configured in CI (see .github/workflows).
// Linux developers can set up the cluster via:
// A cluster is required. See .github/workflows for more. For example:
//
// ./hack/install-binaries.sh && ./hack/allocate.sh && ./hack/registry.sh
//
// Binaries are required: go for compiling functions and git for
// repository-related tests.
//
// ## Cluster Cleanup
//
// The test cluster and most resources can be removed with:
// ./hack/delete.sh
//
// ## Configuration
//
// Use the FUNC_INT_* environment variables to alter behavior, binaries to
// use, etc.
//
// NOTE: Downloaded images are not removed.
//
const (
// DefaultRegistry must contain both the registry host and
// registry namespace at this time. This will likely be
// split and defaulted to the forthcoming in-cluster registry.
DefaultRegistry = "localhost:50000/func"
// DefaultNamespace for the underlying deployments. Must be the same
// as is set up and configured (see hack/configure.sh)
DefaultNamespace = "func"
DefaultIntTestHome = "./testdata/default_home"
DefaultIntTestKubeconfig = "../../hack/bin/kubeconfig.yaml"
DefaultIntTestRegistry = "localhost:50000/func"
DefaultIntTestNamespace = "default"
DefaultIntTestVerbose = false
)
func TestList(t *testing.T) {
verbose := true
var (
Go = getEnvAsBin("FUNC_INT_GO", "go")
Git = getEnvAsBin("FUNC_INT_GIT", "git")
Kubeconfig = getEnvAsPath("FUNC_INT_KUBECONFIG", DefaultIntTestKubeconfig)
Verbose = getEnvAsBool("FUNC_INT_VERBOSE", DefaultIntTestVerbose)
Registry = getEnv("FUNC_INT_REGISTRY", DefaultIntTestRegistry)
Home, _ = filepath.Abs(DefaultIntTestHome)
)
// Assemble
lister := knative.NewLister(verbose)
client := fn.New(
fn.WithLister(lister),
fn.WithVerbose(verbose))
// Act
names, err := client.List(context.Background(), DefaultNamespace)
if err != nil {
t.Fatal(err)
}
// Assert
if len(names) != 0 {
t.Fatalf("Expected no functions, got %v", names)
// containsInstance checks if the list includes the given instance.
func containsInstance(list []fn.ListItem, name, namespace string) bool {
for _, v := range list {
if v.Name == name && v.Namespace == namespace {
return true
}
}
return false
// Note that client.List is tested implicitly via its use in TestInt_New
// and TestInt_Delete.
}
// TestNew creates
func TestNew(t *testing.T) {
// TestInt_New creates
func TestInt_New(t *testing.T) {
resetEnv()
// Assemble
root, cleanup := Mktemp(t)
defer cleanup()
@ -90,32 +94,32 @@ func TestNew(t *testing.T) {
client := newClient(verbose)
// Act
if _, _, err := client.New(context.Background(), fn.Function{Name: name, Namespace: DefaultNamespace, Root: root, Runtime: "go"}); err != nil {
if _, _, err := client.New(context.Background(), fn.Function{Name: name, Namespace: DefaultIntTestNamespace, Root: root, Runtime: "go"}); err != nil {
t.Fatal(err)
}
defer del(t, client, name, DefaultNamespace)
defer del(t, client, name, DefaultIntTestNamespace)
// Assert
items, err := client.List(context.Background(), DefaultNamespace)
names := []string{}
for _, item := range items {
names = append(names, item.Name)
}
list, err := client.List(context.Background(), DefaultIntTestNamespace)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(names, []string{name}) {
t.Fatalf("Expected function list ['%v'], got %v", name, names)
if !containsInstance(list, name, DefaultIntTestNamespace) {
t.Log(list)
t.Fatalf("deployed instance list does not contain function %q", name)
}
}
// TestDeploy_Defaults deployes using client methods from New but manually
func TestDeploy_Defaults(t *testing.T) {
defer Within(t, "testdata/example.com/deploy")()
// TestInt_Deploy_Defaults deployes using client methods from New but manually
func TestInt_Deploy_Defaults(t *testing.T) {
resetEnv()
_, cleanup := Mktemp(t)
defer cleanup()
verbose := true
client := newClient(verbose)
f := fn.Function{Name: "deploy", Namespace: DefaultNamespace, Root: ".", Runtime: "go"}
f := fn.Function{Name: "deploy", Namespace: DefaultIntTestNamespace, Runtime: "go"}
var err error
if f, err = client.Init(f); err != nil {
@ -128,7 +132,7 @@ func TestDeploy_Defaults(t *testing.T) {
t.Fatal(err)
}
defer del(t, client, "deploy", DefaultNamespace)
defer del(t, client, "deploy", DefaultIntTestNamespace)
// TODO: gauron99 -- remove this when you set full image name after build instead
// of push -- this has to be here because of a workaround
f.Deploy.Image = f.Build.Image
@ -138,13 +142,14 @@ func TestDeploy_Defaults(t *testing.T) {
}
}
// TestDeploy_WithOptions deploys function with all options explicitly set
func TestDeploy_WithOptions(t *testing.T) {
// TestInt_Deploy_WithOptions deploys function with all options explicitly set
func TestInt_Deploy_WithOptions(t *testing.T) {
resetEnv()
root, cleanup := Mktemp(t)
defer cleanup()
verbose := false
f := fn.Function{Runtime: "go", Name: "test-deploy-with-options", Root: root, Namespace: DefaultNamespace}
f := fn.Function{Runtime: "go", Name: "test-deploy-with-options", Root: root, Namespace: DefaultIntTestNamespace}
f.Deploy = fn.DeploySpec{
Options: fn.Options{
Scale: &fn.ScaleOptions{
@ -172,15 +177,16 @@ func TestDeploy_WithOptions(t *testing.T) {
if _, _, err := client.New(context.Background(), f); err != nil {
t.Fatal(err)
}
defer del(t, client, "test-deploy-with-options", DefaultNamespace)
defer del(t, client, "test-deploy-with-options", DefaultIntTestNamespace)
}
func TestDeployWithTriggers(t *testing.T) {
func TestInt_Deploy_WithTriggers(t *testing.T) {
resetEnv()
root, cleanup := Mktemp(t)
defer cleanup()
verbose := true
f := fn.Function{Runtime: "go", Name: "test-deploy-with-triggers", Root: root, Namespace: DefaultNamespace}
f := fn.Function{Runtime: "go", Name: "test-deploy-with-triggers", Root: root, Namespace: DefaultIntTestNamespace}
f.Deploy = fn.DeploySpec{
Subscriptions: []fn.KnativeSubscription{
{
@ -197,27 +203,29 @@ func TestDeployWithTriggers(t *testing.T) {
if _, _, err := client.New(context.Background(), f); err != nil {
t.Fatal(err)
}
defer del(t, client, "test-deploy-with-triggers", DefaultNamespace)
defer del(t, client, "test-deploy-with-triggers", DefaultIntTestNamespace)
}
func TestUpdateWithAnnotationsAndLabels(t *testing.T) {
func TestInt_Update_WithAnnotationsAndLabels(t *testing.T) {
resetEnv()
_, cleanup := Mktemp(t)
defer cleanup()
functionName := "updateannlab"
defer Within(t, "testdata/example.com/"+functionName)()
verbose := false
servingClient, err := knative.NewServingClient(DefaultNamespace)
servingClient, err := knative.NewServingClient(DefaultIntTestNamespace)
if err != nil {
t.Fatal(err)
}
// Deploy a function without any annotations or labels
client := newClient(verbose)
f := fn.Function{Name: functionName, Root: ".", Runtime: "go", Namespace: DefaultNamespace}
f := fn.Function{Name: functionName, Runtime: "go", Namespace: DefaultIntTestNamespace}
if _, f, err = client.New(context.Background(), f); err != nil {
t.Fatal(err)
}
defer del(t, client, functionName, DefaultNamespace)
defer del(t, client, functionName, DefaultIntTestNamespace)
// Updated function with a new set of annotations and labels
// deploy and check that deployed kcsv contains correct annotations and labels
@ -295,41 +303,47 @@ func TestUpdateWithAnnotationsAndLabels(t *testing.T) {
}
}
// TestRemove ensures removal of a function instance.
func TestRemove(t *testing.T) {
defer Within(t, "testdata/example.com/remove")()
// TestInt_Remove ensures removal of a function instance.
func TestInt_Remove(t *testing.T) {
resetEnv()
_, cleanup := Mktemp(t)
defer cleanup()
verbose := true
name := "remove"
client := newClient(verbose)
f := fn.Function{Name: "remove", Namespace: DefaultNamespace, Root: ".", Runtime: "go"}
f := fn.Function{Name: name, Namespace: DefaultIntTestNamespace, Runtime: "go"}
var err error
if _, _, err = client.New(context.Background(), f); err != nil {
t.Fatal(err)
}
del(t, client, "remove", DefaultNamespace)
del(t, client, "remove", DefaultIntTestNamespace)
names, err := client.List(context.Background(), DefaultNamespace)
list, err := client.List(context.Background(), DefaultIntTestNamespace)
if err != nil {
t.Fatal(err)
}
if len(names) != 0 {
t.Fatalf("Expected empty functions list, got %v", names)
if containsInstance(list, name, DefaultIntTestNamespace) {
t.Log(list)
t.Fatalf("deployed instance list still contains function %q", name)
}
}
// TestRemoteRepositories ensures that initializing a function
// TestInt_RemoteRepositories ensures that initializing a function
// defined in a remote repository finds the template, writes
// the expected files, and retains the expected modes.
// NOTE: this test only succeeds due to an override in
// templates' copyNode which forces mode 755 for directories.
// See https://github.com/go-git/go-git/issues/364
func TestRemoteRepositories(t *testing.T) {
defer Within(t, "testdata/example.com/remote")()
func TestInt_RemoteRepositories(t *testing.T) {
resetEnv()
_, cleanup := Mktemp(t)
defer cleanup()
// Write the test template from the remote onto root
client := fn.New(
fn.WithRegistry(DefaultRegistry),
fn.WithRegistry(DefaultIntTestRegistry),
fn.WithRepository("https://github.com/boson-project/test-templates"),
)
_, err := client.Init(fn.Function{
@ -368,22 +382,23 @@ func TestRemoteRepositories(t *testing.T) {
}
}
// TestInvoke_ClientToService ensures that the client can invoke a remotely
// TestInt_Invoke_ClientToService ensures that the client can invoke a remotely
// deployed service, both by the route returned directly as well as using
// the invocation helper client.Invoke.
func TestInvoke_ClientToService(t *testing.T) {
func TestInt_Invoke_ClientToService(t *testing.T) {
resetEnv()
root, cleanup := Mktemp(t)
defer cleanup()
var (
root, done = Mktemp(t)
verbose = true
ctx = context.Background()
client = newClient(verbose)
route string
err error
)
defer done()
// Create a function
f := fn.Function{Name: "f", Runtime: "go", Namespace: DefaultNamespace}
f := fn.Function{Name: "f", Runtime: "go", Namespace: DefaultIntTestNamespace}
f, err = client.Init(f)
if err != nil {
t.Fatal(err)
@ -391,12 +406,9 @@ func TestInvoke_ClientToService(t *testing.T) {
source := `
package function
import (
"context"
"net/http"
)
import "net/http"
func Handle(ctx context.Context, res http.ResponseWriter, req *http.Request) {
func Handle(res http.ResponseWriter, req *http.Request) {
res.Write([]byte("TestInvoke_ClientToService OK"))
}
`
@ -411,7 +423,7 @@ func Handle(ctx context.Context, res http.ResponseWriter, req *http.Request) {
if err := f.Write(); err != nil {
t.Fatal(err)
}
defer del(t, client, "f", DefaultNamespace)
defer del(t, client, "f", DefaultIntTestNamespace)
// Invoke via the route
resp, err := http.Get(route)
@ -439,9 +451,10 @@ func Handle(ctx context.Context, res http.ResponseWriter, req *http.Request) {
}
}
// TestInvoke_ServiceToService ensures that a Function can invoke another
// TestInt_Invoke_ServiceToService ensures that a Function can invoke another
// service via localhost service discovery api provided by the Dapr sidecar.
func TestInvoke_ServiceToService(t *testing.T) {
func TestInt_Invoke_ServiceToService(t *testing.T) {
resetEnv()
var (
verbose = true
ctx = context.Background()
@ -455,7 +468,7 @@ func TestInvoke_ServiceToService(t *testing.T) {
// A function which responds to GET requests with a static value.
root, done := Mktemp(t)
defer done()
f := fn.Function{Name: "a", Runtime: "go", Namespace: DefaultNamespace}
f := fn.Function{Name: "a", Runtime: "go", Namespace: DefaultIntTestNamespace}
f, err = client.Init(f)
if err != nil {
t.Fatal(err)
@ -463,12 +476,9 @@ func TestInvoke_ServiceToService(t *testing.T) {
source = `
package function
import (
"context"
"net/http"
)
import "net/http"
func Handle(ctx context.Context, res http.ResponseWriter, req *http.Request) {
func Handle(res http.ResponseWriter, req *http.Request) {
res.Write([]byte("TestInvoke_ServiceToService OK"))
}
`
@ -479,15 +489,16 @@ func Handle(ctx context.Context, res http.ResponseWriter, req *http.Request) {
if _, _, err = client.Apply(ctx, f); err != nil {
t.Fatal(err)
}
defer del(t, client, "a", DefaultNamespace)
defer del(t, client, "a", DefaultIntTestNamespace)
// Create Function B
// which responds with the response from an invocation of 'a' via the
// localhost service discovery and invocation API.
client2 := newClient(verbose)
root, done = Mktemp(t)
defer done()
f = fn.Function{Name: "b", Runtime: "go", Namespace: DefaultNamespace}
f, err = client.Init(f)
f = fn.Function{Name: "b", Runtime: "go", Namespace: DefaultIntTestNamespace}
f, err = client2.Init(f)
if err != nil {
t.Fatal(err)
}
@ -497,7 +508,6 @@ package function
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
@ -505,7 +515,7 @@ import (
"time"
)
func Handle(ctx context.Context, w http.ResponseWriter, req *http.Request) {
func Handle(w http.ResponseWriter, req *http.Request) {
var e error
var r *http.Response
var buff bytes.Buffer
@ -535,10 +545,10 @@ func Handle(ctx context.Context, w http.ResponseWriter, req *http.Request) {
if err != nil {
t.Fatal(err)
}
if route, _, err = client.Apply(ctx, f); err != nil {
if route, f, err = client2.Apply(ctx, f); err != nil {
t.Fatal(err)
}
defer del(t, client, "b", DefaultNamespace)
defer client2.Remove(ctx, "", "", f, true)
resp, err := http.Get(route)
if err != nil {
@ -566,7 +576,7 @@ func TestDeployWithoutHome(t *testing.T) {
verbose := false
name := "test-deploy-no-home"
f := fn.Function{Runtime: "node", Name: name, Root: root, Namespace: DefaultNamespace}
f := fn.Function{Runtime: "node", Name: name, Root: root, Namespace: DefaultIntTestNamespace}
// client with s2i builder because pack needs HOME
client := newClientWithS2i(verbose)
@ -577,32 +587,84 @@ func TestDeployWithoutHome(t *testing.T) {
t.Fatalf("expected no errors but got %v", err)
}
defer del(t, client, name, DefaultNamespace)
defer del(t, client, name, DefaultIntTestNamespace)
}
// ***********
// Helpers
// ***********
func getEnvAsPath(env, dflt string) (val string) {
val = getEnv(env, dflt)
if !filepath.IsAbs(val) { // convert to abs
var err error
if val, err = filepath.Abs(val); err != nil {
panic(fmt.Sprintf("error converting path to absolute. %v", err))
}
}
return
}
func getEnvAsBool(env string, dfltBool bool) bool {
dflt := fmt.Sprintf("%t", dfltBool)
val, err := strconv.ParseBool(getEnv(env, dflt))
if err != nil {
panic(fmt.Sprintf("value for %v expected to be boolean. %v", env, err))
}
return val
}
func getEnvAsBin(env, dflt string) string {
val, err := exec.LookPath(getEnv(env, dflt))
if err != nil {
fmt.Fprintf(os.Stderr, "error locating command %q. %v", val, err)
}
return val
}
func getEnv(env, dflt string) (val string) {
if v := os.Getenv(env); v != "" {
val = v
}
if val == "" {
val = dflt
}
return
}
func resetEnv() {
os.Clearenv()
os.Setenv("HOME", Home)
os.Setenv("KUBECONFIG", Kubeconfig)
os.Setenv("FUNC_GO", Go)
os.Setenv("FUNC_GIT", Git)
os.Setenv("FUNC_VERBOSE", fmt.Sprintf("%t", Verbose))
// The Registry will be set either during first-time setup using the
// global config, or already defaulted by the user via environment variable.
os.Setenv("FUNC_REGISTRY", Registry)
// The following host-builder related settings will become the defaults
// once the host builder supports the core runtimes. Setting them here in
// order to futureproof individual tests.
os.Setenv("FUNC_ENABLE_HOST_BUILDER", "true") // Enable the host builder
os.Setenv("FUNC_BUILDER", "host") // default to host builder
os.Setenv("FUNC_CONTAINER", "false") // "run" uses host builder
}
// newClient creates an instance of the func client with concrete impls
// sufficient for running integration tests.
func newClient(verbose bool) *fn.Client {
builder := buildpacks.NewBuilder(buildpacks.WithVerbose(verbose))
pusher := docker.NewPusher(docker.WithVerbose(verbose))
deployer := knative.NewDeployer(knative.WithDeployerVerbose(verbose))
describer := knative.NewDescriber(verbose)
remover := knative.NewRemover(verbose)
lister := knative.NewLister(verbose)
return fn.New(
fn.WithRegistry(DefaultRegistry),
fn.WithRegistry(DefaultIntTestRegistry),
fn.WithBuilder(oci.NewBuilder("", verbose)),
fn.WithPusher(oci.NewPusher(true, true, verbose)),
fn.WithDeployer(knative.NewDeployer(knative.WithDeployerVerbose(verbose))),
fn.WithDescriber(knative.NewDescriber(verbose)),
fn.WithRemover(knative.NewRemover(verbose)),
fn.WithLister(knative.NewLister(verbose)),
fn.WithVerbose(verbose),
fn.WithBuilder(builder),
fn.WithPusher(pusher),
fn.WithDeployer(deployer),
fn.WithDescriber(describer),
fn.WithRemover(remover),
fn.WithLister(lister),
)
}
@ -616,7 +678,7 @@ func newClientWithS2i(verbose bool) *fn.Client {
lister := knative.NewLister(verbose)
return fn.New(
fn.WithRegistry(DefaultRegistry),
fn.WithRegistry(DefaultIntTestRegistry),
fn.WithVerbose(verbose),
fn.WithBuilder(builder),
fn.WithPusher(pusher),
@ -640,10 +702,23 @@ func newClientWithS2i(verbose bool) *fn.Client {
func del(t *testing.T, c *fn.Client, name, namespace string) {
t.Helper()
waitFor(t, c, name, namespace)
f := fn.Function{Name: name, Deploy: fn.DeploySpec{Namespace: DefaultNamespace}}
f := fn.Function{Name: name, Deploy: fn.DeploySpec{Namespace: DefaultIntTestNamespace}}
if err := c.Remove(context.Background(), "", "", f, false); err != nil {
t.Fatal(err)
}
// TODO(lkingland): The below breaks things
// The following should not exist, nor its imports.
//
// If there is ever a problem with dangling volumes, that is
// something which should be fixed in the _remover_, and tested there.
//
// ... check with Jefferson what caused such a state, open an
// issue to alter remover if necessary, and then delete all of this.
if true {
return
}
cli, _, err := docker.NewClient(client.DefaultDockerHost)
if err != nil {
t.Fatal(err)

View File

@ -45,7 +45,7 @@ const (
// but functional deployer, use fn.WithDeployer(mock.NewDeployer()) which
// will return a result with the target namespace populated "mocking"
// that the function was actually deployed.
TestNamespace = "func"
TestNamespace = "default"
)
var (
@ -348,7 +348,9 @@ func TestClient_New_HiddenFilesIgnored(t *testing.T) {
// $FUNC_REPOSITORIES_PATH/boson/go/json
// See the CLI for full details, but a standard default location is
// $HOME/.config/func/repositories/boson/go/json
func TestClient_New_RepositoriesExtensible(t *testing.T) {
func TestClient_New_RepositoriesExtensible_B(t *testing.T) {
skipIfNoGit(t) // see function doc
root := "testdata/example.com/test-repositories-extensible"
defer Using(t, root)()
@ -391,6 +393,7 @@ func TestClient_New_RuntimeNotFoundError(t *testing.T) {
// TestClient_New_RuntimeNotFoundCustom ensures that the correct error is returned
// when the requested runtime is not found in a given custom repository
func TestClient_New_RuntimeNotFoundCustom(t *testing.T) {
skipIfNoGit(t) // see docs
root := "testdata/example.com/testRuntimeNotFoundCustom"
defer Using(t, root)()
@ -429,6 +432,7 @@ func TestClient_New_TemplateNotFoundError(t *testing.T) {
// TestClient_New_TemplateNotFoundCustom ensures that the correct error is returned
// when the requested template is not found in the given custom repository.
func TestClient_New_TemplateNotFoundCustom(t *testing.T) {
skipIfNoGit(t) // see docs
root := "testdata/example.com/testTemplateNotFoundCustom"
defer Using(t, root)()
@ -1611,6 +1615,7 @@ func TestClient_Scaffold(t *testing.T) {
// TestClient_Runtimes ensures that the total set of runtimes are returned.
func TestClient_Runtimes(t *testing.T) {
skipIfNoGit(t) // see docs
// TODO: test when a specific repo override is indicated
// (remote repo which takes precedence over embedded and extended)

View File

@ -448,8 +448,7 @@ func TestFunction_Local(t *testing.T) {
//
// The new function should not have Local.Remote set (as it is a transient field)
func TestFunction_LocalTransient(t *testing.T) {
// Initialise a new function
skipIfNoGit(t) // see docs
root, rm := Mktemp(t)
defer rm()
@ -520,7 +519,7 @@ func TestFunction_LocalTransient(t *testing.T) {
InsecureSkipTLS: true,
})
if err != nil {
t.Fatal()
t.Fatal(err)
}
// Create a new directory to clone the function in

View File

@ -5,6 +5,7 @@ package functions_test
import (
"os"
"os/exec"
"path/filepath"
"testing"
@ -52,6 +53,7 @@ func TestRepositories_GetInvalid(t *testing.T) {
// TestRepositories_Get ensures a repository can be accessed by name.
func TestRepositories_Get(t *testing.T) {
skipIfNoGit(t) // see docs
client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))
// valid should not error
@ -69,6 +71,7 @@ func TestRepositories_Get(t *testing.T) {
// TestRepositories_All ensures repos are returned from
// .All accessor. Tests both builtin and buitlin+extensible cases.
func TestRepositories_All(t *testing.T) {
skipIfNoGit(t) // see docs
uri := ServeRepo(RepositoriesTestRepo, t)
root, rm := Mktemp(t)
defer rm()
@ -106,6 +109,7 @@ func TestRepositories_All(t *testing.T) {
// TestRepositories_Add checks basic adding of a repository by URI.
func TestRepositories_Add(t *testing.T) {
skipIfNoGit(t) // see docs
uri := ServeRepo(RepositoriesTestRepo, t) // ./testdata/$RepositoriesTestRepo.git
root, rm := Mktemp(t) // create and cd to a temp dir, returning path.
defer rm()
@ -140,6 +144,7 @@ func TestRepositories_Add(t *testing.T) {
// TestRepositories_AddDefaultName ensures that repository name is optional,
// by default being set to the name of the repoisotory from the URI.
func TestRepositories_AddDeafultName(t *testing.T) {
skipIfNoGit(t) // see docs
// The test repository is the "base case" repo, which is a manifestless
// repo meant to exemplify the simplest use case: a repo with no metadata
// that simply contains templates, grouped by runtime. It therefore does
@ -175,6 +180,7 @@ func TestRepositories_AddDeafultName(t *testing.T) {
// a manfest wherein a default name is specified, is used as the name for the
// added repository when a name is not explicitly specified.
func TestRepositories_AddWithManifest(t *testing.T) {
skipIfNoGit(t) // see docs
// repository-b is meant to exemplify the use case of a repository which
// defines a custom language pack and makes full use of the manifest.yaml.
// The manifest.yaml is included which specifies things like custom templates
@ -210,6 +216,7 @@ func TestRepositories_AddWithManifest(t *testing.T) {
// TestRepositories_AddExistingErrors ensures that adding a repository that
// already exists yields an error.
func TestRepositories_AddExistingErrors(t *testing.T) {
skipIfNoGit(t) // see docs
uri := ServeRepo(RepositoriesTestRepo, t)
root, rm := Mktemp(t) // create and cd to a temp dir, returning path.
defer rm()
@ -244,6 +251,7 @@ func TestRepositories_AddExistingErrors(t *testing.T) {
// TestRepositories_Rename ensures renaming a repository succeeds.
func TestRepositories_Rename(t *testing.T) {
skipIfNoGit(t) // see docs
uri := ServeRepo(RepositoriesTestRepo, t)
root, rm := Mktemp(t) // create and cd to a temp dir, returning path.
defer rm()
@ -278,6 +286,7 @@ func TestRepositories_Rename(t *testing.T) {
// TestRepositories_Remove ensures that removing a repository by name
// removes it from the list and FS.
func TestRepositories_Remove(t *testing.T) {
skipIfNoGit(t) // see docs
uri := ServeRepo(RepositoriesTestRepo, t) // ./testdata/repository.git
root, rm := Mktemp(t) // create and cd to a temp dir
defer rm()
@ -313,6 +322,7 @@ func TestRepositories_Remove(t *testing.T) {
// 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)
func TestRepositories_URL(t *testing.T) {
skipIfNoGit(t) // see docs
uri := ServeRepo(RepositoriesTestRepo, t)
root, rm := Mktemp(t)
defer rm()
@ -353,3 +363,34 @@ func TestRepositories_Missing(t *testing.T) {
t.Fatal(err)
}
}
// skipIfNoGit skips the test if there is no 'git' available in PATH.
//
// The 'go-git' dependency only has partial support for file:// paths.
// see: https://github.com/go-git/go-git/blob/master/COMPATIBILITY.md
// Therefore the "git" binary is used as a fallback.
//
// These file:// paths we rely on for cloning repositories from the
// inbuilt repositories. Therefore, it is not a "pure-go" implementation
// of git. This results in there being a hard dependency on the "git"
// program, which this library uses as a fallback.
// TODO: We can either 1) reimplement this functionality ourselves
// by capturing file:// paths and just copying the files (we don't need
// the git repository; just the files), 2) find a library which
// does actually implmement this part of git without relying on "git",
// 3) submit a patch upstream which implements this functionality.
// 4) At least submit a patch upstream allowing the path of the git bin
// to be specified when no PATH.
// Temporary workaround: Detect when this test is being run alongside
// the integration/e2e tests (which clear the environment), and skip.
func skipIfNoGit(t *testing.T) {
// integration and e2e tests clear their environment in order to
// obtain isolation. If there is no 'git' available; skip the test.
// Note that our libraries accept an environmental override for these
// binaries (FUNC_E2E_GIT for example). There is no such setting for
// the go-git dependency.
_, err := exec.LookPath("git")
if err != nil {
t.Skip("No 'git' found in path. Skipping test.")
}
}

View File

@ -15,6 +15,7 @@ import (
// TestRepository_TemplatesPath ensures that repositories can specify
// an alternate location for templates using a manifest.
func TestRepository_TemplatesPath(t *testing.T) {
skipIfNoGit(t) // see docs
client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))
// The repo ./testdata/repositories/customLanguagePackRepo includes a
@ -40,6 +41,7 @@ func TestRepository_TemplatesPath(t *testing.T) {
// and template level. The tests check for both embedded structures:
// HealthEndpoints BuildConfig.
func TestRepository_Inheritance(t *testing.T) {
skipIfNoGit(t) // see docs
var err error
client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))

View File

@ -19,6 +19,7 @@ import (
// TestTemplates_List ensures that all templates are listed taking into account
// both internal and extensible (prefixed) repositories.
func TestTemplates_List(t *testing.T) {
skipIfNoGit(t) // see docs
// A client which specifies a location of exensible repositoreis on disk
// will list all builtin plus exensible
client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))
@ -48,6 +49,8 @@ func TestTemplates_List(t *testing.T) {
// when retrieving the list of templates for a runtime that does not exist
// in an extended repository, but does in the default.
func TestTemplates_List_ExtendedNotFound(t *testing.T) {
skipIfNoGit(t) // see docs
// An external template repo which does not contain python
client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))
@ -72,6 +75,7 @@ func TestTemplates_List_ExtendedNotFound(t *testing.T) {
// TestTemplates_Get ensures that a template's metadata object can
// be retrieved by full name (full name prefix optional for embedded).
func TestTemplates_Get(t *testing.T) {
skipIfNoGit(t) // see docs
client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))
// Check embedded
@ -127,6 +131,7 @@ func TestTemplates_Embedded(t *testing.T) {
// (ie. custom provider on disk) can be specified as the source for a
// template.
func TestTemplates_Custom(t *testing.T) {
skipIfNoGit(t) // see docs
// Create test directory
root := "testdata/testTemplatesCustom"
defer Using(t, root)()
@ -160,6 +165,7 @@ func TestTemplates_Custom(t *testing.T) {
// can be specificed on creation of client, with subsequent calls to Create
// using this remote by default.
func TestTemplates_Remote(t *testing.T) {
skipIfNoGit(t) // see docs
var err error
root := "testdata/testTemplatesRemote"
@ -285,6 +291,7 @@ func TestTemplates_ModeEmbedded(t *testing.T) {
// TestTemplates_ModeCustom ensures that templates written from custom templates
// retain their mode.
func TestTemplates_ModeCustom(t *testing.T) {
skipIfNoGit(t) // see docs
if runtime.GOOS == "windows" {
return // not applicable
}
@ -320,6 +327,7 @@ func TestTemplates_ModeCustom(t *testing.T) {
// TestTemplates_ModeRemote ensures that templates written from remote templates
// retain their mode.
func TestTemplates_ModeRemote(t *testing.T) {
skipIfNoGit(t) // see docs
var err error
if runtime.GOOS == "windows" {
@ -370,6 +378,7 @@ func TestTemplates_ModeRemote(t *testing.T) {
// TestTemplates_RuntimeManifestBuildEnvs ensures that BuildEnvs specified in a
// runtimes's manifest are included in the final function.
func TestTemplates_RuntimeManifestBuildEnvs(t *testing.T) {
skipIfNoGit(t) // see docs
// create test directory
root := "testdata/testTemplatesRuntimeManifestBuildEnvs"
defer Using(t, root)()
@ -417,6 +426,7 @@ func TestTemplates_RuntimeManifestBuildEnvs(t *testing.T) {
// TestTemplates_ManifestBuildEnvs ensures that BuildEnvs specified in a
// template's manifest are included in the final function.
func TestTemplates_ManifestBuildEnvs(t *testing.T) {
skipIfNoGit(t) // see docs
// create test directory
root := "testdata/testTemplatesManifestBuildEnvs"
defer Using(t, root)()
@ -464,6 +474,7 @@ func TestTemplates_ManifestBuildEnvs(t *testing.T) {
// TestTemplates_RepositoryManifestBuildEnvs ensures that BuildEnvs specified in a
// repository's manifest are included in the final function.
func TestTemplates_RepositoryManifestBuildEnvs(t *testing.T) {
skipIfNoGit(t) // see docs
// create test directory
root := "testdata/testRepositoryManifestBuildEnvs"
defer Using(t, root)()
@ -511,6 +522,7 @@ func TestTemplates_RepositoryManifestBuildEnvs(t *testing.T) {
// TestTemplates_ManifestInvocationHints ensures that invocation hints
// from a template's manifest are included in the final function.
func TestTemplates_ManifestInvocationHints(t *testing.T) {
skipIfNoGit(t) // see docs
root := "testdata/testTemplatesManifestInvocationHints"
defer Using(t, root)()
@ -535,6 +547,7 @@ func TestTemplates_ManifestInvocationHints(t *testing.T) {
// TestTemplates_ManifestRemoved ensures that the manifest is not left in
// the resultant function after write.
func TestTemplates_ManifestRemoved(t *testing.T) {
skipIfNoGit(t) // see docs
// create test directory
root := "testdata/testTemplateManifestRemoved"
defer Using(t, root)()
@ -571,6 +584,7 @@ func TestTemplates_ManifestRemoved(t *testing.T) {
// does not define an invocation hint defaults to empty string (since 0.35.0
// default value is omitted from func.yaml file for Invoke)
func TestTemplates_InvocationDefault(t *testing.T) {
skipIfNoGit(t) // see docs
expectedInvoke := ""
root := "testdata/testTemplatesInvocationDefault"
defer Using(t, root)()

View File

@ -0,0 +1,3 @@
# default_home
For use by tests which isolate their environment and need a HOME

View File

@ -127,8 +127,8 @@ func goBuildCmd(p v1.Platform, cfg buildJob) (gobin string, args []string, outpa
* Either replace or append to gobin
*/
// Use the binary specified FUNC_GO_PATH if defined
gobin = os.Getenv("FUNC_GO_PATH") // TODO: move to main and plumb through
// Use the binary specified FUNC_GO if defined
gobin = os.Getenv("FUNC_GO") // TODO: move to main and plumb through
if gobin == "" {
gobin = "go"
}