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 }