| // Copyright (c) HashiCorp, Inc. | |
| // SPDX-License-Identifier: MPL-2.0 | |
| package tfjson | |
| import ( | |
| "encoding/json" | |
| "os" | |
| "reflect" | |
| "testing" | |
| "github.com/google/go-cmp/cmp" | |
| ) | |
| func TestPlanValidate(t *testing.T) { | |
| cases := map[string]struct { | |
| planPath string | |
| }{ | |
| "basic plan": { | |
| planPath: "testdata/basic/plan.json", | |
| }, | |
| "plan with identity": { | |
| planPath: "testdata/identity/plan.json", | |
| }, | |
| } | |
| for tn, tc := range cases { | |
| t.Run(tn, func(t *testing.T) { | |
| f, err := os.Open(tc.planPath) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer f.Close() | |
| var plan *Plan | |
| if err := json.NewDecoder(f).Decode(&plan); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := plan.Validate(); err != nil { | |
| t.Fatal(err) | |
| } | |
| }) | |
| } | |
| } | |
| func TestPlan_015(t *testing.T) { | |
| f, err := os.Open("testdata/basic/plan-0.15.json") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer f.Close() | |
| var plan *Plan | |
| if err := json.NewDecoder(f).Decode(&plan); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := plan.Validate(); err != nil { | |
| t.Fatal(err) | |
| } | |
| expectedChange := &Change{ | |
| Actions: Actions{"create"}, | |
| After: map[string]interface{}{"ami": "boop"}, | |
| AfterUnknown: map[string]interface{}{"id": true}, | |
| BeforeSensitive: false, | |
| AfterSensitive: map[string]interface{}{"ami": true}, | |
| } | |
| if diff := cmp.Diff(expectedChange, plan.ResourceChanges[0].Change); diff != "" { | |
| t.Fatalf("unexpected change: %s", diff) | |
| } | |
| expectedVariable := map[string]*ConfigVariable{ | |
| "test_var": { | |
| Default: "boop", | |
| Sensitive: true, | |
| }, | |
| } | |
| if diff := cmp.Diff(expectedVariable, plan.Config.RootModule.Variables); diff != "" { | |
| t.Fatalf("unexpected variables: %s", diff) | |
| } | |
| } | |
| func TestPlan_withChecks(t *testing.T) { | |
| f, err := os.Open("testdata/has_checks/plan.json") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer f.Close() | |
| var plan *Plan | |
| if err := json.NewDecoder(f).Decode(&plan); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := plan.Validate(); err != nil { | |
| t.Fatal(err) | |
| } | |
| if len(plan.Checks) == 0 { | |
| t.Fatal("expected checks to not be empty") | |
| } | |
| for _, c := range plan.Checks { | |
| for _, instance := range c.Instances { | |
| k := reflect.TypeOf(instance.Address.InstanceKey).Kind() | |
| switch k { | |
| case reflect.Int, reflect.Float32, reflect.Float64, reflect.String: | |
| t.Log("instance key is a valid type") | |
| default: | |
| t.Fatalf("unexpected type %s, expected string or int", k.String()) | |
| } | |
| } | |
| } | |
| } | |
| func TestPlan_movedBlock(t *testing.T) { | |
| f, err := os.Open("testdata/moved_block/plan.json") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer f.Close() | |
| var plan *Plan | |
| if err := json.NewDecoder(f).Decode(&plan); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := plan.Validate(); err != nil { | |
| t.Fatal(err) | |
| } | |
| if plan.ResourceChanges[0].PreviousAddress != "random_id.test" { | |
| t.Fatalf("unexpected previous address %s, expected is random_id.test", plan.ResourceChanges[0].PreviousAddress) | |
| } | |
| } | |
| func TestPlan_UnmarshalJSON(t *testing.T) { | |
| t.Parallel() | |
| b, err := os.ReadFile("testdata/numerics/plan.json") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| testCases := map[string]struct { | |
| useJSONNumber bool | |
| expected any | |
| }{ | |
| "float64": { | |
| expected: 1.23, | |
| }, | |
| "json-number": { | |
| useJSONNumber: true, | |
| expected: json.Number("1.23"), | |
| }, | |
| } | |
| for name, testCase := range testCases { | |
| name, testCase := name, testCase | |
| t.Run(name, func(t *testing.T) { | |
| t.Parallel() | |
| plan := &Plan{} | |
| plan.UseJSONNumber(testCase.useJSONNumber) | |
| err = plan.UnmarshalJSON(b) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| after, ok := plan.ResourceChanges[0].Change.After.(map[string]any) | |
| if !ok { | |
| t.Fatal("plan.ResourceChanges[0].Change.After cannot be asserted as map[string]any") | |
| } | |
| attr, ok := after["configurable_attribute"] | |
| if !ok { | |
| t.Fatal("configurable attribute not found") | |
| } | |
| if diff := cmp.Diff(attr, testCase.expected); diff != "" { | |
| t.Errorf("unexpected difference: %s", diff) | |
| } | |
| }) | |
| } | |
| } |