command/show: tests for -json output (#19980)

* command/show: added test scaffold for json output

More test cases will be added once the basic shape of the tests is
validated.

- command/json* packages now sort resources by address, matching
behavior elsewhere
- using cmp in tests instead of reflect.DeepEqual for the diffs
- updating expected output in tests to match sorting
This commit is contained in:
Kristin Laemmert 2019-01-11 15:13:55 -08:00 committed by GitHub
parent e168d81894
commit 5df9cd0f52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 514 additions and 3 deletions

View File

@ -3,6 +3,7 @@ package jsonconfig
import (
"encoding/json"
"fmt"
"sort"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
@ -244,5 +245,8 @@ func marshalResources(resources map[string]*configs.Resource, schemas *terraform
rs = append(rs, r)
}
sort.Slice(rs, func(i, j int) bool {
return rs[i].Address < rs[j].Address
})
return rs, nil
}

View File

@ -3,6 +3,7 @@ package jsonplan
import (
"encoding/json"
"fmt"
"sort"
"github.com/zclconf/go-cty/cty"
@ -210,6 +211,10 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
}
sort.Slice(p.ResourceChanges, func(i, j int) bool {
return p.ResourceChanges[i].Address < p.ResourceChanges[j].Address
})
return nil
}

View File

@ -38,9 +38,8 @@ func marshalAttributeValues(value cty.Value, schema *configschema.Block) attribu
return ret
}
// marshalPlannedOutputs takes a list of changes and returns two output maps,
// the former with output values and the latter with true/false in place of
// values indicating whether the values are known at plan time.
// marshalPlannedOutputs takes a list of changes and returns a map of output
// values
func marshalPlannedOutputs(changes *plans.Changes) (map[string]output, error) {
if changes.Outputs == nil {
// No changes - we're done here!

View File

@ -3,6 +3,7 @@ package jsonstate
import (
"encoding/json"
"fmt"
"sort"
"github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"
@ -273,5 +274,9 @@ func marshalResources(resources map[string]*states.Resource, schemas *terraform.
}
sort.Slice(ret, func(i, j int) bool {
return ret[i].Address < ret[j].Address
})
return ret, nil
}

View File

@ -1,9 +1,13 @@
package command
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/plans"
@ -150,6 +154,92 @@ func TestShow_state(t *testing.T) {
}
}
func TestPlan_json_output(t *testing.T) {
fixtureDir := "test-fixtures/show-json"
testDirs, err := ioutil.ReadDir(fixtureDir)
if err != nil {
t.Fatal(err)
}
for _, entry := range testDirs {
if !entry.IsDir() {
continue
}
t.Run(entry.Name(), func(t *testing.T) {
inputDir := filepath.Join(fixtureDir, entry.Name())
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)
}
if err := os.Chdir(inputDir); err != nil {
t.Fatalf("err: %s", err)
}
defer os.Chdir(cwd)
p := showFixtureProvider()
ui := new(cli.MockUi)
pc := &PlanCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}
args := []string{
"-out=terraform.plan",
}
if code := pc.Run(args); code != 0 {
t.Fatalf("wrong exit status %d; want 0\nstderr: %s", code, ui.ErrorWriter.String())
}
// flush the plan output from the mock ui
ui.OutputWriter.Reset()
sc := &ShowCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}
args = []string{
"-json",
"terraform.plan",
}
defer os.Remove("terraform.plan")
if code := sc.Run(args); code != 0 {
t.Fatalf("wrong exit status %d; want 0\nstderr: %s", code, ui.ErrorWriter.String())
}
// compare ui output to wanted output
var got, want plan
gotString := ui.OutputWriter.String()
json.Unmarshal([]byte(gotString), &got)
wantFile, err := os.Open("output.json")
if err != nil {
t.Fatalf("err: %s", err)
}
defer wantFile.Close()
byteValue, err := ioutil.ReadAll(wantFile)
if err != nil {
t.Fatalf("err: %s", err)
}
json.Unmarshal([]byte(byteValue), &want)
if !cmp.Equal(got, want) {
t.Fatalf("wrong result:\n %v\n", cmp.Diff(got, want))
}
})
}
}
// showFixtureSchema returns a schema suitable for processing the configuration
// in test-fixtures/show. This schema should be assigned to a mock provider
// named "test".
@ -225,3 +315,14 @@ func showFixturePlanFile(t *testing.T) string {
plan,
)
}
// this simplified plan struct allows us to preserve field order when marshaling
// the command output.
type plan struct {
FormatVersion string `json:"format_version,omitempty"`
PlannedValues map[string]interface{} `json:"planned_values,omitempty"`
ResourceChanges []interface{} `json:"resource_changes,omitempty"`
OutputChanges map[string]interface{} `json:"output_changes,omitempty"`
PriorState string `json:"prior_state,omitempty"`
Config string `json:"configuration,omitempty"`
}

View File

