github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/views/apply_test.go (about)

     1  package views
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/hashicorp/terraform/internal/command/arguments"
     9  	"github.com/hashicorp/terraform/internal/lang/marks"
    10  	"github.com/hashicorp/terraform/internal/states"
    11  	"github.com/hashicorp/terraform/internal/terminal"
    12  	"github.com/zclconf/go-cty/cty"
    13  )
    14  
    15  // This test is mostly because I am paranoid about having two consecutive
    16  // boolean arguments.
    17  func TestApply_new(t *testing.T) {
    18  	streams, done := terminal.StreamsForTesting(t)
    19  	defer done(t)
    20  	v := NewApply(arguments.ViewHuman, false, NewView(streams).SetRunningInAutomation(true))
    21  	hv, ok := v.(*ApplyHuman)
    22  	if !ok {
    23  		t.Fatalf("unexpected return type %t", v)
    24  	}
    25  
    26  	if hv.destroy != false {
    27  		t.Fatalf("unexpected destroy value")
    28  	}
    29  
    30  	if hv.inAutomation != true {
    31  		t.Fatalf("unexpected inAutomation value")
    32  	}
    33  }
    34  
    35  // Basic test coverage of Outputs, since most of its functionality is tested
    36  // elsewhere.
    37  func TestApplyHuman_outputs(t *testing.T) {
    38  	streams, done := terminal.StreamsForTesting(t)
    39  	v := NewApply(arguments.ViewHuman, false, NewView(streams))
    40  
    41  	v.Outputs(map[string]*states.OutputValue{
    42  		"foo": {Value: cty.StringVal("secret")},
    43  	})
    44  
    45  	got := done(t).Stdout()
    46  	for _, want := range []string{"Outputs:", `foo = "secret"`} {
    47  		if !strings.Contains(got, want) {
    48  			t.Errorf("wrong result\ngot:  %q\nwant: %q", got, want)
    49  		}
    50  	}
    51  }
    52  
    53  // Outputs should do nothing if there are no outputs to render.
    54  func TestApplyHuman_outputsEmpty(t *testing.T) {
    55  	streams, done := terminal.StreamsForTesting(t)
    56  	v := NewApply(arguments.ViewHuman, false, NewView(streams))
    57  
    58  	v.Outputs(map[string]*states.OutputValue{})
    59  
    60  	got := done(t).Stdout()
    61  	if got != "" {
    62  		t.Errorf("output should be empty, but got: %q", got)
    63  	}
    64  }
    65  
    66  // Ensure that the correct view type and in-automation settings propagate to the
    67  // Operation view.
    68  func TestApplyHuman_operation(t *testing.T) {
    69  	streams, done := terminal.StreamsForTesting(t)
    70  	defer done(t)
    71  	v := NewApply(arguments.ViewHuman, false, NewView(streams).SetRunningInAutomation(true)).Operation()
    72  	if hv, ok := v.(*OperationHuman); !ok {
    73  		t.Fatalf("unexpected return type %t", v)
    74  	} else if hv.inAutomation != true {
    75  		t.Fatalf("unexpected inAutomation value on Operation view")
    76  	}
    77  }
    78  
    79  // This view is used for both apply and destroy commands, so the help output
    80  // needs to cover both.
    81  func TestApplyHuman_help(t *testing.T) {
    82  	testCases := map[string]bool{
    83  		"apply":   false,
    84  		"destroy": true,
    85  	}
    86  
    87  	for name, destroy := range testCases {
    88  		t.Run(name, func(t *testing.T) {
    89  			streams, done := terminal.StreamsForTesting(t)
    90  			v := NewApply(arguments.ViewHuman, destroy, NewView(streams))
    91  			v.HelpPrompt()
    92  			got := done(t).Stderr()
    93  			if !strings.Contains(got, name) {
    94  				t.Errorf("wrong result\ngot:  %q\nwant: %q", got, name)
    95  			}
    96  		})
    97  	}
    98  }
    99  
   100  // Hooks and ResourceCount are tangled up and easiest to test together.
   101  func TestApply_resourceCount(t *testing.T) {
   102  	testCases := map[string]struct {
   103  		destroy bool
   104  		want    string
   105  	}{
   106  		"apply": {
   107  			false,
   108  			"Apply complete! Resources: 1 added, 2 changed, 3 destroyed.",
   109  		},
   110  		"destroy": {
   111  			true,
   112  			"Destroy complete! Resources: 3 destroyed.",
   113  		},
   114  	}
   115  
   116  	// For compatibility reasons, these tests should hold true for both human
   117  	// and JSON output modes
   118  	views := []arguments.ViewType{arguments.ViewHuman, arguments.ViewJSON}
   119  
   120  	for name, tc := range testCases {
   121  		for _, viewType := range views {
   122  			t.Run(fmt.Sprintf("%s (%s view)", name, viewType), func(t *testing.T) {
   123  				streams, done := terminal.StreamsForTesting(t)
   124  				v := NewApply(viewType, tc.destroy, NewView(streams))
   125  				hooks := v.Hooks()
   126  
   127  				var count *countHook
   128  				for _, hook := range hooks {
   129  					if ch, ok := hook.(*countHook); ok {
   130  						count = ch
   131  					}
   132  				}
   133  				if count == nil {
   134  					t.Fatalf("expected Hooks to include a countHook: %#v", hooks)
   135  				}
   136  
   137  				count.Added = 1
   138  				count.Changed = 2
   139  				count.Removed = 3
   140  
   141  				v.ResourceCount("")
   142  
   143  				got := done(t).Stdout()
   144  				if !strings.Contains(got, tc.want) {
   145  					t.Errorf("wrong result\ngot:  %q\nwant: %q", got, tc.want)
   146  				}
   147  			})
   148  		}
   149  	}
   150  }
   151  
   152  func TestApplyHuman_resourceCountStatePath(t *testing.T) {
   153  	testCases := map[string]struct {
   154  		added        int
   155  		changed      int
   156  		removed      int
   157  		statePath    string
   158  		wantContains bool
   159  	}{
   160  		"default state path": {
   161  			added:        1,
   162  			changed:      2,
   163  			removed:      3,
   164  			statePath:    "",
   165  			wantContains: false,
   166  		},
   167  		"only removed": {
   168  			added:        0,
   169  			changed:      0,
   170  			removed:      5,
   171  			statePath:    "foo.tfstate",
   172  			wantContains: false,
   173  		},
   174  		"added": {
   175  			added:        5,
   176  			changed:      0,
   177  			removed:      0,
   178  			statePath:    "foo.tfstate",
   179  			wantContains: true,
   180  		},
   181  		"changed": {
   182  			added:        0,
   183  			changed:      5,
   184  			removed:      0,
   185  			statePath:    "foo.tfstate",
   186  			wantContains: true,
   187  		},
   188  	}
   189  
   190  	for name, tc := range testCases {
   191  		t.Run(name, func(t *testing.T) {
   192  			streams, done := terminal.StreamsForTesting(t)
   193  			v := NewApply(arguments.ViewHuman, false, NewView(streams))
   194  			hooks := v.Hooks()
   195  
   196  			var count *countHook
   197  			for _, hook := range hooks {
   198  				if ch, ok := hook.(*countHook); ok {
   199  					count = ch
   200  				}
   201  			}
   202  			if count == nil {
   203  				t.Fatalf("expected Hooks to include a countHook: %#v", hooks)
   204  			}
   205  
   206  			count.Added = tc.added
   207  			count.Changed = tc.changed
   208  			count.Removed = tc.removed
   209  
   210  			v.ResourceCount(tc.statePath)
   211  
   212  			got := done(t).Stdout()
   213  			want := "State path: " + tc.statePath
   214  			contains := strings.Contains(got, want)
   215  			if contains && !tc.wantContains {
   216  				t.Errorf("wrong result\ngot:  %q\nshould not contain: %q", got, want)
   217  			} else if !contains && tc.wantContains {
   218  				t.Errorf("wrong result\ngot:  %q\nshould contain: %q", got, want)
   219  			}
   220  		})
   221  	}
   222  }
   223  
   224  // Basic test coverage of Outputs, since most of its functionality is tested
   225  // elsewhere.
   226  func TestApplyJSON_outputs(t *testing.T) {
   227  	streams, done := terminal.StreamsForTesting(t)
   228  	v := NewApply(arguments.ViewJSON, false, NewView(streams))
   229  
   230  	v.Outputs(map[string]*states.OutputValue{
   231  		"boop_count": {Value: cty.NumberIntVal(92)},
   232  		"password":   {Value: cty.StringVal("horse-battery").Mark(marks.Sensitive), Sensitive: true},
   233  	})
   234  
   235  	want := []map[string]interface{}{
   236  		{
   237  			"@level":   "info",
   238  			"@message": "Outputs: 2",
   239  			"@module":  "terraform.ui",
   240  			"type":     "outputs",
   241  			"outputs": map[string]interface{}{
   242  				"boop_count": map[string]interface{}{
   243  					"sensitive": false,
   244  					"value":     float64(92),
   245  					"type":      "number",
   246  				},
   247  				"password": map[string]interface{}{
   248  					"sensitive": true,
   249  					"type":      "string",
   250  				},
   251  			},
   252  		},
   253  	}
   254  	testJSONViewOutputEquals(t, done(t).Stdout(), want)
   255  }