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 }