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  }