diff --git a/Taskfile.yml b/Taskfile.yml index c5555b0..32330be 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -15,7 +15,7 @@ tasks: test: cmds: - - kind delete cluster --name=tests && kind create cluster --name=tests + # - kind delete cluster --name=tests && kind create cluster --name=tests - go test -coverprofile cover.out -p 1 -v ./... update-deps: diff --git a/cmd/atc/main_test.go b/cmd/atc/main_test.go index 113d12c..a292c53 100644 --- a/cmd/atc/main_test.go +++ b/cmd/atc/main_test.go @@ -1,23 +1,16 @@ package main import ( - "bytes" "context" - "encoding/json" "fmt" - "io" "os" - "os/exec" "reflect" - "regexp" "strings" "testing" "time" "github.com/stretchr/testify/require" - "github.com/davidmdm/ansi" - apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,6 +18,7 @@ import ( "github.com/yokecd/yoke/internal" "github.com/yokecd/yoke/internal/home" "github.com/yokecd/yoke/internal/k8s" + "github.com/yokecd/yoke/internal/testutils" "github.com/yokecd/yoke/pkg/apis/airway/v1alpha1" "github.com/yokecd/yoke/pkg/openapi" "github.com/yokecd/yoke/pkg/yoke" @@ -34,29 +28,29 @@ func TestAirTrafficController(t *testing.T) { require.NoError(t, os.RemoveAll("./test_output")) require.NoError(t, os.MkdirAll("./test_output", 0o755)) - require.NoError(t, x("kind delete clusters --all")) - require.NoError(t, x("kind create cluster --name=atc-test")) + require.NoError(t, testutils.X("kind delete clusters --all")) + require.NoError(t, testutils.X("kind create cluster --name=atc-test")) - require.NoError(t, x( + require.NoError(t, testutils.X( "go build -o ./test_output/atc-installer.wasm ../atc-installer", - env("GOOS=wasip1", "GOARCH=wasm"), + testutils.Env("GOOS=wasip1", "GOARCH=wasm"), )) - require.NoError(t, x( + require.NoError(t, testutils.X( "go build -o ./test_output/backend.wasm ./internal/testing/apis/backend/flight", - env("GOOS=wasip1", "GOARCH=wasm"), + testutils.Env("GOOS=wasip1", "GOARCH=wasm"), )) - require.NoError(t, x( + require.NoError(t, testutils.X( "docker build -t yokecd/atc:test -f Dockerfile.atc .", - dir("../.."), + testutils.Dir("../.."), )) - require.NoError(t, x("kind load --name=atc-test docker-image yokecd/atc:test")) + require.NoError(t, testutils.X("kind load --name=atc-test docker-image yokecd/atc:test")) - require.NoError(t, x("docker build -t yokecd/wasmcache:test -f ./internal/testing/Dockerfile.wasmcache ../..")) - require.NoError(t, x("kind load --name=atc-test docker-image yokecd/wasmcache:test")) + require.NoError(t, testutils.X("docker build -t yokecd/wasmcache:test -f ./internal/testing/Dockerfile.wasmcache ../..")) + require.NoError(t, testutils.X("kind load --name=atc-test docker-image yokecd/wasmcache:test")) - require.NoError(t, x("docker build -t yokecd/c4ts:test -f ./internal/testing/Dockerfile.c4ts ./internal/testing")) - require.NoError(t, x("kind load --name=atc-test docker-image yokecd/c4ts:test")) + require.NoError(t, testutils.X("docker build -t yokecd/c4ts:test -f ./internal/testing/Dockerfile.c4ts ./internal/testing")) + require.NoError(t, testutils.X("kind load --name=atc-test docker-image yokecd/c4ts:test")) client, err := k8s.NewClientFromKubeConfig(home.Kubeconfig) require.NoError(t, err) @@ -107,7 +101,7 @@ func TestAirTrafficController(t *testing.T) { airwayTakeoffParams := yoke.TakeoffParams{ Release: "backend-airway", Flight: yoke.FlightParams{ - Input: jsonReader(v1alpha1.Airway{ + Input: testutils.JsonReader(v1alpha1.Airway{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Name: "backends.examples.com", @@ -147,13 +141,13 @@ func TestAirTrafficController(t *testing.T) { require.NoError(t, commander.Takeoff(ctx, airwayTakeoffParams)) // TODO: remove eventually code once yoke has proper support for Airway readiness. - EventuallyNoErrorf( + testutils.EventuallyNoErrorf( t, func() error { return commander.Takeoff(ctx, yoke.TakeoffParams{ Release: "c4ts", Flight: yoke.FlightParams{ - Input: jsonReader(backendv1.Backend{ + Input: testutils.JsonReader(backendv1.Backend{ ObjectMeta: metav1.ObjectMeta{ Name: "c4ts", }, @@ -173,7 +167,7 @@ func TestAirTrafficController(t *testing.T) { "failed to create backend resource", ) - EventuallyNoErrorf( + testutils.EventuallyNoErrorf( t, func() error { pods, err := client.Clientset.CoreV1().Pods("default").List(ctx, metav1.ListOptions{ @@ -196,71 +190,3 @@ func TestAirTrafficController(t *testing.T) { "failed to assert expected replica count for c4ts backend deployment", ) } - -type xoptions struct { - Env []string - Dir string -} - -func env(e ...string) XOpt { - return func(opts *xoptions) { - opts.Env = e - } -} - -func dir(d string) XOpt { - return func(opts *xoptions) { - opts.Dir = d - } -} - -type XOpt func(*xoptions) - -func x(line string, opts ...XOpt) error { - var options xoptions - for _, apply := range opts { - apply(&options) - } - - args := regexp.MustCompile(`\s+`).Split(line, -1) - - cmd := exec.Command(args[0], args[1:]...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Env = append(os.Environ(), options.Env...) - cmd.Dir = options.Dir - - ansi.MakeStyle(ansi.FgCyan).Println(line) - return cmd.Run() -} - -func jsonReader(value any) io.Reader { - data, err := json.Marshal(value) - if err != nil { - pr, pw := io.Pipe() - pw.CloseWithError(err) - return pr - } - return bytes.NewReader(data) -} - -func EventuallyNoErrorf(t *testing.T, fn func() error, tick time.Duration, timeout time.Duration, msg string, args ...any) { - t.Helper() - - var ( - ticker = time.NewTimer(0) - deadline = time.Now().Add(timeout) - ) - - for range ticker.C { - err := fn() - if err == nil { - return - } - if time.Now().After(deadline) { - require.NoErrorf(t, err, msg, args...) - return - } - ticker.Reset(tick) - } -} diff --git a/cmd/yoke/main_test.go b/cmd/yoke/main_test.go index 22b3c83..e93a40a 100644 --- a/cmd/yoke/main_test.go +++ b/cmd/yoke/main_test.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "math/rand/v2" + "os" "strconv" "strings" "testing" @@ -26,9 +27,23 @@ import ( "github.com/yokecd/yoke/internal" "github.com/yokecd/yoke/internal/home" "github.com/yokecd/yoke/internal/k8s" + "github.com/yokecd/yoke/internal/testutils" "github.com/yokecd/yoke/pkg/yoke" ) +func TestMain(m *testing.M) { + must := func(err error) { + if err != nil { + panic(err) + } + } + + must(testutils.X("kind delete clusters --all")) + must(testutils.X("kind create cluster --name=tests")) + + os.Exit(m.Run()) +} + var background = context.Background() func createBasicDeployment(t *testing.T, name, namespace string) io.Reader { diff --git a/internal/testutils/utils.go b/internal/testutils/utils.go new file mode 100644 index 0000000..81eee57 --- /dev/null +++ b/internal/testutils/utils.go @@ -0,0 +1,85 @@ +package testutils + +import ( + "bytes" + "encoding/json" + "io" + "os" + "os/exec" + "regexp" + "testing" + "time" + + "github.com/davidmdm/ansi" + "github.com/stretchr/testify/require" +) + +var cyan = ansi.MakeStyle(ansi.FgCyan) + +type xoptions struct { + Env []string + Dir string +} + +func Env(e ...string) XOpt { + return func(opts *xoptions) { + opts.Env = e + } +} + +func Dir(d string) XOpt { + return func(opts *xoptions) { + opts.Dir = d + } +} + +type XOpt func(*xoptions) + +func X(line string, opts ...XOpt) error { + var options xoptions + for _, apply := range opts { + apply(&options) + } + + args := regexp.MustCompile(`\s+`).Split(line, -1) + + cmd := exec.Command(args[0], args[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = append(os.Environ(), options.Env...) + cmd.Dir = options.Dir + + cyan.Println(line) + return cmd.Run() +} + +func JsonReader(value any) io.Reader { + data, err := json.Marshal(value) + if err != nil { + pr, pw := io.Pipe() + pw.CloseWithError(err) + return pr + } + return bytes.NewReader(data) +} + +func EventuallyNoErrorf(t *testing.T, fn func() error, tick time.Duration, timeout time.Duration, msg string, args ...any) { + t.Helper() + + var ( + ticker = time.NewTimer(0) + deadline = time.Now().Add(timeout) + ) + + for range ticker.C { + err := fn() + if err == nil { + return + } + if time.Now().After(deadline) { + require.NoErrorf(t, err, msg, args...) + return + } + ticker.Reset(tick) + } +}