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 := param [ "required" ] . ( bool )
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
}
}