//go:build !integration // +build !integration package scaffolding import ( "errors" "os" "path/filepath" "testing" "knative.dev/func/pkg/filesystem" . "knative.dev/func/pkg/testing" ) // TestWrite_RuntimeErrors ensures that known runtimes which are not // yet implemented return a "not yet available" message, and unrecognized // runtimes state as much. func TestWrite_RuntimeErrors(t *testing.T) { tests := []struct { Runtime string Expected any }{ {"go", nil}, {"python", nil}, {"rust", &ErrDetectorNotImplemented{}}, {"node", &ErrDetectorNotImplemented{}}, {"typescript", &ErrDetectorNotImplemented{}}, {"quarkus", &ErrDetectorNotImplemented{}}, {"java", &ErrDetectorNotImplemented{}}, {"other", &ErrRuntimeNotRecognized{}}, } for _, test := range tests { t.Run(test.Runtime, func(t *testing.T) { // Since runtime validation during signature detection is the very first // thing that occurs, we can elide most of the setup and pass zero // values for source directory, output directory and invocation. // This may need to be expanded in the event the Write function is // expanded to have more preconditions. err := Write("", "", test.Runtime, "", nil) if test.Expected != nil && err == nil { t.Fatalf("expected runtime %v to yield a detection error", test.Runtime) } if test.Expected != nil && !errors.As(err, test.Expected) { t.Fatalf("did not receive expected error type for %v runtime.", test.Runtime) } t.Logf("ok: %v", err) }) } } // TestWrite ensures that the Write method writes Scaffolding to the given // destination. This is a fairly shallow test. See the Scaffolding and // Detector tests for more depth. func TestWrite(t *testing.T) { // The filesystem containing scaffolding is expected to conform to the // structure: // /[language]/scaffolding/["instanced"|"static"]-[invocation] // ex: // "./go/scaffolding/instanced-http/main.go" cwd, err := os.Getwd() if err != nil { t.Fatal(err) } fs := filesystem.NewOsFilesystem(filepath.Join(cwd, "testdata", "testwrite")) root, done := Mktemp(t) defer done() // Write out a test implementation that will result in the InstancedHTTP // signature being detected. impl := ` package f type F struct{} func New() *F { return nil } ` err = os.WriteFile(filepath.Join(root, "f.go"), []byte(impl), os.ModePerm) if err != nil { t.Fatal(err) } err = os.WriteFile(filepath.Join(root, "go.mod"), []byte("module foo"), 0644) if err != nil { t.Fatal(err) } // The output destination for the scaffolding out := filepath.Join(root, "out") // Write Scaffolding to err = Write( out, // output directory root, // source code location "go", // Runtime "", // optional invocation hint (http is the default) fs) // The filesystem from which the scaffolding should be pulled if err != nil { t.Fatal(err) } // Assert there exists a main.go (from the testdata scaffolding filesystem). if _, err = os.Stat(filepath.Join(root, "out", "main.go")); err != nil { t.Fatal(err) } // Assert there exists a symbolic link to the source code root, err = filepath.EvalSymlinks(root) // dereference any current symlinks if err != nil { t.Fatal(err) } target, err := filepath.EvalSymlinks(filepath.Join(out, "f")) if err != nil { t.Fatal(err) } if target != root { t.Fatalf("scaffolding symlink should be:\n%v\n But got target:\n%v", root, target) } } // TestWrite_ScaffoldingNotFound ensures that a typed error is returned // when scaffolding is not found. func TestWrite_ScaffoldingNotFound(t *testing.T) { cwd, err := os.Getwd() if err != nil { t.Fatal(err) } fs := filesystem.NewOsFilesystem(filepath.Join(cwd, "testdata", "testnotfound")) root, done := Mktemp(t) defer done() impl := ` package f type F struct{} func New() *F { return nil } ` err = os.WriteFile(filepath.Join(root, "f.go"), []byte(impl), os.ModePerm) if err != nil { t.Fatal(err) } out := filepath.Join(root, "out") err = Write(out, root, "go", "", fs) if err == nil { t.Fatal("did not receive expected error") } if !errors.Is(err, ErrScaffoldingNotFound) { t.Fatalf("error received was not ErrScaffoldingNotFound. %v", err) } } // TestNewScaffoldingError ensures that a scaffolding error wraps its // underlying error such that callers can use errors.Is/As. func TestNewScaffoldingError(t *testing.T) { // exampleError that would come from something scaffolding employs to // accomplish a task var ExampleError = errors.New("example error") err := ScaffoldingError{"some ExampleError", ExampleError} if !errors.Is(err, ExampleError) { t.Fatalf("type ScaffoldingError does not wrap errors.") } t.Logf("ok: %v", err) }