From acc56b0900113ca68270bd3ac68310864e42b5a7 Mon Sep 17 00:00:00 2001 From: Luke Kingland Date: Mon, 29 Mar 2021 20:06:18 +0900 Subject: [PATCH] feat: positive error when runtimme or template unrecognized --- client_test.go | 38 +++++++++++++++++++++++++++++++------- tarfs/tarfs.go | 5 +---- tarfs/tarfs_test.go | 19 +++++++++++++++++++ templates.go | 19 ++++++++++++++----- templates_test.go | 26 ++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 16 deletions(-) diff --git a/client_test.go b/client_test.go index cbe1543f4..cd37b1ac3 100644 --- a/client_test.go +++ b/client_test.go @@ -195,10 +195,10 @@ func TestExtensibleTemplates(t *testing.T) { } } -// TestUnsupportedRuntime generates an error. -func TestUnsupportedRuntime(t *testing.T) { +// TestRuntimeNotFound generates an error (embedded default repository). +func TestRuntimeNotFound(t *testing.T) { // Create a directory for the Function - root := "testdata/example.com/testUnsupportedRuntime" + root := "testdata/example.com/testRuntimeNotFound" if err := os.MkdirAll(root, 0700); err != nil { t.Fatal(err) } @@ -206,13 +206,37 @@ func TestUnsupportedRuntime(t *testing.T) { client := bosonFunc.New(bosonFunc.WithRegistry(TestRegistry)) - // create a Function call witn an unsupported runtime should bubble - // the error generated by the underlying initializer. - if err := client.New(context.Background(), bosonFunc.Function{Root: root, Runtime: "invalid"}); err == nil { - t.Fatal("unsupported runtime did not generate error") + // creating a Function with an unsupported runtime should bubble + // the error generated by the underlying template initializer. + f := bosonFunc.Function{Root: root, Runtime: "invalid"} + err := client.New(context.Background(), f) + if !errors.Is(err, bosonFunc.ErrRuntimeNotFound) { + t.Fatalf("Expected ErrRuntimeNotFound, got %T", err) } } +// TestTemplateNotFound generates an error (embedded default repository). +func TestTemplateNotFound(t *testing.T) { + root := "testdata/example.com/testTemplateNotFound" + if err := os.MkdirAll(root, 0700); err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) + + client := bosonFunc.New(bosonFunc.WithRegistry(TestRegistry)) + + // creating a Function with an unsupported runtime should bubble + // the error generated by the unsderlying template initializer. + f := bosonFunc.Function{Root: root, Runtime: "go", Trigger: "invalid"} + err := client.New(context.Background(), f) + if !errors.Is(err, bosonFunc.ErrTemplateNotFound) { + t.Fatalf("Expected ErrRuntimeNotFound, got %T", err) + } +} + +// TODO: TestRuntimeNotFound in custom repository +// TODO: TestTemplateNotFound in custom repository + // TestNamed ensures that an explicitly passed name is used in leau of the // path derived name when provided, and persists through instantiations. func TestNamed(t *testing.T) { diff --git a/tarfs/tarfs.go b/tarfs/tarfs.go index dd4088104..4768a62f0 100644 --- a/tarfs/tarfs.go +++ b/tarfs/tarfs.go @@ -3,7 +3,6 @@ package tarfs import ( "archive/tar" "bytes" - "fmt" "io" "io/fs" "path" @@ -37,7 +36,6 @@ func New(r io.Reader) (FS, error) { ModTime: header.FileInfo().ModTime(), Sys: header.FileInfo().Sys, } - fmt.Printf("Adding [%v]%v\n", header.Name, mapfs[header.Name]) // Done if directory if header.FileInfo().IsDir() { @@ -54,7 +52,6 @@ func New(r io.Reader) (FS, error) { } func (fsys FS) Open(name string) (fs.File, error) { - fmt.Printf("Seeking %v\n", name) if !fs.ValidPath(name) { return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} } @@ -98,7 +95,7 @@ func (fsys FS) Open(name string) (fs.File, error) { // If the directory name is not in the map, // and there are no children of the name in the map, // then the directory is treated as not existing. - if f == nil && list == nil && len(need) == 0 { + if f == nil && len(list) == 0 && len(need) == 0 { return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} } } diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index eb0f086af..e2dfcfa95 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -41,3 +41,22 @@ func TestSingle(t *testing.T) { t.Fatal(err) } } + +// TestIsNotExist ensures that a request to read a file or directory which does not +// exist returns the appropriate error. +func TestIsNotExist(t *testing.T) { + f, err := os.Open("testdata/empty.tar") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + tfs, err := New(f) + if err != nil { + t.Fatal(err) + } + + if err := fstest.TestFS(tfs, "invalid"); err == nil { + t.Fatalf("did not receive expected error testing for a missing file") + } +} diff --git a/templates.go b/templates.go index 518ac5302..b28012455 100644 --- a/templates.go +++ b/templates.go @@ -50,6 +50,8 @@ type templateWriter struct { var ( ErrRepositoryNotFound = errors.New("repository not found") ErrRepositoriesNotDefined = errors.New("custom template location not specified") + ErrRuntimeNotFound = errors.New("runtime not found") + ErrTemplateNotFound = errors.New("template not found") ErrTemplateMissingRepository = errors.New("template name missing repository prefix") ) @@ -105,12 +107,19 @@ func (t *templateWriter) writeCustom(repositories, runtime, template, dest strin } func (t *templateWriter) writeEmbedded(runtime, template, dest string) error { - _, err := fs.Stat(t.templates, filepath.Join("templates", runtime, template)) - if err != nil { - return err + runtimePath := filepath.Join("templates", runtime) + _, err := fs.Stat(t.templates, runtimePath) + if errors.Is(err, fs.ErrNotExist) { + return ErrRuntimeNotFound } - src := filepath.Join("templates", runtime, template) - return t.cp(src, dest, t.templates) + + templatePath := filepath.Join("templates", runtime, template) + _, err = fs.Stat(t.templates, templatePath) + if errors.Is(err, fs.ErrNotExist) { + return ErrTemplateNotFound + } + + return t.cp(templatePath, dest, t.templates) } func repositoryExists(repositories, template string) bool { diff --git a/templates_test.go b/templates_test.go index f17636d38..3f0836c14 100644 --- a/templates_test.go +++ b/templates_test.go @@ -3,12 +3,15 @@ package function import ( + "errors" "os" "path/filepath" "runtime" "testing" ) +// TestRuntime consists of a specially designed templates directory +// used exclusively for embedded template write tests. const TestRuntime = "test" // TestWriteEmbedded ensures that embedded templates are copied. @@ -72,6 +75,29 @@ func TestWriteDefault(t *testing.T) { } } +// TestWriteInvalid ensures that specifying unrecgoznized runtime/template errors +func TestWriteInvalid(t *testing.T) { + // create test directory + root := "testdata/testWriteInvalid" + defer using(t, root)() + + w := templateWriter{} + var err error // should be populated with the correct error type + + // Test for error writing an invalid runtime + // (the http template + err = w.Write("invalid", DefaultTemplate, root) + if !errors.Is(err, ErrRuntimeNotFound) { + t.Fatalf("Expected ErrRuntimeNotFound, got %T", err) + } + + // Test for error writing an invalid template + err = w.Write(TestRuntime, "invalid", root) + if !errors.Is(err, ErrTemplateNotFound) { + t.Fatalf("Expected ErrTemplateNotFound, got %T", err) + } +} + // TestWriteModeEmbedded ensures that templates written from the embedded // templates retain their mode. func TestWriteModeEmbedded(t *testing.T) {