github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/helper/resource/testing_test.go (about)

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