diff --git a/internal/states/remote/state_test.go b/internal/states/remote/state_test.go index 1d348549db..85fba59392 100644 --- a/internal/states/remote/state_test.go +++ b/internal/states/remote/state_test.go @@ -49,19 +49,23 @@ type testCase struct { name string // A function to mutate state and return a cleanup function mutationFunc func(*State) (*states.State, func()) - // The expected request to have taken place - expectedRequest mockClientRequest + // The expected requests to have taken place + expectedRequests []mockClientRequest // Mark this case as not having a request noRequest bool + // The expected current state + currentState []byte } // isRequested ensures a test that is specified as not having // a request doesn't have one by checking if a method exists // on the expectedRequest. func (tc testCase) isRequested(t *testing.T) bool { - hasMethod := tc.expectedRequest.Method != "" - if tc.noRequest && hasMethod { - t.Fatalf("expected no content for %q but got: %v", tc.name, tc.expectedRequest) + for _, expectedMethod := range tc.expectedRequests { + hasMethod := expectedMethod.Method != "" + if tc.noRequest && hasMethod { + t.Fatalf("expected no content for %q but got: %v", tc.name, expectedMethod) + } } return !tc.noRequest } @@ -74,17 +78,20 @@ func TestStatePersist(t *testing.T) { mutationFunc: func(mgr *State) (*states.State, func()) { return mgr.State(), func() {} }, - expectedRequest: mockClientRequest{ - Method: "Get", - Content: map[string]interface{}{ - "version": 4.0, // encoding/json decodes this as float64 by default - "lineage": "mock-lineage", - "serial": 1.0, // encoding/json decodes this as float64 by default - "terraform_version": "0.0.0", - "outputs": map[string]interface{}{}, - "resources": []interface{}{}, + expectedRequests: []mockClientRequest{ + { + Method: "Get", + Content: map[string]interface{}{ + "version": 4.0, // encoding/json decodes this as float64 by default + "lineage": "mock-lineage", + "serial": 1.0, // encoding/json decodes this as float64 by default + "terraform_version": "0.0.0", + "outputs": map[string]interface{}{}, + "resources": []interface{}{}, + }, }, }, + currentState: nil, }, { name: "change lineage", @@ -95,18 +102,21 @@ func TestStatePersist(t *testing.T) { mgr.lineage = originalLineage } }, - expectedRequest: mockClientRequest{ - Method: "Put", - Content: map[string]interface{}{ - "version": 4.0, // encoding/json decodes this as float64 by default - "lineage": "some-new-lineage", - "serial": 2.0, // encoding/json decodes this as float64 by default - "terraform_version": version.Version, - "outputs": map[string]interface{}{}, - "resources": []interface{}{}, - "check_results": nil, + expectedRequests: []mockClientRequest{ + { + Method: "Put", + Content: map[string]interface{}{ + "version": 4.0, // encoding/json decodes this as float64 by default + "lineage": "some-new-lineage", + "serial": 2.0, // encoding/json decodes this as float64 by default + "terraform_version": version.Version, + "outputs": map[string]interface{}{}, + "resources": []interface{}{}, + "check_results": nil, + }, }, }, + currentState: nil, }, { name: "change serial", @@ -117,18 +127,21 @@ func TestStatePersist(t *testing.T) { mgr.serial = originalSerial } }, - expectedRequest: mockClientRequest{ - Method: "Put", - Content: map[string]interface{}{ - "version": 4.0, // encoding/json decodes this as float64 by default - "lineage": "mock-lineage", - "serial": 4.0, // encoding/json decodes this as float64 by default - "terraform_version": version.Version, - "outputs": map[string]interface{}{}, - "resources": []interface{}{}, - "check_results": nil, + expectedRequests: []mockClientRequest{ + { + Method: "Put", + Content: map[string]interface{}{ + "version": 4.0, // encoding/json decodes this as float64 by default + "lineage": "mock-lineage", + "serial": 4.0, // encoding/json decodes this as float64 by default + "terraform_version": version.Version, + "outputs": map[string]interface{}{}, + "resources": []interface{}{}, + "check_results": nil, + }, }, }, + currentState: nil, }, { name: "add output to state", @@ -137,23 +150,26 @@ func TestStatePersist(t *testing.T) { s.RootModule().SetOutputValue("foo", cty.StringVal("bar"), false) return s, func() {} }, - expectedRequest: mockClientRequest{ - Method: "Put", - Content: map[string]interface{}{ - "version": 4.0, // encoding/json decodes this as float64 by default - "lineage": "mock-lineage", - "serial": 3.0, // encoding/json decodes this as float64 by default - "terraform_version": version.Version, - "outputs": map[string]interface{}{ - "foo": map[string]interface{}{ - "type": "string", - "value": "bar", + expectedRequests: []mockClientRequest{ + { + Method: "Put", + Content: map[string]interface{}{ + "version": 4.0, // encoding/json decodes this as float64 by default + "lineage": "mock-lineage", + "serial": 3.0, // encoding/json decodes this as float64 by default + "terraform_version": version.Version, + "outputs": map[string]interface{}{ + "foo": map[string]interface{}{ + "type": "string", + "value": "bar", + }, }, + "resources": []interface{}{}, + "check_results": nil, }, - "resources": []interface{}{}, - "check_results": nil, }, }, + currentState: nil, }, { name: "mutate state bar -> baz", @@ -162,23 +178,26 @@ func TestStatePersist(t *testing.T) { s.RootModule().SetOutputValue("foo", cty.StringVal("baz"), false) return s, func() {} }, - expectedRequest: mockClientRequest{ - Method: "Put", - Content: map[string]interface{}{ - "version": 4.0, // encoding/json decodes this as float64 by default - "lineage": "mock-lineage", - "serial": 4.0, // encoding/json decodes this as float64 by default - "terraform_version": version.Version, - "outputs": map[string]interface{}{ - "foo": map[string]interface{}{ - "type": "string", - "value": "baz", + expectedRequests: []mockClientRequest{ + { + Method: "Put", + Content: map[string]interface{}{ + "version": 4.0, // encoding/json decodes this as float64 by default + "lineage": "mock-lineage", + "serial": 4.0, // encoding/json decodes this as float64 by default + "terraform_version": version.Version, + "outputs": map[string]interface{}{ + "foo": map[string]interface{}{ + "type": "string", + "value": "baz", + }, }, + "resources": []interface{}{}, + "check_results": nil, }, - "resources": []interface{}{}, - "check_results": nil, }, }, + currentState: nil, }, { name: "nothing changed", @@ -194,21 +213,77 @@ func TestStatePersist(t *testing.T) { mgr.serial = 2 return mgr.State(), func() {} }, - expectedRequest: mockClientRequest{ - Method: "Put", - Content: map[string]interface{}{ - "version": 4.0, // encoding/json decodes this as float64 by default - "lineage": "mock-lineage", - "serial": 3.0, // encoding/json decodes this as float64 by default - "terraform_version": version.Version, - "outputs": map[string]interface{}{ - "foo": map[string]interface{}{ - "type": "string", - "value": "baz", + expectedRequests: []mockClientRequest{ + { + Method: "Put", + Content: map[string]interface{}{ + "version": 4.0, // encoding/json decodes this as float64 by default + "lineage": "mock-lineage", + "serial": 3.0, // encoding/json decodes this as float64 by default + "terraform_version": version.Version, + "outputs": map[string]interface{}{ + "foo": map[string]interface{}{ + "type": "string", + "value": "baz", + }, }, + "resources": []interface{}{}, + "check_results": nil, + }, + }, + }, + currentState: nil, + }, + { + name: "first state persistence", + mutationFunc: func(mgr *State) (*states.State, func()) { + mgr.readState = nil + return mgr.State(), func() {} + }, + currentState: []byte(` + { + "version": 4, + "lineage": "", + "serial": 0, + "terraform_version":"0.0.0", + "outputs": {}, + "resources": [] + } + `), + expectedRequests: []mockClientRequest{ + { + Method: "Get", + Content: map[string]interface{}{ + "version": 4.0, // encoding/json decodes this as float64 by default + "lineage": "mock-lineage", + "serial": 3.0, // encoding/json decodes this as float64 by default + "terraform_version": version.Version, + "outputs": map[string]interface{}{ + "foo": map[string]interface{}{ + "type": "string", + "value": "baz", + }, + }, + "resources": []interface{}{}, + "check_results": nil, + }, + }, + { + Method: "Put", + Content: map[string]interface{}{ + "version": 4.0, // encoding/json decodes this as float64 by default + "lineage": "mock-lineage", + "serial": 3.0, // encoding/json decodes this as float64 by default + "terraform_version": version.Version, + "outputs": map[string]interface{}{ + "foo": map[string]interface{}{ + "type": "string", + "value": "baz", + }, + }, + "resources": []interface{}{}, + "check_results": nil, }, - "resources": []interface{}{}, - "check_results": nil, }, }, }, @@ -220,15 +295,15 @@ func TestStatePersist(t *testing.T) { mgr := &State{ Client: &mockClient{ current: []byte(` - { - "version": 4, - "lineage": "mock-lineage", - "serial": 1, - "terraform_version":"0.0.0", - "outputs": {}, - "resources": [] - } - `), + { + "version": 4, + "lineage": "mock-lineage", + "serial": 1, + "terraform_version":"0.0.0", + "outputs": {}, + "resources": [] + } + `), }, } @@ -252,6 +327,13 @@ func TestStatePersist(t *testing.T) { // Run tests in order. for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + if tc.currentState != nil { + originalState := mockClient.current + defer func(){ + mockClient.current = originalState + }() + mockClient.current = tc.currentState + } s, cleanup := tc.mutationFunc(mgr) if err := mgr.WriteState(s); err != nil { @@ -267,10 +349,12 @@ func TestStatePersist(t *testing.T) { if logIdx >= len(mockClient.log) { t.Fatalf("request lock and index are out of sync on %q: idx=%d len=%d", tc.name, logIdx, len(mockClient.log)) } - loggedRequest := mockClient.log[logIdx] - logIdx++ - if diff := cmp.Diff(tc.expectedRequest, loggedRequest); len(diff) > 0 { - t.Fatalf("incorrect client requests for %q:\n%s", tc.name, diff) + for expectedRequestIdx := 0; expectedRequestIdx < len(tc.expectedRequests); expectedRequestIdx++ { + loggedRequest := mockClient.log[logIdx] + logIdx++ + if diff := cmp.Diff(tc.expectedRequests[expectedRequestIdx], loggedRequest); len(diff) > 0 { + t.Fatalf("incorrect client requests for %q:\n%s", tc.name, diff) + } } } cleanup() @@ -278,7 +362,7 @@ func TestStatePersist(t *testing.T) { } logCnt := len(mockClient.log) if logIdx != logCnt { - log.Fatalf("not all requests were read. Expected logIdx to be %d but got %d", logCnt, logIdx) + t.Fatalf("not all requests were read. Expected logIdx to be %d but got %d", logCnt, logIdx) } }