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