package schema import ( "encoding/json" "fmt" "net/http" "testing" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" ) // ValidateResponseData is a test helper that validates whether the given // response data map conforms to the response schema (schema.Fields). It cycles // through the data map and validates conversions in the schema. In "strict" // mode, this function will also ensure that the data map has all schema's // requred fields and does not have any fields outside of the schema. func ValidateResponse(t *testing.T, schema *framework.Response, response *logical.Response, strict bool) { t.Helper() if response != nil { ValidateResponseData(t, schema, response.Data, strict) } else { ValidateResponseData(t, schema, nil, strict) } } // ValidateResponse is a test helper that validates whether the given response // object conforms to the response schema (schema.Fields). It cycles through // the data map and validates conversions in the schema. In "strict" mode, this // function will also ensure that the data map has all schema-required fields // and does not have any fields outside of the schema. func ValidateResponseData(t *testing.T, schema *framework.Response, data map[string]interface{}, strict bool) { t.Helper() if err := validateResponseDataImpl( schema, data, strict, ); err != nil { t.Fatalf("validation error: %v; response data: %#v", err, data) } } // validateResponseDataImpl is extracted so that it can be tested func validateResponseDataImpl(schema *framework.Response, data map[string]interface{}, strict bool) error { // nothing to validate if schema == nil { return nil } // Marshal the data to JSON and back to convert the map's values into // JSON strings expected by Validate() and ValidateStrict(). This is // not efficient and is done for testing purposes only. jsonBytes, err := json.Marshal(data) if err != nil { return fmt.Errorf("failed to convert input to json: %w", err) } var dataWithStringValues map[string]interface{} if err := json.Unmarshal( jsonBytes, &dataWithStringValues, ); err != nil { return fmt.Errorf("failed to unmashal data: %w", err) } // Validate fd := framework.FieldData{ Raw: dataWithStringValues, Schema: schema.Fields, } if strict { return fd.ValidateStrict() } return fd.Validate() } // FindResponseSchema is a test helper to extract the response schema from a given framework path / operation func FindResponseSchema(t *testing.T, paths []*framework.Path, pathIdx int, operation logical.Operation) *framework.Response { t.Helper() if pathIdx >= len(paths) { t.Fatalf("path index %d is out of range", pathIdx) } schemaPath := paths[pathIdx] return GetResponseSchema(t, schemaPath, operation) } func GetResponseSchema(t *testing.T, path *framework.Path, operation logical.Operation) *framework.Response { t.Helper() schemaOperation, ok := path.Operations[operation] if !ok { t.Fatalf( "could not find response schema: %s: %q operation does not exist", path.Pattern, operation, ) } var schemaResponses []framework.Response for _, status := range []int{ http.StatusOK, // 200 http.StatusAccepted, // 202 http.StatusNoContent, // 204 } { schemaResponses, ok = schemaOperation.Properties().Responses[status] if ok { break } } if len(schemaResponses) == 0 { t.Fatalf( "could not find response schema: %s: %q operation: no responses found", path.Pattern, operation, ) } return &schemaResponses[0] }