github.com/bilus/oya@v0.0.3-0.20190301162104-da4acbd394c6/oya_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"regexp"
    10  	"runtime"
    11  	"strings"
    12  
    13  	"github.com/DATA-DOG/godog"
    14  	"github.com/DATA-DOG/godog/gherkin"
    15  	"github.com/bilus/oya/cmd"
    16  	"github.com/bilus/oya/pkg/oyafile"
    17  	"github.com/pkg/errors"
    18  	log "github.com/sirupsen/logrus"
    19  )
    20  
    21  const SOPS_PGP_KEY = "317D 6971 DD80 4501 A6B8  65B9 0F1F D46E 2E8C 7202"
    22  
    23  type SuiteContext struct {
    24  	projectDir string
    25  
    26  	lastCommandErr error
    27  	stdout         *bytes.Buffer
    28  	stderr         *bytes.Buffer
    29  }
    30  
    31  func (c *SuiteContext) MustSetUp() {
    32  	projectDir, err := ioutil.TempDir("", "oya")
    33  	if err != nil {
    34  		panic(err)
    35  	}
    36  
    37  	overrideOyaCmd(projectDir)
    38  	setEnv(projectDir)
    39  
    40  	log.SetLevel(log.DebugLevel)
    41  	c.projectDir = projectDir
    42  	c.stdout = bytes.NewBuffer(nil)
    43  	c.stderr = bytes.NewBuffer(nil)
    44  }
    45  
    46  func (c *SuiteContext) MustTearDown() {
    47  	err := os.RemoveAll(c.projectDir)
    48  	if err != nil {
    49  		panic(err)
    50  	}
    51  }
    52  
    53  func setEnv(projectDir string) {
    54  	err := os.Setenv("OYA_HOME", projectDir)
    55  	if err != nil {
    56  		panic(err)
    57  	}
    58  	err = os.Setenv("SOPS_PGP_FP", SOPS_PGP_KEY)
    59  	if err != nil {
    60  		panic(err)
    61  	}
    62  }
    63  
    64  // overrideOyaCmd overrides `oya` command used by $Tasks in templates
    65  // to run oya tasks.
    66  // It builds oya to a temporary directory and use it to launch Oya in scripts.
    67  func overrideOyaCmd(projectDir string) {
    68  	executablePath := filepath.Join(projectDir, "_bin/oya")
    69  	oyaCmdOverride := fmt.Sprintf(
    70  		"(cd %v && go build -o %v oya.go); %v",
    71  		sourceFileDirectory(), executablePath, executablePath)
    72  	oyafile.OyaCmdOverride = &oyaCmdOverride
    73  }
    74  
    75  func (c *SuiteContext) writeFile(path, contents string) error {
    76  	targetPath := c.resolvePath(path)
    77  	dir := filepath.Dir(targetPath)
    78  	err := os.MkdirAll(dir, 0700)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	return ioutil.WriteFile(targetPath, []byte(contents), 0600)
    83  }
    84  
    85  func (c *SuiteContext) readFile(path string) (string, error) {
    86  	sourcePath := c.resolvePath(path)
    87  	contents, err := ioutil.ReadFile(sourcePath)
    88  	if err != nil {
    89  		return "", err
    90  	}
    91  	return string(contents), err
    92  }
    93  
    94  func (c *SuiteContext) resolvePath(path string) string {
    95  	if filepath.IsAbs(path) {
    96  		return path
    97  	}
    98  	return filepath.Join(c.projectDir, path)
    99  }
   100  
   101  func (c *SuiteContext) iAmInProjectDir() error {
   102  	return os.Chdir(c.projectDir)
   103  }
   104  
   105  func (c *SuiteContext) imInDir(subdir string) error {
   106  	return os.Chdir(subdir)
   107  }
   108  
   109  func (c *SuiteContext) fileContaining(path string, contents *gherkin.DocString) error {
   110  	return c.writeFile(path, contents.Content)
   111  }
   112  
   113  func (c *SuiteContext) environmentVariableSet(name, value string) error {
   114  	return os.Setenv(name, value)
   115  }
   116  
   117  func (c *SuiteContext) fileContains(path string, contents *gherkin.DocString) error {
   118  	actual, err := c.readFile(path)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	if actual != contents.Content {
   123  		return fmt.Errorf("unexpected file %v contents: %q expected: %q", path, actual, contents.Content)
   124  	}
   125  	return nil
   126  }
   127  
   128  func (c *SuiteContext) fileDoesNotContain(path string, contents *gherkin.DocString) error {
   129  	actual, err := c.readFile(path)
   130  	if err != nil {
   131  		return err
   132  	}
   133  	re := regexp.MustCompile(".*" + contents.Content + ".*")
   134  	if len(re.FindString(actual)) > 0 {
   135  		return fmt.Errorf("unexpected file %v contents: %q NOT expected: %q", path, actual, contents.Content)
   136  	}
   137  	return nil
   138  }
   139  
   140  func (c *SuiteContext) fileExists(path string) error {
   141  	_, err := os.Stat(c.resolvePath(path))
   142  	return err
   143  }
   144  
   145  func (c *SuiteContext) fileDoesNotExist(path string) error {
   146  	_, err := os.Stat(path)
   147  	if os.IsNotExist(err) {
   148  		return nil
   149  	}
   150  	if err != nil {
   151  		return err
   152  	}
   153  	return errors.Errorf("expected %v to not exist", path)
   154  }
   155  
   156  func (c *SuiteContext) execute(command string) error {
   157  	c.stdout.Reset()
   158  	c.stderr.Reset()
   159  	cmd.ResetFlags()
   160  
   161  	oldArgs := os.Args
   162  	os.Args = strings.Fields(command)
   163  	defer func() {
   164  		os.Args = oldArgs
   165  	}()
   166  	cmd.SetOutput(c.stdout)
   167  	c.lastCommandErr = cmd.ExecuteE()
   168  	return nil
   169  }
   170  
   171  func (c *SuiteContext) iRunOya(command string) error {
   172  	return c.execute("oya " + command)
   173  }
   174  
   175  func (c *SuiteContext) modifyFileToContain(path string, contents *gherkin.DocString) error {
   176  	return c.writeFile(path, contents.Content)
   177  }
   178  
   179  func (c *SuiteContext) theCommandSucceeds() error {
   180  	if c.lastCommandErr != nil {
   181  		log.Println(c.stdout.String())
   182  		log.Println(c.stderr.String())
   183  		return errors.Wrap(c.lastCommandErr, "command unexpectedly failed")
   184  	}
   185  	return nil
   186  }
   187  
   188  func (c *SuiteContext) theCommandFailsWithError(errMsg *gherkin.DocString) error {
   189  	errMsg.Content = fmt.Sprintf("^%v$", errMsg.Content)
   190  	return c.theCommandFailsWithErrorMatching(errMsg)
   191  }
   192  
   193  func (c *SuiteContext) theCommandFailsWithErrorMatching(errMsg *gherkin.DocString) error {
   194  	if c.lastCommandErr == nil {
   195  		return errors.Errorf("last command unexpectedly succeeded")
   196  	}
   197  
   198  	rx := regexp.MustCompile(errMsg.Content)
   199  	if !rx.MatchString(c.lastCommandErr.Error()) {
   200  		return errors.Wrap(c.lastCommandErr,
   201  			fmt.Sprintf("unexpected error %q; expected to match %q", c.lastCommandErr, errMsg.Content))
   202  	}
   203  	return nil
   204  }
   205  
   206  func (c *SuiteContext) theCommandOutputs(target string, expected *gherkin.DocString) error {
   207  	var actual string
   208  	switch target {
   209  	case "stdout":
   210  		actual = c.stdout.String()
   211  	case "stderr":
   212  		actual = c.stderr.String()
   213  	default:
   214  		return fmt.Errorf("Unexpected command output target: %v", target)
   215  	}
   216  	if actual != expected.Content {
   217  		return fmt.Errorf("unexpected %v output: %q expected: %q", target, actual, expected.Content)
   218  	}
   219  	return nil
   220  }
   221  
   222  func (c *SuiteContext) theCommandOutputsTextMatching(target string, expected *gherkin.DocString) error {
   223  	var actual string
   224  	switch target {
   225  	case "stdout":
   226  		actual = c.stdout.String()
   227  	case "stderr":
   228  		actual = c.stderr.String()
   229  	default:
   230  		return fmt.Errorf("Unexpected command output target: %v", target)
   231  	}
   232  	rx := regexp.MustCompile(expected.Content)
   233  	if !rx.MatchString(actual) {
   234  		return fmt.Errorf("unexpected %v output: %q expected to match: %q", target, actual, expected.Content)
   235  	}
   236  	return nil
   237  }
   238  
   239  func FeatureContext(s *godog.Suite) {
   240  	c := SuiteContext{}
   241  	s.Step(`^I'm in project dir$`, c.iAmInProjectDir)
   242  	s.Step(`^I\'m in the (.+) dir$`, c.imInDir)
   243  	s.Step(`^file (.+) containing$`, c.fileContaining)
   244  	s.Step(`^I run "oya (.+)"$`, c.iRunOya)
   245  	s.Step(`^I modify file (.+) to contain$`, c.modifyFileToContain)
   246  	s.Step(`^file (.+) contains$`, c.fileContains)
   247  	s.Step(`^file (.+) does not contain$`, c.fileDoesNotContain)
   248  	s.Step(`^file (.+) exists$`, c.fileExists)
   249  	s.Step(`^file (.+) does not exist$`, c.fileDoesNotExist)
   250  	s.Step(`^the command succeeds$`, c.theCommandSucceeds)
   251  	s.Step(`^the command fails with error$`, c.theCommandFailsWithError)
   252  	s.Step(`^the command fails with error matching$`, c.theCommandFailsWithErrorMatching)
   253  	s.Step(`^the command outputs to (stdout|stderr)$`, c.theCommandOutputs)
   254  	s.Step(`^the command outputs to (stdout|stderr) text matching$`, c.theCommandOutputsTextMatching)
   255  	s.Step(`^the ([^ ]+) environment variable set to "([^"]*)"$`, c.environmentVariableSet)
   256  
   257  	s.BeforeScenario(func(interface{}) { c.MustSetUp() })
   258  	s.AfterScenario(func(interface{}, error) { c.MustTearDown() })
   259  }
   260  
   261  // sourceFileDirectory returns the current .go source file directory.
   262  func sourceFileDirectory() string {
   263  	_, filename, _, _ := runtime.Caller(1)
   264  	return filepath.Dir(filename)
   265  }