github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/console_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package command
     5  
     6  import (
     7  	"bytes"
     8  	"io/ioutil"
     9  	"path/filepath"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/mitchellh/cli"
    14  	"github.com/terramate-io/tf/configs/configschema"
    15  	"github.com/terramate-io/tf/providers"
    16  	"github.com/zclconf/go-cty/cty"
    17  )
    18  
    19  // ConsoleCommand is tested primarily with tests in the "repl" package.
    20  // It is not tested here because the Console uses a readline-like library
    21  // that takes over stdin/stdout. It is difficult to test directly. The
    22  // core logic is tested in "repl"
    23  //
    24  // This file still contains some tests using the stdin-based input.
    25  
    26  func TestConsole_basic(t *testing.T) {
    27  	testCwd(t)
    28  
    29  	p := testProvider()
    30  	ui := cli.NewMockUi()
    31  	view, _ := testView(t)
    32  	c := &ConsoleCommand{
    33  		Meta: Meta{
    34  			testingOverrides: metaOverridesForProvider(p),
    35  			Ui:               ui,
    36  			View:             view,
    37  		},
    38  	}
    39  
    40  	var output bytes.Buffer
    41  	defer testStdinPipe(t, strings.NewReader("1+5\n"))()
    42  	outCloser := testStdoutCapture(t, &output)
    43  
    44  	args := []string{}
    45  	code := c.Run(args)
    46  	outCloser()
    47  	if code != 0 {
    48  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
    49  	}
    50  
    51  	actual := output.String()
    52  	if actual != "6\n" {
    53  		t.Fatalf("bad: %q", actual)
    54  	}
    55  }
    56  
    57  func TestConsole_tfvars(t *testing.T) {
    58  	td := t.TempDir()
    59  	testCopyDir(t, testFixturePath("apply-vars"), td)
    60  	defer testChdir(t, td)()
    61  
    62  	// Write a terraform.tvars
    63  	varFilePath := filepath.Join(td, "terraform.tfvars")
    64  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
    65  		t.Fatalf("err: %s", err)
    66  	}
    67  
    68  	p := testProvider()
    69  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
    70  		ResourceTypes: map[string]providers.Schema{
    71  			"test_instance": {
    72  				Block: &configschema.Block{
    73  					Attributes: map[string]*configschema.Attribute{
    74  						"value": {Type: cty.String, Optional: true},
    75  					},
    76  				},
    77  			},
    78  		},
    79  	}
    80  	ui := cli.NewMockUi()
    81  	view, _ := testView(t)
    82  	c := &ConsoleCommand{
    83  		Meta: Meta{
    84  			testingOverrides: metaOverridesForProvider(p),
    85  			Ui:               ui,
    86  			View:             view,
    87  		},
    88  	}
    89  
    90  	var output bytes.Buffer
    91  	defer testStdinPipe(t, strings.NewReader("var.foo\n"))()
    92  	outCloser := testStdoutCapture(t, &output)
    93  
    94  	args := []string{}
    95  	code := c.Run(args)
    96  	outCloser()
    97  	if code != 0 {
    98  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
    99  	}
   100  
   101  	actual := output.String()
   102  	if actual != "\"bar\"\n" {
   103  		t.Fatalf("bad: %q", actual)
   104  	}
   105  }
   106  
   107  func TestConsole_unsetRequiredVars(t *testing.T) {
   108  	// This test is verifying that it's possible to run "terraform console"
   109  	// without providing values for all required variables, without
   110  	// "terraform console" producing an interactive prompt for those variables
   111  	// or producing errors. Instead, it should allow evaluation in that
   112  	// partial context but see the unset variables values as being unknown.
   113  	//
   114  	// This test fixture includes variable "foo" {}, which we are
   115  	// intentionally not setting here.
   116  	td := t.TempDir()
   117  	testCopyDir(t, testFixturePath("apply-vars"), td)
   118  	defer testChdir(t, td)()
   119  
   120  	p := testProvider()
   121  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
   122  		ResourceTypes: map[string]providers.Schema{
   123  			"test_instance": {
   124  				Block: &configschema.Block{
   125  					Attributes: map[string]*configschema.Attribute{
   126  						"value": {Type: cty.String, Optional: true},
   127  					},
   128  				},
   129  			},
   130  		},
   131  	}
   132  	ui := cli.NewMockUi()
   133  	view, _ := testView(t)
   134  	c := &ConsoleCommand{
   135  		Meta: Meta{
   136  			testingOverrides: metaOverridesForProvider(p),
   137  			Ui:               ui,
   138  			View:             view,
   139  		},
   140  	}
   141  
   142  	var output bytes.Buffer
   143  	defer testStdinPipe(t, strings.NewReader("var.foo\n"))()
   144  	outCloser := testStdoutCapture(t, &output)
   145  
   146  	args := []string{}
   147  	code := c.Run(args)
   148  	outCloser()
   149  
   150  	if code != 0 {
   151  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   152  	}
   153  
   154  	if got, want := output.String(), "(known after apply)\n"; got != want {
   155  		t.Fatalf("unexpected output\n got: %q\nwant: %q", got, want)
   156  	}
   157  }
   158  
   159  func TestConsole_variables(t *testing.T) {
   160  	td := t.TempDir()
   161  	testCopyDir(t, testFixturePath("variables"), td)
   162  	defer testChdir(t, td)()
   163  
   164  	p := testProvider()
   165  	ui := cli.NewMockUi()
   166  	view, _ := testView(t)
   167  	c := &ConsoleCommand{
   168  		Meta: Meta{
   169  			testingOverrides: metaOverridesForProvider(p),
   170  			Ui:               ui,
   171  			View:             view,
   172  		},
   173  	}
   174  
   175  	commands := map[string]string{
   176  		"var.foo\n":          "\"bar\"\n",
   177  		"var.snack\n":        "\"popcorn\"\n",
   178  		"var.secret_snack\n": "(sensitive value)\n",
   179  		"local.snack_bar\n":  "[\n  \"popcorn\",\n  (sensitive value),\n]\n",
   180  	}
   181  
   182  	args := []string{}
   183  
   184  	for cmd, val := range commands {
   185  		var output bytes.Buffer
   186  		defer testStdinPipe(t, strings.NewReader(cmd))()
   187  		outCloser := testStdoutCapture(t, &output)
   188  		code := c.Run(args)
   189  		outCloser()
   190  		if code != 0 {
   191  			t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   192  		}
   193  
   194  		actual := output.String()
   195  		if output.String() != val {
   196  			t.Fatalf("bad: %q, expected %q", actual, val)
   197  		}
   198  	}
   199  }
   200  
   201  func TestConsole_modules(t *testing.T) {
   202  	td := t.TempDir()
   203  	testCopyDir(t, testFixturePath("modules"), td)
   204  	defer testChdir(t, td)()
   205  
   206  	p := applyFixtureProvider()
   207  	ui := cli.NewMockUi()
   208  	view, _ := testView(t)
   209  
   210  	c := &ConsoleCommand{
   211  		Meta: Meta{
   212  			testingOverrides: metaOverridesForProvider(p),
   213  			Ui:               ui,
   214  			View:             view,
   215  		},
   216  	}
   217  
   218  	commands := map[string]string{
   219  		"module.child.myoutput\n":          "\"bar\"\n",
   220  		"module.count_child[0].myoutput\n": "\"bar\"\n",
   221  		"local.foo\n":                      "3\n",
   222  	}
   223  
   224  	args := []string{}
   225  
   226  	for cmd, val := range commands {
   227  		var output bytes.Buffer
   228  		defer testStdinPipe(t, strings.NewReader(cmd))()
   229  		outCloser := testStdoutCapture(t, &output)
   230  		code := c.Run(args)
   231  		outCloser()
   232  		if code != 0 {
   233  			t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   234  		}
   235  
   236  		actual := output.String()
   237  		if output.String() != val {
   238  			t.Fatalf("bad: %q, expected %q", actual, val)
   239  		}
   240  	}
   241  }