Fix flapping JSON output test
This test would previously fail randomly due to the use of multiple resource instances. Instance keys are iterated over as a map for presentation, which has intentionally inconsistent ordering. To fix this, I changed the test to use different resource addresses for the three drift cases. I also extracted them to a separate test, and tweaked the test helper functions to reduce the number of fatal exit points, to make failed test debugging easier.
This commit is contained in:
parent
3e5bfa7364
commit
c51112a30c
|
@ -303,12 +303,16 @@ func testJSONViewOutputEqualsFull(t *testing.T, output string, want []map[string
|
||||||
gotLines := strings.Split(output, "\n")
|
gotLines := strings.Split(output, "\n")
|
||||||
|
|
||||||
if len(gotLines) != len(want) {
|
if len(gotLines) != len(want) {
|
||||||
t.Fatalf("unexpected number of messages. got %d, want %d", len(gotLines), len(want))
|
t.Errorf("unexpected number of messages. got %d, want %d", len(gotLines), len(want))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal each line and compare to the expected value
|
// Unmarshal each line and compare to the expected value
|
||||||
for i := range gotLines {
|
for i := range gotLines {
|
||||||
var gotStruct map[string]interface{}
|
var gotStruct map[string]interface{}
|
||||||
|
if i >= len(want) {
|
||||||
|
t.Error("reached end of want messages too soon")
|
||||||
|
break
|
||||||
|
}
|
||||||
wantStruct := want[i]
|
wantStruct := want[i]
|
||||||
|
|
||||||
if err := json.Unmarshal([]byte(gotLines[i]), &gotStruct); err != nil {
|
if err := json.Unmarshal([]byte(gotLines[i]), &gotStruct); err != nil {
|
||||||
|
@ -323,12 +327,12 @@ func testJSONViewOutputEqualsFull(t *testing.T, output string, want []map[string
|
||||||
|
|
||||||
// Verify the timestamp format
|
// Verify the timestamp format
|
||||||
if _, err := time.Parse("2006-01-02T15:04:05.000000Z07:00", timestamp.(string)); err != nil {
|
if _, err := time.Parse("2006-01-02T15:04:05.000000Z07:00", timestamp.(string)); err != nil {
|
||||||
t.Fatalf("error parsing timestamp: %s", err)
|
t.Errorf("error parsing timestamp on line %d: %s", i, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cmp.Equal(wantStruct, gotStruct) {
|
if !cmp.Equal(wantStruct, gotStruct) {
|
||||||
t.Fatalf("unexpected output on line %d:\n%s", i, cmp.Diff(wantStruct, gotStruct))
|
t.Errorf("unexpected output on line %d:\n%s", i, cmp.Diff(wantStruct, gotStruct))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -517,103 +517,10 @@ func TestOperationJSON_plan(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PrevRunState: states.BuildState(func(state *states.SyncState) {
|
|
||||||
// Update
|
|
||||||
state.SetResourceInstanceCurrent(
|
|
||||||
boop.Instance(addrs.IntKey(0)).Absolute(root),
|
|
||||||
&states.ResourceInstanceObjectSrc{
|
|
||||||
Status: states.ObjectReady,
|
|
||||||
AttrsJSON: []byte(`{"foo":"bar"}`),
|
|
||||||
},
|
|
||||||
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
|
||||||
)
|
|
||||||
// Delete
|
|
||||||
state.SetResourceInstanceCurrent(
|
|
||||||
boop.Instance(addrs.IntKey(1)).Absolute(root),
|
|
||||||
&states.ResourceInstanceObjectSrc{
|
|
||||||
Status: states.ObjectReady,
|
|
||||||
AttrsJSON: []byte(`{"foo":"boop"}`),
|
|
||||||
},
|
|
||||||
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
|
||||||
)
|
|
||||||
// No-op
|
|
||||||
state.SetResourceInstanceCurrent(
|
|
||||||
beep.Instance(addrs.NoKey).Absolute(root),
|
|
||||||
&states.ResourceInstanceObjectSrc{
|
|
||||||
Status: states.ObjectReady,
|
|
||||||
AttrsJSON: []byte(`{"foo":"boop"}`),
|
|
||||||
},
|
|
||||||
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
PriorState: states.BuildState(func(state *states.SyncState) {
|
|
||||||
// Update
|
|
||||||
state.SetResourceInstanceCurrent(
|
|
||||||
boop.Instance(addrs.IntKey(0)).Absolute(root),
|
|
||||||
&states.ResourceInstanceObjectSrc{
|
|
||||||
Status: states.ObjectReady,
|
|
||||||
AttrsJSON: []byte(`{"foo":"baz"}`),
|
|
||||||
},
|
|
||||||
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
|
||||||
)
|
|
||||||
// Delete
|
|
||||||
state.SetResourceInstanceCurrent(
|
|
||||||
boop.Instance(addrs.IntKey(1)).Absolute(root),
|
|
||||||
nil,
|
|
||||||
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
|
||||||
)
|
|
||||||
// No-op
|
|
||||||
state.SetResourceInstanceCurrent(
|
|
||||||
beep.Instance(addrs.NoKey).Absolute(root),
|
|
||||||
&states.ResourceInstanceObjectSrc{
|
|
||||||
Status: states.ObjectReady,
|
|
||||||
AttrsJSON: []byte(`{"foo":"boop"}`),
|
|
||||||
},
|
|
||||||
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
v.Plan(plan, testSchemas())
|
v.Plan(plan, testSchemas())
|
||||||
|
|
||||||
want := []map[string]interface{}{
|
want := []map[string]interface{}{
|
||||||
// Drift detected: update
|
|
||||||
{
|
|
||||||
"@level": "info",
|
|
||||||
"@message": "test_resource.boop[0]: Drift detected (update)",
|
|
||||||
"@module": "terraform.ui",
|
|
||||||
"type": "resource_drift",
|
|
||||||
"change": map[string]interface{}{
|
|
||||||
"action": "update",
|
|
||||||
"resource": map[string]interface{}{
|
|
||||||
"addr": "test_resource.boop[0]",
|
|
||||||
"implied_provider": "test",
|
|
||||||
"module": "",
|
|
||||||
"resource": "test_resource.boop[0]",
|
|
||||||
"resource_key": float64(0),
|
|
||||||
"resource_name": "boop",
|
|
||||||
"resource_type": "test_resource",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Drift detected: delete
|
|
||||||
{
|
|
||||||
"@level": "info",
|
|
||||||
"@message": "test_resource.boop[1]: Drift detected (delete)",
|
|
||||||
"@module": "terraform.ui",
|
|
||||||
"type": "resource_drift",
|
|
||||||
"change": map[string]interface{}{
|
|
||||||
"action": "delete",
|
|
||||||
"resource": map[string]interface{}{
|
|
||||||
"addr": "test_resource.boop[1]",
|
|
||||||
"implied_provider": "test",
|
|
||||||
"module": "",
|
|
||||||
"resource": "test_resource.boop[1]",
|
|
||||||
"resource_key": float64(1),
|
|
||||||
"resource_name": "boop",
|
|
||||||
"resource_type": "test_resource",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Create-then-delete should result in replace
|
// Create-then-delete should result in replace
|
||||||
{
|
{
|
||||||
"@level": "info",
|
"@level": "info",
|
||||||
|
@ -728,6 +635,134 @@ func TestOperationJSON_plan(t *testing.T) {
|
||||||
testJSONViewOutputEquals(t, done(t).Stdout(), want)
|
testJSONViewOutputEquals(t, done(t).Stdout(), want)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOperationJSON_planDrift(t *testing.T) {
|
||||||
|
streams, done := terminal.StreamsForTesting(t)
|
||||||
|
v := &OperationJSON{view: NewJSONView(NewView(streams))}
|
||||||
|
|
||||||
|
root := addrs.RootModuleInstance
|
||||||
|
boop := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_resource", Name: "boop"}
|
||||||
|
beep := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_resource", Name: "beep"}
|
||||||
|
derp := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_resource", Name: "derp"}
|
||||||
|
|
||||||
|
plan := &plans.Plan{
|
||||||
|
Changes: &plans.Changes{
|
||||||
|
Resources: []*plans.ResourceInstanceChangeSrc{},
|
||||||
|
},
|
||||||
|
PrevRunState: states.BuildState(func(state *states.SyncState) {
|
||||||
|
// Update
|
||||||
|
state.SetResourceInstanceCurrent(
|
||||||
|
boop.Instance(addrs.NoKey).Absolute(root),
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{"foo":"bar"}`),
|
||||||
|
},
|
||||||
|
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
||||||
|
)
|
||||||
|
// Delete
|
||||||
|
state.SetResourceInstanceCurrent(
|
||||||
|
beep.Instance(addrs.NoKey).Absolute(root),
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{"foo":"boop"}`),
|
||||||
|
},
|
||||||
|
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
||||||
|
)
|
||||||
|
// No-op
|
||||||
|
state.SetResourceInstanceCurrent(
|
||||||
|
derp.Instance(addrs.NoKey).Absolute(root),
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{"foo":"boop"}`),
|
||||||
|
},
|
||||||
|
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
PriorState: states.BuildState(func(state *states.SyncState) {
|
||||||
|
// Update
|
||||||
|
state.SetResourceInstanceCurrent(
|
||||||
|
boop.Instance(addrs.NoKey).Absolute(root),
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{"foo":"baz"}`),
|
||||||
|
},
|
||||||
|
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
||||||
|
)
|
||||||
|
// Delete
|
||||||
|
state.SetResourceInstanceCurrent(
|
||||||
|
beep.Instance(addrs.NoKey).Absolute(root),
|
||||||
|
nil,
|
||||||
|
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
||||||
|
)
|
||||||
|
// No-op
|
||||||
|
state.SetResourceInstanceCurrent(
|
||||||
|
derp.Instance(addrs.NoKey).Absolute(root),
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{"foo":"boop"}`),
|
||||||
|
},
|
||||||
|
root.ProviderConfigDefault(addrs.NewDefaultProvider("test")),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
v.Plan(plan, testSchemas())
|
||||||
|
|
||||||
|
want := []map[string]interface{}{
|
||||||
|
// Drift detected: update
|
||||||
|
{
|
||||||
|
"@level": "info",
|
||||||
|
"@message": "test_resource.boop: Drift detected (update)",
|
||||||
|
"@module": "terraform.ui",
|
||||||
|
"type": "resource_drift",
|
||||||
|
"change": map[string]interface{}{
|
||||||
|
"action": "update",
|
||||||
|
"resource": map[string]interface{}{
|
||||||
|
"addr": "test_resource.boop",
|
||||||
|
"implied_provider": "test",
|
||||||
|
"module": "",
|
||||||
|
"resource": "test_resource.boop",
|
||||||
|
"resource_key": nil,
|
||||||
|
"resource_name": "boop",
|
||||||
|
"resource_type": "test_resource",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Drift detected: delete
|
||||||
|
{
|
||||||
|
"@level": "info",
|
||||||
|
"@message": "test_resource.beep: Drift detected (delete)",
|
||||||
|
"@module": "terraform.ui",
|
||||||
|
"type": "resource_drift",
|
||||||
|
"change": map[string]interface{}{
|
||||||
|
"action": "delete",
|
||||||
|
"resource": map[string]interface{}{
|
||||||
|
"addr": "test_resource.beep",
|
||||||
|
"implied_provider": "test",
|
||||||
|
"module": "",
|
||||||
|
"resource": "test_resource.beep",
|
||||||
|
"resource_key": nil,
|
||||||
|
"resource_name": "beep",
|
||||||
|
"resource_type": "test_resource",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// No changes
|
||||||
|
{
|
||||||
|
"@level": "info",
|
||||||
|
"@message": "Plan: 0 to add, 0 to change, 0 to destroy.",
|
||||||
|
"@module": "terraform.ui",
|
||||||
|
"type": "change_summary",
|
||||||
|
"changes": map[string]interface{}{
|
||||||
|
"operation": "plan",
|
||||||
|
"add": float64(0),
|
||||||
|
"change": float64(0),
|
||||||
|
"remove": float64(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testJSONViewOutputEquals(t, done(t).Stdout(), want)
|
||||||
|
}
|
||||||
|
|
||||||
func TestOperationJSON_plannedChange(t *testing.T) {
|
func TestOperationJSON_plannedChange(t *testing.T) {
|
||||||
streams, done := terminal.StreamsForTesting(t)
|
streams, done := terminal.StreamsForTesting(t)
|
||||||
v := &OperationJSON{view: NewJSONView(NewView(streams))}
|
v := &OperationJSON{view: NewJSONView(NewView(streams))}
|
||||||
|
|
Loading…
Reference in New Issue