github.com/kevinklinger/open_terraform@v1.3.6/noninternal/checks/state_test.go (about)

     1  package checks
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"github.com/kevinklinger/open_terraform/noninternal/addrs"
     9  	"github.com/kevinklinger/open_terraform/noninternal/configs/configload"
    10  	"github.com/kevinklinger/open_terraform/noninternal/initwd"
    11  )
    12  
    13  func TestChecksHappyPath(t *testing.T) {
    14  	const fixtureDir = "testdata/happypath"
    15  	loader, close := configload.NewLoaderForTests(t)
    16  	defer close()
    17  	inst := initwd.NewModuleInstaller(loader.ModulesDir(), nil)
    18  	_, instDiags := inst.InstallModules(context.Background(), fixtureDir, true, initwd.ModuleInstallHooksImpl{})
    19  	if instDiags.HasErrors() {
    20  		t.Fatal(instDiags.Err())
    21  	}
    22  	if err := loader.RefreshModules(); err != nil {
    23  		t.Fatalf("failed to refresh modules after installation: %s", err)
    24  	}
    25  
    26  	/////////////////////////////////////////////////////////////////////////
    27  
    28  	cfg, hclDiags := loader.LoadConfig(fixtureDir)
    29  	if hclDiags.HasErrors() {
    30  		t.Fatalf("invalid configuration: %s", hclDiags.Error())
    31  	}
    32  
    33  	resourceA := addrs.Resource{
    34  		Mode: addrs.ManagedResourceMode,
    35  		Type: "null_resource",
    36  		Name: "a",
    37  	}.InModule(addrs.RootModule)
    38  	resourceNoChecks := addrs.Resource{
    39  		Mode: addrs.ManagedResourceMode,
    40  		Type: "null_resource",
    41  		Name: "no_checks",
    42  	}.InModule(addrs.RootModule)
    43  	resourceNonExist := addrs.Resource{
    44  		Mode: addrs.ManagedResourceMode,
    45  		Type: "null_resource",
    46  		Name: "nonexist",
    47  	}.InModule(addrs.RootModule)
    48  	rootOutput := addrs.OutputValue{
    49  		Name: "a",
    50  	}.InModule(addrs.RootModule)
    51  	moduleChild := addrs.RootModule.Child("child")
    52  	resourceB := addrs.Resource{
    53  		Mode: addrs.ManagedResourceMode,
    54  		Type: "null_resource",
    55  		Name: "b",
    56  	}.InModule(moduleChild)
    57  	resourceC := addrs.Resource{
    58  		Mode: addrs.ManagedResourceMode,
    59  		Type: "null_resource",
    60  		Name: "c",
    61  	}.InModule(moduleChild)
    62  	childOutput := addrs.OutputValue{
    63  		Name: "b",
    64  	}.InModule(moduleChild)
    65  
    66  	// First some consistency checks to make sure our configuration is the
    67  	// shape we are relying on it to be.
    68  	if addr := resourceA; cfg.Module.ResourceByAddr(addr.Resource) == nil {
    69  		t.Fatalf("configuration does not include %s", addr)
    70  	}
    71  	if addr := resourceB; cfg.Children["child"].Module.ResourceByAddr(addr.Resource) == nil {
    72  		t.Fatalf("configuration does not include %s", addr)
    73  	}
    74  	if addr := resourceNoChecks; cfg.Module.ResourceByAddr(addr.Resource) == nil {
    75  		t.Fatalf("configuration does not include %s", addr)
    76  	}
    77  	if addr := resourceNonExist; cfg.Module.ResourceByAddr(addr.Resource) != nil {
    78  		t.Fatalf("configuration includes %s, which is not supposed to exist", addr)
    79  	}
    80  
    81  	/////////////////////////////////////////////////////////////////////////
    82  
    83  	checks := NewState(cfg)
    84  
    85  	missing := 0
    86  	if addr := resourceA; !checks.ConfigHasChecks(addr) {
    87  		t.Errorf("checks not detected for %s", addr)
    88  		missing++
    89  	}
    90  	if addr := resourceB; !checks.ConfigHasChecks(addr) {
    91  		t.Errorf("checks not detected for %s", addr)
    92  		missing++
    93  	}
    94  	if addr := resourceC; !checks.ConfigHasChecks(addr) {
    95  		t.Errorf("checks not detected for %s", addr)
    96  		missing++
    97  	}
    98  	if addr := rootOutput; !checks.ConfigHasChecks(addr) {
    99  		t.Errorf("checks not detected for %s", addr)
   100  		missing++
   101  	}
   102  	if addr := childOutput; !checks.ConfigHasChecks(addr) {
   103  		t.Errorf("checks not detected for %s", addr)
   104  		missing++
   105  	}
   106  	if addr := resourceNoChecks; checks.ConfigHasChecks(addr) {
   107  		t.Errorf("checks detected for %s, even though it has none", addr)
   108  	}
   109  	if addr := resourceNonExist; checks.ConfigHasChecks(addr) {
   110  		t.Errorf("checks detected for %s, even though it doesn't exist", addr)
   111  	}
   112  	if missing > 0 {
   113  		t.Fatalf("missing some configuration objects we'd need for subsequent testing")
   114  	}
   115  
   116  	/////////////////////////////////////////////////////////////////////////
   117  
   118  	// Everything should start with status unknown.
   119  
   120  	{
   121  		wantConfigAddrs := addrs.MakeSet[addrs.ConfigCheckable](
   122  			resourceA,
   123  			resourceB,
   124  			resourceC,
   125  			rootOutput,
   126  			childOutput,
   127  		)
   128  		gotConfigAddrs := checks.AllConfigAddrs()
   129  		if diff := cmp.Diff(wantConfigAddrs, gotConfigAddrs); diff != "" {
   130  			t.Errorf("wrong detected config addresses\n%s", diff)
   131  		}
   132  
   133  		for _, configAddr := range gotConfigAddrs {
   134  			if got, want := checks.AggregateCheckStatus(configAddr), StatusUnknown; got != want {
   135  				t.Errorf("incorrect initial aggregate check status for %s: %s, but want %s", configAddr, got, want)
   136  			}
   137  		}
   138  	}
   139  
   140  	/////////////////////////////////////////////////////////////////////////
   141  
   142  	// The following are steps that would normally be done by Terraform Core
   143  	// as part of visiting checkable objects during the graph walk. We're
   144  	// simulating a likely sequence of calls here for testing purposes, but
   145  	// Terraform Core won't necessarily visit all of these in exactly the
   146  	// same order every time and so this is just one possible valid ordering
   147  	// of calls.
   148  
   149  	resourceInstA := resourceA.Resource.Absolute(addrs.RootModuleInstance).Instance(addrs.NoKey)
   150  	rootOutputInst := rootOutput.OutputValue.Absolute(addrs.RootModuleInstance)
   151  	moduleChildInst := addrs.RootModuleInstance.Child("child", addrs.NoKey)
   152  	resourceInstB := resourceB.Resource.Absolute(moduleChildInst).Instance(addrs.NoKey)
   153  	resourceInstC0 := resourceC.Resource.Absolute(moduleChildInst).Instance(addrs.IntKey(0))
   154  	resourceInstC1 := resourceC.Resource.Absolute(moduleChildInst).Instance(addrs.IntKey(1))
   155  	childOutputInst := childOutput.OutputValue.Absolute(moduleChildInst)
   156  
   157  	checks.ReportCheckableObjects(resourceA, addrs.MakeSet[addrs.Checkable](resourceInstA))
   158  	checks.ReportCheckResult(resourceInstA, addrs.ResourcePrecondition, 0, StatusPass)
   159  	checks.ReportCheckResult(resourceInstA, addrs.ResourcePrecondition, 1, StatusPass)
   160  	checks.ReportCheckResult(resourceInstA, addrs.ResourcePostcondition, 0, StatusPass)
   161  
   162  	checks.ReportCheckableObjects(resourceB, addrs.MakeSet[addrs.Checkable](resourceInstB))
   163  	checks.ReportCheckResult(resourceInstB, addrs.ResourcePrecondition, 0, StatusPass)
   164  
   165  	checks.ReportCheckableObjects(resourceC, addrs.MakeSet[addrs.Checkable](resourceInstC0, resourceInstC1))
   166  	checks.ReportCheckResult(resourceInstC0, addrs.ResourcePostcondition, 0, StatusPass)
   167  	checks.ReportCheckResult(resourceInstC1, addrs.ResourcePostcondition, 0, StatusPass)
   168  
   169  	checks.ReportCheckableObjects(childOutput, addrs.MakeSet[addrs.Checkable](childOutputInst))
   170  	checks.ReportCheckResult(childOutputInst, addrs.OutputPrecondition, 0, StatusPass)
   171  
   172  	checks.ReportCheckableObjects(rootOutput, addrs.MakeSet[addrs.Checkable](rootOutputInst))
   173  	checks.ReportCheckResult(rootOutputInst, addrs.OutputPrecondition, 0, StatusPass)
   174  
   175  	/////////////////////////////////////////////////////////////////////////
   176  
   177  	// This "section" is simulating what we might do to report the results
   178  	// of the checks after a run completes.
   179  
   180  	{
   181  		configCount := 0
   182  		for _, configAddr := range checks.AllConfigAddrs() {
   183  			configCount++
   184  			if got, want := checks.AggregateCheckStatus(configAddr), StatusPass; got != want {
   185  				t.Errorf("incorrect final aggregate check status for %s: %s, but want %s", configAddr, got, want)
   186  			}
   187  		}
   188  		if got, want := configCount, 5; got != want {
   189  			t.Errorf("incorrect number of known config addresses %d; want %d", got, want)
   190  		}
   191  	}
   192  
   193  	{
   194  		objAddrs := addrs.MakeSet[addrs.Checkable](
   195  			resourceInstA,
   196  			rootOutputInst,
   197  			resourceInstB,
   198  			resourceInstC0,
   199  			resourceInstC1,
   200  			childOutputInst,
   201  		)
   202  		for _, addr := range objAddrs {
   203  			if got, want := checks.ObjectCheckStatus(addr), StatusPass; got != want {
   204  				t.Errorf("incorrect final check status for object %s: %s, but want %s", addr, got, want)
   205  			}
   206  		}
   207  	}
   208  }