terraform/command/hook_ui_test.go

312 lines
7.7 KiB
Go

package command
import (
"fmt"
"regexp"
"testing"
"time"
"github.com/mitchellh/cli"
"github.com/mitchellh/colorstring"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/terraform"
)
func TestUiHookPreApply_periodicTimer(t *testing.T) {
ui := cli.NewMockUi()
h := &UiHook{
Colorize: &colorstring.Colorize{
Colors: colorstring.DefaultColors,
Disable: true,
Reset: true,
},
Ui: ui,
PeriodicUiTimer: 1 * time.Second,
}
h.init()
h.resources = map[string]uiResourceState{
"data.aws_availability_zones.available": uiResourceState{
Op: uiResourceDestroy,
Start: time.Now(),
},
}
addr := addrs.Resource{
Mode: addrs.DataResourceMode,
Type: "aws_availability_zones",
Name: "available",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
priorState := cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("2017-03-05 10:56:59.298784526 +0000 UTC"),
"names": cty.ListVal([]cty.Value{
cty.StringVal("us-east-1a"),
cty.StringVal("us-east-1b"),
cty.StringVal("us-east-1c"),
cty.StringVal("us-east-1d"),
}),
})
plannedNewState := cty.NullVal(cty.Object(map[string]cty.Type{
"id": cty.String,
"names": cty.List(cty.String),
}))
action, err := h.PreApply(addr, states.CurrentGen, plans.Delete, priorState, plannedNewState)
if err != nil {
t.Fatal(err)
}
if action != terraform.HookActionContinue {
t.Fatalf("Expected hook to continue, given: %#v", action)
}
time.Sleep(3100 * time.Millisecond)
// stop the background writer
uiState := h.resources[addr.String()]
close(uiState.DoneCh)
<-uiState.done
expectedOutput := `data.aws_availability_zones.available: Destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC]
data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC, 1s elapsed]
data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC, 2s elapsed]
data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC, 3s elapsed]
`
output := ui.OutputWriter.String()
if output != expectedOutput {
t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output)
}
expectedErrOutput := ""
errOutput := ui.ErrorWriter.String()
if errOutput != expectedErrOutput {
t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput)
}
}
func TestUiHookPreApply_destroy(t *testing.T) {
ui := cli.NewMockUi()
h := &UiHook{
Colorize: &colorstring.Colorize{
Colors: colorstring.DefaultColors,
Disable: true,
Reset: true,
},
Ui: ui,
}
h.init()
h.resources = map[string]uiResourceState{
"data.aws_availability_zones.available": uiResourceState{
Op: uiResourceDestroy,
Start: time.Now(),
},
}
addr := addrs.Resource{
Mode: addrs.DataResourceMode,
Type: "aws_availability_zones",
Name: "available",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
priorState := cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("2017-03-05 10:56:59.298784526 +0000 UTC"),
"names": cty.ListVal([]cty.Value{
cty.StringVal("us-east-1a"),
cty.StringVal("us-east-1b"),
cty.StringVal("us-east-1c"),
cty.StringVal("us-east-1d"),
}),
})
plannedNewState := cty.NullVal(cty.Object(map[string]cty.Type{
"id": cty.String,
"names": cty.List(cty.String),
}))
action, err := h.PreApply(addr, states.CurrentGen, plans.Delete, priorState, plannedNewState)
if err != nil {
t.Fatal(err)
}
if action != terraform.HookActionContinue {
t.Fatalf("Expected hook to continue, given: %#v", action)
}
// stop the background writer
uiState := h.resources[addr.String()]
close(uiState.DoneCh)
<-uiState.done
expectedOutput := "data.aws_availability_zones.available: Destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC]\n"
output := ui.OutputWriter.String()
if output != expectedOutput {
t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output)
}
expectedErrOutput := ""
errOutput := ui.ErrorWriter.String()
if errOutput != expectedErrOutput {
t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput)
}
}
func TestUiHookPostApply_emptyState(t *testing.T) {
ui := cli.NewMockUi()
h := &UiHook{
Colorize: &colorstring.Colorize{
Colors: colorstring.DefaultColors,
Disable: true,
Reset: true,
},
Ui: ui,
}
h.init()
h.resources = map[string]uiResourceState{
"data.google_compute_zones.available": uiResourceState{
Op: uiResourceDestroy,
Start: time.Now(),
},
}
addr := addrs.Resource{
Mode: addrs.DataResourceMode,
Type: "google_compute_zones",
Name: "available",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
newState := cty.NullVal(cty.Object(map[string]cty.Type{
"id": cty.String,
"names": cty.List(cty.String),
}))
action, err := h.PostApply(addr, states.CurrentGen, newState, nil)
if err != nil {
t.Fatal(err)
}
if action != terraform.HookActionContinue {
t.Fatalf("Expected hook to continue, given: %#v", action)
}
expectedRegexp := "^data.google_compute_zones.available: Destruction complete after -?[a-z0-9µ.]+\n$"
output := ui.OutputWriter.String()
if matched, _ := regexp.MatchString(expectedRegexp, output); !matched {
t.Fatalf("Output didn't match regexp.\nExpected: %q\nGiven: %q", expectedRegexp, output)
}
expectedErrOutput := ""
errOutput := ui.ErrorWriter.String()
if errOutput != expectedErrOutput {
t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput)
}
}
func TestTruncateId(t *testing.T) {
testCases := []struct {
Input string
Expected string
MaxLen int
}{
{
Input: "Hello world",
Expected: "H...d",
MaxLen: 3,
},
{
Input: "Hello world",
Expected: "H...d",
MaxLen: 5,
},
{
Input: "Hello world",
Expected: "He...d",
MaxLen: 6,
},
{
Input: "Hello world",
Expected: "He...ld",
MaxLen: 7,
},
{
Input: "Hello world",
Expected: "Hel...ld",
MaxLen: 8,
},
{
Input: "Hello world",
Expected: "Hel...rld",
MaxLen: 9,
},
{
Input: "Hello world",
Expected: "Hell...rld",
MaxLen: 10,
},
{
Input: "Hello world",
Expected: "Hello world",
MaxLen: 11,
},
{
Input: "Hello world",
Expected: "Hello world",
MaxLen: 12,
},
{
Input: "あいうえおかきくけこさ",
Expected: "あ...さ",
MaxLen: 3,
},
{
Input: "あいうえおかきくけこさ",
Expected: "あ...さ",
MaxLen: 5,
},
{
Input: "あいうえおかきくけこさ",
Expected: "あい...さ",
MaxLen: 6,
},
{
Input: "あいうえおかきくけこさ",
Expected: "あい...こさ",
MaxLen: 7,
},
{
Input: "あいうえおかきくけこさ",
Expected: "あいう...こさ",
MaxLen: 8,
},
{
Input: "あいうえおかきくけこさ",
Expected: "あいう...けこさ",
MaxLen: 9,
},
{
Input: "あいうえおかきくけこさ",
Expected: "あいうえ...けこさ",
MaxLen: 10,
},
{
Input: "あいうえおかきくけこさ",
Expected: "あいうえおかきくけこさ",
MaxLen: 11,
},
{
Input: "あいうえおかきくけこさ",
Expected: "あいうえおかきくけこさ",
MaxLen: 12,
},
}
for i, tc := range testCases {
testName := fmt.Sprintf("%d", i)
t.Run(testName, func(t *testing.T) {
out := truncateId(tc.Input, tc.MaxLen)
if out != tc.Expected {
t.Fatalf("Expected %q to be shortened to %d as %q (given: %q)",
tc.Input, tc.MaxLen, tc.Expected, out)
}
})
}
}