package test import ( "fmt" "io" "os" "reflect" "strings" "testing" ) func getParameters(input map[string]interface{}) []interface{} { var emptySlice []interface{} post, ok := input["post"] if !ok { return emptySlice } parameters, ok := post.(map[string]interface{}) if !ok { return emptySlice } params, ok := parameters["parameters"] if !ok { return emptySlice } res, ok := params.([]interface{}) if !ok { return emptySlice } return res } func getBytesFromJSON(fileName string, t *testing.T) []byte { jsonFile, err := os.Open(fileName) if err != nil { t.Error(err) } defer jsonFile.Close() bytes, err := io.ReadAll(jsonFile) if err != nil { t.Error(err) } return bytes } func getErrorsFromJSON(bytes []byte, t *testing.T, cloud string) { var requests map[string]interface{} var logFileName string switch cloud { case "cloudapi": requests = getRequestsMapCloudAPI() logFileName = "test_requests_cloudAPI.log" case "cloudbroker": requests = getRequestsMapCloudbroker() logFileName = "test_requests_cloudbroker.log" default: t.Fatalf("Wrong cloud provided, expected `cloudapi` or `cloudbroker`, got %s", cloud) } var dataLogs []string paths, err := getMapFromFile(bytes) if err != nil { t.Error(err) } i := 0 for k, v := range paths { // exclude deprecated urls from analysis if !validateUrlFromJson(k) { continue } params := getParameters(v.(map[string]interface{})) structure, ok := requests[k] if !ok { continue } i++ typStruct := reflect.TypeOf(structure) var errs []string // empty request case if len(params) == 0 && structure == nil { continue } if len(params) != typStruct.NumField() { errs = append(errs, fmt.Sprintf("Platform (%d) and golang structure (%d) have different amount of fields.", len(params), typStruct.NumField())) } for _, p := range params { param, ok := p.(map[string]interface{}) if !ok { continue } name := param["name"].(string) required, ok := param["required"].(bool) if !ok { required = false } typ := p.(map[string]interface{})["type"].(string) var items string if p.(map[string]interface{})["items"] != nil { itemsTemp := p.(map[string]interface{})["items"] if itemsTemp != nil { itemsType := itemsTemp.(map[string]interface{})["type"] if itemsType != nil { items = itemsType.(string) } } } var found bool for i := 0; i < typStruct.NumField(); i++ { jsonTag := typStruct.Field(i).Tag.Get("json") validation, _ := typStruct.Field(i).Tag.Lookup("validate") if checkName(name, jsonTag) { if !checkRequired(required, validation, typ) { errs = append(errs, fmt.Sprintf("Field %s has different required parameters on the platform and in golang structure", name)) } if !checkKind(typ, items, typStruct.Field(i).Type) { errs = append(errs, fmt.Sprintf("Field %s has different type parameters on the platform and in golang structure", name)) } found = true break } } if !found { errs = append(errs, fmt.Sprintf("Platform has field %s that golang structure doesn't", name)) } } if len(errs) > 0 { msg := fmt.Sprintf("Path %s has following errors: %v", k, errs) t.Error(msg) dataLogs = append(dataLogs, msg) } } if len(requests) != i { msg := fmt.Sprintf("Amount of structure checked (%d) is not the same as amount of platform requests available (%d), please check getRequestsMapCloudAPI func in code.", i, len(requests)) t.Error(msg) dataLogs = append(dataLogs, msg) } compareLogs(logFileName, dataLogs, t, "request") } // checkName checks if name field from platform has the same value as json tag in golang structure (maybe including omitempty) func checkName(name, tag string) bool { return strings.Contains(tag, name) } // checkRequired checks if required field from platform has the same value as validate tag in golang structure func checkRequired(required bool, validation, fieldType string) bool { if required && strings.Contains(validation, "required") { return true } if required && (!strings.Contains(validation, "omitempty") && validation != "") { return true } if !required && (validation == "" || strings.Contains(validation, "omitempty")) { return true } if fieldType == "boolean" { return true // otherwise we have issues with setting false/true } return false } // checkKind checks if type field from platform has the same value as field type in golang structure func checkKind(platformType, items string, typ reflect.Type) bool { //nolint switch typ.Kind() { case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Int16, reflect.Uint, reflect.Uint64, reflect.Uint16, reflect.Uint32, reflect.Uint8: if platformType == "integer" { return true } return false case reflect.String: if platformType == "string" { return true } return false case reflect.Bool: if platformType == "boolean" { return true } return false case reflect.Float32, reflect.Float64: if platformType == "number" { return true } return false case reflect.Array, reflect.Slice: if platformType != "array" { return false } elem := typ.Elem() switch elem.Kind() { case reflect.String: if items == "string" { return true } return false case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Int16, reflect.Uint, reflect.Uint64, reflect.Uint16, reflect.Uint32, reflect.Uint8: if items == "integer" { return true } return false default: // for cases like dataDisks etc. return true } default: // for cases like drivers etc return true } }