github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/backend/remote/backend_test.go (about) 1 package remote 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "strings" 8 "testing" 9 10 tfe "github.com/hashicorp/go-tfe" 11 version "github.com/hashicorp/go-version" 12 "github.com/hashicorp/terraform-svchost/disco" 13 "github.com/eliastor/durgaform/internal/backend" 14 "github.com/eliastor/durgaform/internal/tfdiags" 15 tfversion "github.com/eliastor/durgaform/version" 16 "github.com/zclconf/go-cty/cty" 17 18 backendLocal "github.com/eliastor/durgaform/internal/backend/local" 19 ) 20 21 func TestRemote(t *testing.T) { 22 var _ backend.Enhanced = New(nil) 23 var _ backend.CLI = New(nil) 24 } 25 26 func TestRemote_backendDefault(t *testing.T) { 27 b, bCleanup := testBackendDefault(t) 28 defer bCleanup() 29 30 backend.TestBackendStates(t, b) 31 backend.TestBackendStateLocks(t, b, b) 32 backend.TestBackendStateForceUnlock(t, b, b) 33 } 34 35 func TestRemote_backendNoDefault(t *testing.T) { 36 b, bCleanup := testBackendNoDefault(t) 37 defer bCleanup() 38 39 backend.TestBackendStates(t, b) 40 } 41 42 func TestRemote_config(t *testing.T) { 43 cases := map[string]struct { 44 config cty.Value 45 confErr string 46 valErr string 47 }{ 48 "with_a_nonexisting_organization": { 49 config: cty.ObjectVal(map[string]cty.Value{ 50 "hostname": cty.NullVal(cty.String), 51 "organization": cty.StringVal("nonexisting"), 52 "token": cty.NullVal(cty.String), 53 "workspaces": cty.ObjectVal(map[string]cty.Value{ 54 "name": cty.StringVal("prod"), 55 "prefix": cty.NullVal(cty.String), 56 }), 57 }), 58 confErr: "organization \"nonexisting\" at host app.durgaform.io not found", 59 }, 60 "with_an_unknown_host": { 61 config: cty.ObjectVal(map[string]cty.Value{ 62 "hostname": cty.StringVal("nonexisting.local"), 63 "organization": cty.StringVal("hashicorp"), 64 "token": cty.NullVal(cty.String), 65 "workspaces": cty.ObjectVal(map[string]cty.Value{ 66 "name": cty.StringVal("prod"), 67 "prefix": cty.NullVal(cty.String), 68 }), 69 }), 70 confErr: "Failed to request discovery document", 71 }, 72 // localhost advertises TFE services, but has no token in the credentials 73 "without_a_token": { 74 config: cty.ObjectVal(map[string]cty.Value{ 75 "hostname": cty.StringVal("localhost"), 76 "organization": cty.StringVal("hashicorp"), 77 "token": cty.NullVal(cty.String), 78 "workspaces": cty.ObjectVal(map[string]cty.Value{ 79 "name": cty.StringVal("prod"), 80 "prefix": cty.NullVal(cty.String), 81 }), 82 }), 83 confErr: "durgaform login localhost", 84 }, 85 "with_a_name": { 86 config: cty.ObjectVal(map[string]cty.Value{ 87 "hostname": cty.NullVal(cty.String), 88 "organization": cty.StringVal("hashicorp"), 89 "token": cty.NullVal(cty.String), 90 "workspaces": cty.ObjectVal(map[string]cty.Value{ 91 "name": cty.StringVal("prod"), 92 "prefix": cty.NullVal(cty.String), 93 }), 94 }), 95 }, 96 "with_a_prefix": { 97 config: cty.ObjectVal(map[string]cty.Value{ 98 "hostname": cty.NullVal(cty.String), 99 "organization": cty.StringVal("hashicorp"), 100 "token": cty.NullVal(cty.String), 101 "workspaces": cty.ObjectVal(map[string]cty.Value{ 102 "name": cty.NullVal(cty.String), 103 "prefix": cty.StringVal("my-app-"), 104 }), 105 }), 106 }, 107 "without_either_a_name_and_a_prefix": { 108 config: cty.ObjectVal(map[string]cty.Value{ 109 "hostname": cty.NullVal(cty.String), 110 "organization": cty.StringVal("hashicorp"), 111 "token": cty.NullVal(cty.String), 112 "workspaces": cty.ObjectVal(map[string]cty.Value{ 113 "name": cty.NullVal(cty.String), 114 "prefix": cty.NullVal(cty.String), 115 }), 116 }), 117 valErr: `Either workspace "name" or "prefix" is required`, 118 }, 119 "with_both_a_name_and_a_prefix": { 120 config: cty.ObjectVal(map[string]cty.Value{ 121 "hostname": cty.NullVal(cty.String), 122 "organization": cty.StringVal("hashicorp"), 123 "token": cty.NullVal(cty.String), 124 "workspaces": cty.ObjectVal(map[string]cty.Value{ 125 "name": cty.StringVal("prod"), 126 "prefix": cty.StringVal("my-app-"), 127 }), 128 }), 129 valErr: `Only one of workspace "name" or "prefix" is allowed`, 130 }, 131 "null config": { 132 config: cty.NullVal(cty.EmptyObject), 133 }, 134 } 135 136 for name, tc := range cases { 137 s := testServer(t) 138 b := New(testDisco(s)) 139 140 // Validate 141 _, valDiags := b.PrepareConfig(tc.config) 142 if (valDiags.Err() != nil || tc.valErr != "") && 143 (valDiags.Err() == nil || !strings.Contains(valDiags.Err().Error(), tc.valErr)) { 144 t.Fatalf("%s: unexpected validation result: %v", name, valDiags.Err()) 145 } 146 147 // Configure 148 confDiags := b.Configure(tc.config) 149 if (confDiags.Err() != nil || tc.confErr != "") && 150 (confDiags.Err() == nil || !strings.Contains(confDiags.Err().Error(), tc.confErr)) { 151 t.Fatalf("%s: unexpected configure result: %v", name, confDiags.Err()) 152 } 153 } 154 } 155 156 func TestRemote_versionConstraints(t *testing.T) { 157 cases := map[string]struct { 158 config cty.Value 159 prerelease string 160 version string 161 result string 162 }{ 163 "compatible version": { 164 config: cty.ObjectVal(map[string]cty.Value{ 165 "hostname": cty.NullVal(cty.String), 166 "organization": cty.StringVal("hashicorp"), 167 "token": cty.NullVal(cty.String), 168 "workspaces": cty.ObjectVal(map[string]cty.Value{ 169 "name": cty.StringVal("prod"), 170 "prefix": cty.NullVal(cty.String), 171 }), 172 }), 173 version: "0.11.1", 174 }, 175 "version too old": { 176 config: cty.ObjectVal(map[string]cty.Value{ 177 "hostname": cty.NullVal(cty.String), 178 "organization": cty.StringVal("hashicorp"), 179 "token": cty.NullVal(cty.String), 180 "workspaces": cty.ObjectVal(map[string]cty.Value{ 181 "name": cty.StringVal("prod"), 182 "prefix": cty.NullVal(cty.String), 183 }), 184 }), 185 version: "0.0.1", 186 result: "upgrade Durgaform to >= 0.1.0", 187 }, 188 "version too new": { 189 config: cty.ObjectVal(map[string]cty.Value{ 190 "hostname": cty.NullVal(cty.String), 191 "organization": cty.StringVal("hashicorp"), 192 "token": cty.NullVal(cty.String), 193 "workspaces": cty.ObjectVal(map[string]cty.Value{ 194 "name": cty.StringVal("prod"), 195 "prefix": cty.NullVal(cty.String), 196 }), 197 }), 198 version: "10.0.1", 199 result: "downgrade Durgaform to <= 10.0.0", 200 }, 201 } 202 203 // Save and restore the actual version. 204 p := tfversion.Prerelease 205 v := tfversion.Version 206 defer func() { 207 tfversion.Prerelease = p 208 tfversion.Version = v 209 }() 210 211 for name, tc := range cases { 212 s := testServer(t) 213 b := New(testDisco(s)) 214 215 // Set the version for this test. 216 tfversion.Prerelease = tc.prerelease 217 tfversion.Version = tc.version 218 219 // Validate 220 _, valDiags := b.PrepareConfig(tc.config) 221 if valDiags.HasErrors() { 222 t.Fatalf("%s: unexpected validation result: %v", name, valDiags.Err()) 223 } 224 225 // Configure 226 confDiags := b.Configure(tc.config) 227 if (confDiags.Err() != nil || tc.result != "") && 228 (confDiags.Err() == nil || !strings.Contains(confDiags.Err().Error(), tc.result)) { 229 t.Fatalf("%s: unexpected configure result: %v", name, confDiags.Err()) 230 } 231 } 232 } 233 234 func TestRemote_localBackend(t *testing.T) { 235 b, bCleanup := testBackendDefault(t) 236 defer bCleanup() 237 238 local, ok := b.local.(*backendLocal.Local) 239 if !ok { 240 t.Fatalf("expected b.local to be \"*local.Local\", got: %T", b.local) 241 } 242 243 remote, ok := local.Backend.(*Remote) 244 if !ok { 245 t.Fatalf("expected local.Backend to be *remote.Remote, got: %T", remote) 246 } 247 } 248 249 func TestRemote_addAndRemoveWorkspacesDefault(t *testing.T) { 250 b, bCleanup := testBackendDefault(t) 251 defer bCleanup() 252 253 if _, err := b.Workspaces(); err != backend.ErrWorkspacesNotSupported { 254 t.Fatalf("expected error %v, got %v", backend.ErrWorkspacesNotSupported, err) 255 } 256 257 if _, err := b.StateMgr(backend.DefaultStateName); err != nil { 258 t.Fatalf("expected no error, got %v", err) 259 } 260 261 if _, err := b.StateMgr("prod"); err != backend.ErrWorkspacesNotSupported { 262 t.Fatalf("expected error %v, got %v", backend.ErrWorkspacesNotSupported, err) 263 } 264 265 if err := b.DeleteWorkspace(backend.DefaultStateName); err != nil { 266 t.Fatalf("expected no error, got %v", err) 267 } 268 269 if err := b.DeleteWorkspace("prod"); err != backend.ErrWorkspacesNotSupported { 270 t.Fatalf("expected error %v, got %v", backend.ErrWorkspacesNotSupported, err) 271 } 272 } 273 274 func TestRemote_addAndRemoveWorkspacesNoDefault(t *testing.T) { 275 b, bCleanup := testBackendNoDefault(t) 276 defer bCleanup() 277 278 states, err := b.Workspaces() 279 if err != nil { 280 t.Fatal(err) 281 } 282 283 expectedWorkspaces := []string(nil) 284 if !reflect.DeepEqual(states, expectedWorkspaces) { 285 t.Fatalf("expected states %#+v, got %#+v", expectedWorkspaces, states) 286 } 287 288 if _, err := b.StateMgr(backend.DefaultStateName); err != backend.ErrDefaultWorkspaceNotSupported { 289 t.Fatalf("expected error %v, got %v", backend.ErrDefaultWorkspaceNotSupported, err) 290 } 291 292 expectedA := "test_A" 293 if _, err := b.StateMgr(expectedA); err != nil { 294 t.Fatal(err) 295 } 296 297 states, err = b.Workspaces() 298 if err != nil { 299 t.Fatal(err) 300 } 301 302 expectedWorkspaces = append(expectedWorkspaces, expectedA) 303 if !reflect.DeepEqual(states, expectedWorkspaces) { 304 t.Fatalf("expected %#+v, got %#+v", expectedWorkspaces, states) 305 } 306 307 expectedB := "test_B" 308 if _, err := b.StateMgr(expectedB); err != nil { 309 t.Fatal(err) 310 } 311 312 states, err = b.Workspaces() 313 if err != nil { 314 t.Fatal(err) 315 } 316 317 expectedWorkspaces = append(expectedWorkspaces, expectedB) 318 if !reflect.DeepEqual(states, expectedWorkspaces) { 319 t.Fatalf("expected %#+v, got %#+v", expectedWorkspaces, states) 320 } 321 322 if err := b.DeleteWorkspace(backend.DefaultStateName); err != backend.ErrDefaultWorkspaceNotSupported { 323 t.Fatalf("expected error %v, got %v", backend.ErrDefaultWorkspaceNotSupported, err) 324 } 325 326 if err := b.DeleteWorkspace(expectedA); err != nil { 327 t.Fatal(err) 328 } 329 330 states, err = b.Workspaces() 331 if err != nil { 332 t.Fatal(err) 333 } 334 335 expectedWorkspaces = []string{expectedB} 336 if !reflect.DeepEqual(states, expectedWorkspaces) { 337 t.Fatalf("expected %#+v got %#+v", expectedWorkspaces, states) 338 } 339 340 if err := b.DeleteWorkspace(expectedB); err != nil { 341 t.Fatal(err) 342 } 343 344 states, err = b.Workspaces() 345 if err != nil { 346 t.Fatal(err) 347 } 348 349 expectedWorkspaces = []string(nil) 350 if !reflect.DeepEqual(states, expectedWorkspaces) { 351 t.Fatalf("expected %#+v, got %#+v", expectedWorkspaces, states) 352 } 353 } 354 355 func TestRemote_checkConstraints(t *testing.T) { 356 b, bCleanup := testBackendDefault(t) 357 defer bCleanup() 358 359 cases := map[string]struct { 360 constraints *disco.Constraints 361 prerelease string 362 version string 363 result string 364 }{ 365 "compatible version": { 366 constraints: &disco.Constraints{ 367 Minimum: "0.11.0", 368 Maximum: "0.11.11", 369 }, 370 version: "0.11.1", 371 result: "", 372 }, 373 "version too old": { 374 constraints: &disco.Constraints{ 375 Minimum: "0.11.0", 376 Maximum: "0.11.11", 377 }, 378 version: "0.10.1", 379 result: "upgrade Durgaform to >= 0.11.0", 380 }, 381 "version too new": { 382 constraints: &disco.Constraints{ 383 Minimum: "0.11.0", 384 Maximum: "0.11.11", 385 }, 386 version: "0.12.0", 387 result: "downgrade Durgaform to <= 0.11.11", 388 }, 389 "version excluded - ordered": { 390 constraints: &disco.Constraints{ 391 Minimum: "0.11.0", 392 Excluding: []string{"0.11.7", "0.11.8"}, 393 Maximum: "0.11.11", 394 }, 395 version: "0.11.7", 396 result: "upgrade Durgaform to > 0.11.8", 397 }, 398 "version excluded - unordered": { 399 constraints: &disco.Constraints{ 400 Minimum: "0.11.0", 401 Excluding: []string{"0.11.8", "0.11.6"}, 402 Maximum: "0.11.11", 403 }, 404 version: "0.11.6", 405 result: "upgrade Durgaform to > 0.11.8", 406 }, 407 "list versions": { 408 constraints: &disco.Constraints{ 409 Minimum: "0.11.0", 410 Maximum: "0.11.11", 411 }, 412 version: "0.10.1", 413 result: "versions >= 0.11.0, <= 0.11.11.", 414 }, 415 "list exclusion": { 416 constraints: &disco.Constraints{ 417 Minimum: "0.11.0", 418 Excluding: []string{"0.11.6"}, 419 Maximum: "0.11.11", 420 }, 421 version: "0.11.6", 422 result: "excluding version 0.11.6.", 423 }, 424 "list exclusions": { 425 constraints: &disco.Constraints{ 426 Minimum: "0.11.0", 427 Excluding: []string{"0.11.8", "0.11.6"}, 428 Maximum: "0.11.11", 429 }, 430 version: "0.11.6", 431 result: "excluding versions 0.11.6, 0.11.8.", 432 }, 433 } 434 435 // Save and restore the actual version. 436 p := tfversion.Prerelease 437 v := tfversion.Version 438 defer func() { 439 tfversion.Prerelease = p 440 tfversion.Version = v 441 }() 442 443 for name, tc := range cases { 444 // Set the version for this test. 445 tfversion.Prerelease = tc.prerelease 446 tfversion.Version = tc.version 447 448 // Check the constraints. 449 diags := b.checkConstraints(tc.constraints) 450 if (diags.Err() != nil || tc.result != "") && 451 (diags.Err() == nil || !strings.Contains(diags.Err().Error(), tc.result)) { 452 t.Fatalf("%s: unexpected constraints result: %v", name, diags.Err()) 453 } 454 } 455 } 456 457 func TestRemote_StateMgr_versionCheck(t *testing.T) { 458 b, bCleanup := testBackendDefault(t) 459 defer bCleanup() 460 461 // Some fixed versions for testing with. This logic is a simple string 462 // comparison, so we don't need many test cases. 463 v0135 := version.Must(version.NewSemver("0.13.5")) 464 v0140 := version.Must(version.NewSemver("0.14.0")) 465 466 // Save original local version state and restore afterwards 467 p := tfversion.Prerelease 468 v := tfversion.Version 469 s := tfversion.SemVer 470 defer func() { 471 tfversion.Prerelease = p 472 tfversion.Version = v 473 tfversion.SemVer = s 474 }() 475 476 // For this test, the local Durgaform version is set to 0.14.0 477 tfversion.Prerelease = "" 478 tfversion.Version = v0140.String() 479 tfversion.SemVer = v0140 480 481 // Update the mock remote workspace Durgaform version to match the local 482 // Durgaform version 483 if _, err := b.client.Workspaces.Update( 484 context.Background(), 485 b.organization, 486 b.workspace, 487 tfe.WorkspaceUpdateOptions{ 488 DurgaformVersion: tfe.String(v0140.String()), 489 }, 490 ); err != nil { 491 t.Fatalf("error: %v", err) 492 } 493 494 // This should succeed 495 if _, err := b.StateMgr(backend.DefaultStateName); err != nil { 496 t.Fatalf("expected no error, got %v", err) 497 } 498 499 // Now change the remote workspace to a different Durgaform version 500 if _, err := b.client.Workspaces.Update( 501 context.Background(), 502 b.organization, 503 b.workspace, 504 tfe.WorkspaceUpdateOptions{ 505 DurgaformVersion: tfe.String(v0135.String()), 506 }, 507 ); err != nil { 508 t.Fatalf("error: %v", err) 509 } 510 511 // This should fail 512 want := `Remote workspace Durgaform version "0.13.5" does not match local Terraform version "0.14.0"` 513 if _, err := b.StateMgr(backend.DefaultStateName); err.Error() != want { 514 t.Fatalf("wrong error\n got: %v\nwant: %v", err.Error(), want) 515 } 516 } 517 518 func TestRemote_StateMgr_versionCheckLatest(t *testing.T) { 519 b, bCleanup := testBackendDefault(t) 520 defer bCleanup() 521 522 v0140 := version.Must(version.NewSemver("0.14.0")) 523 524 // Save original local version state and restore afterwards 525 p := tfversion.Prerelease 526 v := tfversion.Version 527 s := tfversion.SemVer 528 defer func() { 529 tfversion.Prerelease = p 530 tfversion.Version = v 531 tfversion.SemVer = s 532 }() 533 534 // For this test, the local Durgaform version is set to 0.14.0 535 tfversion.Prerelease = "" 536 tfversion.Version = v0140.String() 537 tfversion.SemVer = v0140 538 539 // Update the remote workspace to the pseudo-version "latest" 540 if _, err := b.client.Workspaces.Update( 541 context.Background(), 542 b.organization, 543 b.workspace, 544 tfe.WorkspaceUpdateOptions{ 545 DurgaformVersion: tfe.String("latest"), 546 }, 547 ); err != nil { 548 t.Fatalf("error: %v", err) 549 } 550 551 // This should succeed despite not being a string match 552 if _, err := b.StateMgr(backend.DefaultStateName); err != nil { 553 t.Fatalf("expected no error, got %v", err) 554 } 555 } 556 557 func TestRemote_VerifyWorkspaceDurgaformVersion(t *testing.T) { 558 testCases := []struct { 559 local string 560 remote string 561 executionMode string 562 wantErr bool 563 }{ 564 {"0.13.5", "0.13.5", "remote", false}, 565 {"0.14.0", "0.13.5", "remote", true}, 566 {"0.14.0", "0.13.5", "local", false}, 567 {"0.14.0", "0.14.1", "remote", false}, 568 {"0.14.0", "1.0.99", "remote", false}, 569 {"0.14.0", "1.1.0", "remote", false}, 570 {"0.14.0", "1.3.0", "remote", true}, 571 {"1.2.0", "1.2.99", "remote", false}, 572 {"1.2.0", "1.3.0", "remote", true}, 573 {"0.15.0", "latest", "remote", false}, 574 } 575 for _, tc := range testCases { 576 t.Run(fmt.Sprintf("local %s, remote %s", tc.local, tc.remote), func(t *testing.T) { 577 b, bCleanup := testBackendDefault(t) 578 defer bCleanup() 579 580 local := version.Must(version.NewSemver(tc.local)) 581 582 // Save original local version state and restore afterwards 583 p := tfversion.Prerelease 584 v := tfversion.Version 585 s := tfversion.SemVer 586 defer func() { 587 tfversion.Prerelease = p 588 tfversion.Version = v 589 tfversion.SemVer = s 590 }() 591 592 // Override local version as specified 593 tfversion.Prerelease = "" 594 tfversion.Version = local.String() 595 tfversion.SemVer = local 596 597 // Update the mock remote workspace Durgaform version to the 598 // specified remote version 599 if _, err := b.client.Workspaces.Update( 600 context.Background(), 601 b.organization, 602 b.workspace, 603 tfe.WorkspaceUpdateOptions{ 604 ExecutionMode: &tc.executionMode, 605 DurgaformVersion: tfe.String(tc.remote), 606 }, 607 ); err != nil { 608 t.Fatalf("error: %v", err) 609 } 610 611 diags := b.VerifyWorkspaceDurgaformVersion(backend.DefaultStateName) 612 if tc.wantErr { 613 if len(diags) != 1 { 614 t.Fatal("expected diag, but none returned") 615 } 616 if got := diags.Err().Error(); !strings.Contains(got, "Durgaform version mismatch") { 617 t.Fatalf("unexpected error: %s", got) 618 } 619 } else { 620 if len(diags) != 0 { 621 t.Fatalf("unexpected diags: %s", diags.Err()) 622 } 623 } 624 }) 625 } 626 } 627 628 func TestRemote_VerifyWorkspaceDurgaformVersion_workspaceErrors(t *testing.T) { 629 b, bCleanup := testBackendDefault(t) 630 defer bCleanup() 631 632 // Attempting to check the version against a workspace which doesn't exist 633 // should result in no errors 634 diags := b.VerifyWorkspaceDurgaformVersion("invalid-workspace") 635 if len(diags) != 0 { 636 t.Fatalf("unexpected error: %s", diags.Err()) 637 } 638 639 // Use a special workspace ID to trigger a 500 error, which should result 640 // in a failed check 641 diags = b.VerifyWorkspaceDurgaformVersion("network-error") 642 if len(diags) != 1 { 643 t.Fatal("expected diag, but none returned") 644 } 645 if got := diags.Err().Error(); !strings.Contains(got, "Error looking up workspace: Workspace read failed") { 646 t.Fatalf("unexpected error: %s", got) 647 } 648 649 // Update the mock remote workspace Durgaform version to an invalid version 650 if _, err := b.client.Workspaces.Update( 651 context.Background(), 652 b.organization, 653 b.workspace, 654 tfe.WorkspaceUpdateOptions{ 655 DurgaformVersion: tfe.String("1.0.cheetarah"), 656 }, 657 ); err != nil { 658 t.Fatalf("error: %v", err) 659 } 660 diags = b.VerifyWorkspaceDurgaformVersion(backend.DefaultStateName) 661 662 if len(diags) != 1 { 663 t.Fatal("expected diag, but none returned") 664 } 665 if got := diags.Err().Error(); !strings.Contains(got, "Error looking up workspace: Invalid Durgaform version") { 666 t.Fatalf("unexpected error: %s", got) 667 } 668 } 669 670 func TestRemote_VerifyWorkspaceDurgaformVersion_ignoreFlagSet(t *testing.T) { 671 b, bCleanup := testBackendDefault(t) 672 defer bCleanup() 673 674 // If the ignore flag is set, the behaviour changes 675 b.IgnoreVersionConflict() 676 677 // Different local & remote versions to cause an error 678 local := version.Must(version.NewSemver("0.14.0")) 679 remote := version.Must(version.NewSemver("0.13.5")) 680 681 // Save original local version state and restore afterwards 682 p := tfversion.Prerelease 683 v := tfversion.Version 684 s := tfversion.SemVer 685 defer func() { 686 tfversion.Prerelease = p 687 tfversion.Version = v 688 tfversion.SemVer = s 689 }() 690 691 // Override local version as specified 692 tfversion.Prerelease = "" 693 tfversion.Version = local.String() 694 tfversion.SemVer = local 695 696 // Update the mock remote workspace Durgaform version to the 697 // specified remote version 698 if _, err := b.client.Workspaces.Update( 699 context.Background(), 700 b.organization, 701 b.workspace, 702 tfe.WorkspaceUpdateOptions{ 703 DurgaformVersion: tfe.String(remote.String()), 704 }, 705 ); err != nil { 706 t.Fatalf("error: %v", err) 707 } 708 709 diags := b.VerifyWorkspaceDurgaformVersion(backend.DefaultStateName) 710 if len(diags) != 1 { 711 t.Fatal("expected diag, but none returned") 712 } 713 714 if got, want := diags[0].Severity(), tfdiags.Warning; got != want { 715 t.Errorf("wrong severity: got %#v, want %#v", got, want) 716 } 717 if got, want := diags[0].Description().Summary, "Durgaform version mismatch"; got != want { 718 t.Errorf("wrong summary: got %s, want %s", got, want) 719 } 720 wantDetail := "The local Durgaform version (0.14.0) does not match the configured version for remote workspace hashicorp/prod (0.13.5)." 721 if got := diags[0].Description().Detail; got != wantDetail { 722 t.Errorf("wrong summary: got %s, want %s", got, wantDetail) 723 } 724 }