github.com/opentofu/opentofu@v1.7.1/internal/command/views/json/output_test.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package json
     7  
     8  import (
     9  	"encoding/json"
    10  	"testing"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  	"github.com/opentofu/opentofu/internal/addrs"
    14  	"github.com/opentofu/opentofu/internal/lang/marks"
    15  	"github.com/opentofu/opentofu/internal/plans"
    16  	"github.com/opentofu/opentofu/internal/states"
    17  	"github.com/zclconf/go-cty/cty"
    18  )
    19  
    20  func TestOutputsFromMap(t *testing.T) {
    21  	got, diags := OutputsFromMap(map[string]*states.OutputValue{
    22  		// Normal non-sensitive output
    23  		"boop": {
    24  			Value: cty.NumberIntVal(1234),
    25  		},
    26  		// Sensitive string output
    27  		"beep": {
    28  			Value:     cty.StringVal("horse-battery").Mark(marks.Sensitive),
    29  			Sensitive: true,
    30  		},
    31  		// Sensitive object output which is marked at the leaf
    32  		"blorp": {
    33  			Value: cty.ObjectVal(map[string]cty.Value{
    34  				"a": cty.ObjectVal(map[string]cty.Value{
    35  					"b": cty.ObjectVal(map[string]cty.Value{
    36  						"c": cty.StringVal("oh, hi").Mark(marks.Sensitive),
    37  					}),
    38  				}),
    39  			}),
    40  			Sensitive: true,
    41  		},
    42  		// Null value
    43  		"honk": {
    44  			Value: cty.NullVal(cty.Map(cty.Bool)),
    45  		},
    46  	})
    47  	if len(diags) > 0 {
    48  		t.Fatal(diags.Err())
    49  	}
    50  
    51  	want := Outputs{
    52  		"boop": {
    53  			Sensitive: false,
    54  			Type:      json.RawMessage(`"number"`),
    55  			Value:     json.RawMessage(`1234`),
    56  		},
    57  		"beep": {
    58  			Sensitive: true,
    59  			Type:      json.RawMessage(`"string"`),
    60  		},
    61  		"blorp": {
    62  			Sensitive: true,
    63  			Type:      json.RawMessage(`["object",{"a":["object",{"b":["object",{"c":"string"}]}]}]`),
    64  		},
    65  		"honk": {
    66  			Sensitive: false,
    67  			Type:      json.RawMessage(`["map","bool"]`),
    68  			Value:     json.RawMessage(`null`),
    69  		},
    70  	}
    71  
    72  	if !cmp.Equal(want, got) {
    73  		t.Fatalf("unexpected result\n%s", cmp.Diff(want, got))
    74  	}
    75  }
    76  
    77  func TestOutputsFromChanges(t *testing.T) {
    78  	root := addrs.RootModuleInstance
    79  	num, err := plans.NewDynamicValue(cty.NumberIntVal(1234), cty.Number)
    80  	if err != nil {
    81  		t.Fatalf("unexpected error creating dynamic value: %v", err)
    82  	}
    83  	str, err := plans.NewDynamicValue(cty.StringVal("1234"), cty.String)
    84  	if err != nil {
    85  		t.Fatalf("unexpected error creating dynamic value: %v", err)
    86  	}
    87  
    88  	got := OutputsFromChanges([]*plans.OutputChangeSrc{
    89  		// Unchanged output "boop", value 1234
    90  		{
    91  			Addr: root.OutputValue("boop"),
    92  			ChangeSrc: plans.ChangeSrc{
    93  				Action: plans.NoOp,
    94  				Before: num,
    95  				After:  num,
    96  			},
    97  			Sensitive: false,
    98  		},
    99  		// New output "beep", value 1234
   100  		{
   101  			Addr: root.OutputValue("beep"),
   102  			ChangeSrc: plans.ChangeSrc{
   103  				Action: plans.Create,
   104  				Before: nil,
   105  				After:  num,
   106  			},
   107  			Sensitive: false,
   108  		},
   109  		// Deleted output "blorp", prior value 1234
   110  		{
   111  			Addr: root.OutputValue("blorp"),
   112  			ChangeSrc: plans.ChangeSrc{
   113  				Action: plans.Delete,
   114  				Before: num,
   115  				After:  nil,
   116  			},
   117  			Sensitive: false,
   118  		},
   119  		// Updated output "honk", prior value 1234, new value "1234"
   120  		{
   121  			Addr: root.OutputValue("honk"),
   122  			ChangeSrc: plans.ChangeSrc{
   123  				Action: plans.Update,
   124  				Before: num,
   125  				After:  str,
   126  			},
   127  			Sensitive: false,
   128  		},
   129  		// New sensitive output "secret", value "1234"
   130  		{
   131  			Addr: root.OutputValue("secret"),
   132  			ChangeSrc: plans.ChangeSrc{
   133  				Action: plans.Create,
   134  				Before: nil,
   135  				After:  str,
   136  			},
   137  			Sensitive: true,
   138  		},
   139  	})
   140  
   141  	want := Outputs{
   142  		"boop": {
   143  			Action:    "noop",
   144  			Sensitive: false,
   145  		},
   146  		"beep": {
   147  			Action:    "create",
   148  			Sensitive: false,
   149  		},
   150  		"blorp": {
   151  			Action:    "delete",
   152  			Sensitive: false,
   153  		},
   154  		"honk": {
   155  			Action:    "update",
   156  			Sensitive: false,
   157  		},
   158  		"secret": {
   159  			Action:    "create",
   160  			Sensitive: true,
   161  		},
   162  	}
   163  
   164  	if !cmp.Equal(want, got) {
   165  		t.Fatalf("unexpected result\n%s", cmp.Diff(want, got))
   166  	}
   167  }
   168  
   169  func TestOutputs_String(t *testing.T) {
   170  	outputs := Outputs{
   171  		"boop": {
   172  			Sensitive: false,
   173  			Type:      json.RawMessage(`"number"`),
   174  			Value:     json.RawMessage(`1234`),
   175  		},
   176  		"beep": {
   177  			Sensitive: true,
   178  			Type:      json.RawMessage(`"string"`),
   179  			Value:     json.RawMessage(`"horse-battery"`),
   180  		},
   181  	}
   182  	if got, want := outputs.String(), "Outputs: 2"; got != want {
   183  		t.Fatalf("unexpected value\n got: %q\nwant: %q", got, want)
   184  	}
   185  }