Files
opentf/internal/command/get_test.go
2025-05-09 10:16:38 +01:00

204 lines
5.3 KiB
Go

// 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())
ui := cli.NewMockUi()
c := &GetCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
WorkingDir: wd,
},
}
args := []string{}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
}
output := ui.OutputWriter.String()
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())
ui := cli.NewMockUi()
c := &GetCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
WorkingDir: wd,
},
}
args := []string{
"bad",
"bad",
}
if code := c.Run(args); code != 1 {
t.Fatalf("bad: \n%s", ui.OutputWriter.String())
}
}
func TestGet_update(t *testing.T) {
wd := tempWorkingDirFixture(t, "get")
t.Chdir(wd.RootModuleDir())
ui := cli.NewMockUi()
c := &GetCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
WorkingDir: wd,
},
}
args := []string{
"-update",
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
}
output := ui.OutputWriter.String()
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
}()
ui := cli.NewMockUi()
c := &GetCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
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 := c.Run(args)
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", ui.OutputWriter.String())
}
if got, want := ui.ErrorWriter.String(), `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())
ui := cli.NewMockUi()
c := &GetCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
WorkingDir: wd,
},
}
if code := c.Run(tc.args); code != 1 {
t.Errorf("Expected error code 1 for invalid arguments, got %d", code)
}
if !strings.Contains(ui.ErrorWriter.String(), tc.expected) {
t.Errorf("Expected error message to contain '%s', got: %s", tc.expected, ui.ErrorWriter.String())
}
})
}
}