github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/states/instance_object_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package states
     5  
     6  import (
     7  	"sync"
     8  	"testing"
     9  
    10  	"github.com/google/go-cmp/cmp"
    11  	"github.com/terramate-io/tf/addrs"
    12  	"github.com/zclconf/go-cty/cty"
    13  )
    14  
    15  func TestResourceInstanceObject_encode(t *testing.T) {
    16  	value := cty.ObjectVal(map[string]cty.Value{
    17  		"foo": cty.True,
    18  	})
    19  	// The in-memory order of resource dependencies is random, since they're an
    20  	// unordered set.
    21  	depsOne := []addrs.ConfigResource{
    22  		addrs.RootModule.Resource(addrs.ManagedResourceMode, "test", "honk"),
    23  		addrs.RootModule.Child("child").Resource(addrs.ManagedResourceMode, "test", "flub"),
    24  		addrs.RootModule.Resource(addrs.ManagedResourceMode, "test", "boop"),
    25  	}
    26  	depsTwo := []addrs.ConfigResource{
    27  		addrs.RootModule.Child("child").Resource(addrs.ManagedResourceMode, "test", "flub"),
    28  		addrs.RootModule.Resource(addrs.ManagedResourceMode, "test", "boop"),
    29  		addrs.RootModule.Resource(addrs.ManagedResourceMode, "test", "honk"),
    30  	}
    31  
    32  	// multiple instances may have been assigned the same deps slice
    33  	objs := []*ResourceInstanceObject{
    34  		&ResourceInstanceObject{
    35  			Value:        value,
    36  			Status:       ObjectPlanned,
    37  			Dependencies: depsOne,
    38  		},
    39  		&ResourceInstanceObject{
    40  			Value:        value,
    41  			Status:       ObjectPlanned,
    42  			Dependencies: depsTwo,
    43  		},
    44  		&ResourceInstanceObject{
    45  			Value:        value,
    46  			Status:       ObjectPlanned,
    47  			Dependencies: depsOne,
    48  		},
    49  		&ResourceInstanceObject{
    50  			Value:        value,
    51  			Status:       ObjectPlanned,
    52  			Dependencies: depsOne,
    53  		},
    54  	}
    55  
    56  	var encoded []*ResourceInstanceObjectSrc
    57  
    58  	// Encoding can happen concurrently, so we need to make sure the shared
    59  	// Dependencies are safely handled
    60  	var wg sync.WaitGroup
    61  	var mu sync.Mutex
    62  
    63  	for _, obj := range objs {
    64  		obj := obj
    65  		wg.Add(1)
    66  		go func() {
    67  			defer wg.Done()
    68  			rios, err := obj.Encode(value.Type(), 0)
    69  			if err != nil {
    70  				t.Errorf("unexpected error: %s", err)
    71  			}
    72  			mu.Lock()
    73  			encoded = append(encoded, rios)
    74  			mu.Unlock()
    75  		}()
    76  	}
    77  	wg.Wait()
    78  
    79  	// However, identical sets of dependencies should always be written to state
    80  	// in an identical order, so we don't do meaningless state updates on refresh.
    81  	for i := 0; i < len(encoded)-1; i++ {
    82  		if diff := cmp.Diff(encoded[i].Dependencies, encoded[i+1].Dependencies); diff != "" {
    83  			t.Errorf("identical dependencies got encoded in different orders:\n%s", diff)
    84  		}
    85  	}
    86  }