github.com/iron-io/functions@v0.0.0-20180820112432-d59d7d1c40b2/fn/commands/testfn.go (about) 1 package commands 2 3 import ( 4 "bufio" 5 "bytes" 6 "errors" 7 "fmt" 8 "github.com/iron-io/functions_go/models" 9 "net/url" 10 "os" 11 "path" 12 "strings" 13 "time" 14 15 image_commands "github.com/iron-io/functions/fn/commands/images" 16 "github.com/iron-io/functions/fn/common" 17 functions "github.com/iron-io/functions_go" 18 "github.com/urfave/cli" 19 ) 20 21 func testfn() cli.Command { 22 cmd := testcmd{RoutesApi: functions.NewRoutesApi()} 23 return cli.Command{ 24 Name: "test", 25 Usage: "run functions test if present", 26 Flags: cmd.flags(), 27 Action: cmd.test, 28 } 29 } 30 31 type testcmd struct { 32 *functions.RoutesApi 33 34 build bool 35 remote string 36 } 37 38 func (t *testcmd) flags() []cli.Flag { 39 return []cli.Flag{ 40 cli.BoolFlag{ 41 Name: "b", 42 Usage: "build before test", 43 Destination: &t.build, 44 }, 45 cli.StringFlag{ 46 Name: "remote", 47 Usage: "run tests by calling the function on IronFunctions daemon on `appname`", 48 Destination: &t.remote, 49 }, 50 } 51 } 52 53 func (t *testcmd) test(c *cli.Context) error { 54 if t.build { 55 b := &image_commands.Buildcmd{Verbose: true} 56 if err := b.Build(c); err != nil { 57 return err 58 } 59 fmt.Println() 60 } 61 62 ff, err := common.LoadFuncfile() 63 if err != nil { 64 return err 65 } 66 67 if len(ff.Tests) == 0 { 68 return errors.New("no tests found for this function") 69 } 70 71 target := ff.FullName() 72 runtest := runlocaltest 73 if t.remote != "" { 74 if ff.Path == nil || *ff.Path == "" { 75 return errors.New("execution of tests on remote server demand that this function to have a `path`.") 76 } 77 if err := common.ResetBasePath(t.Configuration); err != nil { 78 return fmt.Errorf("error setting endpoint: %v", err) 79 } 80 baseURL, err := url.Parse(t.Configuration.BasePath) 81 if err != nil { 82 return fmt.Errorf("error parsing base path: %v", err) 83 } 84 85 u, err := url.Parse("../") 86 u.Path = path.Join(u.Path, "r", t.remote, *ff.Path) 87 target = baseURL.ResolveReference(u).String() 88 runtest = runremotetest 89 } 90 91 var foundErr bool 92 fmt.Println("running tests on", ff.FullName(), ":") 93 for _, tt := range ff.Tests { 94 start := time.Now() 95 var err error 96 err = runtest(target, tt.In, tt.Out, tt.Err, tt.Env) 97 98 fmt.Print("\t - ", tt.Name, " (", time.Since(start), "): ") 99 100 if err != nil { 101 fmt.Println() 102 foundErr = true 103 scanner := bufio.NewScanner(strings.NewReader(err.Error())) 104 for scanner.Scan() { 105 fmt.Println("\t\t", scanner.Text()) 106 } 107 if err := scanner.Err(); err != nil { 108 fmt.Fprintln(os.Stderr, "reading test result:", err) 109 break 110 } 111 continue 112 } 113 114 fmt.Println("OK") 115 } 116 117 if foundErr { 118 return errors.New("errors found") 119 } 120 return nil 121 } 122 123 func runlocaltest(target string, in, expectedOut, expectedErr *string, env map[string]string) error { 124 stdin := &bytes.Buffer{} 125 if in != nil { 126 stdin = bytes.NewBufferString(*in) 127 } 128 129 var stdout, stderr bytes.Buffer 130 var restrictedEnv []string 131 for k, v := range env { 132 oldv := os.Getenv(k) 133 defer func(oldk, oldv string) { 134 os.Setenv(oldk, oldv) 135 }(k, oldv) 136 os.Setenv(k, v) 137 restrictedEnv = append(restrictedEnv, k) 138 } 139 140 if err := image_commands.Runff(target, stdin, &stdout, &stderr, "", restrictedEnv, nil); err != nil { 141 return fmt.Errorf("%v\nstdout:%s\nstderr:%s\n", err, stdout.String(), stderr.String()) 142 } 143 144 out := stdout.String() 145 if expectedOut == nil && out != "" { 146 return fmt.Errorf("unexpected output found: %s", out) 147 } else if expectedOut != nil && *expectedOut != out { 148 return fmt.Errorf("mismatched output found.\nexpected (%d bytes):\n%s\ngot (%d bytes):\n%s\n", len(*expectedOut), *expectedOut, len(out), out) 149 } 150 151 err := stderr.String() 152 if expectedErr == nil && err != "" { 153 return fmt.Errorf("unexpected error output found: %s", err) 154 } else if expectedErr != nil && *expectedErr != err { 155 return fmt.Errorf("mismatched error output found.\nexpected (%d bytes):\n%s\ngot (%d bytes):\n%s\n", len(*expectedErr), *expectedErr, len(err), err) 156 } 157 158 return nil 159 } 160 161 func runremotetest(target string, in, expectedOut, expectedErr *string, env map[string]string) error { 162 stdin := &bytes.Buffer{} 163 if in != nil { 164 stdin = bytes.NewBufferString(*in) 165 } 166 167 var stdout bytes.Buffer 168 169 var restrictedEnv []string 170 for k, v := range env { 171 oldv := os.Getenv(k) 172 defer func(oldk, oldv string) { 173 os.Setenv(oldk, oldv) 174 }(k, oldv) 175 os.Setenv(k, v) 176 restrictedEnv = append(restrictedEnv, k) 177 } 178 dummyRoute := &models.Route{} 179 if err := callfn(target, dummyRoute, stdin, &stdout, "", restrictedEnv); err != nil { 180 return fmt.Errorf("%v\nstdout:%s\n", err, stdout.String()) 181 } 182 183 out := stdout.String() 184 if expectedOut == nil && out != "" { 185 return fmt.Errorf("unexpected output found: %s", out) 186 } else if expectedOut != nil && *expectedOut != out { 187 return fmt.Errorf("mismatched output found.\nexpected (%d bytes):\n%s\ngot (%d bytes):\n%s\n", len(*expectedOut), *expectedOut, len(out), out) 188 } 189 190 if expectedErr != nil { 191 return fmt.Errorf("cannot process stderr in remote calls") 192 } 193 194 return nil 195 }