github.com/VMitov/casper@v0.4.0/cmd/casper/main_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/miracl/casper/storage/consul"
    15  )
    16  
    17  // It is defined in each package so you can run `go test ./...`
    18  var full = flag.Bool("full", false, "Run all tests including integration")
    19  
    20  var consulAddr = flag.String("consul-addr", "http://172.17.0.1:8500/?token=the_one_ring", "Consul instance to run tests agains")
    21  
    22  func TestExample(t *testing.T) {
    23  	wd, err := os.Getwd()
    24  	if err != nil {
    25  		t.Fatal(err)
    26  	}
    27  
    28  	outputFileName := filepath.Join(wd, "../../example/output.yaml")
    29  	outputFileData, err := ioutil.ReadFile(outputFileName)
    30  	if err != nil {
    31  		t.Fatal(err)
    32  	}
    33  	outputFile := string(outputFileData)
    34  
    35  	noChanges := "No changes\n"
    36  	changes := "-key1: val1\n" +
    37  		"key2: val2\n" +
    38  		"\n" +
    39  		"+key1: val1a\n" +
    40  		"key2: val2a\n" +
    41  		"\n"
    42  	prompt := "Continue[y/N]: "
    43  	applyingChanges := "Applying changes...\n"
    44  	canceled := "Canceled"
    45  
    46  	configAbsPath := filepath.Join(wd, "../../example/config.yaml")
    47  	templateAbsPath := filepath.Join(wd, "../../example/template.yaml")
    48  
    49  	cases := []struct {
    50  		cmd  string // command
    51  		out  string // expected output
    52  		pwd  string // change the directory relative to working dir if set
    53  		copy bool   // copy the example folder in temporary dir if set
    54  		err  string // string representation of expected error
    55  	}{
    56  		{cmd: "casper fetch", out: outputFile + "\n", pwd: "../../example"},
    57  		{cmd: "casper diff -key somekey", out: "No changes for key somekey\n", pwd: "../../example"},
    58  
    59  		// without config file
    60  		{cmd: "casper build -t ../../example/template.yaml -s placeholder1=val1 -s placeholder2=val2", out: outputFile},
    61  
    62  		// with bad source
    63  		{cmd: "casper build -t ../../example/template.yaml -s placeholder1=val1 -s source.yaml", out: outputFile, err: "creating context failed: invalid source: source.yaml"},
    64  
    65  		// without sources
    66  		{cmd: "casper build -t ../../example/output.yaml", out: outputFile},
    67  		{cmd: "casper diff -t ../../example/output.yaml -storage file -file-path ../../example/output.yaml", out: noChanges},
    68  
    69  		// with overwritten placeholders so it there are differences
    70  		{cmd: "casper diff -s placeholder1=val1a -s placeholder2=val2a --plain", out: changes, pwd: "../../example"},
    71  		{cmd: "casper push -s placeholder1=val1a -s placeholder2=val2a --plain --force", out: changes + applyingChanges, pwd: "../../example"},
    72  		{cmd: "casper push -s placeholder1=val1a -s placeholder2=val2a --plain", out: changes + prompt + canceled + "\n", pwd: "../../example"},
    73  
    74  		//
    75  		// Tests for correct relative path resolving.
    76  		//
    77  
    78  		// Build tests
    79  
    80  		// from the same directory where the config is
    81  		{cmd: "casper build", out: outputFile, pwd: "../../example"},
    82  		{cmd: "casper -c ./config.yaml build", out: outputFile, pwd: "../../example"},
    83  		{cmd: "casper -c ../example/config.yaml build -t ../example/template.yaml", out: outputFile, pwd: "../../example"},
    84  
    85  		// from different directory where the config is
    86  		{cmd: "casper -c ../../example/config.yaml build", out: outputFile},
    87  		{cmd: "casper -c ../../example/config.yaml build -t ../../example/template.yaml", out: outputFile},
    88  
    89  		// with abs paths
    90  		{cmd: fmt.Sprintf("casper -c %v build -t %v", configAbsPath, templateAbsPath), out: outputFile},
    91  
    92  		// Diff tests
    93  
    94  		// from the same directory where the config is
    95  		{cmd: "casper diff", out: noChanges, pwd: "../../example"},
    96  		{cmd: "casper -c ./config.yaml diff", out: noChanges, pwd: "../../example"},
    97  		{cmd: "casper -c ../example/config.yaml diff -t ../example/template.yaml", out: noChanges, pwd: "../../example"},
    98  
    99  		// from different directory where the config is
   100  		{cmd: "casper -c ../../example/config.yaml diff", out: noChanges},
   101  		{cmd: "casper -c ../../example/config.yaml diff -t ../../example/template.yaml", out: noChanges},
   102  
   103  		// with abs paths
   104  		{cmd: fmt.Sprintf("casper -c %v build -t %v", configAbsPath, templateAbsPath), out: outputFile},
   105  	}
   106  
   107  	for i, tc := range cases {
   108  		t.Run(fmt.Sprintf("Case%v", i), func(t *testing.T) {
   109  			os.Chdir(wd)
   110  
   111  			// revert any changes to the output.yaml
   112  			defer func() {
   113  				err := ioutil.WriteFile(outputFileName, outputFileData, 0664)
   114  				if err != nil {
   115  					t.Fatal(err)
   116  				}
   117  			}()
   118  
   119  			if tc.err != "" {
   120  				app := newApp()
   121  				err := app.Run(strings.Split(tc.cmd, " "))
   122  				if err.Error() != tc.err {
   123  					t.Fatalf("\nunexpected error: %v\n\texpected: %v", err, tc.err)
   124  				}
   125  				return
   126  			}
   127  
   128  			if tc.pwd != "" {
   129  				os.Chdir(tc.pwd)
   130  			}
   131  
   132  			os.Args = strings.Split(tc.cmd, " ")
   133  			out := getStdout(t, main)
   134  			if out != tc.out {
   135  				t.Errorf("\n%vtest:/$ %v\n%v;\nExpected:\n%v;", tc.pwd, tc.cmd, out, tc.out)
   136  			}
   137  		})
   138  	}
   139  }
   140  
   141  func TestConsulIntegration(t *testing.T) {
   142  	if !*full {
   143  		t.SkipNow()
   144  	}
   145  
   146  	// Cleanup
   147  	defer func() {
   148  		s, err := consul.New(*consulAddr)
   149  		if err != nil {
   150  			t.Fatalf("cleanup failed: %v", err)
   151  		}
   152  
   153  		c, err := s.GetChanges([]byte{}, "yaml", "")
   154  		if err != nil {
   155  			t.Fatalf("cleanup failed: %v", err)
   156  		}
   157  
   158  		err = s.Push(c)
   159  		if err != nil {
   160  			t.Fatalf("cleanup failed: %v", err)
   161  		}
   162  	}()
   163  
   164  	steps := []struct {
   165  		cmd string
   166  		exp string
   167  	}{
   168  		{
   169  			cmd: fmt.Sprintf("casper fetch -format yaml -storage consul -consul-addr %v", *consulAddr),
   170  			exp: "{}\n\n",
   171  		},
   172  
   173  		{
   174  			cmd: "casper diff -plain" +
   175  				" -storage consul -consul-addr " + *consulAddr +
   176  				" -template ../../example/template.yaml -s placeholder1=val1 -s placeholder2=val2",
   177  			exp: "+key1=val1\n+key2=val2\n\n",
   178  		},
   179  		{
   180  			cmd: "casper push -plain -force" +
   181  				" -storage consul -consul-addr " + *consulAddr +
   182  				" -template ../../example/template.yaml -s placeholder1=val1 -s placeholder2=val2",
   183  			exp: "+key1=val1\n+key2=val2\n\nApplying changes...\n",
   184  		},
   185  		{
   186  			cmd: "casper diff -plain" +
   187  				" -storage consul -consul-addr " + *consulAddr +
   188  				" -template ../../example/template.yaml -s placeholder1=val1 -s placeholder2=val2",
   189  			exp: "No changes\n",
   190  		},
   191  		{
   192  			cmd: "casper fetch -format yaml -storage consul -consul-addr " + *consulAddr,
   193  			exp: "key1: val1\nkey2: val2\n\n",
   194  		},
   195  
   196  		{
   197  			cmd: "casper diff -plain" +
   198  				" -storage consul -consul-addr " + *consulAddr +
   199  				" -template ../../example/template.yaml -s placeholder1=diffval1 -s placeholder2=diffval2",
   200  			exp: "-key1=val1\n+key1=diffval1\n-key2=val2\n+key2=diffval2\n\n",
   201  		},
   202  		{
   203  			cmd: "casper push -plain -force" +
   204  				" -storage consul -consul-addr " + *consulAddr +
   205  				" -template ../../example/template.yaml -s placeholder1=diffval1 -s placeholder2=diffval2",
   206  			exp: "-key1=val1\n+key1=diffval1\n-key2=val2\n+key2=diffval2\n\nApplying changes...\n",
   207  		},
   208  		{
   209  			cmd: "casper diff -plain" +
   210  				" -storage consul -consul-addr " + *consulAddr +
   211  				" -template ../../example/template.yaml -s placeholder1=diffval1 -s placeholder2=diffval2",
   212  			exp: "No changes\n",
   213  		},
   214  		{
   215  			cmd: "casper fetch -format yaml -storage consul -consul-addr " + *consulAddr,
   216  			exp: "key1: diffval1\nkey2: diffval2\n\n",
   217  		},
   218  	}
   219  
   220  	for i, step := range steps {
   221  		os.Args = strings.Split(step.cmd, " ")
   222  		out := getStdout(t, main)
   223  		if out != step.exp {
   224  			t.Errorf("\nstep%v:/$ %v\n%v;\nExpected:\n%v;", i, step.cmd, out, step.exp)
   225  		}
   226  	}
   227  }
   228  
   229  func TestAppErrors(t *testing.T) {
   230  	cases := []struct {
   231  		cmd string
   232  		err string
   233  	}{
   234  		// no configurations
   235  		{cmd: "casper fetch", err: "reading file casper.yaml failed: open casper.yaml: no such file or directory"},
   236  		{cmd: "casper build", err: "creating context failed: getting template template.yaml failed: open template.yaml: no such file or directory"},
   237  		{cmd: "casper diff", err: "creating context failed: getting template template.yaml failed: open template.yaml: no such file or directory"},
   238  		{cmd: "casper push", err: "creating context failed: getting template template.yaml failed: open template.yaml: no such file or directory"},
   239  
   240  		// invalid storage
   241  		{cmd: "casper fetch -storage invalid", err: "invalid storage type 'invalid'"},
   242  		{cmd: "casper diff -storage invalid -t ../../example/template.yaml -s key=value", err: "invalid storage type 'invalid'"},
   243  		{cmd: "casper push -storage invalid -t ../../example/template.yaml -s key=value", err: "invalid storage type 'invalid'"},
   244  
   245  		// file storage - invalid file
   246  		{cmd: "casper fetch -storage file -file-path invalid.txt", err: "reading file invalid.txt failed: open invalid.txt: no such file or directory"},
   247  		{cmd: "casper diff -storage file -file-path invalid.txt", err: "creating context failed: getting template template.yaml failed: open template.yaml: no such file or directory"},
   248  		{cmd: "casper push -storage file -file-path invalid.txt", err: "creating context failed: getting template template.yaml failed: open template.yaml: no such file or directory"},
   249  
   250  		// invalid source
   251  		{cmd: "casper build -t ../../example/template.yaml -s key:val", err: "creating context failed: invalid source format key"},
   252  		{cmd: "casper diff -t ../../example/template.yaml -s key:val", err: "creating context failed: invalid source format key"},
   253  		{cmd: "casper push -t ../../example/template.yaml -s key:val", err: "creating context failed: invalid source format key"},
   254  	}
   255  
   256  	for i, tc := range cases {
   257  		t.Run(fmt.Sprintf("Case%v", i), func(t *testing.T) {
   258  			args := strings.Split(tc.cmd, " ")
   259  			app := newApp()
   260  			err := app.Run(args)
   261  
   262  			if err != nil && tc.err == "" {
   263  				t.Fatalf("unexpected error '%v'", err)
   264  			}
   265  
   266  			if err.Error() != tc.err {
   267  				t.Errorf("\ntest:/$ %v\n%v\nExpected:\n%v;", tc.cmd, err, tc.err)
   268  			}
   269  		})
   270  	}
   271  
   272  }
   273  
   274  // Runs a function and returns the stdout from it.
   275  func getStdout(t *testing.T, f func()) string {
   276  	old := os.Stdout // keep backup of the real stdout
   277  	defer func() { os.Stdout = old }()
   278  	r, w, err := os.Pipe()
   279  	if err != nil {
   280  		t.Errorf("an error wasn't expected: %v", err)
   281  	}
   282  	os.Stdout = w
   283  
   284  	f() // executes the main function
   285  
   286  	outC := make(chan string)
   287  	// copy the output in a separate goroutine so printing can't block indefinitely
   288  	go func() {
   289  		var buf bytes.Buffer
   290  		io.Copy(&buf, r)
   291  		outC <- buf.String()
   292  	}()
   293  
   294  	// back to normal state
   295  	w.Close()
   296  	out := <-outC
   297  
   298  	return out
   299  }