mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00

- add validators for simulation model endpoints - revise endpoint implementations and fix responses of simulation models - clean up testdata, serializers and responses for simulation models - change default values of output length and input length of signal mappings to 0
272 lines
7.6 KiB
Go
272 lines
7.6 KiB
Go
package common
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/jinzhu/gorm"
|
|
"github.com/nsf/jsondiff"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
const UserIDCtx = "user_id"
|
|
const UserRoleCtx = "user_role"
|
|
|
|
// used in testing
|
|
type KeyModels map[string]interface{}
|
|
|
|
func ProvideErrorResponse(c *gin.Context, err error) bool {
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
errormsg := "Record not Found in DB: " + err.Error()
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"success": false,
|
|
"message": errormsg,
|
|
})
|
|
} else {
|
|
errormsg := "Error on DB Query or transaction: " + err.Error()
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"success": false,
|
|
"message": errormsg,
|
|
})
|
|
}
|
|
return true // Error
|
|
}
|
|
return false // No error
|
|
}
|
|
|
|
func GetResponseID(resp *bytes.Buffer) (int, error) {
|
|
|
|
// Transform bytes buffer into byte slice
|
|
respBytes := []byte(resp.String())
|
|
|
|
// Map JSON response to a map[string]map[string]interface{}
|
|
var respRemapped map[string]map[string]interface{}
|
|
err := json.Unmarshal(respBytes, &respRemapped)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("Unmarshal failed for respRemapped %v", err)
|
|
}
|
|
|
|
// Get an arbitrary key from tha map. The only key (entry) of
|
|
// course is the model's name. With that trick we do not have to
|
|
// pass the higher level key as argument.
|
|
for arbitrary_key := range respRemapped {
|
|
|
|
// The marshaler turns numerical values into float64 types so we
|
|
// first have to make a type assertion to the interface and then
|
|
// the conversion to integer before returning
|
|
id, ok := respRemapped[arbitrary_key]["id"].(float64)
|
|
if !ok {
|
|
return 0, fmt.Errorf("Cannot type assert respRemapped")
|
|
}
|
|
return int(id), nil
|
|
}
|
|
return 0, fmt.Errorf("GetResponse reached exit")
|
|
}
|
|
|
|
func LengthOfResponse(router *gin.Engine, token string, url string,
|
|
method string, body []byte) (int, error) {
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
req, err := http.NewRequest(method, url, nil)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("Failed to create new request: %v", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Add("Authorization", "Bearer "+token)
|
|
router.ServeHTTP(w, req)
|
|
|
|
// HTTP Code of response must be 200
|
|
if w.Code != 200 {
|
|
return 0, fmt.Errorf("HTTP Code: Expected \"200\". Got \"%v\""+
|
|
".\nResponse message:\n%v", w.Code, w.Body.String())
|
|
}
|
|
|
|
// Convert the response in array of bytes
|
|
responseBytes := []byte(w.Body.String())
|
|
|
|
// First we are trying to unmarshal the response into an array of
|
|
// general type variables ([]interface{}). If this fails we will try
|
|
// to unmarshal into a single general type variable (interface{}).
|
|
// If that also fails we will return 0.
|
|
|
|
// Response might be array of objects
|
|
var arrayResponse map[string][]interface{}
|
|
err = json.Unmarshal(responseBytes, &arrayResponse)
|
|
if err == nil {
|
|
|
|
// Get an arbitrary key from tha map. The only key (entry) of
|
|
// course is the model's name. With that trick we do not have to
|
|
// pass the higher level key as argument.
|
|
for arbitrary_key := range arrayResponse {
|
|
return len(arrayResponse[arbitrary_key]), nil
|
|
}
|
|
}
|
|
|
|
// Response might be a single object
|
|
var singleResponse map[string]interface{}
|
|
err = json.Unmarshal(responseBytes, &singleResponse)
|
|
if err == nil {
|
|
return 1, nil
|
|
}
|
|
|
|
// Failed to identify response.
|
|
return 0, fmt.Errorf("Length of response cannot be detected")
|
|
}
|
|
|
|
func NewTestEndpoint(router *gin.Engine, token string, url string,
|
|
method string, requestBody interface{}) (int, *bytes.Buffer, error) {
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
// Marshal the HTTP request body
|
|
body, err := json.Marshal(requestBody)
|
|
if err != nil {
|
|
return 0, nil, fmt.Errorf("Failed to marshal request body: %v", err)
|
|
}
|
|
|
|
// Create the request
|
|
req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
|
|
if err != nil {
|
|
return 0, nil, fmt.Errorf("Failed to create new request: %v", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Add("Authorization", "Bearer "+token)
|
|
router.ServeHTTP(w, req)
|
|
|
|
return w.Code, w.Body, nil
|
|
}
|
|
|
|
func CompareResponse(resp *bytes.Buffer, expected interface{}) error {
|
|
// Serialize expected response
|
|
expectedBytes, err := json.Marshal(expected)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to marshal expected response: %v", err)
|
|
}
|
|
// Compare
|
|
opts := jsondiff.DefaultConsoleOptions()
|
|
diff, text := jsondiff.Compare(resp.Bytes(), expectedBytes, &opts)
|
|
if diff.String() != "FullMatch" && diff.String() != "SupersetMatch" {
|
|
fmt.Println(text)
|
|
return fmt.Errorf("Response: Expected \"%v\". Got \"%v\".",
|
|
"(FullMatch OR SupersetMatch)", diff.String())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func TestEndpoint(t *testing.T, router *gin.Engine, token string, url string, method string, body []byte, expected_code int, expected_response []byte) {
|
|
w := httptest.NewRecorder()
|
|
|
|
if body != nil {
|
|
req, _ := http.NewRequest(method, url, bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Add("Authorization", "Bearer "+token)
|
|
router.ServeHTTP(w, req)
|
|
} else {
|
|
req, _ := http.NewRequest(method, url, nil)
|
|
req.Header.Add("Authorization", "Bearer "+token)
|
|
router.ServeHTTP(w, req)
|
|
}
|
|
|
|
assert.Equal(t, expected_code, w.Code)
|
|
//fmt.Println("Actual:", w.Body.String())
|
|
//fmt.Println("Expected: ", string(expected_response))
|
|
opts := jsondiff.DefaultConsoleOptions()
|
|
diff, _ := jsondiff.Compare(w.Body.Bytes(), expected_response, &opts)
|
|
assert.Equal(t, "FullMatch", diff.String())
|
|
|
|
}
|
|
|
|
func NewAuthenticateForTest(router *gin.Engine, url string,
|
|
method string, credentials interface{}) (string, error) {
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
// Marshal credentials
|
|
body, err := json.Marshal(credentials)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Failed to marshal credentials: %v", err)
|
|
}
|
|
|
|
req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
|
|
if err != nil {
|
|
return "", fmt.Errorf("Faile to create new request: %v", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
// Check that return HTTP Code is 200 (OK)
|
|
if w.Code != http.StatusOK {
|
|
return "", fmt.Errorf("HTTP Code: Expected \"%v\". Got \"%v\".",
|
|
http.StatusOK, w.Code)
|
|
}
|
|
|
|
// Get the response
|
|
var body_data map[string]interface{}
|
|
err = json.Unmarshal([]byte(w.Body.String()), &body_data)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Check the response
|
|
success, ok := body_data["success"].(bool)
|
|
if !ok {
|
|
return "", fmt.Errorf("Type asssertion of response[\"success\"] failed")
|
|
}
|
|
if !success {
|
|
return "", fmt.Errorf("Authentication failed: %v", body_data["message"])
|
|
}
|
|
|
|
// Extract the token
|
|
token, ok := body_data["token"].(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("Type assertion of response[\"token\"] failed")
|
|
}
|
|
|
|
// Return the token and nil error
|
|
return token, nil
|
|
}
|
|
|
|
func AuthenticateForTest(t *testing.T, router *gin.Engine, url string, method string, body []byte, expected_code int) string {
|
|
w := httptest.NewRecorder()
|
|
|
|
req, _ := http.NewRequest(method, url, bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, expected_code, w.Code)
|
|
|
|
var body_data map[string]interface{}
|
|
|
|
err := json.Unmarshal([]byte(w.Body.String()), &body_data)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
success := body_data["success"].(bool)
|
|
if !success {
|
|
fmt.Println("Authentication not successful: ", body_data["message"])
|
|
panic(-1)
|
|
}
|
|
|
|
fmt.Println(w.Body.String())
|
|
|
|
return body_data["token"].(string)
|
|
}
|
|
|
|
// Read the parameter with name paramName from the gin Context and
|
|
// return it as uint variable
|
|
func UintParamFromCtx(c *gin.Context, paramName string) (uint, error) {
|
|
|
|
param, err := strconv.Atoi(c.Param(paramName))
|
|
|
|
return uint(param), err
|
|
}
|