github.com/hairyhenderson/gomplate/v3@v3.11.7/internal/tests/integration/integration_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"log"
    10  	"net"
    11  	"net/http"
    12  	"os"
    13  	"runtime"
    14  	"strings"
    15  	"testing"
    16  	"time"
    17  
    18  	gcmd "github.com/hairyhenderson/gomplate/v3/internal/cmd"
    19  	vaultapi "github.com/hashicorp/vault/api"
    20  	"github.com/stretchr/testify/assert"
    21  	"gotest.tools/v3/icmd"
    22  )
    23  
    24  const isWindows = runtime.GOOS == "windows"
    25  
    26  // a convenience...
    27  func inOutTest(t *testing.T, i, o string) {
    28  	t.Helper()
    29  
    30  	stdout, stderr, err := cmd(t, "-i", i).run()
    31  	assert.NoError(t, err)
    32  	assert.Equal(t, "", stderr)
    33  	assert.Equal(t, o, stdout)
    34  }
    35  
    36  func inOutTestExperimental(t *testing.T, i, o string) {
    37  	t.Helper()
    38  
    39  	stdout, stderr, err := cmd(t, "--experimental", "-i", i).run()
    40  	assert.NoError(t, err)
    41  	assert.Equal(t, "", stderr)
    42  	assert.Equal(t, o, stdout)
    43  }
    44  
    45  func inOutContains(t *testing.T, i, o string) {
    46  	t.Helper()
    47  
    48  	stdout, stderr, err := cmd(t, "-i", i).run()
    49  	assert.NoError(t, err)
    50  	assert.Equal(t, "", stderr)
    51  	assert.Contains(t, stdout, o)
    52  }
    53  
    54  func assertSuccess(t *testing.T, o, e string, err error, expected string) {
    55  	t.Helper()
    56  
    57  	assert.NoError(t, err)
    58  	assert.Equal(t, "", e)
    59  	assert.Equal(t, expected, o)
    60  }
    61  
    62  // mirrorHandler - reflects back the HTTP headers from the request
    63  func mirrorHandler(w http.ResponseWriter, r *http.Request) {
    64  	type Req struct {
    65  		Headers http.Header `json:"headers"`
    66  	}
    67  	req := Req{r.Header}
    68  	b, err := json.Marshal(req)
    69  	if err != nil {
    70  		log.Println(err)
    71  		w.WriteHeader(http.StatusBadRequest)
    72  	}
    73  	w.Header().Set("Content-Type", "application/json")
    74  	w.Write(b)
    75  }
    76  
    77  func typeHandler(t, body string) func(http.ResponseWriter, *http.Request) {
    78  	return func(w http.ResponseWriter, r *http.Request) {
    79  		w.Header().Set("Content-Type", t)
    80  		w.Write([]byte(body))
    81  	}
    82  }
    83  
    84  // freeport - find a free TCP port for immediate use. No guarantees!
    85  func freeport(t *testing.T) (port int, addr string) {
    86  	l, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1")})
    87  	if err != nil {
    88  		t.Fatal(err)
    89  	}
    90  	defer l.Close()
    91  	a := l.Addr().(*net.TCPAddr)
    92  	port = a.Port
    93  	return port, a.String()
    94  }
    95  
    96  // waitForURL - waits up to 20s for a given URL to respond with a 200
    97  func waitForURL(t *testing.T, url string) error {
    98  	client := http.DefaultClient
    99  	retries := 100
   100  	for retries > 0 {
   101  		retries--
   102  		time.Sleep(200 * time.Millisecond)
   103  		resp, err := client.Get(url)
   104  		if err != nil {
   105  			t.Logf("Got error, retries left: %d (error: %v)", retries, err)
   106  			continue
   107  		}
   108  		body, err := io.ReadAll(resp.Body)
   109  		t.Logf("Body is: %s", body)
   110  		if err != nil {
   111  			return err
   112  		}
   113  		defer resp.Body.Close()
   114  		if resp.StatusCode == 200 {
   115  			return nil
   116  		}
   117  	}
   118  	return fmt.Errorf("URL %s never responded with 200", url)
   119  }
   120  
   121  type vaultClient struct {
   122  	vc        *vaultapi.Client
   123  	addr      string
   124  	rootToken string
   125  }
   126  
   127  func createVaultClient(addr string, rootToken string) (*vaultClient, error) {
   128  	config := vaultapi.DefaultConfig()
   129  	config.Address = "http://" + addr
   130  	client, err := vaultapi.NewClient(config)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	v := &vaultClient{
   135  		vc:        client,
   136  		addr:      addr,
   137  		rootToken: rootToken,
   138  	}
   139  	client.SetToken(rootToken)
   140  	return v, nil
   141  }
   142  
   143  func (v *vaultClient) tokenCreate(policy string, uses int) (string, error) {
   144  	opts := &vaultapi.TokenCreateRequest{
   145  		Policies: []string{policy},
   146  		TTL:      "1m",
   147  		NumUses:  uses,
   148  	}
   149  	token, err := v.vc.Auth().Token().Create(opts)
   150  	if err != nil {
   151  		return "", err
   152  	}
   153  	return token.Auth.ClientToken, nil
   154  }
   155  
   156  type command struct {
   157  	t     *testing.T
   158  	dir   string
   159  	stdin string
   160  	env   map[string]string
   161  	envK  []string
   162  	args  []string
   163  }
   164  
   165  func cmd(t *testing.T, args ...string) *command {
   166  	return &command{t: t, args: args}
   167  }
   168  
   169  func (c *command) withDir(dir string) *command {
   170  	c.dir = dir
   171  	return c
   172  }
   173  
   174  func (c *command) withStdin(in string) *command {
   175  	c.stdin = in
   176  	return c
   177  }
   178  
   179  func (c *command) withEnv(k, v string) *command {
   180  	if c.env == nil {
   181  		c.env = map[string]string{}
   182  	}
   183  	if c.envK == nil {
   184  		c.envK = []string{}
   185  	}
   186  	c.env[k] = v
   187  	c.envK = append(c.envK, k)
   188  	return c
   189  }
   190  
   191  // set this at 'go test' time to test with a pre-compiled binary instead of
   192  // running all tests in-process
   193  var GomplateBinPath = ""
   194  
   195  func (c *command) run() (o, e string, err error) {
   196  	if GomplateBinPath != "" {
   197  		return c.runCompiled(GomplateBinPath)
   198  	}
   199  	return c.runInProcess()
   200  }
   201  
   202  func (c *command) runInProcess() (o, e string, err error) {
   203  	// iterate env vars by order of insertion
   204  	for _, k := range c.envK {
   205  		k := k
   206  		// clean up after ourselves
   207  		if orig, ok := os.LookupEnv(k); ok {
   208  			defer os.Setenv(k, orig)
   209  		} else {
   210  			defer os.Unsetenv(k)
   211  		}
   212  		os.Setenv(k, c.env[k])
   213  	}
   214  
   215  	if c.dir != "" {
   216  		//nolint:govet
   217  		origWd, err := os.Getwd()
   218  		if err != nil {
   219  			c.t.Fatal(err)
   220  		}
   221  		defer os.Chdir(origWd)
   222  
   223  		err = os.Chdir(c.dir)
   224  		if err != nil {
   225  			c.t.Fatal(err)
   226  		}
   227  	}
   228  
   229  	stdin := strings.NewReader(c.stdin)
   230  
   231  	ctx := context.Background()
   232  	stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
   233  	err = gcmd.Main(ctx, c.args, stdin, stdout, stderr)
   234  	return stdout.String(), stderr.String(), err
   235  }
   236  
   237  func (c *command) runCompiled(bin string) (o, e string, err error) {
   238  	cmd := icmd.Command(bin, c.args...)
   239  	cmd.Dir = c.dir
   240  	cmd.Stdin = strings.NewReader(c.stdin)
   241  	cmd.Env = os.Environ()
   242  	cmd.Env = append(cmd.Env, "GOMPLATE_LOG_FORMAT=simple")
   243  	for _, k := range c.envK {
   244  		cmd.Env = append(cmd.Env, k+"="+c.env[k])
   245  	}
   246  
   247  	result := icmd.RunCmd(cmd)
   248  	if result.Error != nil {
   249  		result.Error = fmt.Errorf("%w: %s", result.Error, result.Stderr())
   250  	}
   251  	return result.Stdout(), result.Stderr(), result.Error
   252  }