github.com/lkingland/gridd@v0.0.0-20230313082622-f3ae21fe9d22/client_int_test.go (about)

     1  // Licensed under the Apache License, Version 2.0.  See LICENSE file.
     2  
     3  // +build integration
     4  
     5  package gridd_test
     6  
     7  import (
     8  	"os"
     9  	"reflect"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/lkingland/gridd"
    14  	"github.com/lkingland/gridd/boson"
    15  )
    16  
    17  /*
    18   NOTE:  Running integration tests locally requires a configured test cluster.
    19          Test failures may require manual removal of dangling resources.
    20  
    21   ## Integration Cluster
    22   These integration tests require a properly configured cluster,
    23   such as that which is setup and configured in CI (see .github/workflows).
    24   A local KinD cluster can be started via:
    25     ./hack/allocate.sh && ./hack/configure.sh
    26  
    27   ## Integration Testing
    28   These tests can be run via the make target:
    29     make integration
    30  	or manually by specifying the tag
    31     go test -v -tags integration ./...
    32  
    33   ## Teardown and Cleanup
    34   Tests should clean up after themselves.  In the event of failures, one may
    35   need to manually remove files:
    36     rm -rf ./testdata/example.com
    37   The test cluster is not automatically removed, as it can be reused.  To remove:
    38     ./hack/delete.sh
    39  */
    40  
    41  func TestList(t *testing.T) {
    42  	verbose := true
    43  	ctx := context.Background()
    44  
    45  	// Assemble
    46  	grid := gridd.New(
    47  		boson.NewProvider(verbose),
    48  		gridd.WithVerbose(verbose))
    49  
    50  	// Act
    51  	names, err := grid.List(ctx)
    52  	if err != nil {
    53  		t.Fatal(err)
    54  	}
    55  
    56  	// Assert
    57  	if len(names) != 0 {
    58  		t.Fatalf("Expected no Functions, got %v", names)
    59  	}
    60  }
    61  
    62  func TestCreate(t *testing.T) {
    63  	defer within(t, "testdata/example.com/create")()
    64  	verbose := true
    65  	ctx := context.Background()
    66  
    67  	// Assemble
    68  	grid := gridd.New(
    69  		boson.NewProvider(verbose),
    70  		gridd.WithVerbose(verbose))
    71  
    72  	// Act
    73  	if err := grid.Create(ctx, gridd.Function{}); err != nil {
    74  		t.Fatal(err)
    75  	}
    76  	defer del(t, grid, "create")
    77  
    78  	// Assert
    79  	names, err := grid.List(ctx)
    80  	if err != nil {
    81  		t.Fatal(err)
    82  	}
    83  	if !reflect.DeepEqual(names, []string{"create"}) {
    84  		t.Fatalf("Expected function list ['create'], got %v", names)
    85  	}
    86  }
    87  
    88  func TestRead(t *testing.T) {
    89  	// TODO
    90  	return
    91  	defer within(t, "testdata/example.com/read")()
    92  
    93  	touch("RUNSTAMP-TestRead")
    94  }
    95  
    96  func TestUpdate(t *testing.T) {
    97  	defer within(t, "testdata/example.com/update")()
    98  	verbose := true
    99  	ctx := context.Background()
   100  
   101  	grid := gridd.New(
   102  		boson.NewProvider(verbose),
   103  		gridd.WithVerbose(verbose))
   104  
   105  	if err := grid.Create(ctx, gridd.Function{}); err != nil {
   106  		t.Fatal(err)
   107  	}
   108  	defer del(t, grid, "update")
   109  
   110  	if err := grid.Update(ctx, gridd.Function{Root: "."}); err != nil {
   111  		t.Fatal(err)
   112  	}
   113  }
   114  
   115  func TestDelete(t *testing.T) {
   116  	defer within(t, "testdata/example.com/delete")()
   117  	verbose := true
   118  	ctx := context.Background()
   119  
   120  	grid := gridd.New(
   121  		boson.NewProvider(verbose),
   122  		gridd.WithVerbose(verbose))
   123  
   124  	if err := grid.Create(ctx, gridd.Function{}); err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	waitFor(t, grid, "delete")
   128  
   129  	if err := grid.Delete(ctx, "delete"); err != nil {
   130  		t.Fatal(err)
   131  	}
   132  
   133  	names, err := grid.List(ctx)
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  	if len(names) != 0 {
   138  		t.Fatalf("Expected empty Functions list, got %v", names)
   139  	}
   140  }
   141  
   142  // Helpers
   143  
   144  // Del cleans up after a test by removing a function by name.
   145  // (test fails if the named function does not exist)
   146  //
   147  // Intended to be run in a defer statement immediately after create, del
   148  // works around the asynchronicity of the underlying platform's creation
   149  // step by polling the provider until the names function becomes available
   150  // (or the test times out), before firing off a deletion request.
   151  // Of course, ideally this would be replaced by the use of a synchronous Create
   152  // method, or at a minimum a way to register a callback/listener for the
   153  // creation event.  This is what we have for now, and the show must go on.
   154  func del(t *testing.T, c *gridd.Client, name string) {
   155  	t.Helper()
   156  	waitFor(t, c, name)
   157  	if err := c.Delete(name); err != nil {
   158  		t.Fatal(err)
   159  	}
   160  }
   161  
   162  // waitFor the named Function to become available in List output.
   163  // TODO: the API should be synchronous, but that depends first on
   164  // Create returning the derived name such that we can bake polling in.
   165  // Ideally the Boson provider's Creaet would be made syncrhonous.
   166  func waitFor(t *testing.T, c *gridd.Client, name string) {
   167  	t.Helper()
   168  	var pollInterval = 2 * time.Second
   169  
   170  	for { // ever (i.e. defer to global test timeout)
   171  		nn, err := c.List()
   172  		if err != nil {
   173  			t.Fatal(err)
   174  		}
   175  		for _, n := range nn {
   176  			if n == name {
   177  				return
   178  			}
   179  		}
   180  		time.Sleep(pollInterval)
   181  	}
   182  }
   183  
   184  // Create the given directory, CD to it, and return a function which can be
   185  // run in a defer statement to return to the original directory and cleanup.
   186  // Note must be executed, not deferred itself
   187  // NO:  defer within(t, "somedir")
   188  // YES: defer within(t, "somedir")()
   189  func within(t *testing.T, root string) func() {
   190  	t.Helper()
   191  	cwd := pwd(t)
   192  	mkdir(t, root)
   193  	cd(t, root)
   194  	return func() {
   195  		cd(t, cwd)
   196  		rm(t, root)
   197  	}
   198  }
   199  
   200  func pwd(t *testing.T) string {
   201  	t.Helper()
   202  	dir, err := os.Getwd()
   203  	if err != nil {
   204  		t.Fatal(err)
   205  	}
   206  	return dir
   207  }
   208  
   209  func mkdir(t *testing.T, dir string) {
   210  	t.Helper()
   211  	if err := os.MkdirAll(dir, 0700); err != nil {
   212  		t.Fatal(err)
   213  	}
   214  }
   215  
   216  func cd(t *testing.T, dir string) {
   217  	t.Helper()
   218  	if err := os.Chdir(dir); err != nil {
   219  		t.Fatal(err)
   220  	}
   221  }
   222  
   223  func rm(t *testing.T, dir string) {
   224  	t.Helper()
   225  	if err := os.RemoveAll(dir); err != nil {
   226  		t.Fatal(err)
   227  	}
   228  }
   229  
   230  func touch(file string) {
   231  	_, err := os.Stat(file)
   232  	if os.IsNotExist(err) {
   233  		f, err := os.Create(file)
   234  		if err != nil {
   235  			panic(err)
   236  		}
   237  		defer f.Close()
   238  	}
   239  	t := time.Now().Local()
   240  	if err := os.Chtimes(file, t, t); err != nil {
   241  		panic(err)
   242  	}
   243  }