diff --git a/common/responses.go b/common/responses.go deleted file mode 100644 index 8ced76a..0000000 --- a/common/responses.go +++ /dev/null @@ -1,27 +0,0 @@ -package common - -type FileResponse struct { - Name string `json:"name"` - ID uint `json:"id"` - Type string `json:"type"` - Size uint `json:"size"` - ImageWidth uint `json:"imageHeight"` - ImageHeight uint `json:"imageWidth"` - Date string `json:"date"` - WidgetID uint `json:"widgetID"` - SimulationModelID uint `json:"simulationModelID"` -} - -// Response messages - -type ResponseMsg struct { - Message string `json:"message"` -} - -type ResponseMsgFiles struct { - Files []FileResponse `json:"files"` -} - -type ResponseMsgFile struct { - File FileResponse `json:"file"` -} diff --git a/common/serializers.go b/common/serializers.go deleted file mode 100644 index d3b1344..0000000 --- a/common/serializers.go +++ /dev/null @@ -1,42 +0,0 @@ -package common - -import ( - "github.com/gin-gonic/gin" -) - -// File/s Serializers - -type FilesSerializerNoAssoc struct { - Ctx *gin.Context - Files []File -} - -func (self *FilesSerializerNoAssoc) Response() []FileResponse { - response := []FileResponse{} - for _, files := range self.Files { - serializer := FileSerializerNoAssoc{self.Ctx, files} - response = append(response, serializer.Response()) - } - return response -} - -type FileSerializerNoAssoc struct { - Ctx *gin.Context - File -} - -func (self *FileSerializerNoAssoc) Response() FileResponse { - response := FileResponse{ - Name: self.Name, - ID: self.ID, - //Path: self.Path, - Type: self.Type, - Size: self.Size, - ImageHeight: self.ImageHeight, - ImageWidth: self.ImageWidth, - Date: self.Date, - WidgetID: self.WidgetID, - SimulationModelID: self.SimulationModelID, - } - return response -} diff --git a/common/testdata.go b/common/testdata.go index 40ce8b7..7eba237 100644 --- a/common/testdata.go +++ b/common/testdata.go @@ -7,12 +7,6 @@ import ( "time" ) -// Generic - -var MsgOK = ResponseMsg{ - Message: "OK.", -} - // Users var StrPassword0 = "xyz789" var StrPasswordA = "abc123" @@ -160,16 +154,6 @@ var FileA = File{ Date: time.Now().String(), } -var FileA_response = FileResponse{ - ID: 1, - Name: FileA.Name, - Type: FileA.Type, - Size: FileA.Size, - ImageWidth: FileA.ImageWidth, - ImageHeight: FileA.ImageHeight, - Date: FileA.Date, -} - var FileB = File{ Name: "File_B", Type: "text/plain", @@ -179,16 +163,6 @@ var FileB = File{ Date: time.Now().String(), } -var FileB_response = FileResponse{ - ID: 2, - Name: FileB.Name, - Type: FileB.Type, - Size: FileB.Size, - ImageWidth: FileB.ImageWidth, - ImageHeight: FileB.ImageHeight, - Date: FileB.Date, -} - var FileC = File{ Name: "File_C", Type: "text/plain", diff --git a/doc/api/responses.go b/doc/api/responses.go index 16956be..7e38121 100644 --- a/doc/api/responses.go +++ b/doc/api/responses.go @@ -72,3 +72,11 @@ type ResponseSignals struct { type ResponseSignal struct { signal common.Signal } + +type ResponseFiles struct { + files []common.File +} + +type ResponseFile struct { + file common.File +} diff --git a/routes/file/file_endpoints.go b/routes/file/file_endpoints.go index fe60277..b634f42 100644 --- a/routes/file/file_endpoints.go +++ b/routes/file/file_endpoints.go @@ -25,11 +25,10 @@ func RegisterFileEndpoints(r *gin.RouterGroup) { // @ID getFiles // @Tags files // @Produce json -// @Success 200 {array} common.FileResponse "File parameters requested by user" -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" +// @Success 200 {object} docs.ResponseFiles "Files which belong to simulation model or widget" +// @Failure 404 {object} docs.ResponseError "Not found" +// @Failure 422 {object} docs.ResponseError "Unprocessable entity" +// @Failure 500 {object} docs.ResponseError "Internal server error" // @Param objectType query string true "Set to model for files of model, set to widget for files of widget" // @Param objectID query int true "ID of either model or widget of which files are requested" // @Router /files [get] @@ -37,18 +36,18 @@ func getFiles(c *gin.Context) { objectType := c.Request.URL.Query().Get("objectType") if objectType != "model" && objectType != "widget" { - errormsg := fmt.Sprintf("Bad request. Object type not supported for files: %s", objectType) c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "message": fmt.Sprintf("Bad request. Object type not supported for files: %s", objectType), }) return } objectID_s := c.Request.URL.Query().Get("objectID") objectID, err := strconv.Atoi(objectID_s) if err != nil { - errormsg := fmt.Sprintf("Bad request. Error on ID conversion: %s", err.Error()) c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "message": fmt.Sprintf("Bad request. Error on ID conversion: %s", err.Error()), }) return } @@ -85,9 +84,8 @@ func getFiles(c *gin.Context) { } } - serializer := common.FilesSerializerNoAssoc{c, files} c.JSON(http.StatusOK, gin.H{ - "files": serializer.Response(), + "files": files, }) } @@ -103,11 +101,11 @@ func getFiles(c *gin.Context) { // @Accept gif // @Accept model/x-cim // @Accept model/x-cim.zip -// @Success 200 "OK" -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" +// @Success 200 {object} docs.ResponseFile "File 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" // @Param inputFile formData file true "File to be uploaded" // @Param objectType query string true "Set to model for files of model, set to widget for files of widget" // @Param objectID query int true "ID of either model or widget of which files are requested" @@ -116,18 +114,18 @@ func addFile(c *gin.Context) { objectType := c.Request.URL.Query().Get("objectType") if objectType != "model" && objectType != "widget" { - errormsg := fmt.Sprintf("Bad request. Object type not supported for files: %s", objectType) c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "message": fmt.Sprintf("Bad request. Object type not supported for files: %s", objectType), }) return } objectID_s := c.Request.URL.Query().Get("objectID") objectID, err := strconv.Atoi(objectID_s) if err != nil { - errormsg := fmt.Sprintf("Bad request. Error on ID conversion: %s", err.Error()) c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "message": fmt.Sprintf("Bad request. Error on ID conversion: %s", err.Error()), }) return } @@ -149,20 +147,23 @@ func addFile(c *gin.Context) { // Extract file from POST request form file_header, err := c.FormFile("file") if err != nil { - errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error()) c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "message": fmt.Sprintf("Bad request. Get form error: %s", err.Error()), }) return } var newFile File err = newFile.register(file_header, objectType, uint(objectID)) - 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{ + "file": newFile.File, + }) } // getFile godoc @@ -175,11 +176,11 @@ func addFile(c *gin.Context) { // @Produce gif // @Produce model/x-cim // @Produce model/x-cim.zip -// @Success 200 "OK" -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" +// @Success 200 {object} docs.ResponseFile "File 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 fileID path int true "ID of the file to download" // @Router /files/{fileID} [get] func getFile(c *gin.Context) { @@ -191,9 +192,15 @@ func getFile(c *gin.Context) { } err := f.download(c) - if common.ProvideErrorResponse(c, err) { + if err != nil { + common.ProvideErrorResponse(c, err) return } + + c.JSON(http.StatusOK, gin.H{ + "file": f.File, + }) + } // updateFile godoc @@ -207,11 +214,11 @@ func getFile(c *gin.Context) { // @Accept gif // @Accept model/x-cim // @Accept model/x-cim.zip -// @Success 200 "OK" -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" +// @Success 200 {object} docs.ResponseFile "File 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 inputFile formData file true "File to be uploaded" // @Param fileID path int true "ID of the file to update" // @Router /files/{fileID} [put] @@ -226,28 +233,31 @@ func updateFile(c *gin.Context) { // Extract file from PUT request form err := c.Request.ParseForm() if err != nil { - errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error()) c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "message": fmt.Sprintf("Bad request. Get form error: %s", err.Error()), }) return } file_header, err := c.FormFile("file") if err != nil { - errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error()) c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "error": fmt.Sprintf("Bad request. Get form error: %s", err.Error()), }) return } err = f.update(file_header) - 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{ + "file": f.File, + }) } // deleteFile godoc @@ -255,11 +265,11 @@ func updateFile(c *gin.Context) { // @ID deleteFile // @Tags files // @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.ResponseFile "File 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 fileID path int true "ID of the file to update" // @Router /files/{fileID} [delete] func deleteFile(c *gin.Context) { @@ -271,9 +281,12 @@ func deleteFile(c *gin.Context) { } err := f.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{ + "file": f.File, + }) } diff --git a/routes/file/file_methods.go b/routes/file/file_methods.go index bdaac0e..687ea33 100644 --- a/routes/file/file_methods.go +++ b/routes/file/file_methods.go @@ -22,7 +22,7 @@ func (f *File) byPath(path string) error { db := common.GetDB() err := db.Where("Path = ?", path).Find(f).Error if err != nil { - return fmt.Errorf("File with path=%s does not exist", path) + return err } return err } @@ -31,7 +31,7 @@ func (f *File) byID(id uint) error { db := common.GetDB() err := db.Find(f, id).Error if err != nil { - return fmt.Errorf("File with id=%v does not exist", id) + return err } return nil } @@ -137,7 +137,6 @@ func (f *File) update(fileHeader *multipart.FileHeader) error { } fileData, err := ioutil.ReadAll(fileContent) - fmt.Println("File content: ", string(fileData)) defer fileContent.Close() db := common.GetDB() diff --git a/routes/file/file_middleware.go b/routes/file/file_middleware.go index 7d77992..06071fc 100644 --- a/routes/file/file_middleware.go +++ b/routes/file/file_middleware.go @@ -16,15 +16,18 @@ func checkPermissions(c *gin.Context, operation common.CRUD) (bool, File) { err := common.ValidateRole(c, common.ModelFile, 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, f } fileID, err := strconv.Atoi(c.Param("fileID")) if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of fileID path parameter") c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "error": fmt.Sprintf("Bad request. No or incorrect format of fileID path parameter"), }) return false, f } diff --git a/routes/file/file_test.go b/routes/file/file_test.go index c48e386..2ee9af5 100644 --- a/routes/file/file_test.go +++ b/routes/file/file_test.go @@ -2,11 +2,15 @@ package file import ( "bytes" - "encoding/json" "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" "io" "io/ioutil" @@ -17,185 +21,431 @@ import ( "testing" ) -// Test /files endpoints -func TestSignalEndpoints(t *testing.T) { +var router *gin.Engine +var db *gorm.DB - var token string - var filecontent = "This is my testfile" - var filecontent_update = "This is my updated testfile with a dot at the end." - var filename = "testfile.txt" - var filename_update = "testfileupdate.txt" +type SimulationModelRequest struct { + Name string `json:"name,omitempty"` + ScenarioID uint `json:"scenarioID,omitempty"` + SimulatorID uint `json:"simulatorID,omitempty"` + StartParameters postgres.Jsonb `json:"startParameters,omitempty"` +} - var myFiles = []common.FileResponse{common.FileA_response, common.FileB_response} - var msgFiles = common.ResponseMsgFiles{Files: myFiles} +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"` +} - db := common.DummyInitDB() +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) - // create a testfile in local folder - c1 := []byte(filecontent) - c2 := []byte(filecontent_update) - err := ioutil.WriteFile(filename, c1, 0644) - if err != nil { - panic(err) - } - err = ioutil.WriteFile(filename_update, c2, 0644) - if err != nil { - panic(err) - } - - 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")) RegisterFileEndpoints(api.Group("/files")) - credjson, err := json.Marshal(common.CredUser) - if err != nil { - panic(err) - } + os.Exit(m.Run()) +} - msgOKjson, err := json.Marshal(common.MsgOK) - if err != nil { - panic(err) - } +func TestAddFile(t *testing.T) { + common.DropTables(db) + common.MigrateModels(db) + common.DummyAddOnlyUserTableWithAdminAndUsersDB(db) - msgFilesjson, err := json.Marshal(msgFiles) - if err != nil { - panic(err) - } + // 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() - token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200) + // authenticate as normal user + token, err := common.NewAuthenticateForTest(router, + "/api/authenticate", "POST", common.UserACredentials) + assert.NoError(t, err) - // test GET files - common.TestEndpoint(t, router, token, "/api/files?objectID=1&objectType=widget", "GET", nil, 200, msgFilesjson) + // create a testfile.txt in local folder + c1 := []byte("This is my testfile\n") + err = ioutil.WriteFile("testfile.txt", c1, 0644) + assert.NoError(t, err) // test POST files bodyBuf := &bytes.Buffer{} bodyWriter := multipart.NewWriter(bodyBuf) fileWriter, err := bodyWriter.CreateFormFile("file", "testuploadfile.txt") - if err != nil { - fmt.Println("error writing to buffer") - panic(err) - } + assert.NoError(t, err, "writing to buffer") // open file handle - fh, err := os.Open(filename) - if err != nil { - fmt.Println("error opening file") - panic(err) - } + fh, err := os.Open("testfile.txt") + assert.NoError(t, err, "opening file") defer fh.Close() // io copy _, err = io.Copy(fileWriter, fh) - if err != nil { - fmt.Println("error on IO copy") - panic(err) - } + assert.NoError(t, err, "IO copy") contentType := bodyWriter.FormDataContentType() bodyWriter.Close() + //req, err := http.NewRequest("POST", "/api/files?objectID=1&objectType=widget", bodyBuf) + // Create the request w := httptest.NewRecorder() - req, err := http.NewRequest("POST", "/api/files?objectID=1&objectType=widget", bodyBuf) - req.Header.Add("Authorization", "Bearer "+token) - req.Header.Set("Content-Type", contentType) - if err != nil { - fmt.Println("error creating post request") - panic(err) - } + req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=model", simulationModelID), bodyBuf) + assert.NoError(t, err, "create request") + req.Header.Set("Content-Type", contentType) + req.Header.Add("Authorization", "Bearer "+token) router.ServeHTTP(w, req) - assert.Equal(t, 200, w.Code) - fmt.Println(w.Body.String()) - assert.Equal(t, string(msgOKjson), w.Body.String()) + assert.Equalf(t, 200, w.Code, "Response body: \n%v\n", w.Body) + fmt.Println(w.Body) - // test GET files/:fileID - w2 := httptest.NewRecorder() - req2, _ := http.NewRequest("GET", "/api/files/5", nil) - req2.Header.Add("Authorization", "Bearer "+token) - router.ServeHTTP(w2, req2) + newFileID, err := common.GetResponseID(w.Body) + assert.NoError(t, err) - assert.Equal(t, 200, w2.Code) - fmt.Println(w2.Body.String()) - assert.Equal(t, filecontent, w2.Body.String()) - - //common.TestEndpoint(t, router, token, "/api/files?objectID=1&objectType=widget", "GET", nil, 200, string(msgFilesjson)) - - // test PUT files/:fileID - bodyBuf_update := &bytes.Buffer{} - bodyWriter_update := multipart.NewWriter(bodyBuf_update) - fileWriter_update, err := bodyWriter_update.CreateFormFile("file", "testuploadfile.txt") - if err != nil { - fmt.Println("error writing to buffer") - panic(err) - } - - // open file handle - fh_update, err := os.Open(filename_update) - if err != nil { - fmt.Println("error opening file") - panic(err) - } - defer fh_update.Close() - - // io copy - _, err = io.Copy(fileWriter_update, fh_update) - if err != nil { - fmt.Println("error on IO copy") - panic(err) - } - - contentType_update := bodyWriter_update.FormDataContentType() - bodyWriter_update.Close() - w_update := httptest.NewRecorder() - req_update, err := http.NewRequest("PUT", "/api/files/5", bodyBuf_update) - req_update.Header.Add("Authorization", "Bearer "+token) - req_update.Header.Set("Content-Type", contentType_update) - if err != nil { - fmt.Println("error creating post request") - panic(err) - } - - router.ServeHTTP(w_update, req_update) - - assert.Equal(t, 200, w_update.Code) - fmt.Println(w_update.Body.String()) - assert.Equal(t, string(msgOKjson), w_update.Body.String()) - - // Test GET on updated file content - w3 := httptest.NewRecorder() - req3, _ := http.NewRequest("GET", "/api/files/5", nil) - req3.Header.Add("Authorization", "Bearer "+token) - router.ServeHTTP(w3, req3) - - assert.Equal(t, 200, w3.Code) - fmt.Println(w3.Body.String()) - assert.Equal(t, filecontent_update, w3.Body.String()) - - // test DELETE files/:fileID - common.TestEndpoint(t, router, token, "/api/files/5", "DELETE", nil, 200, msgOKjson) - common.TestEndpoint(t, router, token, "/api/files?objectID=1&objectType=widget", "GET", nil, 200, msgFilesjson) - - // TODO add testing for other return codes - - // clean up temporary file - err = os.Remove(filename) - if err != nil { - panic(err) - } - - err = os.Remove(filename_update) - if err != nil { - panic(err) - } + // Get the new file + code, resp, err := common.NewTestEndpoint(router, token, + fmt.Sprintf("/api/files/%v", newFileID), "GET", nil) + assert.NoError(t, err) + assert.Equalf(t, 200, code, "Response body: \n%v\n", resp) } + +func TestUpdateFile(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) + + // create a testfile.txt in local folder + c1 := []byte("This is my testfile\n") + err = ioutil.WriteFile("testfile.txt", c1, 0644) + assert.NoError(t, err) + + bodyBuf := &bytes.Buffer{} + bodyWriter := multipart.NewWriter(bodyBuf) + fileWriter, err := bodyWriter.CreateFormFile("file", "testfile.txt") + assert.NoError(t, err, "writing to buffer") + + // open file handle + fh, err := os.Open("testfile.txt") + assert.NoError(t, err, "opening file") + defer fh.Close() + + // io copy + _, err = io.Copy(fileWriter, fh) + assert.NoError(t, err, "IO copy") + + contentType := bodyWriter.FormDataContentType() + bodyWriter.Close() + //req, err := http.NewRequest("POST", "/api/files?objectID=1&objectType=widget", bodyBuf) + + // Create the POST request + w := httptest.NewRecorder() + req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=model", simulationModelID), bodyBuf) + assert.NoError(t, err, "create request") + + req.Header.Set("Content-Type", contentType) + req.Header.Add("Authorization", "Bearer "+token) + router.ServeHTTP(w, req) + + assert.Equalf(t, 200, w.Code, "Response body: \n%v\n", w.Body) + fmt.Println(w.Body) + + newFileID, err := common.GetResponseID(w.Body) + assert.NoError(t, err) + + // Prepare update + + // create a testfile_updated.txt in local folder + c2 := []byte("This is my updated testfile\n") + err = ioutil.WriteFile("testfileupdated.txt", c2, 0644) + assert.NoError(t, err) + + bodyBufUpdated := &bytes.Buffer{} + bodyWriterUpdated := multipart.NewWriter(bodyBufUpdated) + fileWriterUpdated, err := bodyWriterUpdated.CreateFormFile("file", "testfileupdated.txt") + assert.NoError(t, err, "writing to buffer") + + // open file handle for updated file + fh_updated, err := os.Open("testfileupdated.txt") + assert.NoError(t, err, "opening file") + defer fh_updated.Close() + + // io copy + _, err = io.Copy(fileWriterUpdated, fh_updated) + assert.NoError(t, err, "IO copy") + + contentType = bodyWriterUpdated.FormDataContentType() + bodyWriterUpdated.Close() + + // Create the PUT request + w_updated := httptest.NewRecorder() + req, err = http.NewRequest("PUT", fmt.Sprintf("/api/files/%v", newFileID), bodyBufUpdated) + assert.NoError(t, err, "create request") + + req.Header.Set("Content-Type", contentType) + req.Header.Add("Authorization", "Bearer "+token) + router.ServeHTTP(w_updated, req) + + assert.Equalf(t, 200, w_updated.Code, "Response body: \n%v\n", w_updated.Body) + fmt.Println(w_updated.Body) + + newFileIDUpdated, err := common.GetResponseID(w_updated.Body) + + assert.Equal(t, newFileID, newFileIDUpdated) + + // Get the updated file + code, resp, err := common.NewTestEndpoint(router, token, + fmt.Sprintf("/api/files/%v", newFileIDUpdated), "GET", nil) + assert.NoError(t, err) + assert.Equalf(t, 200, code, "Response body: \n%v\n", resp) + +} + +func TestDeleteFile(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) + + // create a testfile.txt in local folder + c1 := []byte("This is my testfile\n") + err = ioutil.WriteFile("testfile.txt", c1, 0644) + assert.NoError(t, err) + + // test POST files + bodyBuf := &bytes.Buffer{} + bodyWriter := multipart.NewWriter(bodyBuf) + fileWriter, err := bodyWriter.CreateFormFile("file", "testuploadfile.txt") + assert.NoError(t, err, "writing to buffer") + + // open file handle + fh, err := os.Open("testfile.txt") + assert.NoError(t, err, "opening file") + defer fh.Close() + + // io copy + _, err = io.Copy(fileWriter, fh) + assert.NoError(t, err, "IO copy") + + contentType := bodyWriter.FormDataContentType() + bodyWriter.Close() + //req, err := http.NewRequest("POST", "/api/files?objectID=1&objectType=widget", bodyBuf) + + // Create the request + w := httptest.NewRecorder() + req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=model", simulationModelID), bodyBuf) + assert.NoError(t, err, "create request") + + req.Header.Set("Content-Type", contentType) + req.Header.Add("Authorization", "Bearer "+token) + router.ServeHTTP(w, req) + + assert.Equalf(t, 200, w.Code, "Response body: \n%v\n", w.Body) + //fmt.Println(w.Body) + + newFileID, err := common.GetResponseID(w.Body) + assert.NoError(t, err) + + // Count the number of all files returned for simulation model + initialNumber, err := common.LengthOfResponse(router, token, + fmt.Sprintf("/api/files?objectID=%v&objectType=model", simulationModelID), "GET", nil) + assert.NoError(t, err) + + // Delete the added file + code, resp, err := common.NewTestEndpoint(router, token, + fmt.Sprintf("/api/files/%v", newFileID), "DELETE", nil) + assert.NoError(t, err) + assert.Equalf(t, 200, code, "Response body: \n%v\n", resp) + + // Again count the number of all the files returned for simulation model + finalNumber, err := common.LengthOfResponse(router, token, + fmt.Sprintf("/api/files?objectID=%v&objectType=model", simulationModelID), "GET", nil) + assert.NoError(t, err) + + assert.Equal(t, initialNumber-1, finalNumber) +} + +func TestGetAllFilesOfSimulationModel(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 files returned for simulation model + initialNumber, err := common.LengthOfResponse(router, token, + fmt.Sprintf("/api/files?objectID=%v&objectType=model", simulationModelID), "GET", nil) + assert.NoError(t, err) + + // create a testfile.txt in local folder + c1 := []byte("This is my testfile\n") + err = ioutil.WriteFile("testfile.txt", c1, 0644) + assert.NoError(t, err) + + // test POST files + bodyBuf := &bytes.Buffer{} + bodyWriter := multipart.NewWriter(bodyBuf) + fileWriter, err := bodyWriter.CreateFormFile("file", "testuploadfile.txt") + assert.NoError(t, err, "writing to buffer") + + // open file handle + fh, err := os.Open("testfile.txt") + assert.NoError(t, err, "opening file") + defer fh.Close() + + // io copy + _, err = io.Copy(fileWriter, fh) + assert.NoError(t, err, "IO copy") + + contentType := bodyWriter.FormDataContentType() + bodyWriter.Close() + //req, err := http.NewRequest("POST", "/api/files?objectID=1&objectType=widget", bodyBuf) + + // Create the request + w := httptest.NewRecorder() + req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=model", simulationModelID), bodyBuf) + assert.NoError(t, err, "create request") + + req.Header.Set("Content-Type", contentType) + req.Header.Add("Authorization", "Bearer "+token) + router.ServeHTTP(w, req) + assert.Equalf(t, 200, w.Code, "Response body: \n%v\n", w.Body) + //fmt.Println(w.Body) + + // POST a second file + + bodyBuf2 := &bytes.Buffer{} + bodyWriter2 := multipart.NewWriter(bodyBuf2) + fileWriter2, err := bodyWriter2.CreateFormFile("file", "testuploadfile2.txt") + assert.NoError(t, err, "writing to buffer") + + // open file handle + fh2, err := os.Open("testfile.txt") + assert.NoError(t, err, "opening file") + defer fh2.Close() + + // io copy + _, err = io.Copy(fileWriter2, fh2) + assert.NoError(t, err, "IO copy") + + contentType = bodyWriter2.FormDataContentType() + bodyWriter2.Close() + + w2 := httptest.NewRecorder() + req, err = http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=model", simulationModelID), bodyBuf2) + assert.NoError(t, err, "create request") + + req.Header.Set("Content-Type", contentType) + req.Header.Add("Authorization", "Bearer "+token) + router.ServeHTTP(w2, req) + assert.Equalf(t, 200, w2.Code, "Response body: \n%v\n", w2.Body) + + // Again count the number of all the files returned for simulation model + finalNumber, err := common.LengthOfResponse(router, token, + fmt.Sprintf("/api/files?objectID=%v&objectType=model", simulationModelID), "GET", nil) + assert.NoError(t, err) + + assert.Equal(t, initialNumber+2, finalNumber) +}