You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
decort-golang-sdk/tests/platform_upgrade/utils_requests.go

296 lines
6.8 KiB

package test
import (
"fmt"
"io"
"os"
"reflect"
"strings"
"testing"
)
func getParameters(input map[string]interface{}) []interface{} {
var emptySlice []interface{}
methods := []string{"get", "post", "put", "delete", "patch", "head", "options"}
var methodData interface{}
found := false
for _, method := range methods {
if data, ok := input[method]; ok {
methodData = data
found = true
break
}
}
if !found {
return emptySlice
}
parameters, ok := methodData.(map[string]interface{})
if !ok {
return emptySlice
}
params, ok := parameters["parameters"]
if !ok {
return emptySlice
}
res, ok := params.([]interface{})
if !ok {
return emptySlice
}
// Check if there's a body parameter
for _, p := range res {
param, ok := p.(map[string]interface{})
if !ok {
continue
}
if param["name"] == "body" {
schema, ok := param["schema"].(map[string]interface{})
if !ok {
continue
}
properties, ok := schema["properties"].(map[string]interface{})
if !ok {
continue
}
requiredFields := make(map[string]bool)
if req, ok := schema["required"].([]interface{}); ok {
for _, r := range req {
requiredFields[r.(string)] = true
}
}
var newParams []interface{}
for name, prop := range properties {
propMap, ok := prop.(map[string]interface{})
if !ok {
continue
}
newParam := make(map[string]interface{})
newParam["name"] = name
newParam["type"] = propMap["type"]
newParam["required"] = requiredFields[name]
if propMap["type"] == "array" {
if items, ok := propMap["items"].(map[string]interface{}); ok {
newParam["items"] = items
}
}
newParams = append(newParams, newParam)
}
return newParams
}
}
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{}
switch cloud {
case "cloudapi":
requests = getRequestsMapCloudAPI()
case "cloudbroker":
requests = getRequestsMapCloudbroker()
case "sdn":
requests = getRequestsMapSDN()
default:
t.Fatalf("Wrong cloud provided, expected `cloudapi`, `cloudbroker` or `sdn`, 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, ok := param["name"].(string)
if !ok {
name = ""
}
required, ok := param["required"].(bool)
if !ok {
required = false
}
typ, ok := p.(map[string]interface{})["type"].(string)
if !ok {
typ = ""
}
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, ok = itemsType.(string)
if !ok {
items = ""
}
}
}
}
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 getRequests func in code.",
i, len(requests))
t.Error(msg)
dataLogs = append(dataLogs, msg)
}
}
// 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
}
}