| // Copyright (c) The OpenTofu Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| // Copyright (c) 2023 HashiCorp, Inc. | |
| // SPDX-License-Identifier: MPL-2.0 | |
| package command | |
| import ( | |
| "context" | |
| "strings" | |
| "testing" | |
| "time" | |
| "github.com/mitchellh/cli" | |
| "github.com/opentofu/opentofu/internal/getmodules" | |
| ) | |
| func TestGet(t *testing.T) { | |
| wd := tempWorkingDirFixture(t, "get") | |
| t.Chdir(wd.RootModuleDir()) | |
| getView, getDone := testView(t) | |
| meta := Meta{ | |
| testingOverrides: metaOverridesForProvider(testProvider()), | |
| View: getView, | |
| WorkingDir: wd, | |
| } | |
| code := RunCommander(t, GetCommander(), meta, nil) | |
| getOutput := getDone(t) | |
| if code != 0 { | |
| t.Fatalf("bad: \n%s", getOutput.Stderr()) | |
| } | |
| output := getOutput.Stdout() | |
| if !strings.Contains(output, "- foo in") { | |
| t.Fatalf("doesn't look like get: %s", output) | |
| } | |
| } | |
| func TestGet_multipleArgs(t *testing.T) { | |
| wd := tempWorkingDir(t) | |
| t.Chdir(wd.RootModuleDir()) | |
| getView, getDone := testView(t) | |
| meta := Meta{ | |
| testingOverrides: metaOverridesForProvider(testProvider()), | |
| View: getView, | |
| WorkingDir: wd, | |
| } | |
| args := []string{ | |
| "bad", | |
| "bad", | |
| } | |
| code := RunCommander(t, GetCommander(), meta, args) | |
| getOutput := getDone(t) | |
| if code != cli.RunResultHelp { | |
| t.Fatalf("bad: \n%s", getOutput.Stdout()) | |
| } | |
| } | |
| func TestGet_update(t *testing.T) { | |
| wd := tempWorkingDirFixture(t, "get") | |
| t.Chdir(wd.RootModuleDir()) | |
| getView, getDone := testView(t) | |
| meta := Meta{ | |
| testingOverrides: metaOverridesForProvider(testProvider()), | |
| View: getView, | |
| WorkingDir: wd, | |
| } | |
| args := []string{ | |
| "-update", | |
| } | |
| code := RunCommander(t, GetCommander(), meta, args) | |
| getOutput := getDone(t) | |
| if code != 0 { | |
| t.Fatalf("bad: \n%s", getOutput.Stderr()) | |
| } | |
| output := getOutput.Stdout() | |
| if !strings.Contains(output, `- foo in`) { | |
| t.Fatalf("doesn't look like get: %s", output) | |
| } | |
| } | |
| func TestGet_cancel(t *testing.T) { | |
| // This test runs `tofu get` against a server that stalls indefinitely | |
| // instead of responding, and then requests shutdown in the same way | |
| // as package main would in response to SIGINT (or similar on other | |
| // platforms). This ensures that slow requests can be interrupted. | |
| wd := tempWorkingDirFixture(t, "init-module-early-eval") | |
| t.Chdir(wd.RootModuleDir()) | |
| // One failure mode of this test is for the cancellation to fail and | |
| // so the command runs indefinitely, and so we'll impose a timeout | |
| // to allow us to eventually catch that and diagnose it as a test | |
| // failure message. | |
| ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second) | |
| defer cancel() | |
| // This server intentionally stalls any incoming request by leaving | |
| // the connection open but not responding. "reqs" will become | |
| // readable each time a request arrives. | |
| server, reqs := testHangServer(t) | |
| // We'll close this channel once we've been notified that the server | |
| // received our request, which should then cause cancellation. | |
| shutdownCh := make(chan struct{}) | |
| go func() { | |
| select { | |
| case <-reqs: | |
| // Request received, so time to interrupt. | |
| t.Log("server received request, but won't respond") | |
| close(shutdownCh) | |
| case <-ctx.Done(): | |
| // Exit early if we reach our timeout. | |
| t.Log("timeout before server received request") | |
| } | |
| server.CloseClientConnections() // force any active client request to fail | |
| }() | |
| getView, getDone := testView(t) | |
| meta := Meta{ | |
| testingOverrides: metaOverridesForProvider(testProvider()), | |
| View: getView, | |
| WorkingDir: wd, | |
| ShutdownCh: shutdownCh, | |
| // This test needs a real module package fetcher instance because | |
| // we want to attempt installing a module package from our server. | |
| ModulePackageFetcher: getmodules.NewPackageFetcher(t.Context(), nil), | |
| } | |
| fakeModuleSourceAddr := server.URL + "/example.zip" | |
| t.Logf("attempting to install module package from %s", fakeModuleSourceAddr) | |
| args := []string{"-var=module_source=" + fakeModuleSourceAddr} | |
| code := RunCommander(t, GetCommander(), meta, args) | |
| getOutput := getDone(t) | |
| if err := ctx.Err(); err != nil { | |
| t.Errorf("context error: %s", err) // probably reporting a timeout | |
| } | |
| if code == 0 { | |
| t.Fatalf("succeeded; wanted error\n%s", getOutput.Stdout()) | |
| } | |
| if got, want := getOutput.Stderr(), `Module installation was canceled by an interrupt signal`; !strings.Contains(got, want) { | |
| t.Fatalf("wrong error message\nshould contain: %s\ngot:\n%s", want, got) | |
| } | |
| } | |
| func TestGetCommand_InvalidArgs(t *testing.T) { | |
| testCases := []struct { | |
| name string | |
| args []string | |
| expected string | |
| }{ | |
| { | |
| name: "TooManyArgs", | |
| args: []string{"too", "many", "args"}, | |
| expected: "Too many command line arguments", | |
| }, | |
| { | |
| name: "InvalidArgFormat", | |
| args: []string{"--invalid-arg"}, | |
| expected: "flag provided but not defined", | |
| }, | |
| { | |
| name: "MixedValidAndInvalidArgs", | |
| args: []string{"--update", "--invalid-arg"}, | |
| expected: "flag provided but not defined", | |
| }, | |
| } | |
| for _, tc := range testCases { | |
| t.Run(tc.name, func(t *testing.T) { | |
| wd := tempWorkingDirFixture(t, "get") | |
| t.Chdir(wd.RootModuleDir()) | |
| getView, getDone := testView(t) | |
| meta := Meta{ | |
| testingOverrides: metaOverridesForProvider(testProvider()), | |
| View: getView, | |
| WorkingDir: wd, | |
| } | |
| code := RunCommander(t, GetCommander(), meta, tc.args) | |
| getOutput := getDone(t) | |
| if code != cli.RunResultHelp { | |
| t.Errorf("Expected error code 1 for invalid arguments, got %d", code) | |
| } | |
| if !strings.Contains(getOutput.Stderr(), tc.expected) { | |
| t.Errorf("Expected error message to contain '%s', got: %s", tc.expected, getOutput.Stderr()) | |
| } | |
| }) | |
| } | |
| } |