mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 09:48:32 -05:00
204 lines
5.3 KiB
Go
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())
|
|
}
|
|
})
|
|
}
|
|
}
|