| package views |
| |
| import ( |
| "encoding/json" |
| "strings" |
| "testing" |
| |
| "github.com/hashicorp/terraform/internal/addrs" |
| "github.com/hashicorp/terraform/internal/command/arguments" |
| "github.com/hashicorp/terraform/internal/configs/configschema" |
| "github.com/hashicorp/terraform/internal/initwd" |
| "github.com/hashicorp/terraform/internal/plans" |
| "github.com/hashicorp/terraform/internal/states" |
| "github.com/hashicorp/terraform/internal/states/statefile" |
| "github.com/hashicorp/terraform/internal/terminal" |
| "github.com/hashicorp/terraform/internal/terraform" |
| |
| "github.com/zclconf/go-cty/cty" |
| ) |
| |
| func TestShowHuman(t *testing.T) { |
| testCases := map[string]struct { |
| plan *plans.Plan |
| stateFile *statefile.File |
| schemas *terraform.Schemas |
| wantExact bool |
| wantString string |
| }{ |
| "plan file": { |
| testPlan(t), |
| nil, |
| testSchemas(), |
| false, |
| "# test_resource.foo will be created", |
| }, |
| "statefile": { |
| nil, |
| &statefile.File{ |
| Serial: 0, |
| Lineage: "fake-for-testing", |
| State: testState(), |
| }, |
| testSchemas(), |
| false, |
| "# test_resource.foo:", |
| }, |
| "empty statefile": { |
| nil, |
| &statefile.File{ |
| Serial: 0, |
| Lineage: "fake-for-testing", |
| State: states.NewState(), |
| }, |
| testSchemas(), |
| true, |
| "The state file is empty. No resources are represented.\n", |
| }, |
| "nothing": { |
| nil, |
| nil, |
| nil, |
| true, |
| "No state.\n", |
| }, |
| } |
| for name, testCase := range testCases { |
| t.Run(name, func(t *testing.T) { |
| streams, done := terminal.StreamsForTesting(t) |
| view := NewView(streams) |
| view.Configure(&arguments.View{NoColor: true}) |
| v := NewShow(arguments.ViewHuman, view) |
| |
| code := v.Display(nil, testCase.plan, testCase.stateFile, testCase.schemas) |
| if code != 0 { |
| t.Errorf("expected 0 return code, got %d", code) |
| } |
| |
| output := done(t) |
| got := output.Stdout() |
| want := testCase.wantString |
| if (testCase.wantExact && got != want) || (!testCase.wantExact && !strings.Contains(got, want)) { |
| t.Fatalf("unexpected output\ngot: %s\nwant: %s", got, want) |
| } |
| }) |
| } |
| } |
| |
| func TestShowJSON(t *testing.T) { |
| testCases := map[string]struct { |
| plan *plans.Plan |
| stateFile *statefile.File |
| }{ |
| "plan file": { |
| testPlan(t), |
| nil, |
| }, |
| "statefile": { |
| nil, |
| &statefile.File{ |
| Serial: 0, |
| Lineage: "fake-for-testing", |
| State: testState(), |
| }, |
| }, |
| "empty statefile": { |
| nil, |
| &statefile.File{ |
| Serial: 0, |
| Lineage: "fake-for-testing", |
| State: states.NewState(), |
| }, |
| }, |
| "nothing": { |
| nil, |
| nil, |
| }, |
| } |
| |
| config, _, configCleanup := initwd.MustLoadConfigForTests(t, "./testdata/show") |
| defer configCleanup() |
| |
| for name, testCase := range testCases { |
| t.Run(name, func(t *testing.T) { |
| streams, done := terminal.StreamsForTesting(t) |
| view := NewView(streams) |
| view.Configure(&arguments.View{NoColor: true}) |
| v := NewShow(arguments.ViewJSON, view) |
| |
| schemas := &terraform.Schemas{ |
| Providers: map[addrs.Provider]*terraform.ProviderSchema{ |
| addrs.NewDefaultProvider("test"): { |
| ResourceTypes: map[string]*configschema.Block{ |
| "test_resource": { |
| Attributes: map[string]*configschema.Attribute{ |
| "id": {Type: cty.String, Optional: true, Computed: true}, |
| "foo": {Type: cty.String, Optional: true}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| } |
| |
| code := v.Display(config, testCase.plan, testCase.stateFile, schemas) |
| |
| if code != 0 { |
| t.Errorf("expected 0 return code, got %d", code) |
| } |
| |
| // Make sure the result looks like JSON; we comprehensively test |
| // the structure of this output in the command package tests. |
| var result map[string]interface{} |
| got := done(t).All() |
| t.Logf("output: %s", got) |
| if err := json.Unmarshal([]byte(got), &result); err != nil { |
| t.Fatal(err) |
| } |
| }) |
| } |
| } |
| |
| // testState returns a test State structure. |
| func testState() *states.State { |
| return states.BuildState(func(s *states.SyncState) { |
| s.SetResourceInstanceCurrent( |
| addrs.Resource{ |
| Mode: addrs.ManagedResourceMode, |
| Type: "test_resource", |
| Name: "foo", |
| }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), |
| &states.ResourceInstanceObjectSrc{ |
| AttrsJSON: []byte(`{"id":"bar","foo":"value"}`), |
| Status: states.ObjectReady, |
| }, |
| addrs.AbsProviderConfig{ |
| Provider: addrs.NewDefaultProvider("test"), |
| Module: addrs.RootModule, |
| }, |
| ) |
| // DeepCopy is used here to ensure our synthetic state matches exactly |
| // with a state that will have been copied during the command |
| // operation, and all fields have been copied correctly. |
| }).DeepCopy() |
| } |