From 8edd0df836055b33473f9a7774e8ae755f46ac2e Mon Sep 17 00:00:00 2001 From: Luke Kingland Date: Sat, 13 Feb 2021 03:56:41 +0900 Subject: [PATCH] feat: basic lifecycle integraiton tests --- client_int_test.go | 227 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 225 insertions(+), 2 deletions(-) diff --git a/client_int_test.go b/client_int_test.go index 0f482dcc44..ea06811e61 100644 --- a/client_int_test.go +++ b/client_int_test.go @@ -3,9 +3,15 @@ package function_test import ( + "os" + "reflect" + "testing" + "time" + boson "github.com/boson-project/func" + "github.com/boson-project/func/buildpacks" + "github.com/boson-project/func/docker" "github.com/boson-project/func/knative" - "testing" ) /* @@ -35,7 +41,16 @@ import ( ./hack/delete.sh */ -const DefaultNamespace = "func" +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:5000/func" + + // DefaultNamespace for the underlying deployments. Must be the same + // as is set up and configured (see hack/configure.sh) + DefaultNamespace = "func" +) func TestList(t *testing.T) { verbose := true @@ -60,3 +75,211 @@ func TestList(t *testing.T) { t.Fatalf("Expected no Functions, got %v", names) } } + +// TestNew creates +func TestNew(t *testing.T) { + defer within(t, "testdata/example.com/testnew")() + verbose := true + + client := newClient(verbose) + + // Act + if err := client.New(boson.Function{Name: "testnew", Root: ".", Runtime: "go"}); err != nil { + t.Fatal(err) + } + defer del(t, client, "testnew") + + // Assert + names, err := client.List() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(names, []string{"testnew"}) { + t.Fatalf("Expected function list ['testnew'], got %v", names) + } +} + +// TestDeploy updates +func TestDeploy(t *testing.T) { + defer within(t, "testdata/example.com/deploy")() + verbose := true + + client := newClient(verbose) + + if err := client.New(boson.Function{Name: "deploy", Root: ".", Runtime: "go"}); err != nil { + t.Fatal(err) + } + defer del(t, client, "deploy") + + if err := client.Deploy("."); err != nil { + t.Fatal(err) + } +} + +// TestRemove deletes +func TestRemove(t *testing.T) { + defer within(t, "testdata/example.com/remove")() + verbose := true + + client := newClient(verbose) + + if err := client.New(boson.Function{Name: "remove", Root: ".", Runtime: "go"}); err != nil { + t.Fatal(err) + } + waitFor(t, client, "remove") + + if err := client.Remove(boson.Function{Name: "remove"}); err != nil { + t.Fatal(err) + } + + names, err := client.List() + if err != nil { + t.Fatal(err) + } + if len(names) != 0 { + t.Fatalf("Expected empty Functions list, got %v", names) + } +} + +// *********** +// Helpers +// *********** + +// newClient creates an instance of the func client whose concrete impls +// match those created by the kn func plugin CLI. +func newClient(verbose bool) *boson.Client { + builder := buildpacks.NewBuilder() + builder.Verbose = verbose + + pusher := docker.NewPusher() + pusher.Verbose = verbose + + deployer, err := knative.NewDeployer(DefaultNamespace) + if err != nil { + panic(err) // TODO: remove error from deployer constructor + } + deployer.Verbose = verbose + + remover, err := knative.NewRemover(DefaultNamespace) + if err != nil { + panic(err) // TODO: remove error from remover constructor + } + remover.Verbose = verbose + + lister, err := knative.NewLister(DefaultNamespace) + if err != nil { + panic(err) // TODO: remove error from lister constructor + } + lister.Verbose = verbose + + return boson.New( + boson.WithRegistry(DefaultRegistry), + boson.WithVerbose(verbose), + boson.WithBuilder(builder), + boson.WithPusher(pusher), + boson.WithDeployer(deployer), + boson.WithRemover(remover), + boson.WithLister(lister), + ) +} + +// Del cleans up after a test by removing a function by name. +// (test fails if the named function does not exist) +// +// Intended to be run in a defer statement immediately after creation, del +// works around the asynchronicity of the underlying platform's creation +// step by polling the provider until the names function becomes available +// (or the test times out), before firing off a deletion request. +// Of course, ideally this would be replaced by the use of a synchronous +// method, or at a minimum a way to register a callback/listener for the +// creation event. This is what we have for now, and the show must go on. +func del(t *testing.T, c *boson.Client, name string) { + t.Helper() + waitFor(t, c, name) + if err := c.Remove(boson.Function{Name: name}); err != nil { + t.Fatal(err) + } +} + +// waitFor the named Function to become available in List output. +// TODO: the API should be synchronous, but that depends first on +// Create returning the derived name such that we can bake polling in. +// Ideally the Boson provider's Creaet would be made syncrhonous. +func waitFor(t *testing.T, c *boson.Client, name string) { + t.Helper() + var pollInterval = 2 * time.Second + + for { // ever (i.e. defer to global test timeout) + nn, err := c.List() + if err != nil { + t.Fatal(err) + } + for _, n := range nn { + if n.Name == name { + return + } + } + time.Sleep(pollInterval) + } +} + +// Create the given directory, CD to it, and return a function which can be +// run in a defer statement to return to the original directory and cleanup. +// Note must be executed, not deferred itself +// NO: defer within(t, "somedir") +// YES: defer within(t, "somedir")() +func within(t *testing.T, root string) func() { + t.Helper() + cwd := pwd(t) + mkdir(t, root) + cd(t, root) + return func() { + cd(t, cwd) + rm(t, root) + } +} + +func pwd(t *testing.T) string { + t.Helper() + dir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + return dir +} + +func mkdir(t *testing.T, dir string) { + t.Helper() + if err := os.MkdirAll(dir, 0700); err != nil { + t.Fatal(err) + } +} + +func cd(t *testing.T, dir string) { + t.Helper() + if err := os.Chdir(dir); err != nil { + t.Fatal(err) + } +} + +func rm(t *testing.T, dir string) { + t.Helper() + if err := os.RemoveAll(dir); err != nil { + t.Fatal(err) + } +} + +func touch(file string) { + _, err := os.Stat(file) + if os.IsNotExist(err) { + f, err := os.Create(file) + if err != nil { + panic(err) + } + defer f.Close() + } + t := time.Now().Local() + if err := os.Chtimes(file, t, t); err != nil { + panic(err) + } +}