@ -0,0 +1,10 @@
variable "test_var" {
default = "bar"
}
resource "test_instance" "test" {
ami = var.test_var
}
output "test" {
value = var.test_var
}

View File

@ -0,0 +1,77 @@
{
"format_version": "0.1",
"planned_values": {
"outputs": {
"test": {
"sensitive": false,
"value": "bar"
}
},
"root_module": {
"resources": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_name": "test",
"schema_version": 0
}
]
}
},
"resource_changes": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"deposed": true,
"change": {
"actions": [
"Create"
],
"before": null,
"after_unknown": {
"ami": false,
"id": true
}
}
}
],
"output_changes": {
"test": {
"actions": [
"Create"
],
"before": null,
"after": "bar",
"after_unknown": false
}
},
"configuration": {
"root_module": {
"outputs": {
"test": {
"expression": {
"references": [
"var.test_var"
]
}
}
},
"resources": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_config_key": "provider.test",
"schema_version": 0,
"count_expression": {},
"for_each_expression": {}
}
]
}
}
}

View File

@ -0,0 +1,10 @@
variable "test_var" {
default = "bar"
}
resource "test_instance" "test" {
ami = var.test_var
}
output "test" {
value = var.test_var
}

View File

@ -0,0 +1,137 @@
{
"format_version": "0.1",
"planned_values": {
"outputs": {
"test": {
"sensitive": false,
"value": "bar"
}
},
"root_module": {
"resources": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_name": "test",
"schema_version": 0,
"values": {
"ami": {},
"id": {}
}
}
]
}
},
"resource_changes": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"deposed": true,
"change": {
"actions": [
"Update"
],
"before": {
"ami": "foo",
"id": null
},
"after": {
"ami": "bar",
"id": null
},
"after_unknown": {
"ami": false,
"id": false
}
}
},
{
"address": "test_instance.test-delete",
"mode": "managed",
"type": "test_instance",
"name": "test-delete",
"deposed": true,
"change": {
"actions": [
"Delete"
],
"before": {
"ami": "foo",
"id": null
},
"after": null,
"after_unknown": false
}
}
],
"output_changes": {
"test": {
"actions": [
"Create"
],
"before": null,
"after": "bar",
"after_unknown": false
}
},
"prior_state": {
"format_version": "0.1",
"values": {
"root_module": {
"resources": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_name": "test",
"values": {
"ami": {},
"id": {}
}
},
{
"address": "test_instance.test-delete",
"mode": "managed",
"type": "test_instance",
"name": "test-delete",
"provider_name": "test",
"values": {
"ami": {},
"id": {}
}
}
]
}
}
},
"configuration": {
"root_module": {
"outputs": {
"test": {
"expression": {
"references": [
"var.test_var"
]
}
}
},
"resources": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_config_key": "provider.test",
"schema_version": 0,
"count_expression": {},
"for_each_expression": {}
}
]
}
}
}

View File

@ -0,0 +1,37 @@
{
"version": 4,
"terraform_version": "0.12.0",
"serial": 7,
"lineage": "configuredUnchanged",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider": "provider.test",
"instances": [
{
"schema_version": 0,
"attributes": {
"ami": "foo"
}
}
]
},
{
"mode": "managed",
"type": "test_instance",
"name": "test-delete",
"provider": "provider.test",
"instances": [
{
"schema_version": 0,
"attributes": {
"ami": "foo"
}
}
]
}
]
}

View File

@ -0,0 +1,10 @@
variable "test_var" {
default = "bar"
}
resource "test_instance" "test" {
ami = var.test_var
}
output "test" {
value = var.test_var
}

View File

@ -0,0 +1,93 @@
{
"format_version": "0.1",
"planned_values": {
"outputs": {
"test": {
"sensitive": false,
"value": "bar"
}
},
"root_module": {}
},
"resource_changes": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"deposed": true,
"change": {
"actions": [
"NoOp"
],
"before": {
"ami": "bar",
"id": null
},
"after": {
"ami": "bar",
"id": null
},
"after_unknown": {
"ami": false,
"id": false
}
}
}
],
"output_changes": {
"test": {
"actions": [
"Create"
],
"before": null,
"after": "bar",
"after_unknown": false
}
},
"prior_state": {
"format_version": "0.1",
"values": {
"root_module": {
"resources": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_name": "test",
"values": {
"ami": {},
"id": {}
}
}
]
}
}
},
"configuration": {
"root_module": {
"outputs": {
"test": {
"expression": {
"references": [
"var.test_var"
]
}
}
},
"resources": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_config_key": "provider.test",
"schema_version": 0,
"count_expression": {},
"for_each_expression": {}
}
]
}
}
}

View File

@ -0,0 +1,23 @@
{
"version": 4,
"terraform_version": "0.12.0",
"serial": 7,
"lineage": "configuredUnchanged",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider": "provider.test",
"instances": [
{
"schema_version": 0,
"attributes": {
"ami": "bar"
}
}
]
}
]
}