github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/helper/resource/testing_test.go (about)

     1  package resource
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"sync/atomic"
     7  	"testing"
     8  
     9  	"github.com/hashicorp/terraform/terraform"
    10  )
    11  
    12  func init() {
    13  	testTesting = true
    14  
    15  	// TODO: Remove when we remove the guard on id checks
    16  	if err := os.Setenv("TF_ACC_IDONLY", "1"); err != nil {
    17  		panic(err)
    18  	}
    19  
    20  	if err := os.Setenv(TestEnvVar, "1"); err != nil {
    21  		panic(err)
    22  	}
    23  }
    24  
    25  func TestTest(t *testing.T) {
    26  	mp := testProvider()
    27  	mp.DiffReturn = nil
    28  
    29  	mp.ApplyFn = func(
    30  		info *terraform.InstanceInfo,
    31  		state *terraform.InstanceState,
    32  		diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
    33  		if !diff.Destroy {
    34  			return &terraform.InstanceState{
    35  				ID: "foo",
    36  			}, nil
    37  		}
    38  
    39  		return nil, nil
    40  	}
    41  
    42  	var refreshCount int32
    43  	mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) {
    44  		atomic.AddInt32(&refreshCount, 1)
    45  		return &terraform.InstanceState{ID: "foo"}, nil
    46  	}
    47  
    48  	checkDestroy := false
    49  	checkStep := false
    50  
    51  	checkDestroyFn := func(*terraform.State) error {
    52  		checkDestroy = true
    53  		return nil
    54  	}
    55  
    56  	checkStepFn := func(s *terraform.State) error {
    57  		checkStep = true
    58  
    59  		rs, ok := s.RootModule().Resources["test_instance.foo"]
    60  		if !ok {
    61  			t.Error("test_instance.foo is not present")
    62  			return nil
    63  		}
    64  		is := rs.Primary
    65  		if is.ID != "foo" {
    66  			t.Errorf("bad check ID: %s", is.ID)
    67  		}
    68  
    69  		return nil
    70  	}
    71  
    72  	mt := new(mockT)
    73  	Test(mt, TestCase{
    74  		Providers: map[string]terraform.ResourceProvider{
    75  			"test": mp,
    76  		},
    77  		CheckDestroy: checkDestroyFn,
    78  		Steps: []TestStep{
    79  			TestStep{
    80  				Config: testConfigStr,
    81  				Check:  checkStepFn,
    82  			},
    83  		},
    84  	})
    85  
    86  	if mt.failed() {
    87  		t.Fatalf("test failed: %s", mt.failMessage())
    88  	}
    89  	if !checkStep {
    90  		t.Fatal("didn't call check for step")
    91  	}
    92  	if !checkDestroy {
    93  		t.Fatal("didn't call check for destroy")
    94  	}
    95  }
    96  
    97  func TestTest_idRefresh(t *testing.T) {
    98  	// Refresh count should be 3:
    99  	//   1.) initial Ref/Plan/Apply
   100  	//   2.) post Ref/Plan/Apply for plan-check
   101  	//   3.) id refresh check
   102  	var expectedRefresh int32 = 3
   103  
   104  	mp := testProvider()
   105  	mp.DiffReturn = nil
   106  
   107  	mp.ApplyFn = func(
   108  		info *terraform.InstanceInfo,
   109  		state *terraform.InstanceState,
   110  		diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   111  		if !diff.Destroy {
   112  			return &terraform.InstanceState{
   113  				ID: "foo",
   114  			}, nil
   115  		}
   116  
   117  		return nil, nil
   118  	}
   119  
   120  	var refreshCount int32
   121  	mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) {
   122  		atomic.AddInt32(&refreshCount, 1)
   123  		return &terraform.InstanceState{ID: "foo"}, nil
   124  	}
   125  
   126  	mt := new(mockT)
   127  	Test(mt, TestCase{
   128  		IDRefreshName: "test_instance.foo",
   129  		Providers: map[string]terraform.ResourceProvider{
   130  			"test": mp,
   131  		},
   132  		Steps: []TestStep{
   133  			TestStep{
   134  				Config: testConfigStr,
   135  			},
   136  		},
   137  	})
   138  
   139  	if mt.failed() {
   140  		t.Fatalf("test failed: %s", mt.failMessage())
   141  	}
   142  
   143  	// See declaration of expectedRefresh for why that number
   144  	if refreshCount != expectedRefresh {
   145  		t.Fatalf("bad refresh count: %d", refreshCount)
   146  	}
   147  }
   148  
   149  func TestTest_idRefreshCustomName(t *testing.T) {
   150  	// Refresh count should be 3:
   151  	//   1.) initial Ref/Plan/Apply
   152  	//   2.) post Ref/Plan/Apply for plan-check
   153  	//   3.) id refresh check
   154  	var expectedRefresh int32 = 3
   155  
   156  	mp := testProvider()
   157  	mp.DiffReturn = nil
   158  
   159  	mp.ApplyFn = func(
   160  		info *terraform.InstanceInfo,
   161  		state *terraform.InstanceState,
   162  		diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   163  		if !diff.Destroy {
   164  			return &terraform.InstanceState{
   165  				ID: "foo",
   166  			}, nil
   167  		}
   168  
   169  		return nil, nil
   170  	}
   171  
   172  	var refreshCount int32
   173  	mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) {
   174  		atomic.AddInt32(&refreshCount, 1)
   175  		return &terraform.InstanceState{ID: "foo"}, nil
   176  	}
   177  
   178  	mt := new(mockT)
   179  	Test(mt, TestCase{
   180  		IDRefreshName: "test_instance.foo",
   181  		Providers: map[string]terraform.ResourceProvider{
   182  			"test": mp,
   183  		},
   184  		Steps: []TestStep{
   185  			TestStep{
   186  				Config: testConfigStr,
   187  			},
   188  		},
   189  	})
   190  
   191  	if mt.failed() {
   192  		t.Fatalf("test failed: %s", mt.failMessage())
   193  	}
   194  
   195  	// See declaration of expectedRefresh for why that number
   196  	if refreshCount != expectedRefresh {
   197  		t.Fatalf("bad refresh count: %d", refreshCount)
   198  	}
   199  }
   200  
   201  func TestTest_idRefreshFail(t *testing.T) {
   202  	// Refresh count should be 3:
   203  	//   1.) initial Ref/Plan/Apply
   204  	//   2.) post Ref/Plan/Apply for plan-check
   205  	//   3.) id refresh check
   206  	var expectedRefresh int32 = 3
   207  
   208  	mp := testProvider()
   209  	mp.DiffReturn = nil
   210  
   211  	mp.ApplyFn = func(
   212  		info *terraform.InstanceInfo,
   213  		state *terraform.InstanceState,
   214  		diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   215  		if !diff.Destroy {
   216  			return &terraform.InstanceState{
   217  				ID: "foo",
   218  			}, nil
   219  		}
   220  
   221  		return nil, nil
   222  	}
   223  
   224  	var refreshCount int32
   225  	mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) {
   226  		atomic.AddInt32(&refreshCount, 1)
   227  		if atomic.LoadInt32(&refreshCount) == expectedRefresh-1 {
   228  			return &terraform.InstanceState{
   229  				ID:         "foo",
   230  				Attributes: map[string]string{"foo": "bar"},
   231  			}, nil
   232  		} else if atomic.LoadInt32(&refreshCount) < expectedRefresh {
   233  			return &terraform.InstanceState{ID: "foo"}, nil
   234  		} else {
   235  			return nil, nil
   236  		}
   237  	}
   238  
   239  	mt := new(mockT)
   240  	Test(mt, TestCase{
   241  		IDRefreshName: "test_instance.foo",
   242  		Providers: map[string]terraform.ResourceProvider{
   243  			"test": mp,
   244  		},
   245  		Steps: []TestStep{
   246  			TestStep{
   247  				Config: testConfigStr,
   248  			},
   249  		},
   250  	})
   251  
   252  	if !mt.failed() {
   253  		t.Fatal("test didn't fail")
   254  	}
   255  	t.Logf("failure reason: %s", mt.failMessage())
   256  
   257  	// See declaration of expectedRefresh for why that number
   258  	if refreshCount != expectedRefresh {
   259  		t.Fatalf("bad refresh count: %d", refreshCount)
   260  	}
   261  }
   262  
   263  func TestTest_empty(t *testing.T) {
   264  	destroyCalled := false
   265  	checkDestroyFn := func(*terraform.State) error {
   266  		destroyCalled = true
   267  		return nil
   268  	}
   269  
   270  	mt := new(mockT)
   271  	Test(mt, TestCase{
   272  		CheckDestroy: checkDestroyFn,
   273  	})
   274  
   275  	if mt.failed() {
   276  		t.Fatal("test failed")
   277  	}
   278  	if destroyCalled {
   279  		t.Fatal("should not call check destroy if there is no steps")
   280  	}
   281  }
   282  
   283  func TestTest_noEnv(t *testing.T) {
   284  	// Unset the variable
   285  	if err := os.Setenv(TestEnvVar, ""); err != nil {
   286  		t.Fatalf("err: %s", err)
   287  	}
   288  	defer os.Setenv(TestEnvVar, "1")
   289  
   290  	mt := new(mockT)
   291  	Test(mt, TestCase{})
   292  
   293  	if !mt.SkipCalled {
   294  		t.Fatal("skip not called")
   295  	}
   296  }
   297  
   298  func TestTest_preCheck(t *testing.T) {
   299  	called := false
   300  
   301  	mt := new(mockT)
   302  	Test(mt, TestCase{
   303  		PreCheck: func() { called = true },
   304  	})
   305  
   306  	if !called {
   307  		t.Fatal("precheck should be called")
   308  	}
   309  }
   310  
   311  func TestTest_stepError(t *testing.T) {
   312  	mp := testProvider()
   313  	mp.ApplyReturn = &terraform.InstanceState{
   314  		ID: "foo",
   315  	}
   316  
   317  	checkDestroy := false
   318  
   319  	checkDestroyFn := func(*terraform.State) error {
   320  		checkDestroy = true
   321  		return nil
   322  	}
   323  
   324  	checkStepFn := func(*terraform.State) error {
   325  		return fmt.Errorf("error")
   326  	}
   327  
   328  	mt := new(mockT)
   329  	Test(mt, TestCase{
   330  		Providers: map[string]terraform.ResourceProvider{
   331  			"test": mp,
   332  		},
   333  		CheckDestroy: checkDestroyFn,
   334  		Steps: []TestStep{
   335  			TestStep{
   336  				Config: testConfigStr,
   337  				Check:  checkStepFn,
   338  			},
   339  		},
   340  	})
   341  
   342  	if !mt.failed() {
   343  		t.Fatal("test should've failed")
   344  	}
   345  	expected := "Step 0 error: Check failed: error"
   346  	if mt.failMessage() != expected {
   347  		t.Fatalf("Expected message: %s\n\ngot:\n\n%s", expected, mt.failMessage())
   348  	}
   349  
   350  	if !checkDestroy {
   351  		t.Fatal("didn't call check for destroy")
   352  	}
   353  }
   354  
   355  func TestComposeTestCheckFunc(t *testing.T) {
   356  	cases := []struct {
   357  		F      []TestCheckFunc
   358  		Result string
   359  	}{
   360  		{
   361  			F: []TestCheckFunc{
   362  				func(*terraform.State) error { return nil },
   363  			},
   364  			Result: "",
   365  		},
   366  
   367  		{
   368  			F: []TestCheckFunc{
   369  				func(*terraform.State) error {
   370  					return fmt.Errorf("error")
   371  				},
   372  				func(*terraform.State) error { return nil },
   373  			},
   374  			Result: "Check 1/2 error: error",
   375  		},
   376  
   377  		{
   378  			F: []TestCheckFunc{
   379  				func(*terraform.State) error { return nil },
   380  				func(*terraform.State) error {
   381  					return fmt.Errorf("error")
   382  				},
   383  			},
   384  			Result: "Check 2/2 error: error",
   385  		},
   386  
   387  		{
   388  			F: []TestCheckFunc{
   389  				func(*terraform.State) error { return nil },
   390  				func(*terraform.State) error { return nil },
   391  			},
   392  			Result: "",
   393  		},
   394  	}
   395  
   396  	for i, tc := range cases {
   397  		f := ComposeTestCheckFunc(tc.F...)
   398  		err := f(nil)
   399  		if err == nil {
   400  			err = fmt.Errorf("")
   401  		}
   402  		if tc.Result != err.Error() {
   403  			t.Fatalf("Case %d bad: %s", i, err)
   404  		}
   405  	}
   406  }
   407  
   408  // mockT implements TestT for testing
   409  type mockT struct {
   410  	ErrorCalled bool
   411  	ErrorArgs   []interface{}
   412  	FatalCalled bool
   413  	FatalArgs   []interface{}
   414  	SkipCalled  bool
   415  	SkipArgs    []interface{}
   416  
   417  	f bool
   418  }
   419  
   420  func (t *mockT) Error(args ...interface{}) {
   421  	t.ErrorCalled = true
   422  	t.ErrorArgs = args
   423  	t.f = true
   424  }
   425  
   426  func (t *mockT) Fatal(args ...interface{}) {
   427  	t.FatalCalled = true
   428  	t.FatalArgs = args
   429  	t.f = true
   430  }
   431  
   432  func (t *mockT) Skip(args ...interface{}) {
   433  	t.SkipCalled = true
   434  	t.SkipArgs = args
   435  	t.f = true
   436  }
   437  
   438  func (t *mockT) failed() bool {
   439  	return t.f
   440  }
   441  
   442  func (t *mockT) failMessage() string {
   443  	if t.FatalCalled {
   444  		return t.FatalArgs[0].(string)
   445  	} else if t.ErrorCalled {
   446  		return t.ErrorArgs[0].(string)
   447  	} else if t.SkipCalled {
   448  		return t.SkipArgs[0].(string)
   449  	}
   450  
   451  	return "unknown"
   452  }
   453  
   454  func testProvider() *terraform.MockResourceProvider {
   455  	mp := new(terraform.MockResourceProvider)
   456  	mp.DiffReturn = &terraform.InstanceDiff{
   457  		Attributes: map[string]*terraform.ResourceAttrDiff{
   458  			"foo": &terraform.ResourceAttrDiff{
   459  				New: "bar",
   460  			},
   461  		},
   462  	}
   463  	mp.ResourcesReturn = []terraform.ResourceType{
   464  		terraform.ResourceType{Name: "test_instance"},
   465  	}
   466  
   467  	return mp
   468  }
   469  
   470  const testConfigStr = `
   471  resource "test_instance" "foo" {}
   472  `