diff --git a/v4/core/authenticator.go b/v4/core/authenticator.go index 8292490..74b8548 100644 --- a/v4/core/authenticator.go +++ b/v4/core/authenticator.go @@ -24,3 +24,13 @@ type Authenticator interface { Authenticate(*http.Request) error Validate() error } + +// AuthenticationError describes the error returned when authentication fails +type AuthenticationError struct { + Response *DetailedResponse + err error +} + +func (e *AuthenticationError) Error() string { + return e.err.Error() +} diff --git a/v4/core/base_service.go b/v4/core/base_service.go index f753b5b..78ae1b3 100644 --- a/v4/core/base_service.go +++ b/v4/core/base_service.go @@ -219,9 +219,13 @@ func (service *BaseService) Request(req *http.Request, result interface{}) (deta return } - err = service.Options.Authenticator.Authenticate(req) - if err != nil { - err = fmt.Errorf(ERRORMSG_AUTHENTICATE_ERROR, err.Error()) + authError := service.Options.Authenticator.Authenticate(req) + if authError != nil { + err = fmt.Errorf(ERRORMSG_AUTHENTICATE_ERROR, authError.Error()) + castErr, ok := authError.(*AuthenticationError) + if ok { + detailedResponse = castErr.Response + } return } diff --git a/v4/core/base_service_test.go b/v4/core/base_service_test.go index 5635dcc..3accc21 100644 --- a/v4/core/base_service_test.go +++ b/v4/core/base_service_test.go @@ -144,7 +144,7 @@ func TestGoodResponseJSONExtraFields(t *testing.T) { Authenticator: &NoAuthAuthenticator{}, } service, _ := NewBaseService(options) - + var foo *Foo detailedResponse, _ := service.Request(req, &foo) result, ok := detailedResponse.Result.(*Foo) @@ -254,7 +254,7 @@ func TestGoodResponseString(t *testing.T) { assert.Nil(t, err) assert.NotNil(t, service.Options.Authenticator) assert.Equal(t, AUTHTYPE_NOAUTH, service.Options.Authenticator.AuthenticationType()) - + var responseString *string detailedResponse, err := service.Request(req, &responseString) assert.Nil(t, err) @@ -264,7 +264,7 @@ func TestGoodResponseString(t *testing.T) { assert.NotNil(t, detailedResponse.Result) assert.NotNil(t, responseString) assert.Equal(t, expectedResponse, *responseString) - + resultField, ok := detailedResponse.Result.(*string) assert.Equal(t, true, ok) assert.NotNil(t, resultField) @@ -330,7 +330,7 @@ func TestGoodResponseJSONDeserFailure(t *testing.T) { Authenticator: &NoAuthAuthenticator{}, } service, _ := NewBaseService(options) - + var foo *Foo detailedResponse, err := service.Request(req, &foo) assert.NotNil(t, detailedResponse) @@ -437,7 +437,7 @@ func TestErrorResponseJSON(t *testing.T) { Authenticator: &NoAuthAuthenticator{}, } service, _ := NewBaseService(options) - + var foo *Foo response, err := service.Request(req, &foo) assert.NotNil(t, err) @@ -473,7 +473,7 @@ func TestErrorResponseJSONDeserError(t *testing.T) { Authenticator: &NoAuthAuthenticator{}, } service, _ := NewBaseService(options) - + var foo *Foo response, err := service.Request(req, &foo) assert.NotNil(t, err) @@ -505,7 +505,7 @@ func TestErrorResponseNotJSON(t *testing.T) { Authenticator: &NoAuthAuthenticator{}, } service, _ := NewBaseService(options) - + var foo *Foo response, err := service.Request(req, &foo) assert.NotNil(t, err) @@ -585,7 +585,7 @@ func TestRequestForDefaultUserAgent(t *testing.T) { Authenticator: authenticator, } service, _ := NewBaseService(options) - + var foo *Foo _, _ = service.Request(req, &foo) } @@ -614,7 +614,7 @@ func TestRequestForProvidedUserAgent(t *testing.T) { headers := http.Header{} headers.Add("User-Agent", "provided user agent") service.SetDefaultHeaders(headers) - + var foo *Foo _, _ = service.Request(req, &foo) } @@ -887,11 +887,55 @@ func TestIAMFailure(t *testing.T) { assert.NotNil(t, service.Options.Authenticator) var foo *Foo - _, err = service.Request(req, &foo) + detailedResponse, err := service.Request(req, &foo) assert.NotNil(t, err) + assert.NotNil(t, detailedResponse) + assert.NotNil(t, detailedResponse.GetHeaders()) + assert.NotNil(t, detailedResponse.GetRawResult()) + statusCode := detailedResponse.GetStatusCode() + assert.Equal(t, http.StatusForbidden, statusCode) assert.Contains(t, err.Error(), "Sorry you are forbidden") } +func TestIAMFailureRetryAfter(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Retry-After", "20") + w.WriteHeader(http.StatusTooManyRequests) + _, _ = w.Write([]byte("Sorry rate limit has been exceeded")) + })) + defer server.Close() + + builder := NewRequestBuilder("GET") + _, err := builder.ConstructHTTPURL(server.URL, nil, nil) + assert.Nil(t, err) + builder.AddQuery("Version", "2018-22-09") + req, _ := builder.Build() + + options := &ServiceOptions{ + URL: server.URL, + Authenticator: &IamAuthenticator{ + URL: server.URL, + ApiKey: "xxxxx", + }, + } + service, err := NewBaseService(options) + assert.Nil(t, err) + assert.NotNil(t, service) + assert.NotNil(t, service.Options.Authenticator) + + var foo *Foo + detailedResponse, err := service.Request(req, &foo) + assert.NotNil(t, err) + assert.NotNil(t, detailedResponse) + assert.NotNil(t, detailedResponse.GetRawResult()) + statusCode := detailedResponse.GetStatusCode() + headers := detailedResponse.GetHeaders() + assert.NotNil(t, headers) + assert.Equal(t, http.StatusTooManyRequests, statusCode) + assert.Contains(t, headers, "Retry-After") + assert.Contains(t, err.Error(), "Sorry rate limit has been exceeded") +} + func TestIAMWithIdSecret(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) @@ -1052,10 +1096,56 @@ func TestCP4DFail(t *testing.T) { assert.NotNil(t, service.Options.Authenticator) var foo *Foo - _, err = service.Request(req, &foo) + detailedResponse, err := service.Request(req, &foo) + assert.NotNil(t, err) + assert.NotNil(t, detailedResponse) + assert.NotNil(t, detailedResponse.GetHeaders()) + assert.NotNil(t, detailedResponse.GetRawResult()) + statusCode := detailedResponse.GetStatusCode() + assert.Equal(t, http.StatusForbidden, statusCode) assert.Contains(t, err.Error(), "Sorry you are forbidden") } +func TestCp4dFailureRetryAfter(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Retry-After", "20") + w.WriteHeader(http.StatusTooManyRequests) + _, _ = w.Write([]byte("Sorry rate limit has been exceeded")) + })) + defer server.Close() + + builder := NewRequestBuilder("GET") + _, err := builder.ConstructHTTPURL(server.URL, nil, nil) + assert.Nil(t, err) + builder.AddQuery("Version", "2018-22-09") + req, _ := builder.Build() + + options := &ServiceOptions{ + URL: server.URL, + Authenticator: &CloudPakForDataAuthenticator{ + URL: server.URL, + Username: "bogus", + Password: "bogus", + }, + } + service, err := NewBaseService(options) + assert.Nil(t, err) + assert.NotNil(t, service) + assert.NotNil(t, service.Options.Authenticator) + + var foo *Foo + detailedResponse, err := service.Request(req, &foo) + assert.NotNil(t, err) + assert.NotNil(t, detailedResponse) + assert.NotNil(t, detailedResponse.GetRawResult()) + statusCode := detailedResponse.GetStatusCode() + headers := detailedResponse.GetHeaders() + assert.NotNil(t, headers) + assert.Equal(t, http.StatusTooManyRequests, statusCode) + assert.Contains(t, headers, "Retry-After") + assert.Contains(t, err.Error(), "Sorry rate limit has been exceeded") +} + // Test for the deprecated SetURL method. func TestSetURL(t *testing.T) { service, err := NewBaseService( diff --git a/v4/core/config_utils_test.go b/v4/core/config_utils_test.go index 2c3f96d..6e4779a 100644 --- a/v4/core/config_utils_test.go +++ b/v4/core/config_utils_test.go @@ -46,8 +46,8 @@ var testEnvironment = map[string]string{ "SERVICE3_USERNAME": "my-cp4d-user", "SERVICE3_PASSWORD": "my-cp4d-password", "SERVICE3_AUTH_DISABLE_SSL": "false", - "EQUAL_SERVICE_URL": "https://my=host.com/my=service/api", - "EQUAL_SERVICE_APIKEY": "===my=iam=apikey===", + "EQUAL_SERVICE_URL": "https://my=host.com/my=service/api", + "EQUAL_SERVICE_APIKEY": "===my=iam=apikey===", } // Set the environment variables described in our map. @@ -179,7 +179,6 @@ func TestGetServicePropertiesFromEnvironment(t *testing.T) { assert.NotNil(t, props) assert.Equal(t, "https://my=host.com/my=service/api", props[PROPNAME_SVC_URL]) assert.Equal(t, "===my=iam=apikey===", props[PROPNAME_APIKEY]) - props, err = getServiceProperties("not_a_service") assert.Nil(t, err) diff --git a/v4/core/cp4d_authenticator.go b/v4/core/cp4d_authenticator.go index c3c3e4c..d3dc16c 100644 --- a/v4/core/cp4d_authenticator.go +++ b/v4/core/cp4d_authenticator.go @@ -258,11 +258,20 @@ func (authenticator *CloudPakForDataAuthenticator) requestToken() (*cp4dTokenSer } if resp.StatusCode < 200 || resp.StatusCode >= 300 { - if resp != nil { - buff := new(bytes.Buffer) - _, _ = buff.ReadFrom(resp.Body) - return nil, fmt.Errorf(buff.String()) + buff := new(bytes.Buffer) + _, _ = buff.ReadFrom(resp.Body) + // Start to populate the DetailedResponse. + detailedResponse := &DetailedResponse{ + StatusCode: resp.StatusCode, + Headers: resp.Header, + RawResult: buff.Bytes(), } + // Return a AuthenticationError in place of a generic "error" + authError := &AuthenticationError{ + Response: detailedResponse, + err: fmt.Errorf(buff.String()), + } + return nil, authError } tokenResponse := &cp4dTokenServerResponse{} diff --git a/v4/core/cp4d_authenticator_test.go b/v4/core/cp4d_authenticator_test.go index 1cf692b..43ebdf5 100644 --- a/v4/core/cp4d_authenticator_test.go +++ b/v4/core/cp4d_authenticator_test.go @@ -40,6 +40,37 @@ func TestCp4dConfigErrors(t *testing.T) { assert.NotNil(t, err) } +func TestCp4dAuthenticateFail(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusUnauthorized) + _, _ = w.Write([]byte("Sorry you are not authorized")) + })) + defer server.Close() + + authenticator, err := NewCloudPakForDataAuthenticator(server.URL, "mookie", "betts", false, nil) + assert.Nil(t, err) + assert.NotNil(t, authenticator) + assert.Equal(t, authenticator.AuthenticationType(), AUTHTYPE_CP4D) + + // Create a new Request object. + builder, err := NewRequestBuilder("GET").ConstructHTTPURL("https://localhost/placeholder/url", nil, nil) + assert.Nil(t, err) + + request, err := builder.Build() + assert.Nil(t, err) + assert.NotNil(t, request) + + err = authenticator.Authenticate(request) + // Validate the resulting error is a valid + assert.NotNil(t, err) + authErr, ok := err.(*AuthenticationError) + assert.True(t, ok) + assert.NotNil(t, authErr) + assert.EqualValues(t, authErr, err) + // The casted error should match the original error message + assert.Equal(t, err.Error(), authErr.Error()) +} + func TestCp4dGetTokenSuccess(t *testing.T) { firstCall := true server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -336,6 +367,9 @@ func TestCp4dBackgroundTokenRefreshFailure(t *testing.T) { _, err = authenticator.getToken() assert.NotNil(t, err) assert.Equal(t, "Error while trying to parse access token!", err.Error()) + // We don't expect a AuthenticateError to be returned, so casting should fail + _, ok := err.(*AuthenticationError) + assert.False(t, ok) } func TestCp4dDisableSSL(t *testing.T) { @@ -435,8 +469,23 @@ func TestGetTokenFailure(t *testing.T) { Password: "snow", } + var expectedResponse = []byte("Sorry you are forbidden") + _, err := authenticator.getToken() + assert.NotNil(t, err) assert.Equal(t, "Sorry you are forbidden", err.Error()) + // We expect an AuthenticationError to be returned, so cast the returned error + authError, ok := err.(*AuthenticationError) + assert.True(t, ok) + assert.NotNil(t, authError) + assert.NotNil(t, authError.Error()) + assert.NotNil(t, authError.Response) + rawResult := authError.Response.GetRawResult() + assert.NotNil(t, rawResult) + assert.Equal(t, expectedResponse, rawResult) + statusCode := authError.Response.GetStatusCode() + assert.Equal(t, "Sorry you are forbidden", authError.Error()) + assert.Equal(t, http.StatusForbidden, statusCode) } func TestNewCloudPakForDataAuthenticatorFromMap(t *testing.T) { @@ -486,7 +535,7 @@ func TestCp4dGetTokenTimeoutError(t *testing.T) { fmt.Fprintf(w, `{ "username":"hello", "role":"user", - "permissions":[ + "permissions":[ "administrator", "deployment_admin" ], @@ -542,8 +591,12 @@ func TestCp4dGetTokenTimeoutError(t *testing.T) { // Set the client timeout to something very low authenticator.Client.Timeout = time.Second * 2 token, err = authenticator.getToken() - assert.NotNil(t, err) assert.Equal(t, "", token) + assert.NotNil(t, err) + assert.NotNil(t, err.Error()) + // We don't expect a AuthenticateError to be returned, so casting should fail + _, ok := err.(*AuthenticationError) + assert.False(t, ok) } func TestCp4dGetTokenServerError(t *testing.T) { @@ -554,7 +607,7 @@ func TestCp4dGetTokenServerError(t *testing.T) { fmt.Fprintf(w, `{ "username":"hello", "role":"user", - "permissions":[ + "permissions":[ "administrator", "deployment_admin" ], @@ -585,9 +638,23 @@ func TestCp4dGetTokenServerError(t *testing.T) { token) assert.NotNil(t, authenticator.tokenData) + var expectedResponse = []byte("Gateway Timeout") + // Force expiration and verify that we got a server error authenticator.tokenData.Expiration = GetCurrentTime() - 3600 token, err = authenticator.getToken() assert.NotNil(t, err) + // We expect an AuthenticationError to be returned, so cast the returned error + authError, ok := err.(*AuthenticationError) + assert.True(t, ok) + assert.NotNil(t, authError) + assert.NotNil(t, authError.Response) + assert.NotNil(t, authError.Error()) + rawResult := authError.Response.GetRawResult() + statusCode := authError.Response.GetStatusCode() + assert.Equal(t, "Gateway Timeout", authError.Error()) + assert.Equal(t, expectedResponse, rawResult) + assert.NotNil(t, rawResult) + assert.Equal(t, http.StatusGatewayTimeout, statusCode) assert.Equal(t, "", token) } diff --git a/v4/core/detailed_response.go b/v4/core/detailed_response.go index fdd7490..4100204 100644 --- a/v4/core/detailed_response.go +++ b/v4/core/detailed_response.go @@ -42,7 +42,7 @@ type DetailedResponse struct { // objects. // // If the operation was successful and the response body contains a non-JSON response, - // the Result field will be an instance of io.ReadCloser that can be used by generated SDK code + // the Result field will be an instance of io.ReadCloser that can be used by generated SDK code // (or the application) to read the response data. // // If the operation was unsuccessful and the response body contains a JSON error response, diff --git a/v4/core/iam_authenticator.go b/v4/core/iam_authenticator.go index f0705b5..28ae48d 100644 --- a/v4/core/iam_authenticator.go +++ b/v4/core/iam_authenticator.go @@ -290,11 +290,20 @@ func (authenticator *IamAuthenticator) requestToken() (*iamTokenServerResponse, } if resp.StatusCode < 200 || resp.StatusCode >= 300 { - if resp != nil { - buff := new(bytes.Buffer) - _, _ = buff.ReadFrom(resp.Body) - return nil, fmt.Errorf(buff.String()) + buff := new(bytes.Buffer) + _, _ = buff.ReadFrom(resp.Body) + // Start to populate the DetailedResponse. + detailedResponse := &DetailedResponse{ + StatusCode: resp.StatusCode, + Headers: resp.Header, + RawResult: buff.Bytes(), } + // Return a AuthenticationError in place of a generic "error" + authError := &AuthenticationError{ + Response: detailedResponse, + err: fmt.Errorf(buff.String()), + } + return nil, authError } tokenResponse := &iamTokenServerResponse{} diff --git a/v4/core/iam_authenticator_test.go b/v4/core/iam_authenticator_test.go index c79900c..1700eb8 100644 --- a/v4/core/iam_authenticator_test.go +++ b/v4/core/iam_authenticator_test.go @@ -44,6 +44,37 @@ func TestIamConfigErrors(t *testing.T) { assert.NotNil(t, err) } +func TestIamAuthenticateFail(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusUnauthorized) + _, _ = w.Write([]byte("Sorry you are not authorized")) + })) + defer server.Close() + + authenticator, err := NewIamAuthenticator("bogus-apikey", server.URL, "", "", false, nil) + assert.Nil(t, err) + assert.NotNil(t, authenticator) + assert.Equal(t, authenticator.AuthenticationType(), AUTHTYPE_IAM) + + // Create a new Request object. + builder, err := NewRequestBuilder("GET").ConstructHTTPURL("https://localhost/placeholder/url", nil, nil) + assert.Nil(t, err) + + request, err := builder.Build() + assert.Nil(t, err) + assert.NotNil(t, request) + + err = authenticator.Authenticate(request) + // Validate the resulting error is a valid + assert.NotNil(t, err) + authErr, ok := err.(*AuthenticationError) + assert.True(t, ok) + assert.NotNil(t, authErr) + assert.EqualValues(t, authErr, err) + // The casted error should match the original error message + assert.Equal(t, err.Error(), authErr.Error()) +} + func TestIamGetTokenSuccess(t *testing.T) { firstCall := true server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -256,6 +287,9 @@ func TestIamBackgroundTokenRefreshFailure(t *testing.T) { _, err = authenticator.getToken() assert.NotNil(t, err) assert.Equal(t, "Error while trying to get access token", err.Error()) + // We don't expect a AuthenticateError to be returned, so casting should fail + _, ok := err.(*AuthenticationError) + assert.False(t, ok) } @@ -375,8 +409,23 @@ func TestIamGetTokenFailure(t *testing.T) { URL: server.URL, } + var expectedResponse = []byte("Sorry you are forbidden") + _, err := authenticator.getToken() + assert.NotNil(t, err) assert.Equal(t, "Sorry you are forbidden", err.Error()) + // We expect an AuthenticationError to be returned, so cast the returned error + authError, ok := err.(*AuthenticationError) + assert.True(t, ok) + assert.NotNil(t, authError) + assert.NotNil(t, authError.Error()) + assert.NotNil(t, authError.Response) + rawResult := authError.Response.GetRawResult() + assert.NotNil(t, rawResult) + assert.Equal(t, expectedResponse, rawResult) + statusCode := authError.Response.GetStatusCode() + assert.Equal(t, "Sorry you are forbidden", authError.Error()) + assert.Equal(t, http.StatusForbidden, statusCode) } func TestIamGetTokenTimeoutError(t *testing.T) { @@ -424,8 +473,12 @@ func TestIamGetTokenTimeoutError(t *testing.T) { // Set the client timeout to something very low authenticator.Client.Timeout = time.Second * 2 token, err = authenticator.getToken() - assert.NotNil(t, err) assert.Equal(t, "", token) + assert.NotNil(t, err) + assert.NotNil(t, err.Error()) + // We don't expect a AuthenticateError to be returned, so casting should fail + _, ok := err.(*AuthenticationError) + assert.False(t, ok) } func TestIamGetTokenServerError(t *testing.T) { @@ -462,10 +515,24 @@ func TestIamGetTokenServerError(t *testing.T) { token) assert.NotNil(t, authenticator.tokenData) + var expectedResponse = []byte("Gateway Timeout") + // Force expiration and verify that we got a server error authenticator.tokenData.Expiration = GetCurrentTime() - 3600 token, err = authenticator.getToken() assert.NotNil(t, err) + // We expect an AuthenticationError to be returned, so cast the returned error + authError, ok := err.(*AuthenticationError) + assert.True(t, ok) + assert.NotNil(t, authError) + assert.NotNil(t, authError.Response) + assert.NotNil(t, authError.Error()) + rawResult := authError.Response.GetRawResult() + statusCode := authError.Response.GetStatusCode() + assert.Equal(t, "Gateway Timeout", authError.Error()) + assert.Equal(t, expectedResponse, rawResult) + assert.NotNil(t, rawResult) + assert.Equal(t, http.StatusGatewayTimeout, statusCode) assert.Equal(t, "", token) } diff --git a/v4/core/marshal_nulls_test.go b/v4/core/marshal_nulls_test.go index b4bacf0..ef4a36d 100644 --- a/v4/core/marshal_nulls_test.go +++ b/v4/core/marshal_nulls_test.go @@ -8,7 +8,6 @@ import ( "testing" ) - // // The purpose of this testcase is to ensure that dynamic properties with nil values are // correctly serialized as JSON null values. @@ -23,8 +22,8 @@ import ( // serve as a test of serialize null dynamic property values. type dynamicModel struct { - Prop1 *string `json:"prop1,omitempty"` - Prop2 *int64 `json:"prop2,omitempty"` + Prop1 *string `json:"prop1,omitempty"` + Prop2 *int64 `json:"prop2,omitempty"` additionalProperties map[string]*string } @@ -88,14 +87,14 @@ func TestAdditionalPropertiesNull(t *testing.T) { Prop2: Int64Ptr(38), } model.SetProperty("bar", nil) - + // Serialize to JSON and ensure that the nil dynamic property value was explicitly serialized as JSON null. b, err := json.Marshal(model) jsonString := string(b) assert.Nil(t, err) t.Logf("Serialized model: %s\n", jsonString) assert.Contains(t, jsonString, `"bar":null`) - + // Next, deserialize the json string into a map of RawMessages to simulate how the SDK code will // deserialize a response body. var rawMap map[string]json.RawMessage @@ -109,7 +108,7 @@ func TestAdditionalPropertiesNull(t *testing.T) { assert.Nil(t, err) assert.NotNil(t, newModel) t.Logf("newModel: %+v\n", *newModel) - + // Make sure the new model is the same as the original model. assert.Equal(t, model, newModel) } diff --git a/v4/core/unmarshal_v2.go b/v4/core/unmarshal_v2.go index f90de5f..aa71041 100644 --- a/v4/core/unmarshal_v2.go +++ b/v4/core/unmarshal_v2.go @@ -478,7 +478,7 @@ func unmarshalModelSliceMap(rawInput interface{}, propertyName string, result in if foundInput && rawMap != nil { for k, v := range rawMap { - + // Make sure our slice raw message isn't an explicit JSON null value. if !isJsonNull(v) { // Each value in 'rawMap' should contain an instance of []. diff --git a/v4/core/unmarshal_v2_primitives_test.go b/v4/core/unmarshal_v2_primitives_test.go index 13c063f..1d82192 100644 --- a/v4/core/unmarshal_v2_primitives_test.go +++ b/v4/core/unmarshal_v2_primitives_test.go @@ -152,7 +152,7 @@ func TestUnmarshalPrimitiveString(t *testing.T) { assert.NotNil(t, err) assert.True(t, strings.Contains(err.Error(), "error unmarshalling property 'bad_slice_type'")) t.Logf("Expected error: %s\n", err.Error()) - + err = UnmarshalPrimitive(rawMap, "", &model.Prop) assert.NotNil(t, err) assert.Equal(t, "the 'propertyName' parameter is required", err.Error()) @@ -979,7 +979,7 @@ func TestUnmarshalPrimitiveDateTime(t *testing.T) { jsonString = strings.ReplaceAll(jsonString, "%d2", d2) jsonString = strings.ReplaceAll(jsonString, "%d3", d3) jsonString = strings.ReplaceAll(jsonString, "%d4", d4) - + // Expected values need to include ms d1 = "1969-07-20T20:17:00.000Z" d2 = "1963-11-22T18:30:00.000Z" @@ -1329,7 +1329,7 @@ func TestUnmarshalPrimitiveAny(t *testing.T) { assert.Nil(t, err) assert.Nil(t, model.PropSlice) - model.PropMap = map[string]interface{}{ "key1": "value1" } + model.PropMap = map[string]interface{}{"key1": "value1"} err = UnmarshalPrimitive(rawMap, "null_prop", &model.PropMap) assert.Nil(t, err) assert.Nil(t, model.PropMap) @@ -1386,7 +1386,7 @@ func TestUnmarshalPrimitiveObject(t *testing.T) { "bad_slice_type": [38, 26], "null_prop": null }` - + o1 := `{"field1": "value1"}` o2 := `{"field2": "value2"}`