pulumi/pkg/integrationtesting/lumiprogram.go
2017-07-13 12:40:10 -07:00

131 lines
4 KiB
Go

package integrationtesting
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/pulumi/lumi/pkg/util/contract"
)
// LumiProgramTestOptions provides options for LumiProgramTest
type LumiProgramTestOptions struct {
// Array of NPM packages which must be `yarn linked` (e.g. {"@lumi/lumi", "@lumi/aws"})
Dependencies []string
// Map of config keys and values to set on the Lumi environment (e.g. {"aws:config:region": "us-east-2"})
Config map[string]string
}
// LumiProgramTest runs a lifecylce of Lumi commands in a Lumi program working directory.
// Uses the `lumijs`, `lumi`, and `yarn` binaries available on PATH. Executes the following
// workflow:
// yarn link <each options.Depencies>
// lumijs --verbose
// lumi env init integrationtesting
// lumi config <each options.Config>
// lumi plan (currently being skipped due to pulumi/lumi#276)
// lumi deploy
// lumi destroy --yes
// lumi env rm --yes integrationtesting
// All commands must return success return codes for the test to succeed.
func LumiProgramTest(t *testing.T, programDir string, options LumiProgramTestOptions) {
t.Parallel()
region := os.Getenv("AWS_REGION")
if region == "" {
t.Skipf("Skipping test due to missing AWS_REGION environment variable")
}
lumijs, err := exec.LookPath("lumijs")
if !assert.NoError(t, err, "expected to find lumijs binary: %v", err) {
return
}
lumi, err := exec.LookPath("lumi")
if !assert.NoError(t, err, "expected to find lumi binary: %v", err) {
return
}
yarn, err := exec.LookPath("yarn")
if !assert.NoError(t, err, "expected to find yarn binary: %v", err) {
return
}
prefix := fmt.Sprintf("[ %30.30s ] ", programDir[len(programDir)-30:])
stdout := newPrefixer(os.Stdout, prefix)
stderr := newPrefixer(os.Stderr, prefix)
_, err = fmt.Fprintf(stdout, "sample: %v\n", programDir)
contract.Assert(err == nil)
_, err = fmt.Fprintf(stdout, "lumijs: %v\n", lumijs)
contract.Assert(err == nil)
_, err = fmt.Fprintf(stdout, "lumi: %v\n", lumi)
contract.Assert(err == nil)
_, err = fmt.Fprintf(stdout, "yarn: %v\n", yarn)
contract.Assert(err == nil)
for _, dependency := range options.Dependencies {
runCmd(t, []string{yarn, "link", dependency}, programDir, stdout, stderr)
}
runCmd(t, []string{lumijs, "--verbose"}, programDir, stdout, stderr)
runCmd(t, []string{lumi, "env", "init", "integrationtesting"}, programDir, stdout, stderr)
for key, value := range options.Config {
runCmd(t, []string{lumi, "config", key, value}, programDir, stdout, stderr)
}
// TODO[pulumi/lumi#276] Disabling `lumi plan` for now as it has issues with output properties
// runCmd(t, []string{lumi, "plan"}, programDir, stdout, stderr)
runCmd(t, []string{lumi, "deploy"}, programDir, stdout, stderr)
runCmd(t, []string{lumi, "destroy", "--yes"}, programDir, stdout, stderr)
runCmd(t, []string{lumi, "env", "rm", "--yes", "integrationtesting"}, programDir, stdout, stderr)
}
func runCmd(t *testing.T, args []string, wd string, stdout, stderr io.Writer) {
path := args[0]
command := strings.Join(args, " ")
_, err := fmt.Fprintf(stdout, "\n**** Invoke '%v' in %v\n", command, wd)
contract.Assert(err == nil)
cmd := exec.Cmd{
Path: path,
Dir: wd,
Args: args,
Stdout: stdout,
Stderr: stderr,
}
err = cmd.Run()
assert.NoError(t, err, "expected to successfully invoke '%v' in %v: %v", command, wd, err)
}
type prefixer struct {
writer io.Writer
prefix []byte
anyOutput bool
}
// newPrefixer wraps an io.Writer, prepending a fixed prefix after each \n emitting on the wrapped writer
func newPrefixer(writer io.Writer, prefix string) *prefixer {
return &prefixer{writer, []byte(prefix), false}
}
var _ io.Writer = (*prefixer)(nil)
func (prefixer *prefixer) Write(p []byte) (int, error) {
n := 0
lines := bytes.SplitAfter(p, []byte{'\n'})
for _, line := range lines {
if len(line) > 0 {
_, err := prefixer.writer.Write(prefixer.prefix)
if err != nil {
return n, err
}
}
m, err := prefixer.writer.Write(line)
n += m
if err != nil {
return n, err
}
}
return n, nil
}