github.com/opentofu/opentofu@v1.7.1/internal/tofu/node_resource_abstract_instance_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 tofu
     7  
     8  import (
     9  	"fmt"
    10  	"testing"
    11  
    12  	"github.com/opentofu/opentofu/internal/addrs"
    13  	"github.com/opentofu/opentofu/internal/configs"
    14  	"github.com/opentofu/opentofu/internal/configs/configschema"
    15  	"github.com/opentofu/opentofu/internal/states"
    16  	"github.com/zclconf/go-cty/cty"
    17  )
    18  
    19  func TestNodeAbstractResourceInstanceProvider(t *testing.T) {
    20  	tests := []struct {
    21  		Addr                 addrs.AbsResourceInstance
    22  		Config               *configs.Resource
    23  		StoredProviderConfig addrs.AbsProviderConfig
    24  		Want                 addrs.Provider
    25  	}{
    26  		{
    27  			Addr: addrs.Resource{
    28  				Mode: addrs.ManagedResourceMode,
    29  				Type: "null_resource",
    30  				Name: "baz",
    31  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    32  			Want: addrs.Provider{
    33  				Hostname:  addrs.DefaultProviderRegistryHost,
    34  				Namespace: "hashicorp",
    35  				Type:      "null",
    36  			},
    37  		},
    38  		{
    39  			Addr: addrs.Resource{
    40  				Mode: addrs.DataResourceMode,
    41  				Type: "terraform_remote_state",
    42  				Name: "baz",
    43  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    44  			Want: addrs.Provider{
    45  				// As a special case, the type prefix "terraform_" maps to
    46  				// the builtin provider, not the default one.
    47  				Hostname:  addrs.BuiltInProviderHost,
    48  				Namespace: addrs.BuiltInProviderNamespace,
    49  				Type:      "terraform",
    50  			},
    51  		},
    52  		{
    53  			Addr: addrs.Resource{
    54  				Mode: addrs.ManagedResourceMode,
    55  				Type: "null_resource",
    56  				Name: "baz",
    57  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    58  			Config: &configs.Resource{
    59  				// Just enough configs.Resource for the Provider method. Not
    60  				// actually valid for general use.
    61  				Provider: addrs.Provider{
    62  					Hostname:  addrs.DefaultProviderRegistryHost,
    63  					Namespace: "awesomecorp",
    64  					Type:      "happycloud",
    65  				},
    66  			},
    67  			// The config overrides the default behavior.
    68  			Want: addrs.Provider{
    69  				Hostname:  addrs.DefaultProviderRegistryHost,
    70  				Namespace: "awesomecorp",
    71  				Type:      "happycloud",
    72  			},
    73  		},
    74  		{
    75  			Addr: addrs.Resource{
    76  				Mode: addrs.DataResourceMode,
    77  				Type: "terraform_remote_state",
    78  				Name: "baz",
    79  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    80  			Config: &configs.Resource{
    81  				// Just enough configs.Resource for the Provider method. Not
    82  				// actually valid for general use.
    83  				Provider: addrs.Provider{
    84  					Hostname:  addrs.DefaultProviderRegistryHost,
    85  					Namespace: "awesomecorp",
    86  					Type:      "happycloud",
    87  				},
    88  			},
    89  			// The config overrides the default behavior.
    90  			Want: addrs.Provider{
    91  				Hostname:  addrs.DefaultProviderRegistryHost,
    92  				Namespace: "awesomecorp",
    93  				Type:      "happycloud",
    94  			},
    95  		},
    96  		{
    97  			Addr: addrs.Resource{
    98  				Mode: addrs.DataResourceMode,
    99  				Type: "null_resource",
   100  				Name: "baz",
   101  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   102  			Config: nil,
   103  			StoredProviderConfig: addrs.AbsProviderConfig{
   104  				Module: addrs.RootModule,
   105  				Provider: addrs.Provider{
   106  					Hostname:  addrs.DefaultProviderRegistryHost,
   107  					Namespace: "awesomecorp",
   108  					Type:      "null",
   109  				},
   110  			},
   111  			// The stored provider config overrides the default behavior.
   112  			Want: addrs.Provider{
   113  				Hostname:  addrs.DefaultProviderRegistryHost,
   114  				Namespace: "awesomecorp",
   115  				Type:      "null",
   116  			},
   117  		},
   118  	}
   119  
   120  	for _, test := range tests {
   121  		var name string
   122  		if test.Config != nil {
   123  			name = fmt.Sprintf("%s with configured %s", test.Addr, test.Config.Provider)
   124  		} else {
   125  			name = fmt.Sprintf("%s with no configuration", test.Addr)
   126  		}
   127  		t.Run(name, func(t *testing.T) {
   128  			node := &NodeAbstractResourceInstance{
   129  				// Just enough NodeAbstractResourceInstance for the Provider
   130  				// function. (This would not be valid for some other functions.)
   131  				Addr: test.Addr,
   132  				NodeAbstractResource: NodeAbstractResource{
   133  					Addr:                 test.Addr.ConfigResource(),
   134  					Config:               test.Config,
   135  					storedProviderConfig: test.StoredProviderConfig,
   136  				},
   137  			}
   138  			got := node.Provider()
   139  			if got != test.Want {
   140  				t.Errorf("wrong result\naddr:  %s\nconfig: %#v\ngot:   %s\nwant:  %s", test.Addr, test.Config, got, test.Want)
   141  			}
   142  		})
   143  	}
   144  }
   145  
   146  func TestNodeAbstractResourceInstance_WriteResourceInstanceState(t *testing.T) {
   147  	state := states.NewState()
   148  	ctx := new(MockEvalContext)
   149  	ctx.StateState = state.SyncWrapper()
   150  	ctx.PathPath = addrs.RootModuleInstance
   151  
   152  	mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{
   153  		Attributes: map[string]*configschema.Attribute{
   154  			"id": {
   155  				Type:     cty.String,
   156  				Optional: true,
   157  			},
   158  		},
   159  	})
   160  
   161  	obj := &states.ResourceInstanceObject{
   162  		Value: cty.ObjectVal(map[string]cty.Value{
   163  			"id": cty.StringVal("i-abc123"),
   164  		}),
   165  		Status: states.ObjectReady,
   166  	}
   167  
   168  	node := &NodeAbstractResourceInstance{
   169  		Addr: mustResourceInstanceAddr("aws_instance.foo"),
   170  		// instanceState:        obj,
   171  		NodeAbstractResource: NodeAbstractResource{
   172  			ResolvedProvider: mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
   173  		},
   174  	}
   175  	ctx.ProviderProvider = mockProvider
   176  	ctx.ProviderSchemaSchema = mockProvider.GetProviderSchema()
   177  
   178  	err := node.writeResourceInstanceState(ctx, obj, workingState)
   179  	if err != nil {
   180  		t.Fatalf("unexpected error: %s", err.Error())
   181  	}
   182  
   183  	checkStateString(t, state, `
   184  aws_instance.foo:
   185    ID = i-abc123
   186    provider = provider["registry.opentofu.org/hashicorp/aws"]
   187  	`)
   188  }