- revise testing of signal endpoints

- revise documentation for swaggo
- clean up testdata, serializers and responses
- add validators for signal endpoints
- revise signal endpoint implementations
This commit is contained in:
Sonja Happ 2019-09-06 16:01:59 +02:00
parent 8882f6069c
commit 7544c863de
9 changed files with 548 additions and 218 deletions

View file

@ -12,28 +12,12 @@ type FileResponse struct {
SimulationModelID uint `json:"simulationModelID"`
}
type SignalResponse struct {
Name string `json:"name"`
Unit string `json:"unit"`
Index uint `json:"index"`
Direction string `json:"direction"`
SimulationModelID uint `json:"simulationModelID"`
}
// Response messages
type ResponseMsg struct {
Message string `json:"message"`
}
type ResponseMsgSignals struct {
Signals []SignalResponse `json:"signals"`
}
type ResponseMsgSignal struct {
Signal SignalResponse `json:"signal"`
}
type ResponseMsgFiles struct {
Files []FileResponse `json:"files"`
}

View file

@ -40,35 +40,3 @@ func (self *FileSerializerNoAssoc) Response() FileResponse {
}
return response
}
// Signal/s Serializers
type SignalsSerializer struct {
Ctx *gin.Context
Signals []Signal
}
func (self *SignalsSerializer) Response() []SignalResponse {
response := []SignalResponse{}
for _, s := range self.Signals {
serializer := SignalSerializer{self.Ctx, s}
response = append(response, serializer.Response())
}
return response
}
type SignalSerializer struct {
Ctx *gin.Context
Signal
}
func (self *SignalSerializer) Response() SignalResponse {
response := SignalResponse{
Name: self.Name,
Unit: self.Unit,
Direction: self.Direction,
SimulationModelID: self.SimulationModelID,
Index: self.Index,
}
return response
}

View file

