github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/terraform/diff_test.go (about) 1 package terraform 2 3 import ( 4 "reflect" 5 "strings" 6 "testing" 7 ) 8 9 func TestDiffEmpty(t *testing.T) { 10 diff := new(Diff) 11 if !diff.Empty() { 12 t.Fatal("should be empty") 13 } 14 15 mod := diff.AddModule(rootModulePath) 16 mod.Resources["nodeA"] = &InstanceDiff{ 17 Attributes: map[string]*ResourceAttrDiff{ 18 "foo": &ResourceAttrDiff{ 19 Old: "foo", 20 New: "bar", 21 }, 22 }, 23 } 24 25 if diff.Empty() { 26 t.Fatal("should not be empty") 27 } 28 } 29 30 func TestModuleDiff_ChangeType(t *testing.T) { 31 cases := []struct { 32 Diff *ModuleDiff 33 Result DiffChangeType 34 }{ 35 { 36 &ModuleDiff{}, 37 DiffNone, 38 }, 39 { 40 &ModuleDiff{ 41 Resources: map[string]*InstanceDiff{ 42 "foo": &InstanceDiff{Destroy: true}, 43 }, 44 }, 45 DiffDestroy, 46 }, 47 { 48 &ModuleDiff{ 49 Resources: map[string]*InstanceDiff{ 50 "foo": &InstanceDiff{ 51 Attributes: map[string]*ResourceAttrDiff{ 52 "foo": &ResourceAttrDiff{ 53 Old: "", 54 New: "bar", 55 }, 56 }, 57 }, 58 }, 59 }, 60 DiffUpdate, 61 }, 62 { 63 &ModuleDiff{ 64 Resources: map[string]*InstanceDiff{ 65 "foo": &InstanceDiff{ 66 Attributes: map[string]*ResourceAttrDiff{ 67 "foo": &ResourceAttrDiff{ 68 Old: "", 69 New: "bar", 70 RequiresNew: true, 71 }, 72 }, 73 }, 74 }, 75 }, 76 DiffCreate, 77 }, 78 { 79 &ModuleDiff{ 80 Resources: map[string]*InstanceDiff{ 81 "foo": &InstanceDiff{ 82 Destroy: true, 83 Attributes: map[string]*ResourceAttrDiff{ 84 "foo": &ResourceAttrDiff{ 85 Old: "", 86 New: "bar", 87 RequiresNew: true, 88 }, 89 }, 90 }, 91 }, 92 }, 93 DiffUpdate, 94 }, 95 } 96 97 for i, tc := range cases { 98 actual := tc.Diff.ChangeType() 99 if actual != tc.Result { 100 t.Fatalf("%d: %#v", i, actual) 101 } 102 } 103 } 104 105 func TestModuleDiff_Empty(t *testing.T) { 106 diff := new(ModuleDiff) 107 if !diff.Empty() { 108 t.Fatal("should be empty") 109 } 110 111 diff.Resources = map[string]*InstanceDiff{ 112 "nodeA": &InstanceDiff{}, 113 } 114 115 if !diff.Empty() { 116 t.Fatal("should be empty") 117 } 118 119 diff.Resources["nodeA"].Attributes = map[string]*ResourceAttrDiff{ 120 "foo": &ResourceAttrDiff{ 121 Old: "foo", 122 New: "bar", 123 }, 124 } 125 126 if diff.Empty() { 127 t.Fatal("should not be empty") 128 } 129 130 diff.Resources["nodeA"].Attributes = nil 131 diff.Resources["nodeA"].Destroy = true 132 133 if diff.Empty() { 134 t.Fatal("should not be empty") 135 } 136 } 137 138 func TestModuleDiff_String(t *testing.T) { 139 diff := &ModuleDiff{ 140 Resources: map[string]*InstanceDiff{ 141 "nodeA": &InstanceDiff{ 142 Attributes: map[string]*ResourceAttrDiff{ 143 "foo": &ResourceAttrDiff{ 144 Old: "foo", 145 New: "bar", 146 }, 147 "bar": &ResourceAttrDiff{ 148 Old: "foo", 149 NewComputed: true, 150 }, 151 "longfoo": &ResourceAttrDiff{ 152 Old: "foo", 153 New: "bar", 154 RequiresNew: true, 155 }, 156 }, 157 }, 158 }, 159 } 160 161 actual := strings.TrimSpace(diff.String()) 162 expected := strings.TrimSpace(moduleDiffStrBasic) 163 if actual != expected { 164 t.Fatalf("bad:\n%s", actual) 165 } 166 } 167 168 func TestInstanceDiff_ChangeType(t *testing.T) { 169 cases := []struct { 170 Diff *InstanceDiff 171 Result DiffChangeType 172 }{ 173 { 174 &InstanceDiff{}, 175 DiffNone, 176 }, 177 { 178 &InstanceDiff{Destroy: true}, 179 DiffDestroy, 180 }, 181 { 182 &InstanceDiff{ 183 Attributes: map[string]*ResourceAttrDiff{ 184 "foo": &ResourceAttrDiff{ 185 Old: "", 186 New: "bar", 187 }, 188 }, 189 }, 190 DiffUpdate, 191 }, 192 { 193 &InstanceDiff{ 194 Attributes: map[string]*ResourceAttrDiff{ 195 "foo": &ResourceAttrDiff{ 196 Old: "", 197 New: "bar", 198 RequiresNew: true, 199 }, 200 }, 201 }, 202 DiffCreate, 203 }, 204 { 205 &InstanceDiff{ 206 Destroy: true, 207 Attributes: map[string]*ResourceAttrDiff{ 208 "foo": &ResourceAttrDiff{ 209 Old: "", 210 New: "bar", 211 RequiresNew: true, 212 }, 213 }, 214 }, 215 DiffDestroyCreate, 216 }, 217 { 218 &InstanceDiff{ 219 DestroyTainted: true, 220 Attributes: map[string]*ResourceAttrDiff{ 221 "foo": &ResourceAttrDiff{ 222 Old: "", 223 New: "bar", 224 RequiresNew: true, 225 }, 226 }, 227 }, 228 DiffDestroyCreate, 229 }, 230 } 231 232 for i, tc := range cases { 233 actual := tc.Diff.ChangeType() 234 if actual != tc.Result { 235 t.Fatalf("%d: %#v", i, actual) 236 } 237 } 238 } 239 240 func TestInstanceDiff_Empty(t *testing.T) { 241 var rd *InstanceDiff 242 243 if !rd.Empty() { 244 t.Fatal("should be empty") 245 } 246 247 rd = new(InstanceDiff) 248 249 if !rd.Empty() { 250 t.Fatal("should be empty") 251 } 252 253 rd = &InstanceDiff{Destroy: true} 254 255 if rd.Empty() { 256 t.Fatal("should not be empty") 257 } 258 259 rd = &InstanceDiff{ 260 Attributes: map[string]*ResourceAttrDiff{ 261 "foo": &ResourceAttrDiff{ 262 New: "bar", 263 }, 264 }, 265 } 266 267 if rd.Empty() { 268 t.Fatal("should not be empty") 269 } 270 } 271 272 func TestModuleDiff_Instances(t *testing.T) { 273 yesDiff := &InstanceDiff{Destroy: true} 274 noDiff := &InstanceDiff{Destroy: true, DestroyTainted: true} 275 276 cases := []struct { 277 Diff *ModuleDiff 278 Id string 279 Result []*InstanceDiff 280 }{ 281 { 282 &ModuleDiff{ 283 Resources: map[string]*InstanceDiff{ 284 "foo": yesDiff, 285 "bar": noDiff, 286 }, 287 }, 288 "foo", 289 []*InstanceDiff{ 290 yesDiff, 291 }, 292 }, 293 294 { 295 &ModuleDiff{ 296 Resources: map[string]*InstanceDiff{ 297 "foo": yesDiff, 298 "foo.0": yesDiff, 299 "bar": noDiff, 300 }, 301 }, 302 "foo", 303 []*InstanceDiff{ 304 yesDiff, 305 yesDiff, 306 }, 307 }, 308 309 { 310 &ModuleDiff{ 311 Resources: map[string]*InstanceDiff{ 312 "foo": yesDiff, 313 "foo.0": yesDiff, 314 "foo_bar": noDiff, 315 "bar": noDiff, 316 }, 317 }, 318 "foo", 319 []*InstanceDiff{ 320 yesDiff, 321 yesDiff, 322 }, 323 }, 324 } 325 326 for i, tc := range cases { 327 actual := tc.Diff.Instances(tc.Id) 328 if !reflect.DeepEqual(actual, tc.Result) { 329 t.Fatalf("%d: %#v", i, actual) 330 } 331 } 332 } 333 334 func TestInstanceDiff_RequiresNew(t *testing.T) { 335 rd := &InstanceDiff{ 336 Attributes: map[string]*ResourceAttrDiff{ 337 "foo": &ResourceAttrDiff{}, 338 }, 339 } 340 341 if rd.RequiresNew() { 342 t.Fatal("should not require new") 343 } 344 345 rd.Attributes["foo"].RequiresNew = true 346 347 if !rd.RequiresNew() { 348 t.Fatal("should require new") 349 } 350 } 351 352 func TestInstanceDiff_RequiresNew_nil(t *testing.T) { 353 var rd *InstanceDiff 354 355 if rd.RequiresNew() { 356 t.Fatal("should not require new") 357 } 358 } 359 360 func TestInstanceDiffSame(t *testing.T) { 361 cases := []struct { 362 One, Two *InstanceDiff 363 Same bool 364 Reason string 365 }{ 366 { 367 &InstanceDiff{}, 368 &InstanceDiff{}, 369 true, 370 "", 371 }, 372 373 { 374 nil, 375 nil, 376 true, 377 "", 378 }, 379 380 { 381 &InstanceDiff{Destroy: false}, 382 &InstanceDiff{Destroy: true}, 383 false, 384 "diff: Destroy; old: false, new: true", 385 }, 386 387 { 388 &InstanceDiff{Destroy: true}, 389 &InstanceDiff{Destroy: true}, 390 true, 391 "", 392 }, 393 394 { 395 &InstanceDiff{ 396 Attributes: map[string]*ResourceAttrDiff{ 397 "foo": &ResourceAttrDiff{}, 398 }, 399 }, 400 &InstanceDiff{ 401 Attributes: map[string]*ResourceAttrDiff{ 402 "foo": &ResourceAttrDiff{}, 403 }, 404 }, 405 true, 406 "", 407 }, 408 409 { 410 &InstanceDiff{ 411 Attributes: map[string]*ResourceAttrDiff{ 412 "bar": &ResourceAttrDiff{}, 413 }, 414 }, 415 &InstanceDiff{ 416 Attributes: map[string]*ResourceAttrDiff{ 417 "foo": &ResourceAttrDiff{}, 418 }, 419 }, 420 false, 421 "attribute mismatch: bar", 422 }, 423 424 // Extra attributes 425 { 426 &InstanceDiff{ 427 Attributes: map[string]*ResourceAttrDiff{ 428 "foo": &ResourceAttrDiff{}, 429 }, 430 }, 431 &InstanceDiff{ 432 Attributes: map[string]*ResourceAttrDiff{ 433 "foo": &ResourceAttrDiff{}, 434 "bar": &ResourceAttrDiff{}, 435 }, 436 }, 437 false, 438 "extra attributes: bar", 439 }, 440 441 { 442 &InstanceDiff{ 443 Attributes: map[string]*ResourceAttrDiff{ 444 "foo": &ResourceAttrDiff{RequiresNew: true}, 445 }, 446 }, 447 &InstanceDiff{ 448 Attributes: map[string]*ResourceAttrDiff{ 449 "foo": &ResourceAttrDiff{RequiresNew: false}, 450 }, 451 }, 452 false, 453 "diff RequiresNew; old: true, new: false", 454 }, 455 456 { 457 &InstanceDiff{ 458 Attributes: map[string]*ResourceAttrDiff{ 459 "foo.#": &ResourceAttrDiff{NewComputed: true}, 460 }, 461 }, 462 &InstanceDiff{ 463 Attributes: map[string]*ResourceAttrDiff{ 464 "foo.#": &ResourceAttrDiff{ 465 Old: "0", 466 New: "1", 467 }, 468 "foo.0": &ResourceAttrDiff{ 469 Old: "", 470 New: "12", 471 }, 472 }, 473 }, 474 true, 475 "", 476 }, 477 478 { 479 &InstanceDiff{ 480 Attributes: map[string]*ResourceAttrDiff{ 481 "foo.#": &ResourceAttrDiff{ 482 Old: "0", 483 New: "1", 484 }, 485 "foo.~35964334.bar": &ResourceAttrDiff{ 486 Old: "", 487 New: "${var.foo}", 488 }, 489 }, 490 }, 491 &InstanceDiff{ 492 Attributes: map[string]*ResourceAttrDiff{ 493 "foo.#": &ResourceAttrDiff{ 494 Old: "0", 495 New: "1", 496 }, 497 "foo.87654323.bar": &ResourceAttrDiff{ 498 Old: "", 499 New: "12", 500 }, 501 }, 502 }, 503 true, 504 "", 505 }, 506 507 { 508 &InstanceDiff{ 509 Attributes: map[string]*ResourceAttrDiff{ 510 "foo.#": &ResourceAttrDiff{ 511 Old: "0", 512 NewComputed: true, 513 }, 514 }, 515 }, 516 &InstanceDiff{ 517 Attributes: map[string]*ResourceAttrDiff{}, 518 }, 519 true, 520 "", 521 }, 522 523 // In a DESTROY/CREATE scenario, the plan diff will be run against the 524 // state of the old instance, while the apply diff will be run against an 525 // empty state (because the state is cleared when the destroy runs.) 526 // For complex attributes, this can result in keys that seem to disappear 527 // between the two diffs, when in reality everything is working just fine. 528 // 529 // Same() needs to take into account this scenario by analyzing NewRemoved 530 // and treating as "Same" a diff that does indeed have that key removed. 531 { 532 &InstanceDiff{ 533 Attributes: map[string]*ResourceAttrDiff{ 534 "somemap.oldkey": &ResourceAttrDiff{ 535 Old: "long ago", 536 New: "", 537 NewRemoved: true, 538 }, 539 "somemap.newkey": &ResourceAttrDiff{ 540 Old: "", 541 New: "brave new world", 542 }, 543 }, 544 }, 545 &InstanceDiff{ 546 Attributes: map[string]*ResourceAttrDiff{ 547 "somemap.newkey": &ResourceAttrDiff{ 548 Old: "", 549 New: "brave new world", 550 }, 551 }, 552 }, 553 true, 554 "", 555 }, 556 } 557 558 for i, tc := range cases { 559 same, reason := tc.One.Same(tc.Two) 560 if same != tc.Same { 561 t.Fatalf("%d: expected same: %t, got %t (%s)\n\n one: %#v\n\ntwo: %#v", 562 i, tc.Same, same, reason, tc.One, tc.Two) 563 } 564 if reason != tc.Reason { 565 t.Fatalf( 566 "%d: bad reason\n\nexpected: %#v\n\ngot: %#v", i, tc.Reason, reason) 567 } 568 } 569 } 570 571 const moduleDiffStrBasic = ` 572 CREATE: nodeA 573 bar: "foo" => "<computed>" 574 foo: "foo" => "bar" 575 longfoo: "foo" => "bar" (forces new resource) 576 `