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 `