github.com/sixgill/terraform@v0.9.0-beta2.0.20170316214032-033f6226ae50/helper/resource/testing_test.go (about)

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