- revise testing of simulators

- add validators for simulators
- clean up testdata, serializers and responses
- update documentation for swaggo
This commit is contained in:
Sonja Happ 2019-09-05 12:23:44 +02:00
parent 7bc94c1131
commit a55d039ee7
8 changed files with 647 additions and 287 deletions

View file

@ -12,18 +12,6 @@ type SimulationModelResponse struct {
StartParameters postgres.Jsonb `json:"startParameters"` StartParameters postgres.Jsonb `json:"startParameters"`
} }
type SimulatorResponse struct {
ID uint `json:"id"`
UUID string `json:"uuid"`
Host string `json:"host"`
Modeltype string `json:"modelType"`
Uptime int `json:"uptime"`
State string `json:"state"`
StateUpdateAt string `json:"stateUpdateAt"`
Properties postgres.Jsonb `json:"properties"`
RawProperties postgres.Jsonb `json:"rawProperties"`
}
type DashboardResponse struct { type DashboardResponse struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`
@ -105,14 +93,6 @@ type ResponseMsgWidget struct {
Widget WidgetResponse `json:"widget"` Widget WidgetResponse `json:"widget"`
} }
type ResponseMsgSimulators struct {
Simulators []SimulatorResponse `json:"simulators"`
}
type ResponseMsgSimulator struct {
Simulator SimulatorResponse `json:"simulator"`
}
type ResponseMsgFiles struct { type ResponseMsgFiles struct {
Files []FileResponse `json:"files"` Files []FileResponse `json:"files"`
} }

View file

@ -38,43 +38,6 @@ func (self *SimulationModelSerializer) Response() SimulationModelResponse {
return response return response
} }
// Simulator/s Serializers
type SimulatorsSerializer struct {
Ctx *gin.Context
Simulators []Simulator
}
func (self *SimulatorsSerializer) Response() []SimulatorResponse {
response := []SimulatorResponse{}
for _, simulator := range self.Simulators {
serializer := SimulatorSerializer{self.Ctx, simulator}
response = append(response, serializer.Response())
}
return response
}
type SimulatorSerializer struct {
Ctx *gin.Context
Simulator
}
func (self *SimulatorSerializer) Response() SimulatorResponse {
response := SimulatorResponse{
ID: self.ID,
UUID: self.UUID,
Host: self.Host,
Modeltype: self.Modeltype,
Uptime: self.Uptime,
State: self.State,
StateUpdateAt: self.StateUpdateAt,
Properties: self.Properties,
RawProperties: self.RawProperties,
}
return response
}
// Dashboard/s Serializers // Dashboard/s Serializers
type DashboardsSerializer struct { type DashboardsSerializer struct {

View file

@ -57,8 +57,6 @@ var UserBCredentials = Credentials{
var propertiesA = json.RawMessage(`{"name" : "TestNameA", "category" : "CategoryA", "location" : "anywhere on earth", "type": "dummy"}`) var propertiesA = json.RawMessage(`{"name" : "TestNameA", "category" : "CategoryA", "location" : "anywhere on earth", "type": "dummy"}`)
var propertiesB = json.RawMessage(`{"name" : "TestNameB", "category" : "CategoryB", "location" : "where ever you want", "type": "generic"}`) var propertiesB = json.RawMessage(`{"name" : "TestNameB", "category" : "CategoryB", "location" : "where ever you want", "type": "generic"}`)
var propertiesC = json.RawMessage(`{"name" : "TestNameC", "category" : "CategoryC", "location" : "my desk", "type": "blubb"}`)
var propertiesCupdated = json.RawMessage(`{"name" : "TestNameCUpdate", "category" : "CategoryC", "location" : "my desk", "type": "blubb"}`)
var SimulatorA = Simulator{ var SimulatorA = Simulator{
UUID: "4854af30-325f-44a5-ad59-b67b2597de68", UUID: "4854af30-325f-44a5-ad59-b67b2597de68",
@ -71,18 +69,6 @@ var SimulatorA = Simulator{
RawProperties: postgres.Jsonb{propertiesA}, RawProperties: postgres.Jsonb{propertiesA},
} }
var SimulatorA_response = SimulatorResponse{
ID: 1,
UUID: SimulatorA.UUID,
Host: SimulatorA.Host,
Modeltype: SimulatorA.Modeltype,
Uptime: SimulatorA.Uptime,
State: SimulatorA.State,
StateUpdateAt: SimulatorA.StateUpdateAt,
Properties: SimulatorA.Properties,
RawProperties: SimulatorA.RawProperties,
}
var SimulatorB = Simulator{ var SimulatorB = Simulator{
UUID: "7be0322d-354e-431e-84bd-ae4c9633138b", UUID: "7be0322d-354e-431e-84bd-ae4c9633138b",
Host: "Host_B", Host: "Host_B",
@ -94,72 +80,22 @@ var SimulatorB = Simulator{
RawProperties: postgres.Jsonb{propertiesB}, RawProperties: postgres.Jsonb{propertiesB},
} }
var SimulatorB_response = SimulatorResponse{
ID: 2,
UUID: SimulatorB.UUID,
Host: SimulatorB.Host,
Modeltype: SimulatorB.Modeltype,
Uptime: SimulatorB.Uptime,
State: SimulatorB.State,
StateUpdateAt: SimulatorB.StateUpdateAt,
Properties: SimulatorB.Properties,
RawProperties: SimulatorB.RawProperties,
}
var SimulatorC = Simulator{
UUID: "6d9776bf-b693-45e8-97b6-4c13d151043f",
Host: "Host_C",
Modeltype: "ModelTypeC",
Uptime: 0,
State: "idle",
StateUpdateAt: time.Now().String(),
Properties: postgres.Jsonb{propertiesC},
RawProperties: postgres.Jsonb{propertiesC},
}
var SimulatorC_response = SimulatorResponse{
ID: 3,
UUID: SimulatorC.UUID,
Host: SimulatorC.Host,
Modeltype: SimulatorC.Modeltype,
Uptime: SimulatorC.Uptime,
State: SimulatorC.State,
StateUpdateAt: SimulatorC.StateUpdateAt,
Properties: SimulatorC.Properties,
RawProperties: SimulatorC.RawProperties,
}
var SimulatorCUpdated = Simulator{
UUID: SimulatorC.UUID,
Host: "Host_Cupdated",
Modeltype: "ModelTypeCUpdated",
Uptime: SimulatorC.Uptime,
State: "running",
StateUpdateAt: time.Now().String(),
Properties: postgres.Jsonb{propertiesCupdated},
RawProperties: postgres.Jsonb{propertiesCupdated},
}
var SimulatorCUpdated_response = SimulatorResponse{
ID: 3,
UUID: SimulatorCUpdated.UUID,
Host: SimulatorCUpdated.Host,
Modeltype: SimulatorCUpdated.Modeltype,
Uptime: SimulatorCUpdated.Uptime,
State: SimulatorCUpdated.State,
StateUpdateAt: SimulatorCUpdated.StateUpdateAt,
Properties: SimulatorCUpdated.Properties,
RawProperties: SimulatorCUpdated.RawProperties,
}
// Scenarios // Scenarios
var startParametersA = json.RawMessage(`{"parameter1" : "testValue1A", "parameter2" : "testValue2A", "parameter3" : 42}`) var startParametersA = json.RawMessage(`{"parameter1" : "testValue1A", "parameter2" : "testValue2A", "parameter3" : 42}`)
var startParametersB = json.RawMessage(`{"parameter1" : "testValue1B", "parameter2" : "testValue2B", "parameter3" : 43}`) var startParametersB = json.RawMessage(`{"parameter1" : "testValue1B", "parameter2" : "testValue2B", "parameter3" : 43}`)
var startParametersC = json.RawMessage(`{"parameter1" : "testValue1C", "parameter2" : "testValue2C", "parameter3" : 44}`) var startParametersC = json.RawMessage(`{"parameter1" : "testValue1C", "parameter2" : "testValue2C", "parameter3" : 44}`)
var ScenarioA = Scenario{Name: "Scenario_A", Running: true, StartParameters: postgres.Jsonb{startParametersA}} var ScenarioA = Scenario{
var ScenarioB = Scenario{Name: "Scenario_B", Running: false, StartParameters: postgres.Jsonb{startParametersB}} Name: "Scenario_A",
Running: true,
StartParameters: postgres.Jsonb{startParametersA},
}
var ScenarioB = Scenario{
Name: "Scenario_B",
Running: false,
StartParameters: postgres.Jsonb{startParametersB},
}
// Simulation Models // Simulation Models

View file

@ -40,3 +40,11 @@ type ResponseScenarios struct {
type ResponseScenario struct { type ResponseScenario struct {
scenario common.Scenario scenario common.Scenario
} }
type ResponseSimulationModels struct {
models []common.SimulationModel
}
type ResponseSimulationModel struct {
model common.SimulationModel
}

View file

@ -3,7 +3,6 @@ package simulator
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strconv"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -29,17 +28,19 @@ func RegisterSimulatorEndpoints(r *gin.RouterGroup) {
// @ID getSimulators // @ID getSimulators
// @Tags simulators // @Tags simulators
// @Produce json // @Produce json
// @Success 200 {array} common.ResponseMsgSimulators "Simulator parameters requested by user" // @Success 200 {object} docs.ResponseSimulators "Simulators requested"
// @Failure 401 "Unauthorized Access" // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 403 "Access forbidden." // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 404 "Not found" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Failure 500 "Internal server error"
// @Router /simulators [get] // @Router /simulators [get]
func getSimulators(c *gin.Context) { func getSimulators(c *gin.Context) {
err := common.ValidateRole(c, common.ModelSimulator, common.Read) err := common.ValidateRole(c, common.ModelSimulator, common.Read)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
@ -49,9 +50,9 @@ func getSimulators(c *gin.Context) {
if common.ProvideErrorResponse(c, err) { if common.ProvideErrorResponse(c, err) {
return return
} }
serializer := common.SimulatorsSerializer{c, simulators}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"simulators": serializer.Response(), "simulators": simulators,
}) })
} }
@ -61,48 +62,57 @@ func getSimulators(c *gin.Context) {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Tags simulators // @Tags simulators
// @Param inputSimulator body common.ResponseMsgSimulator true "Simulator to be added" // @Param inputSimulator body simulator.validNewSimulator true "Simulator to be added"
// @Success 200 "OK." // @Success 200 {object} docs.ResponseSimulator "Simulator that was added"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 403 "Access forbidden." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 404 "Not found" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 "Internal server error" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Router /simulators [post] // @Router /simulators [post]
func addSimulator(c *gin.Context) { func addSimulator(c *gin.Context) {
err := common.ValidateRole(c, common.ModelSimulator, common.Create) err := common.ValidateRole(c, common.ModelSimulator, common.Create)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
var newSimulatorData common.ResponseMsgSimulator var req addSimulatorRequest
err = c.BindJSON(&newSimulatorData) err = c.BindJSON(&req)
if err != nil { if err != nil {
errormsg := "Bad request. Error binding form data to JSON: " + err.Error() errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "success": false,
"message": errormsg,
}) })
return return
} }
var newSimulator Simulator // Validate the request
newSimulator.ID = newSimulatorData.Simulator.ID if err = req.validate(); err != nil {
newSimulator.State = newSimulatorData.Simulator.State c.JSON(http.StatusUnprocessableEntity, gin.H{
newSimulator.StateUpdateAt = newSimulatorData.Simulator.StateUpdateAt "success": false,
newSimulator.Modeltype = newSimulatorData.Simulator.Modeltype "message": fmt.Sprintf("%v", err),
newSimulator.UUID = newSimulatorData.Simulator.UUID
newSimulator.Uptime = newSimulatorData.Simulator.Uptime
newSimulator.Host = newSimulatorData.Simulator.Host
newSimulator.RawProperties = newSimulatorData.Simulator.RawProperties
newSimulator.Properties = newSimulatorData.Simulator.Properties
err = newSimulator.save()
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
}) })
return
} }
// Create the new simulator from the request
newSimulator := req.createSimulator()
// Save new simulator to DB
err = newSimulator.save()
if err != nil {
common.ProvideErrorResponse(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"simulator": newSimulator.Simulator,
})
} }
// updateSimulator godoc // updateSimulator godoc
@ -111,53 +121,80 @@ func addSimulator(c *gin.Context) {
// @Tags simulators // @Tags simulators
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param inputSimulator body common.ResponseMsgSimulator true "Simulator to be updated" // @Param inputSimulator body simulator.validUpdatedSimulator true "Simulator to be updated"
// @Success 200 "OK." // @Success 200 {object} docs.ResponseSimulator "Simulator that was updated"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 403 "Access forbidden." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 404 "Not found" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 "Internal server error" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param simulatorID path int true "Simulator ID" // @Param simulatorID path int true "Simulator ID"
// @Router /simulators/{simulatorID} [put] // @Router /simulators/{simulatorID} [put]
func updateSimulator(c *gin.Context) { func updateSimulator(c *gin.Context) {
err := common.ValidateRole(c, common.ModelSimulator, common.Update) err := common.ValidateRole(c, common.ModelSimulator, common.Update)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") c.JSON(http.StatusUnprocessableEntity, gin.H{
return "success": false,
} "message": fmt.Sprintf("%v", err),
var modifiedSimulator common.ResponseMsgSimulator
err = c.BindJSON(&modifiedSimulator)
if err != nil {
errormsg := "Bad request. Error unmarshalling data to JSON: " + err.Error()
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
}) })
return return
} }
simulatorID, err := strconv.Atoi(c.Param("simulatorID")) var req updateSimulatorRequest
err = c.BindJSON(&req)
if err != nil { if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter")
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "success": false,
"message": "Bad request. Error binding form data to JSON: " + err.Error(),
}) })
return return
} }
var s Simulator // Validate the request
err = s.ByID(uint(simulatorID)) if err = req.validate(); err != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return
}
// Get the ID of the simulator to be updated from the context
toBeUpdatedID, err := common.UintParamFromCtx(c, "simulatorID")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": fmt.Sprintf("Could not get simulator's ID from context"),
})
return
}
var oldSimulator Simulator
err = oldSimulator.ByID(toBeUpdatedID)
if common.ProvideErrorResponse(c, err) { if common.ProvideErrorResponse(c, err) {
return return
} }
err = s.update(modifiedSimulator.Simulator) // Create the updatedSimulator from oldSimulator
if common.ProvideErrorResponse(c, err) == false { updatedSimulator, err := req.updatedSimulator(oldSimulator)
c.JSON(http.StatusOK, gin.H{ if err != nil {
"message": "OK.", c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
}) })
return
} }
// Finally update the simulator in the DB
err = oldSimulator.update(updatedSimulator)
if err != nil {
common.ProvideErrorResponse(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"simulator": updatedSimulator.Simulator,
})
} }
// getSimulator godoc // getSimulator godoc
@ -165,39 +202,42 @@ func updateSimulator(c *gin.Context) {
// @ID getSimulator // @ID getSimulator
// @Produce json // @Produce json
// @Tags simulators // @Tags simulators
// @Success 200 {object} common.ResponseMsgSimulator "Simulator requested by user" // @Success 200 {object} docs.ResponseSimulator "Simulator that was requested"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 403 "Access forbidden." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 404 "Not found" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 "Internal server error" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param simulatorID path int true "Simulator ID" // @Param simulatorID path int true "Simulator ID"
// @Router /simulators/{simulatorID} [get] // @Router /simulators/{simulatorID} [get]
func getSimulator(c *gin.Context) { func getSimulator(c *gin.Context) {
err := common.ValidateRole(c, common.ModelSimulator, common.Read) err := common.ValidateRole(c, common.ModelSimulator, common.Read)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
simulatorID, err := strconv.Atoi(c.Param("simulatorID")) // Get the ID of the simulator from the context
simulatorID, err := common.UintParamFromCtx(c, "simulatorID")
if err != nil { if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter")
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "success": false,
"message": fmt.Sprintf("Could not get simulator's ID from context"),
}) })
return return
} }
var s Simulator var s Simulator
err = s.ByID(uint(simulatorID)) err = s.ByID(simulatorID)
if common.ProvideErrorResponse(c, err) { if common.ProvideErrorResponse(c, err) {
return return
} }
serializer := common.SimulatorSerializer{c, s.Simulator}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"simulator": serializer.Response(), "simulator": s.Simulator,
}) })
} }
@ -206,43 +246,48 @@ func getSimulator(c *gin.Context) {
// @ID deleteSimulator // @ID deleteSimulator
// @Tags simulators // @Tags simulators
// @Produce json // @Produce json
// @Success 200 "OK." // @Success 200 {object} docs.ResponseSimulator "Simulator that was deleted"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 403 "Access forbidden." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 404 "Not found" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 "Internal server error" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param simulatorID path int true "Simulator ID" // @Param simulatorID path int true "Simulator ID"
// @Router /simulators/{simulatorID} [delete] // @Router /simulators/{simulatorID} [delete]
func deleteSimulator(c *gin.Context) { func deleteSimulator(c *gin.Context) {
err := common.ValidateRole(c, common.ModelSimulator, common.Delete) err := common.ValidateRole(c, common.ModelSimulator, common.Delete)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
simulatorID, err := strconv.Atoi(c.Param("simulatorID")) // Get the ID of the simulator from the context
simulatorID, err := common.UintParamFromCtx(c, "simulatorID")
if err != nil { if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter")
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "success": false,
"message": fmt.Sprintf("Could not get simulator's ID from context"),
}) })
return return
} }
var s Simulator var s Simulator
err = s.ByID(uint(simulatorID)) err = s.ByID(simulatorID)
if common.ProvideErrorResponse(c, err) { if common.ProvideErrorResponse(c, err) {
return return
} }
// Delete the simulator
err = s.delete() err = s.delete()
if common.ProvideErrorResponse(c, err) { if common.ProvideErrorResponse(c, err) {
return return
} }
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "OK.", "simulator": s.Simulator,
}) })
} }
@ -251,32 +296,36 @@ func deleteSimulator(c *gin.Context) {
// @ID getModelsOfSimulator // @ID getModelsOfSimulator
// @Tags simulators // @Tags simulators
// @Produce json // @Produce json
// @Success 200 {object} common.ResponseMsgSimulationModels "Simulation models requested by user" // @Success 200 {object} docs.ResponseSimulationModels "Simulation models requested by user"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 403 "Access forbidden." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 404 "Not found" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 "Internal server error" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param simulatorID path int true "Simulator ID" // @Param simulatorID path int true "Simulator ID"
// @Router /simulators/{simulatorID}/models [get] // @Router /simulators/{simulatorID}/models [get]
func getModelsOfSimulator(c *gin.Context) { func getModelsOfSimulator(c *gin.Context) {
err := common.ValidateRole(c, common.ModelSimulator, common.Read) err := common.ValidateRole(c, common.ModelSimulator, common.Read)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
simulatorID, err := strconv.Atoi(c.Param("simulatorID")) // Get the ID of the simulator from the context
simulatorID, err := common.UintParamFromCtx(c, "simulatorID")
if err != nil { if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter")
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "success": false,
"message": fmt.Sprintf("Could not get simulator's ID from context"),
}) })
return return
} }
var s Simulator var s Simulator
err = s.ByID(uint(simulatorID)) err = s.ByID(simulatorID)
if common.ProvideErrorResponse(c, err) { if common.ProvideErrorResponse(c, err) {
return return
} }
@ -287,11 +336,9 @@ func getModelsOfSimulator(c *gin.Context) {
return return
} }
serializer := common.SimulationModelsSerializer{c, allModels}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"models": serializer.Response(), "models": allModels,
}) })
} }
// sendActionToSimulator godoc // sendActionToSimulator godoc
@ -300,26 +347,30 @@ func getModelsOfSimulator(c *gin.Context) {
// @Tags simulators // @Tags simulators
// @Produce json // @Produce json
// @Param inputAction query string true "Action for simulator" // @Param inputAction query string true "Action for simulator"
// @Success 200 "OK." // @Success 200 {object} docs.ResponseError "Action sent successfully"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 403 "Access forbidden." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 404 "Not found" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 "Internal server error" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param simulatorID path int true "Simulator ID" // @Param simulatorID path int true "Simulator ID"
// @Router /simulators/{simulatorID}/action [post] // @Router /simulators/{simulatorID}/action [post]
func sendActionToSimulator(c *gin.Context) { func sendActionToSimulator(c *gin.Context) {
err := common.ValidateRole(c, common.ModelSimulatorAction, common.Update) err := common.ValidateRole(c, common.ModelSimulatorAction, common.Update)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
simulatorID, err := strconv.Atoi(c.Param("simulatorID")) // Get the ID of the simulator from the context
simulatorID, err := common.UintParamFromCtx(c, "simulatorID")
if err != nil { if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter")
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "success": false,
"message": fmt.Sprintf("Could not get simulator's ID from context"),
}) })
return return
} }
@ -358,6 +409,7 @@ func sendActionToSimulator(c *gin.Context) {
} }
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "OK.", "message": "OK.",
}) })
} }

View file

@ -25,13 +25,12 @@ func (s *Simulator) ByID(id uint) error {
return nil return nil
} }
func (s *Simulator) update(modifiedSimulator common.SimulatorResponse) error { func (s *Simulator) update(updatedSimulator Simulator) error {
db := common.GetDB() db := common.GetDB()
err := db.Model(s).Updates(modifiedSimulator).Error err := db.Model(s).Updates(updatedSimulator).Error
return err return err
} }
func (s *Simulator) delete() error { func (s *Simulator) delete() error {

View file

@ -1,7 +1,11 @@
package simulator package simulator
import ( import (
"encoding/json" "fmt"
"github.com/jinzhu/gorm"
"github.com/jinzhu/gorm/dialects/postgres"
"github.com/stretchr/testify/assert"
"os"
"testing" "testing"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -10,62 +14,398 @@ import (
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
) )
// Test /simulator endpoints var router *gin.Engine
func TestSimulatorEndpoints(t *testing.T) { var db *gorm.DB
var token string type SimulatorRequest struct {
UUID string `json:"uuid,omitempty"`
Host string `json:"host,omitempty"`
Modeltype string `json:"modelType,omitempty"`
State string `json:"state,omitempty"`
Properties postgres.Jsonb `json:"properties,omitempty"`
}
var myModels = []common.SimulationModelResponse{common.SimulationModelA_response, common.SimulationModelB_response} func TestMain(m *testing.M) {
var msgModels = common.ResponseMsgSimulationModels{SimulationModels: myModels}
var simulatorC_msg = common.ResponseMsgSimulator{Simulator: common.SimulatorC_response}
var simulatorCupdated_msg = common.ResponseMsgSimulator{Simulator: common.SimulatorCUpdated_response}
var mySimulators = []common.SimulatorResponse{common.SimulatorA_response, common.SimulatorB_response}
var msgSimulators = common.ResponseMsgSimulators{Simulators: mySimulators}
db := common.DummyInitDB() db = common.DummyInitDB()
defer db.Close() defer db.Close()
common.DummyPopulateDB(db)
router := gin.Default() router = gin.Default()
api := router.Group("/api") api := router.Group("/api")
// All endpoints require authentication except when someone wants to
// login (POST /authenticate)
user.RegisterAuthenticate(api.Group("/authenticate")) user.RegisterAuthenticate(api.Group("/authenticate"))
api.Use(user.Authentication(true)) api.Use(user.Authentication(true))
RegisterSimulatorEndpoints(api.Group("/simulators")) RegisterSimulatorEndpoints(api.Group("/simulators"))
credjson, _ := json.Marshal(common.CredAdmin) os.Exit(m.Run())
msgOKjson, _ := json.Marshal(common.MsgOK) }
msgModelsjson, _ := json.Marshal(msgModels)
msgSimulatorsjson, _ := json.Marshal(msgSimulators) func TestAddSimulatorAsAdmin(t *testing.T) {
msgSimulatorjson, _ := json.Marshal(simulatorC_msg) common.DropTables(db)
simulatorCjson, _ := json.Marshal(simulatorC_msg) common.MigrateModels(db)
simulatorCupdatedjson, _ := json.Marshal(simulatorCupdated_msg) common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200) // authenticate as admin
token, err := common.NewAuthenticateForTest(router,
// test GET simulators/ "/api/authenticate", "POST", common.AdminCredentials)
common.TestEndpoint(t, router, token, "/api/simulators", "GET", nil, 200, msgSimulatorsjson) assert.NoError(t, err)
// test POST simulators/ // test POST simulators/ $newSimulator
common.TestEndpoint(t, router, token, "/api/simulators", "POST", simulatorCjson, 200, msgOKjson) newSimulator := SimulatorRequest{
UUID: common.SimulatorA.UUID,
// test GET simulators/:SimulatorID Host: common.SimulatorA.Host,
common.TestEndpoint(t, router, token, "/api/simulators/3", "GET", nil, 200, msgSimulatorjson) Modeltype: common.SimulatorA.Modeltype,
State: common.SimulatorA.State,
// test PUT simulators/:SimulatorID Properties: common.SimulatorA.Properties,
common.TestEndpoint(t, router, token, "/api/simulators/3", "PUT", simulatorCupdatedjson, 200, msgOKjson) }
common.TestEndpoint(t, router, token, "/api/simulators/3", "GET", nil, 200, simulatorCupdatedjson) code, resp, err := common.NewTestEndpoint(router, token,
"/api/simulators", "POST", common.KeyModels{"simulator": newSimulator})
// test DELETE simulators/:SimulatorID assert.NoError(t, err)
common.TestEndpoint(t, router, token, "/api/simulators/3", "DELETE", nil, 200, msgOKjson) assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
common.TestEndpoint(t, router, token, "/api/simulators", "GET", nil, 200, msgSimulatorsjson)
// Compare POST's response with the newSimulator
// test GET simulators/:SimulatorID/models err = common.CompareResponse(resp, common.KeyModels{"simulator": newSimulator})
common.TestEndpoint(t, router, token, "/api/simulators/1/models", "GET", nil, 200, msgModelsjson) assert.NoError(t, err)
// TODO add tests for other return codes // Read newSimulator's ID from the response
newSimulatorID, err := common.GetResponseID(resp)
assert.NoError(t, err)
// Get the newSimulator
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare GET's response with the newSimulator
err = common.CompareResponse(resp, common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
}
func TestAddSimulatorAsUser(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// authenticate as user
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
// test POST simulators/ $newSimulator
newSimulator := SimulatorRequest{
UUID: common.SimulatorA.UUID,
Host: common.SimulatorA.Host,
Modeltype: common.SimulatorA.Modeltype,
State: common.SimulatorA.State,
Properties: common.SimulatorA.Properties,
}
// This should fail with unprocessable entity 422 error code
// Normal users are not allowed to add simulators
code, resp, err := common.NewTestEndpoint(router, token,
"/api/simulators", "POST", common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
}
func TestUpdateSimulatorAsAdmin(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// authenticate as admin
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.AdminCredentials)
assert.NoError(t, err)
// test POST simulators/ $newSimulator
newSimulator := SimulatorRequest{
UUID: common.SimulatorA.UUID,
Host: common.SimulatorA.Host,
Modeltype: common.SimulatorA.Modeltype,
State: common.SimulatorA.State,
Properties: common.SimulatorA.Properties,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/simulators", "POST", common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare POST's response with the newSimulator
err = common.CompareResponse(resp, common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
// Read newSimulator's ID from the response
newSimulatorID, err := common.GetResponseID(resp)
assert.NoError(t, err)
// Test PUT simulators
newSimulator.Host = "ThisIsMyNewHost"
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "PUT", common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare PUT's response with the updated newSimulator
err = common.CompareResponse(resp, common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
// Get the updated newSimulator
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare GET's response with the updated newSimulator
err = common.CompareResponse(resp, common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
}
func TestUpdateSimulatorAsUser(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// authenticate as admin
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.AdminCredentials)
assert.NoError(t, err)
// test POST simulators/ $newSimulator
newSimulator := SimulatorRequest{
UUID: common.SimulatorA.UUID,
Host: common.SimulatorA.Host,
Modeltype: common.SimulatorA.Modeltype,
State: common.SimulatorA.State,
Properties: common.SimulatorA.Properties,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/simulators", "POST", common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Read newSimulator's ID from the response
newSimulatorID, err := common.GetResponseID(resp)
assert.NoError(t, err)
// authenticate as user
token, err = common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
// Test PUT simulators
// This should fail with unprocessable entity status code 422
newSimulator.Host = "ThisIsMyNewHost"
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "PUT", common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
}
func TestDeleteSimulatorAsAdmin(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// authenticate as admin
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.AdminCredentials)
assert.NoError(t, err)
// test POST simulators/ $newSimulator
newSimulator := SimulatorRequest{
UUID: common.SimulatorA.UUID,
Host: common.SimulatorA.Host,
Modeltype: common.SimulatorA.Modeltype,
State: common.SimulatorA.State,
Properties: common.SimulatorA.Properties,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/simulators", "POST", common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Read newSimulator's ID from the response
newSimulatorID, err := common.GetResponseID(resp)
assert.NoError(t, err)
// Count the number of all the simulators returned for admin
initialNumber, err := common.LengthOfResponse(router, token,
"/api/simulators", "GET", nil)
assert.NoError(t, err)
// Delete the added newSimulator
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "DELETE", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare DELETE's response with the newSimulator
err = common.CompareResponse(resp, common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
// Again count the number of all the simulators returned
finalNumber, err := common.LengthOfResponse(router, token,
"/api/simulators", "GET", nil)
assert.NoError(t, err)
assert.Equal(t, finalNumber, initialNumber-1)
}
func TestDeleteSimulatorAsUser(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// authenticate as admin
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.AdminCredentials)
assert.NoError(t, err)
// test POST simulators/ $newSimulator
newSimulator := SimulatorRequest{
UUID: common.SimulatorA.UUID,
Host: common.SimulatorA.Host,
Modeltype: common.SimulatorA.Modeltype,
State: common.SimulatorA.State,
Properties: common.SimulatorA.Properties,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/simulators", "POST", common.KeyModels{"simulator": newSimulator})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Read newSimulator's ID from the response
newSimulatorID, err := common.GetResponseID(resp)
assert.NoError(t, err)
// authenticate as user
token, err = common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
// Test DELETE simulators
// This should fail with unprocessable entity status code 422
newSimulator.Host = "ThisIsMyNewHost"
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "DELETE", nil)
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
}
func TestGetAllSimulators(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// authenticate as admin
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.AdminCredentials)
assert.NoError(t, err)
// get the length of the GET all simulators response for user
initialNumber, err := common.LengthOfResponse(router, token,
"/api/simulators", "GET", nil)
assert.NoError(t, err)
// test POST simulators/ $newSimulatorA
newSimulatorA := SimulatorRequest{
UUID: common.SimulatorA.UUID,
Host: common.SimulatorA.Host,
Modeltype: common.SimulatorA.Modeltype,
State: common.SimulatorA.State,
Properties: common.SimulatorA.Properties,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/simulators", "POST", common.KeyModels{"simulator": newSimulatorA})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// test POST simulators/ $newSimulatorB
newSimulatorB := SimulatorRequest{
UUID: common.SimulatorB.UUID,
Host: common.SimulatorB.Host,
Modeltype: common.SimulatorB.Modeltype,
State: common.SimulatorB.State,
Properties: common.SimulatorB.Properties,
}
code, resp, err = common.NewTestEndpoint(router, token,
"/api/simulators", "POST", common.KeyModels{"simulator": newSimulatorB})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// get the length of the GET all simulators response again
finalNumber, err := common.LengthOfResponse(router, token,
"/api/simulators", "GET", nil)
assert.NoError(t, err)
assert.Equal(t, finalNumber, initialNumber+2)
// authenticate as normal user
token, err = common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
// get the length of the GET all simulators response again
finalNumber2, err := common.LengthOfResponse(router, token,
"/api/simulators", "GET", nil)
assert.NoError(t, err)
assert.Equal(t, finalNumber2, initialNumber+2)
}
func TestGetSimulationModelsOfSimulator(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// authenticate as admin
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.AdminCredentials)
assert.NoError(t, err)
// test POST simulators/ $newSimulatorA
newSimulatorA := SimulatorRequest{
UUID: common.SimulatorA.UUID,
Host: common.SimulatorA.Host,
Modeltype: common.SimulatorA.Modeltype,
State: common.SimulatorA.State,
Properties: common.SimulatorA.Properties,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/simulators", "POST", common.KeyModels{"simulator": newSimulatorA})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Read newSimulator's ID from the response
newSimulatorID, err := common.GetResponseID(resp)
assert.NoError(t, err)
// test GET simulators/ID/models
// TODO how to properly test this without using simulation model endpoints?
numberOfModels, err := common.LengthOfResponse(router, token,
fmt.Sprintf("/api/simulators/%v/models", newSimulatorID), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
assert.Equal(t, 0, numberOfModels)
// authenticate as normal user
token, err = common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
// test GET simulators/ID/models
// TODO how to properly test this without using simulation model endpoints?
numberOfModels, err = common.LengthOfResponse(router, token,
fmt.Sprintf("/api/simulators/%v/models", newSimulatorID), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
assert.Equal(t, 0, numberOfModels)
} }

View file

@ -0,0 +1,82 @@
package simulator
import (
"github.com/jinzhu/gorm/dialects/postgres"
"gopkg.in/go-playground/validator.v9"
)
var validate *validator.Validate
type validNewSimulator struct {
UUID string `form:"UUID" validate:"required"`
Host string `form:"Host" validate:"required"`
Modeltype string `form:"Modeltype" validate:"required"`
Properties postgres.Jsonb `form:"Properties" validate:"required"`
State string `form:"State"`
}
type validUpdatedSimulator struct {
UUID string `form:"UUID" validate:"omitempty"`
Host string `form:"Host" validate:"omitempty"`
Modeltype string `form:"Modeltype" validate:"omitempty"`
Properties postgres.Jsonb `form:"Properties" validate:"omitempty"`
State string `form:"State" validate:"omitempty"`
}
type addSimulatorRequest struct {
validNewSimulator `json:"simulator"`
}
type updateSimulatorRequest struct {
validUpdatedSimulator `json:"simulator"`
}
func (r *addSimulatorRequest) validate() error {
validate = validator.New()
errs := validate.Struct(r)
return errs
}
func (r *validUpdatedSimulator) validate() error {
validate = validator.New()
errs := validate.Struct(r)
return errs
}
func (r *addSimulatorRequest) createSimulator() Simulator {
var s Simulator
s.UUID = r.UUID
s.Host = r.Host
s.Modeltype = r.Modeltype
s.Properties = r.Properties
if r.State != "" {
s.State = r.State
}
return s
}
func (r *updateSimulatorRequest) updatedSimulator(oldSimulator Simulator) (Simulator, error) {
// Use the old Simulator as a basis for the updated Simulator `s`
s := oldSimulator
if r.UUID != "" {
s.UUID = r.UUID
}
if r.Host != "" {
s.Host = r.Host
}
if r.Modeltype != "" {
s.Modeltype = r.Modeltype
}
if r.State != "" {
s.State = r.State
}
// TODO check for empty properties?
s.Properties = r.Properties
return s, nil
}