mirror of https://github.com/knative/func.git
597 lines
16 KiB
Go
597 lines
16 KiB
Go
//go:build !integration
|
|
// +build !integration
|
|
|
|
package functions_test
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
|
fn "knative.dev/func/pkg/functions"
|
|
. "knative.dev/func/pkg/testing"
|
|
)
|
|
|
|
// TestTemplates_List ensures that all templates are listed taking into account
|
|
// both internal and extensible (prefixed) repositories.
|
|
func TestTemplates_List(t *testing.T) {
|
|
// A client which specifies a location of exensible repositoreis on disk
|
|
// will list all builtin plus exensible
|
|
client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
// list templates for the "go" runtime
|
|
templates, err := client.Templates().List("go")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Note that this list will change as the customTemplateRepo
|
|
// and builtin templates are shared. THis could be mitigated
|
|
// by creating a custom repository path for just this test, if
|
|
// that becomes a hassle.
|
|
expected := []string{
|
|
"cloudevents",
|
|
"http",
|
|
"customTemplateRepo/customTemplate",
|
|
}
|
|
|
|
if diff := cmp.Diff(expected, templates); diff != "" {
|
|
t.Error("Unexpected templates (-want, +got):", diff)
|
|
}
|
|
}
|
|
|
|
// TestTemplates_List_ExtendedNotFound ensures that an error is not returned
|
|
// 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) {
|
|
// An external template repo which does not contain python
|
|
client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
// list templates for the "python" runtime, which will be found
|
|
// in the embedded filesystem.
|
|
templates, err := client.Templates().List("python")
|
|
if err != nil {
|
|
t.Fatal(err) // there shouldb be no error..
|
|
}
|
|
|
|
// and the list should be those from the embedded fs
|
|
expected := []string{
|
|
"cloudevents",
|
|
"http",
|
|
}
|
|
|
|
if diff := cmp.Diff(expected, templates); diff != "" {
|
|
t.Error("Unexpected templates (-want, +got):", diff)
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
// Check embedded
|
|
embedded, err := client.Templates().Get("go", "http")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if embedded.Runtime() != "go" || embedded.Repository() != "default" || embedded.Name() != "http" {
|
|
t.Logf("Expected template from embedded to have runtime 'go' repo 'default' name 'http', got '%v', '%v', '%v',",
|
|
embedded.Runtime(), embedded.Repository(), embedded.Name())
|
|
}
|
|
|
|
// Check extended
|
|
extended, err := client.Templates().Get("go", "customTemplateRepo/customTemplate")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if embedded.Runtime() != "go" || embedded.Repository() != "default" || embedded.Name() != "http" {
|
|
t.Logf("Expected template from extended repo to have runtime 'go' repo 'customTemplateRepo' name 'customTemplate', got '%v', '%v', '%v',",
|
|
extended.Runtime(), extended.Repository(), extended.Name())
|
|
}
|
|
}
|
|
|
|
// TestTemplates_Embedded ensures that embedded templates are copied on write.
|
|
func TestTemplates_Embedded(t *testing.T) {
|
|
// create test directory
|
|
root := "testdata/testTemplatesEmbedded"
|
|
defer Using(t, root)()
|
|
|
|
// Client whose internal (builtin default) templates will be used.
|
|
client := fn.New(fn.WithRegistry(TestRegistry))
|
|
|
|
// write out a template
|
|
_, err := client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: TestRuntime,
|
|
Template: "http",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Assert file exists as expected
|
|
_, err = os.Stat(filepath.Join(root, "handle.go"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestTemplates_Custom ensures that a template from a filesystem source
|
|
// (ie. custom provider on disk) can be specified as the source for a
|
|
// template.
|
|
func TestTemplates_Custom(t *testing.T) {
|
|
// Create test directory
|
|
root := "testdata/testTemplatesCustom"
|
|
defer Using(t, root)()
|
|
|
|
// CLient which uses custom repositories
|
|
// in form [provider]/[template], on disk the template is
|
|
// at: testdata/repositories/[provider]/[runtime]/[template]
|
|
client := fn.New(
|
|
fn.WithRegistry(TestRegistry),
|
|
fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
// Create a function specifying a template from
|
|
// the custom provider's directory in the on-disk template repo.
|
|
_, err := client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "customRuntime",
|
|
Template: "customTemplateRepo/customTemplate",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Assert file exists as expected
|
|
_, err = os.Stat(filepath.Join(root, "custom.impl"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestTemplates_Remote ensures that a Git template repository provided via URI
|
|
// can be specificed on creation of client, with subsequent calls to Create
|
|
// using this remote by default.
|
|
func TestTemplates_Remote(t *testing.T) {
|
|
var err error
|
|
|
|
root := "testdata/testTemplatesRemote"
|
|
defer Using(t, root)()
|
|
|
|
url := ServeRepo(RepositoriesTestRepo, t)
|
|
|
|
// Create a client which explicitly specifies the Git repo at URL
|
|
// rather than relying on the default internally builtin template repo
|
|
client := fn.New(
|
|
fn.WithRegistry(TestRegistry),
|
|
fn.WithRepository(url))
|
|
|
|
// Create a default function, which should override builtin and use
|
|
// that from the specified url (git repo)
|
|
_, err = client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "go",
|
|
Template: "remote",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Assert the sample file from the git repo was written
|
|
_, err = os.Stat(filepath.Join(root, "remote-test"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestTemplates_Default ensures that the expected default template
|
|
// is used when none specified.
|
|
func TestTemplates_Default(t *testing.T) {
|
|
// create test directory
|
|
root := "testdata/testTemplates_Default"
|
|
defer Using(t, root)()
|
|
|
|
client := fn.New(fn.WithRegistry(TestRegistry))
|
|
|
|
// The runtime is specified, and explicitly includes a
|
|
// file for the default template of fn.DefaultTemplate
|
|
_, err := client.Init(fn.Function{Root: root, Runtime: TestRuntime})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Assert file exists as expected
|
|
_, err = os.Stat(filepath.Join(root, "handle.go"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestTemplates_InvalidErrors ensures that specifying unrecgognized
|
|
// runtime/template errors
|
|
func TestTemplates_InvalidErrors(t *testing.T) {
|
|
// create test directory
|
|
root := "testdata/testTemplates_InvalidErrors"
|
|
defer Using(t, root)()
|
|
|
|
client := fn.New(fn.WithRegistry(TestRegistry))
|
|
|
|
// Error will be type-checked.
|
|
var err error
|
|
|
|
// Test for error writing an invalid runtime
|
|
_, err = client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "invalid",
|
|
})
|
|
if !errors.Is(err, fn.ErrRuntimeNotFound) {
|
|
t.Fatalf("Expected ErrRuntimeNotFound, got %v", err)
|
|
}
|
|
os.Remove(filepath.Join(root, ".gitignore"))
|
|
|
|
// Test for error writing an invalid template
|
|
_, err = client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: TestRuntime,
|
|
Template: "invalid",
|
|
})
|
|
if !errors.Is(err, fn.ErrTemplateNotFound) {
|
|
t.Fatalf("Expected ErrTemplateNotFound, got %v", err)
|
|
}
|
|
}
|
|
|
|
// TestTemplates_ModeEmbedded ensures that templates written from the embedded
|
|
// templates retain their mode.
|
|
func TestTemplates_ModeEmbedded(t *testing.T) {
|
|
if runtime.GOOS == "windows" {
|
|
return
|
|
// not applicable
|
|
}
|
|
|
|
// set up test directory
|
|
root := "testdata/testTemplatesModeEmbedded"
|
|
defer Using(t, root)()
|
|
|
|
client := fn.New(fn.WithRegistry(TestRegistry))
|
|
|
|
// Write the embedded template that contains a file which
|
|
// needs to be executable (only such is mvnw in quarkus)
|
|
_, err := client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "quarkus",
|
|
Template: "http",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Verify file mode was preserved
|
|
file, err := os.Stat(filepath.Join(root, "mvnw"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if file.Mode() != os.FileMode(0755) {
|
|
t.Fatalf("The embedded executable's mode should be 0755 but was %v", file.Mode())
|
|
}
|
|
}
|
|
|
|
// TestTemplates_ModeCustom ensures that templates written from custom templates
|
|
// retain their mode.
|
|
func TestTemplates_ModeCustom(t *testing.T) {
|
|
if runtime.GOOS == "windows" {
|
|
return // not applicable
|
|
}
|
|
|
|
// test directories
|
|
root := "testdata/testTemplates_ModeCustom"
|
|
defer Using(t, root)()
|
|
|
|
client := fn.New(
|
|
fn.WithRegistry(TestRegistry),
|
|
fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
// Write executable from custom repo
|
|
_, err := client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "test",
|
|
Template: "customTemplateRepo/tplb",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Verify custom file mode was preserved.
|
|
file, err := os.Stat(filepath.Join(root, "executable.sh"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if file.Mode() != os.FileMode(0755) {
|
|
t.Fatalf("The custom executable file's mode should be 0755 but was %v", file.Mode())
|
|
}
|
|
}
|
|
|
|
// TestTemplates_ModeRemote ensures that templates written from remote templates
|
|
// retain their mode.
|
|
func TestTemplates_ModeRemote(t *testing.T) {
|
|
var err error
|
|
|
|
if runtime.GOOS == "windows" {
|
|
return // not applicable
|
|
}
|
|
|
|
// test directories
|
|
root := "testdata/testTemplates_ModeRemote"
|
|
defer Using(t, root)()
|
|
|
|
url := ServeRepo(RepositoriesTestRepo, t)
|
|
|
|
client := fn.New(
|
|
fn.WithRegistry(TestRegistry),
|
|
fn.WithRepository(url))
|
|
|
|
// Write executable from custom repo
|
|
_, err = client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "node",
|
|
Template: "remote",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Verify directory file mode was preserved
|
|
file, err := os.Stat(filepath.Join(root, "test"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if file.Mode() != os.ModeDir|0755 {
|
|
t.Fatalf("The remote repositry directory mode should be 0755 but was %#o", file.Mode())
|
|
}
|
|
|
|
// Verify remote executable file mode was preserved.
|
|
file, err = os.Stat(filepath.Join(root, "test", "executable.sh"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if file.Mode() != os.FileMode(0755) {
|
|
t.Fatalf("The remote executable's mode should be 0755 but was %v", file.Mode())
|
|
}
|
|
}
|
|
|
|
// TODO: test typed errors for custom and remote (embedded checked)
|
|
|
|
// TestTemplates_RuntimeManifestBuildEnvs ensures that BuildEnvs specified in a
|
|
// runtimes's manifest are included in the final function.
|
|
func TestTemplates_RuntimeManifestBuildEnvs(t *testing.T) {
|
|
// create test directory
|
|
root := "testdata/testTemplatesRuntimeManifestBuildEnvs"
|
|
defer Using(t, root)()
|
|
|
|
// Client whose internal templates will be used.
|
|
client := fn.New(
|
|
fn.WithRegistry(TestRegistry),
|
|
fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
// write out a template
|
|
_, err := client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "manifestedRuntime",
|
|
Template: "customLanguagePackRepo/customTemplate",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Assert file exists as expected
|
|
_, err = os.Stat(filepath.Join(root, "func.yaml"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
testVariableName := "TEST_RUNTIME_VARIABLE"
|
|
testVariableValue := "test-runtime"
|
|
|
|
envs := []fn.Env{
|
|
{
|
|
Name: &testVariableName,
|
|
Value: &testVariableValue,
|
|
},
|
|
}
|
|
|
|
f, err := fn.NewFunction(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(fn.Envs(envs), f.Build.BuildEnvs); diff != "" {
|
|
t.Fatalf("Unexpected difference between runtime's manifest.yaml buildEnvs and function BuildEnvs (-want, +got): %v", diff)
|
|
}
|
|
}
|
|
|
|
// TestTemplates_ManifestBuildEnvs ensures that BuildEnvs specified in a
|
|
// template's manifest are included in the final function.
|
|
func TestTemplates_ManifestBuildEnvs(t *testing.T) {
|
|
// create test directory
|
|
root := "testdata/testTemplatesManifestBuildEnvs"
|
|
defer Using(t, root)()
|
|
|
|
// Client whose internal templates will be used.
|
|
client := fn.New(
|
|
fn.WithRegistry(TestRegistry),
|
|
fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
// write out a template
|
|
_, err := client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "manifestedRuntime",
|
|
Template: "customLanguagePackRepo/manifestedTemplate",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Assert file exists as expected
|
|
_, err = os.Stat(filepath.Join(root, "func.yaml"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
testVariableName := "TEST_TEMPLATE_VARIABLE"
|
|
testVariableValue := "test-template"
|
|
|
|
envs := []fn.Env{
|
|
{
|
|
Name: &testVariableName,
|
|
Value: &testVariableValue,
|
|
},
|
|
}
|
|
|
|
f, err := fn.NewFunction(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(fn.Envs(envs), f.Build.BuildEnvs); diff != "" {
|
|
t.Fatalf("Unexpected difference between template's manifest.yaml buildEnvs and function BuildEnvs (-want, +got): %v", diff)
|
|
}
|
|
}
|
|
|
|
// TestTemplates_RepositoryManifestBuildEnvs ensures that BuildEnvs specified in a
|
|
// repository's manifest are included in the final function.
|
|
func TestTemplates_RepositoryManifestBuildEnvs(t *testing.T) {
|
|
// create test directory
|
|
root := "testdata/testRepositoryManifestBuildEnvs"
|
|
defer Using(t, root)()
|
|
|
|
// Client whose internal templates will be used.
|
|
client := fn.New(
|
|
fn.WithRegistry(TestRegistry),
|
|
fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
// write out a template
|
|
_, err := client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "customRuntime",
|
|
Template: "customLanguagePackRepo/customTemplate",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Assert file exists as expected
|
|
_, err = os.Stat(filepath.Join(root, "func.yaml"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
testVariableName := "TEST_REPO_VARIABLE"
|
|
testVariableValue := "test-repo"
|
|
|
|
envs := []fn.Env{
|
|
{
|
|
Name: &testVariableName,
|
|
Value: &testVariableValue,
|
|
},
|
|
}
|
|
|
|
f, err := fn.NewFunction(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(fn.Envs(envs), f.Build.BuildEnvs); diff != "" {
|
|
t.Fatalf("Unexpected difference between repository's manifest.yaml buildEnvs and function BuildEnvs (-want, +got): %v", diff)
|
|
}
|
|
}
|
|
|
|
// TestTemplates_ManifestInvocationHints ensures that invocation hints
|
|
// from a template's manifest are included in the final function.
|
|
func TestTemplates_ManifestInvocationHints(t *testing.T) {
|
|
root := "testdata/testTemplatesManifestInvocationHints"
|
|
defer Using(t, root)()
|
|
|
|
client := fn.New(
|
|
fn.WithRegistry(TestRegistry),
|
|
fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
f, err := client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "manifestedRuntime",
|
|
Template: "customLanguagePackRepo/manifestedTemplate",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if f.Invoke != "format" {
|
|
t.Fatalf("expected invoke format 'format', got '%v'", f.Invoke)
|
|
}
|
|
}
|
|
|
|
// TestTemplates_ManifestRemoved ensures that the manifest is not left in
|
|
// the resultant function after write.
|
|
func TestTemplates_ManifestRemoved(t *testing.T) {
|
|
// create test directory
|
|
root := "testdata/testTemplateManifestRemoved"
|
|
defer Using(t, root)()
|
|
|
|
// Client whose internal templates will be used.
|
|
client := fn.New(
|
|
fn.WithRegistry(TestRegistry),
|
|
fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
// write out a template
|
|
_, err := client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "manifestedRuntime",
|
|
Template: "customLanguagePackRepo/manifestedTemplate",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Assert func.yaml exists as expected
|
|
_, err = os.Stat(filepath.Join(root, "func.yaml"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Assert manifest.yaml does not
|
|
_, err = os.Stat(filepath.Join(root, "manifest.yaml"))
|
|
if err == nil {
|
|
t.Fatal("manifest.yaml should not exist after write")
|
|
}
|
|
|
|
}
|
|
|
|
// TestTemplates_InvocationDefault ensures that creating a function which
|
|
// 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) {
|
|
expectedInvoke := ""
|
|
root := "testdata/testTemplatesInvocationDefault"
|
|
defer Using(t, root)()
|
|
|
|
client := fn.New(
|
|
fn.WithRegistry(TestRegistry),
|
|
fn.WithRepositoriesPath("testdata/repositories"))
|
|
|
|
// The customTemplateRepo explicitly does not
|
|
// include manifests as it exemplifies an entirely default template repo.
|
|
f, err := client.Init(fn.Function{
|
|
Root: root,
|
|
Runtime: "customRuntime",
|
|
Template: "customTemplateRepo/customTemplate",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if f.Invoke != expectedInvoke {
|
|
t.Fatalf("expected '%v' invoke format. Got '%v'", expectedInvoke, f.Invoke)
|
|
}
|
|
}
|