2024-07-16 18:06:26 +02:00
|
|
|
package util
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2024-09-15 17:38:05 +02:00
|
|
|
"fmt"
|
2024-07-16 18:06:26 +02:00
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func AssertJsonStructureMatch(t *testing.T, key string, jsonData string, expected map[string]any) {
|
|
|
|
|
|
|
|
realData := make(map[string]any)
|
|
|
|
|
|
|
|
err := json.Unmarshal([]byte(jsonData), &realData)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Failed to decode json of [%s]: %s", key, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-09-15 17:38:05 +02:00
|
|
|
assertjsonStructureMatchMapObject(t, expected, realData, key)
|
2024-07-16 18:06:26 +02:00
|
|
|
}
|
|
|
|
|
2024-09-15 17:38:05 +02:00
|
|
|
func assertJsonStructureMatch(t *testing.T, schema any, realValue any, keyPath string) {
|
2024-07-16 18:06:26 +02:00
|
|
|
|
2024-09-15 17:38:05 +02:00
|
|
|
if strschema, ok := schema.(string); ok {
|
|
|
|
|
|
|
|
assertjsonStructureMatchSingleValue(t, strschema, realValue, keyPath)
|
|
|
|
|
|
|
|
} else if mapschema, ok := schema.(map[string]any); ok {
|
|
|
|
|
|
|
|
if reflect.ValueOf(realValue).Kind() != reflect.Map {
|
|
|
|
t.Errorf("Key < %s > is not a object (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, ok := realValue.(map[string]any); !ok {
|
|
|
|
t.Errorf("Key < %s > is not a object[recursive] (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
assertjsonStructureMatchMapObject(t, mapschema, realValue.(map[string]any), keyPath)
|
|
|
|
|
|
|
|
} else if arrschema, ok := schema.([]any); ok && len(arrschema) == 1 {
|
|
|
|
|
|
|
|
if _, ok := realValue.([]any); !ok {
|
|
|
|
t.Errorf("Key < %s > is not a array[recursive] (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
2024-07-16 18:06:26 +02:00
|
|
|
}
|
2024-09-15 17:38:05 +02:00
|
|
|
|
|
|
|
assertjsonStructureMatchArray(t, arrschema, realValue.([]any), keyPath)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
t.Errorf("Unknown schema type '%s' for key < %s >", schema, keyPath)
|
2024-07-16 18:06:26 +02:00
|
|
|
}
|
2024-09-15 17:38:05 +02:00
|
|
|
}
|
2024-07-16 18:06:26 +02:00
|
|
|
|
2024-09-15 17:38:05 +02:00
|
|
|
func assertjsonStructureMatchSingleValue(t *testing.T, strschema string, realValue any, keyPath string) {
|
|
|
|
switch strschema {
|
|
|
|
case "id":
|
|
|
|
if _, ok := realValue.(string); !ok {
|
|
|
|
t.Errorf("Key < %s > is not a string<id> (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(realValue.(string)) != 24 { //TODO validate checksum?
|
|
|
|
t.Errorf("Key < %s > is not a valid entity-id date (its '%v')", keyPath, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case "string":
|
|
|
|
if _, ok := realValue.(string); !ok {
|
|
|
|
t.Errorf("Key < %s > is not a string (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case "null":
|
|
|
|
if !langext.IsNil(realValue) {
|
|
|
|
t.Errorf("Key < %s > is not a NULL (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case "string|null":
|
|
|
|
if langext.IsNil(realValue) {
|
|
|
|
return // OK
|
|
|
|
} else if _, ok := realValue.(string); !ok {
|
|
|
|
return // OK
|
|
|
|
} else {
|
|
|
|
t.Errorf("Key < %s > is not a string|null (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case "rfc3339":
|
|
|
|
if _, ok := realValue.(string); !ok {
|
|
|
|
t.Errorf("Key < %s > is not a string<rfc3339> (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err := time.Parse(time.RFC3339, realValue.(string)); err != nil {
|
|
|
|
t.Errorf("Key < %s > is not a valid rfc3339 date (its '%v')", keyPath, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case "rfc3339|null":
|
|
|
|
if langext.IsNil(realValue) {
|
|
|
|
return // OK
|
|
|
|
}
|
|
|
|
if _, ok := realValue.(string); !ok {
|
|
|
|
t.Errorf("Key < %s > is not a string<rfc3339> (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err := time.Parse(time.RFC3339, realValue.(string)); err != nil {
|
|
|
|
t.Errorf("Key < %s > is not a valid rfc3339 date (its '%v')", keyPath, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case "int":
|
|
|
|
if _, ok := realValue.(float64); !ok {
|
|
|
|
t.Errorf("Key < %s > is not a int (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if realValue.(float64) != float64(int(realValue.(float64))) {
|
|
|
|
t.Errorf("Key < %s > is not a int (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case "float":
|
|
|
|
if _, ok := realValue.(float64); !ok {
|
|
|
|
t.Errorf("Key < %s > is not a int (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
2024-07-16 18:06:26 +02:00
|
|
|
}
|
2024-09-15 17:38:05 +02:00
|
|
|
case "bool":
|
|
|
|
if _, ok := realValue.(bool); !ok {
|
|
|
|
t.Errorf("Key < %s > is not a int (its actually %T: '%v')", keyPath, realValue, realValue)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
t.Errorf("Unknown schema type '%s' for key < %s >", strschema, keyPath)
|
|
|
|
return
|
2024-07-16 18:06:26 +02:00
|
|
|
}
|
2024-09-15 17:38:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func assertjsonStructureMatchMapObject(t *testing.T, mapschema map[string]any, realValue map[string]any, keyPath string) {
|
|
|
|
|
|
|
|
for k := range mapschema {
|
|
|
|
if _, ok := realValue[k]; !ok {
|
2024-09-15 21:07:46 +02:00
|
|
|
t.Errorf("Missing Key: < %s >", keyPath+"."+k)
|
2024-09-15 17:38:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for k := range realValue {
|
|
|
|
if _, ok := mapschema[k]; !ok {
|
2024-09-15 21:07:46 +02:00
|
|
|
t.Errorf("Additional key: < %s >", keyPath+"."+k)
|
2024-09-15 17:38:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range realValue {
|
2024-07-16 18:06:26 +02:00
|
|
|
|
2024-09-15 17:38:05 +02:00
|
|
|
kpath := keyPath + "." + k
|
2024-07-16 18:06:26 +02:00
|
|
|
|
2024-09-15 17:38:05 +02:00
|
|
|
schema, ok := mapschema[k]
|
2024-07-16 18:06:26 +02:00
|
|
|
|
|
|
|
if !ok {
|
2024-09-15 17:38:05 +02:00
|
|
|
t.Errorf("Key < %s > is missing in response", kpath)
|
2024-07-16 18:06:26 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-09-15 17:38:05 +02:00
|
|
|
assertJsonStructureMatch(t, schema, v, kpath)
|
|
|
|
|
2024-07-16 18:06:26 +02:00
|
|
|
}
|
2024-09-15 17:38:05 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertjsonStructureMatchArray(t *testing.T, arrschema []any, realValue []any, keyPath string) {
|
|
|
|
|
|
|
|
if len(arrschema) != 1 {
|
|
|
|
t.Errorf("Array schema must have exactly one element, but got %d", len(arrschema))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, realArrVal := range realValue {
|
|
|
|
assertJsonStructureMatch(t, arrschema[0], realArrVal, fmt.Sprintf("%s[%d]", keyPath, i))
|
|
|
|
}
|
|
|
|
|
2024-07-16 18:06:26 +02:00
|
|
|
}
|