From 1c049f10a81675e5b869187b263244ddd0b17ac2 Mon Sep 17 00:00:00 2001 From: Maksim An Date: Wed, 4 May 2022 14:56:01 -0700 Subject: [PATCH] tests: add tests for wait-paths (#1384) Follow up PR to add tests for wait-paths after initial PR #1258 was merged. Signed-off-by: Maksim An --- cmd/hooks/wait-paths/main.go | 25 +++-- cmd/hooks/wait-paths/wait_paths_test.go | 133 ++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 cmd/hooks/wait-paths/wait_paths_test.go diff --git a/cmd/hooks/wait-paths/main.go b/cmd/hooks/wait-paths/main.go index c3c49ccf9a..e821f4d470 100644 --- a/cmd/hooks/wait-paths/main.go +++ b/cmd/hooks/wait-paths/main.go @@ -1,9 +1,8 @@ -// +build linux - package main import ( "context" + "errors" "fmt" "os" "strings" @@ -18,10 +17,20 @@ const ( timeoutFlag = "timeout" ) +var errEmptyPaths = errors.New("paths cannot be empty") + // This is a hook that waits for a specific path to appear. // The hook has required list of comma-separated paths and a default timeout in seconds. func main() { + app := newCliApp() + if err := app.Run(os.Args); err != nil { + logrus.Fatalf("%s\n", err) + } + os.Exit(0) +} + +func newCliApp() *cli.App { app := cli.NewApp() app.Name = "wait-paths" app.Usage = "Provide a list paths and an optional timeout" @@ -38,14 +47,16 @@ func main() { }, } app.Action = run - if err := app.Run(os.Args); err != nil { - logrus.Fatalf("%s\n", err) - } - os.Exit(0) + return app } func run(cCtx *cli.Context) error { timeout := cCtx.GlobalInt(timeoutFlag) + + pathsVal := cCtx.GlobalString(pathsFlag) + if pathsVal == "" { + return errEmptyPaths + } paths := strings.Split(cCtx.GlobalString(pathsFlag), ",") waitCtx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) @@ -59,7 +70,7 @@ func run(cCtx *cli.Context) error { } select { case <-waitCtx.Done(): - return fmt.Errorf("timeout while waiting for path %q to appear", path) + return fmt.Errorf("timeout while waiting for path %q to appear: %w", path, context.DeadlineExceeded) default: time.Sleep(time.Millisecond * 10) continue diff --git a/cmd/hooks/wait-paths/wait_paths_test.go b/cmd/hooks/wait-paths/wait_paths_test.go new file mode 100644 index 0000000000..499aa7ed57 --- /dev/null +++ b/cmd/hooks/wait-paths/wait_paths_test.go @@ -0,0 +1,133 @@ +package main + +import ( + "context" + "errors" + "math" + "os" + "path/filepath" + "strings" + "sync" + "testing" + "time" +) + +func Test_Paths_EmptyString_NotAllowed(t *testing.T) { + args := []string{ + "wait-paths", + "-p", + "", + } + app := newCliApp() + err := app.Run(args) + if !errors.Is(err, errEmptyPaths) { + t.Fatalf("expected 'cannot be empty' error, got: %s", err) + } +} + +func Test_InvalidWaitPath_DefaultTimeout(t *testing.T) { + args := []string{ + "wait-paths", + "-p", + "non-existent", + } + app := newCliApp() + err := app.Run(args) + if cause := errors.Unwrap(err); !errors.Is(cause, context.DeadlineExceeded) { + t.Fatalf("expected 'timeout error', got: %s", err) + } +} + +func Test_InvalidWaitPath_5SecondTimeout(t *testing.T) { + args := []string{ + "wait-paths", + "-p", + "non-existent", + "-t", + "5", + } + app := newCliApp() + start := time.Now() + err := app.Run(args) + if cause := errors.Unwrap(err); !errors.Is(cause, context.DeadlineExceeded) { + t.Fatalf("expected 'timeout error', got: %s", err) + } + + end := time.Now() + diff := end.Sub(start) + diffSeconds := math.Round(diff.Seconds()) + if diffSeconds != 5 { + t.Fatalf("expected 5 second timeout, got: %f", diffSeconds) + } +} + +func Test_Valid_Paths_AlreadyPresent(t *testing.T) { + tmpDir := t.TempDir() + var files []string + for _, name := range []string{"file1", "file2"} { + filePath := filepath.Join(tmpDir, name) + if f, err := os.Create(filePath); err != nil { + t.Fatalf("failed to create temporary file %s: %s", name, err) + } else { + _ = f.Close() + } + files = append(files, filePath) + } + pathsArg := strings.Join(files, ",") + + args := []string{ + "wait-paths", + "-p", + pathsArg, + } + app := newCliApp() + if err := app.Run(args); err != nil { + t.Fatalf("expected no error, got: %s", err) + } +} + +func Test_Valid_Paths_BecomeAvailableLater(t *testing.T) { + tmpDir := t.TempDir() + var files []string + for _, name := range []string{"file1", "file2"} { + files = append(files, filepath.Join(tmpDir, name)) + } + pathsArg := strings.Join(files, ",") + + errChan := make(chan error) + var wg sync.WaitGroup + defer func() { + wg.Wait() + close(errChan) + }() + + args := []string{ + "wait-paths", + "-p", + pathsArg, + } + app := newCliApp() + wg.Add(1) + go func() { + defer wg.Done() + errChan <- app.Run(args) + }() + + wg.Add(1) + go func() { + defer wg.Done() + time.Sleep(5 * time.Second) + for _, fileName := range files { + if f, err := os.Create(fileName); err != nil { + errChan <- err + return + } else { + _ = f.Close() + } + } + }() + + if err := <-errChan; err != nil { + t.Fatalf("expected no error, got: %s", err) + } +}