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 }