@ -117,13 +117,6 @@ var OutSignalA = Signal{
Unit: "V",
}
var OutSignalA_response = SignalResponse{
Name: OutSignalA.Name,
Direction: OutSignalA.Direction,
Index: OutSignalA.Index,
Unit: OutSignalA.Unit,
}
var OutSignalB = Signal{
Name: "outSignal_B",
Direction: "out",
@ -131,13 +124,6 @@ var OutSignalB = Signal{
Unit: "V",
}
var OutSignalB_response = SignalResponse{
Name: OutSignalB.Name,
Direction: OutSignalB.Direction,
Index: OutSignalB.Index,
Unit: OutSignalB.Unit,
}
var InSignalA = Signal{
Name: "inSignal_A",
Direction: "in",
@ -145,13 +131,6 @@ var InSignalA = Signal{
Unit: "A",
}
var InSignalA_response = SignalResponse{
Name: InSignalA.Name,
Direction: InSignalA.Direction,
Index: InSignalA.Index,
Unit: InSignalA.Unit,
}
var InSignalB = Signal{
Name: "inSignal_B",
Direction: "in",
@ -159,41 +138,6 @@ var InSignalB = Signal{
Unit: "A",
}
var InSignalB_response = SignalResponse{
Name: InSignalB.Name,
Direction: InSignalB.Direction,
Index: InSignalB.Index,
Unit: InSignalB.Unit,
}
var InSignalC = Signal{
Name: "inSignal_C",
Direction: "in",
Index: 2,
Unit: "A",
}
var InSignalC_response = SignalResponse{
Name: InSignalC.Name,
Direction: InSignalC.Direction,
Index: InSignalC.Index,
Unit: InSignalC.Unit,
}
var InSignalCUpdated = Signal{
Name: "inSignalupdated_C",
Direction: InSignalC.Direction,
Index: InSignalC.Index,
Unit: "Ohm",
}
var InSignalCUpdated_response = SignalResponse{
Name: InSignalCUpdated.Name,
Direction: InSignalCUpdated.Direction,
Index: InSignalCUpdated.Index,
Unit: InSignalCUpdated.Unit,
}
// Dashboards
var DashboardA = Dashboard{

View file

@ -64,3 +64,11 @@ type ResponseWidgets struct {
type ResponseWidget struct {
widget common.Widget
}
type ResponseSignals struct {
signals []common.Signal
}
type ResponseSignal struct {
signal common.Signal
}

View file

@ -1,6 +1,7 @@
package signal
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
@ -24,11 +25,10 @@ func RegisterSignalEndpoints(r *gin.RouterGroup) {
// @Tags signals
// @Param direction query string true "Direction of signal (in or out)"
// @Param modelID query string true "Model ID of signals to be obtained"
// @Success 200 {array} common.Signal "Requested signals."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Success 200 {object} docs.ResponseSignals "Signals which belong to simulation model"
// @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 {object} docs.ResponseError "Internal server error"
// @Router /signals [get]
func getSignals(c *gin.Context) {
@ -44,9 +44,9 @@ func getSignals(c *gin.Context) {
} else if direction == "out" {
mapping = "OutputMapping"
} else {
errormsg := "Bad request. Direction has to be in or out"
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
"success": false,
"error": "Bad request. Direction has to be in or out",
})
return
}
@ -58,9 +58,8 @@ func getSignals(c *gin.Context) {
return
}
serializer := common.SignalsSerializer{c, sigs}
c.JSON(http.StatusOK, gin.H{
"signals": serializer.Response(),
"signals": sigs,
})
}
@ -70,31 +69,35 @@ func getSignals(c *gin.Context) {
// @Accept json
// @Produce json
// @Tags signals
// @Param inputSignal body common.ResponseMsgSignal true "A signal to be added to the model incl. direction and model ID to which signal shall be added"
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param inputSignal body signal.validNewSignal true "A signal to be added to the model incl. direction and model ID to which signal shall be added"
// @Success 200 {object} docs.ResponseSignal "Signal that was added"
// @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 {object} docs.ResponseError "Internal server error"
// @Router /signals [post]
func addSignal(c *gin.Context) {
var newSignalData common.ResponseMsgSignal
err := c.BindJSON(&newSignalData)
if err != nil {
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
var req addSignalRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
"success": false,
"message": fmt.Sprintf("%v", err),
})
return
}
var newSignal Signal
newSignal.Index = newSignalData.Signal.Index
newSignal.SimulationModelID = newSignalData.Signal.SimulationModelID
newSignal.Direction = newSignalData.Signal.Direction
newSignal.Unit = newSignalData.Signal.Unit
newSignal.Name = newSignalData.Signal.Name
// Validate the request
if err := req.validate(); err != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return
}
// Create the new signal from the request
newSignal := req.createSignal()
ok, _ := simulationmodel.CheckPermissions(c, common.Update, "body", int(newSignal.SimulationModelID))
if !ok {
@ -102,12 +105,15 @@ func addSignal(c *gin.Context) {
}
// Add signal to model
err = newSignal.addToSimulationModel()
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
err := newSignal.addToSimulationModel()
if err != nil {
common.ProvideErrorResponse(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"signal": newSignal.Signal,
})
}
// updateSignal godoc
@ -115,35 +121,58 @@ func addSignal(c *gin.Context) {
// @ID updateSignal
// @Tags signals
// @Produce json
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param inputSignal body signal.validUpdatedSignal true "A signal to be updated"
// @Success 200 {object} docs.ResponseSignal "Signal that was updated"
// @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param signalID path int true "ID of signal to be updated"
// @Router /signals/{signalID} [put]
func updateSignal(c *gin.Context) {
ok, sig := checkPermissions(c, common.Delete)
ok, oldSignal := checkPermissions(c, common.Delete)
if !ok {
return
}
var modifiedSignal common.ResponseMsgSignal
err := c.BindJSON(&modifiedSignal)
if err != nil {
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
var req updateSignalRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
"success": false,
"message": fmt.Sprintf("%v", err),
})
return
}
err = sig.update(modifiedSignal.Signal)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
// Validate the request
if err := req.validate(); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return
}
// Create the updatedSignal from oldDashboard
updatedSignal, err := req.updatedSignal(oldSignal)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return
}
// Update the signal in the DB
err = oldSignal.update(updatedSignal)
if err != nil {
common.ProvideErrorResponse(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"signal": updatedSignal.Signal,
})
}
// getSignal godoc
@ -151,11 +180,11 @@ func updateSignal(c *gin.Context) {
// @ID getSignal
// @Tags signals
// @Produce json
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Success 200 {object} docs.ResponseSignal "Signal that was requested"
// @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param signalID path int true "ID of signal to be obtained"
// @Router /signals/{signalID} [get]
func getSignal(c *gin.Context) {
@ -164,9 +193,8 @@ func getSignal(c *gin.Context) {
return
}
serializer := common.SignalSerializer{c, sig.Signal}
c.JSON(http.StatusOK, gin.H{
"signal": serializer.Response(),
"signal": sig.Signal,
})
}
@ -175,11 +203,11 @@ func getSignal(c *gin.Context) {
// @ID deleteSignal
// @Tags signals
// @Produce json
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Success 200 {object} docs.ResponseSignal "Signal that was deleted"
// @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param signalID path int true "ID of signal to be deleted"
// @Router /signals/{signalID} [delete]
func deleteSignal(c *gin.Context) {
@ -190,10 +218,12 @@ func deleteSignal(c *gin.Context) {
}
err := sig.delete()
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
if err != nil {
common.ProvideErrorResponse(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"signal": sig.Signal,
})
}

View file

@ -1,8 +1,6 @@
package signal
import (
"fmt"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel"
)
@ -21,7 +19,7 @@ func (s *Signal) byID(id uint) error {
db := common.GetDB()
err := db.Find(s, id).Error
if err != nil {
return fmt.Errorf("Signal with id=%v does not exist", id)
return err
}
return nil
}
@ -64,7 +62,7 @@ func (s *Signal) addToSimulationModel() error {
return err
}
func (s *Signal) update(modifiedSignal common.SignalResponse) error {
func (s *Signal) update(modifiedSignal Signal) error {
db := common.GetDB()
err := db.Model(s).Updates(map[string]interface{}{

View file

@ -17,15 +17,18 @@ func checkPermissions(c *gin.Context, operation common.CRUD) (bool, Signal) {
err := common.ValidateRole(c, common.ModelSignal, operation)
if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("Access denied (role validation failed): %v", err),
})
return false, sig
}
signalID, err := strconv.Atoi(c.Param("signalID"))
if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of signalID path parameter")
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
"success": false,
"error": fmt.Sprintf("Bad request. No or incorrect format of signalID path parameter"),
})
return false, sig
}

View file

@ -1,73 +1,395 @@
package signal
import (
"encoding/json"
"testing"
"github.com/gin-gonic/gin"
"fmt"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"github.com/jinzhu/gorm/dialects/postgres"
"github.com/stretchr/testify/assert"
"os"
"testing"
)
// Test /models endpoints
func TestSignalEndpoints(t *testing.T) {
var router *gin.Engine
var db *gorm.DB
var token string
type SignalRequest struct {
Name string `json:"name,omitempty"`
Unit string `json:"unit,omitempty"`
Index uint `json:"index,omitempty"`
Direction string `json:"direction,omitempty"`
SimulationModelID uint `json:"simulationModelID,omitempty"`
}
var myInSignals = []common.SignalResponse{common.InSignalA_response, common.InSignalB_response}
var myOutSignals = []common.SignalResponse{common.OutSignalA_response, common.OutSignalB_response}
var msgInSignals = common.ResponseMsgSignals{Signals: myInSignals}
var msgInSignalC = common.ResponseMsgSignal{Signal: common.InSignalC_response}
var msgInSignalCupdated = common.ResponseMsgSignal{Signal: common.InSignalCUpdated_response}
var msgOutSignals = common.ResponseMsgSignals{Signals: myOutSignals}
type SimulationModelRequest struct {
Name string `json:"name,omitempty"`
ScenarioID uint `json:"scenarioID,omitempty"`
SimulatorID uint `json:"simulatorID,omitempty"`
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
}
db := common.DummyInitDB()
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"`
}
type ScenarioRequest struct {
Name string `json:"name,omitempty"`
Running bool `json:"running,omitempty"`
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
}
func addScenarioAndSimulatorAndSimulationModel() (scenarioID uint, simulatorID uint, simulationModelID uint) {
// authenticate as admin
token, _ := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.AdminCredentials)
// POST $newSimulatorA
newSimulatorA := SimulatorRequest{
UUID: common.SimulatorA.UUID,
Host: common.SimulatorA.Host,
Modeltype: common.SimulatorA.Modeltype,
State: common.SimulatorA.State,
Properties: common.SimulatorA.Properties,
}
_, resp, _ := common.NewTestEndpoint(router, token,
"/api/simulators", "POST", common.KeyModels{"simulator": newSimulatorA})
// Read newSimulator's ID from the response
newSimulatorID, _ := common.GetResponseID(resp)
// authenticate as normal user
token, _ = common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
// POST $newScenario
newScenario := ScenarioRequest{
Name: common.ScenarioA.Name,
Running: common.ScenarioA.Running,
StartParameters: common.ScenarioA.StartParameters,
}
_, resp, _ = common.NewTestEndpoint(router, token,
"/api/scenarios", "POST", common.KeyModels{"scenario": newScenario})
// Read newScenario's ID from the response
newScenarioID, _ := common.GetResponseID(resp)
// test POST models/ $newSimulationModel
newSimulationModel := SimulationModelRequest{
Name: common.SimulationModelA.Name,
ScenarioID: uint(newScenarioID),
SimulatorID: uint(newSimulatorID),
StartParameters: common.SimulationModelA.StartParameters,
}
_, resp, _ = common.NewTestEndpoint(router, token,
"/api/models", "POST", common.KeyModels{"model": newSimulationModel})
// Read newSimulationModel's ID from the response
newSimulationModelID, _ := common.GetResponseID(resp)
return uint(newScenarioID), uint(newSimulatorID), uint(newSimulationModelID)
}
func TestMain(m *testing.M) {
db = common.DummyInitDB()
defer db.Close()
common.DummyPopulateDB(db)
router := gin.Default()
router = gin.Default()
api := router.Group("/api")
// All endpoints require authentication except when someone wants to
// login (POST /authenticate)
user.RegisterAuthenticate(api.Group("/authenticate"))
api.Use(user.Authentication(true))
// simulationmodel endpoints required here to first add a simulation to the DB
// that can be associated with a new signal model
simulationmodel.RegisterSimulationModelEndpoints(api.Group("/models"))
// scenario endpoints required here to first add a scenario to the DB
// that can be associated with a new simulation model
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
// simulator endpoints required here to first add a simulator to the DB
// that can be associated with a new simulation model
simulator.RegisterSimulatorEndpoints(api.Group("/simulators"))
RegisterSignalEndpoints(api.Group("/signals"))
credjson, _ := json.Marshal(common.CredUser)
msgOKjson, _ := json.Marshal(common.MsgOK)
msgInSignalsjson, _ := json.Marshal(msgInSignals)
msgOutSignalsjson, _ := json.Marshal(msgOutSignals)
inSignalCjson, _ := json.Marshal(msgInSignalC)
inSignalCupdatedjson, _ := json.Marshal(msgInSignalCupdated)
os.Exit(m.Run())
}
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
func TestAddSignal(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// test GET signals
common.TestEndpoint(t, router, token, "/api/signals?modelID=1&direction=in", "GET", nil, 200, msgInSignalsjson)
common.TestEndpoint(t, router, token, "/api/signals?modelID=1&direction=out", "GET", nil, 200, msgOutSignalsjson)
// prepare the content of the DB for testing
// by adding a scenario and a simulator to the DB
// using the respective endpoints of the API
_, _, simulationModelID := addScenarioAndSimulatorAndSimulationModel()
// test POST signals
common.TestEndpoint(t, router, token, "/api/signals", "POST", inSignalCjson, 200, msgOKjson)
// authenticate as normal user
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
// test GET signals/:signalID
common.TestEndpoint(t, router, token, "/api/signals/5", "GET", nil, 200, inSignalCjson)
// test POST signals/ $newSignal
newSignal := SignalRequest{
Name: common.InSignalA.Name,
Unit: common.InSignalA.Unit,
Direction: common.InSignalA.Direction,
Index: 1,
SimulationModelID: simulationModelID,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/signals", "POST", common.KeyModels{"signal": newSignal})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// test PUT signals/:signalID
common.TestEndpoint(t, router, token, "/api/signals/5", "PUT", inSignalCupdatedjson, 200, msgOKjson)
common.TestEndpoint(t, router, token, "/api/signals/5", "GET", nil, 200, inSignalCupdatedjson)
// Compare POST's response with the newSignal
err = common.CompareResponse(resp, common.KeyModels{"signal": newSignal})
assert.NoError(t, err)
// test DELETE signals/:signalID
common.TestEndpoint(t, router, token, "/api/signals/5", "DELETE", nil, 200, msgOKjson)
common.TestEndpoint(t, router, token, "/api/signals?modelID=1&direction=in", "GET", nil, 200, msgInSignalsjson)
common.TestEndpoint(t, router, token, "/api/signals?modelID=1&direction=out", "GET", nil, 200, msgOutSignalsjson)
// Read newSignal's ID from the response
newSignalID, err := common.GetResponseID(resp)
assert.NoError(t, err)
// TODO test GET models/:ModelID to check if POST and DELETE adapt InputLength correctly??
//common.TestEndpoint(t, router, token, "/api/models/1", "GET", nil, 200, string(msgModelAUpdated2json))
// Get the newSignal
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/signals/%v", newSignalID), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// TODO add testing for other return codes
// Compare GET's response with the newSSignal
err = common.CompareResponse(resp, common.KeyModels{"signal": newSignal})
assert.NoError(t, err)
// try to POST a malformed signal
// Required fields are missing
malformedNewSignal := SignalRequest{
Name: "ThisIsAMalformedRequest",
}
// this should NOT work and return a unprocessable entity 442 status code
code, resp, err = common.NewTestEndpoint(router, token,
"/api/signals", "POST", common.KeyModels{"model": malformedNewSignal})
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
}
func TestUpdateSignal(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// prepare the content of the DB for testing
// by adding a scenario and a simulator to the DB
// using the respective endpoints of the API
_, _, simulationModelID := addScenarioAndSimulatorAndSimulationModel()
// authenticate as normal user
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
// test POST signals/ $newSignal
newSignal := SignalRequest{
Name: common.InSignalA.Name,
Unit: common.InSignalA.Unit,
Direction: common.InSignalA.Direction,
Index: 1,
SimulationModelID: simulationModelID,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/signals", "POST", common.KeyModels{"signal": newSignal})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Read newSignal's ID from the response
newSignalID, err := common.GetResponseID(resp)
assert.NoError(t, err)
updatedSignal := SignalRequest{
Name: common.InSignalB.Name,
Unit: common.InSignalB.Unit,
Index: 1,
}
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/signals/%v", newSignalID), "PUT", common.KeyModels{"signal": updatedSignal})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare PUT's response with the updatedSignal
err = common.CompareResponse(resp, common.KeyModels{"signal": updatedSignal})
assert.NoError(t, err)
// Get the updatedSignal
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/signals/%v", newSignalID), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare GET's response with the updatedSignal
err = common.CompareResponse(resp, common.KeyModels{"signal": updatedSignal})
assert.NoError(t, err)
// try to update a signal that does not exist (should return not found 404 status code)
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/signals/%v", newSignalID+1), "PUT", common.KeyModels{"signal": updatedSignal})
assert.NoError(t, err)
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
}
func TestDeleteSignal(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// prepare the content of the DB for testing
// by adding a scenario and a simulator to the DB
// using the respective endpoints of the API
_, _, simulationModelID := addScenarioAndSimulatorAndSimulationModel()
// authenticate as normal user
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
// test POST signals/ $newSignal
newSignal := SignalRequest{
Name: common.InSignalA.Name,
Unit: common.InSignalA.Unit,
Direction: common.InSignalA.Direction,
Index: 1,
SimulationModelID: simulationModelID,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/signals", "POST", common.KeyModels{"signal": newSignal})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Read newSignal's ID from the response
newSignalID, err := common.GetResponseID(resp)
assert.NoError(t, err)
// Count the number of all the input signals returned for simulation model
initialNumber, err := common.LengthOfResponse(router, token,
fmt.Sprintf("/api/signals?modelID=%v&direction=in", simulationModelID), "GET", nil)
assert.NoError(t, err)
// add an output signal to make sure that counting of input signals works
newSignalout := SignalRequest{
Name: common.OutSignalA.Name,
Unit: common.OutSignalA.Unit,
Direction: common.OutSignalA.Direction,
Index: 1,
SimulationModelID: simulationModelID,
}
code, resp, err = common.NewTestEndpoint(router, token,
"/api/signals", "POST", common.KeyModels{"signal": newSignalout})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Delete the added newSignal
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/signals/%v", newSignalID), "DELETE", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare DELETE's response with the newSignal
err = common.CompareResponse(resp, common.KeyModels{"signal": newSignal})
assert.NoError(t, err)
// Again count the number of all the input signals returned for simulation model
finalNumber, err := common.LengthOfResponse(router, token,
fmt.Sprintf("/api/signals?modelID=%v&direction=in", simulationModelID), "GET", nil)
assert.NoError(t, err)
assert.Equal(t, initialNumber-1, finalNumber)
}
func TestGetAllInputSignalsOfSimulationModel(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// prepare the content of the DB for testing
// by adding a scenario and a simulator to the DB
// using the respective endpoints of the API
_, _, simulationModelID := addScenarioAndSimulatorAndSimulationModel()
// authenticate as normal user
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
// Count the number of all the input signals returned for simulation model
initialNumber, err := common.LengthOfResponse(router, token,
fmt.Sprintf("/api/signals?modelID=%v&direction=in", simulationModelID), "GET", nil)
assert.NoError(t, err)
// test POST signals/ $newSignal
newSignalA := SignalRequest{
Name: common.InSignalA.Name,
Unit: common.InSignalA.Unit,
Direction: common.InSignalA.Direction,
Index: 1,
SimulationModelID: simulationModelID,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/signals", "POST", common.KeyModels{"signal": newSignalA})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// add a second input signal
newSignalB := SignalRequest{
Name: common.InSignalB.Name,
Unit: common.InSignalB.Unit,
Direction: common.InSignalB.Direction,
Index: 2,
SimulationModelID: simulationModelID,
}
code, resp, err = common.NewTestEndpoint(router, token,
"/api/signals", "POST", common.KeyModels{"signal": newSignalB})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// add an output signal
newSignalAout := SignalRequest{
Name: common.OutSignalA.Name,
Unit: common.OutSignalA.Unit,
Direction: common.OutSignalA.Direction,
Index: 1,
SimulationModelID: simulationModelID,
}
code, resp, err = common.NewTestEndpoint(router, token,
"/api/signals", "POST", common.KeyModels{"signal": newSignalAout})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// add a second output signal
newSignalBout := SignalRequest{
Name: common.OutSignalB.Name,
Unit: common.OutSignalB.Unit,
Direction: common.OutSignalB.Direction,
Index: 1,
SimulationModelID: simulationModelID,
}
code, resp, err = common.NewTestEndpoint(router, token,
"/api/signals", "POST", common.KeyModels{"signal": newSignalBout})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Again count the number of all the input signals returned for simulation model
finalNumber, err := common.LengthOfResponse(router, token,
fmt.Sprintf("/api/signals?modelID=%v&direction=in", simulationModelID), "GET", nil)
assert.NoError(t, err)
assert.Equal(t, initialNumber+2, finalNumber)
}

View file

@ -0,0 +1,73 @@
package signal
import (
"gopkg.in/go-playground/validator.v9"
)
var validate *validator.Validate
type validNewSignal struct {
Name string `form:"Name" validate:"required"`
Unit string `form:"unit" validate:"omitempty"`
Index uint `form:"index" validate:"required"`
Direction string `form:"direction" validate:"required,oneof=in out"`
SimulationModelID uint `form:"simulationModelID" validate:"required"`
}
type validUpdatedSignal struct {
Name string `form:"Name" validate:"omitempty"`
Unit string `form:"unit" validate:"omitempty"`
Index uint `form:"index" validate:"omitempty"`
}
type addSignalRequest struct {
validNewSignal `json:"signal"`
}
type updateSignalRequest struct {
validUpdatedSignal `json:"signal"`
}
func (r *addSignalRequest) validate() error {
validate = validator.New()
errs := validate.Struct(r)
return errs
}
func (r *validUpdatedSignal) validate() error {
validate = validator.New()
errs := validate.Struct(r)
return errs
}
func (r *addSignalRequest) createSignal() Signal {
var s Signal
s.Name = r.Name
s.Unit = r.Unit
s.Index = r.Index
s.Direction = r.Direction
s.SimulationModelID = r.SimulationModelID
return s
}
func (r *updateSignalRequest) updatedSignal(oldSignal Signal) (Signal, error) {
// Use the old Signal as a basis for the updated Signal `s`
s := oldSignal
if r.Name != "" {
s.Name = r.Name
}
if r.Index != 0 {
// TODO this implies that we start indexing at 1
s.Index = r.Index
}
if r.Unit != "" {
s.Unit = r.Unit
}
return s, nil
}