Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 256 additions & 0 deletions app/handler/projects_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
package handler

import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

"github.com/KothariMansi/GO-TODO-REST-API-EXAMPLE/app/model"
"github.com/gorilla/mux"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/stretchr/testify/require"
)

func setupTest(t *testing.T) (*gorm.DB, *mux.Router) {
db, err := gorm.Open("sqlite3", ":memory:")
if err != nil {
t.Fatalf("failed to open test db: %v", err)
}

db.AutoMigrate(&model.Project{})

r := mux.NewRouter()
r.HandleFunc("/projects", func(w http.ResponseWriter, r *http.Request) {
GetAllProjects(db, w, r)
}).Methods("GET")

r.HandleFunc("/projects", func(w http.ResponseWriter, r *http.Request) {
CreateProject(db, w, r)
}).Methods("POST")

r.HandleFunc("/projects/{title}", func(w http.ResponseWriter, r *http.Request) {
GetProject(db, w, r)
}).Methods("GET")

r.HandleFunc("/projects/{title}", func(w http.ResponseWriter, r *http.Request) {
UpdateProject(db, w, r)
}).Methods("PUT")

r.HandleFunc("/projects/{title}", func(w http.ResponseWriter, r *http.Request) {
DeleteProject(db, w, r)
}).Methods("DELETE")

r.HandleFunc("/projects/{title}/archive", func(w http.ResponseWriter, r *http.Request) {
ArchiveProject(db, w, r)
}).Methods("PUT")

r.HandleFunc("/projects/{title}/restore", func(w http.ResponseWriter, r *http.Request) {
RestoreProject(db, w, r)
}).Methods("PUT")

return db, r
}

func seedProject(db *gorm.DB, title string) model.Project {
p := model.Project{Title: title}
db.Create(&p)
return p
}

func TestGetAllProjects(t *testing.T) {
_, r := setupTest(t)

// Http request
req := httptest.NewRequest(http.MethodGet, "/projects", nil)
rr := httptest.NewRecorder()

// Serve the request
r.ServeHTTP(rr, req)

require.Equal(t, http.StatusOK, rr.Code)

var projects []model.Project

require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &projects), "should decode JSON without error")

}

func TestCreateProject(t *testing.T) {
db, r := setupTest(t)

// prepare request payload
payload := []byte(`{"title": "NewProjects"}`)

var project1 model.Project
require.NoError(t, json.Unmarshal(payload, &project1))

// Create Http request
req := httptest.NewRequest(http.MethodPost, "/projects", bytes.NewBuffer(payload))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()

// Serve the request
r.ServeHTTP(rr, req)

require.Equal(t, http.StatusCreated, rr.Code)

var project2 model.Project
require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &project2))
require.Equal(t, project1.Title, project2.Title)
require.NotZero(t, project2.ID)

// Assert persistence in DB
var project3 model.Project

require.NoError(t, db.First(&project3, "title = ?", project1.Title).Error)
require.Equal(t, project1.Title, project3.Title)
require.NotZero(t, project3.ID)

}

func TestGetProject(t *testing.T) {
db, r := setupTest(t)

project1 := seedProject(db, "ExistingProject")

// Case: Project exists
req := httptest.NewRequest(http.MethodGet, "/projects/ExistingProject", nil)
rr := httptest.NewRecorder()

// Server the request
r.ServeHTTP(rr, req)

require.Equal(t, http.StatusOK, rr.Code)

var project2 model.Project
require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &project2))

require.Equal(t, project1.Title, project2.Title)

// Case: Project does not exists
reqNotFound := httptest.NewRequest(http.MethodGet, "/projects/NonExisting", nil)
rrNotFound := httptest.NewRecorder()

r.ServeHTTP(rrNotFound, reqNotFound)

require.Equal(t, http.StatusNotFound, rrNotFound.Code)
}

func TestUpdateProject(t *testing.T) {
db, r := setupTest(t)

// Seed project
project := seedProject(db, "OldTitle")

// update payload
updatePayload := []byte(`{"title":"UpdatedTitle"}`)

// HTTP Request
req := httptest.NewRequest(http.MethodPut, "/projects/"+project.Title, bytes.NewBuffer(updatePayload))
req = mux.SetURLVars(req, map[string]string{"title": project.Title})
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()

// server http
r.ServeHTTP(rr, req)

// Assertion
require.Equal(t, http.StatusOK, rr.Code)

var updateProject model.Project
require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &updateProject))
require.Equal(t, "UpdatedTitle", updateProject.Title)

// Check Persistence
var dbProject model.Project
require.NoError(t, db.First(&dbProject, updateProject.ID).Error)
require.Equal(t, "UpdatedTitle", dbProject.Title)

}

func TestDeleteProject(t *testing.T) {
db, r := setupTest(t)

// Seed Project
project := seedProject(db, "ToDeleteProject")

// HTTP Request
req := httptest.NewRequest(http.MethodDelete, "/projects/"+project.Title, nil)
req = mux.SetURLVars(req, map[string]string{"title": project.Title})
rr := httptest.NewRecorder()

// Server request
r.ServeHTTP(rr, req)

// Assertions
require.Equal(t, http.StatusNoContent, rr.Code)

// Verify it's Gone
var dbProject model.Project
err := db.First(&dbProject, project.ID).Error
require.Error(t, err)
require.True(t, gorm.IsRecordNotFoundError(err))

}

func TestArchiveProject(t *testing.T) {
db, r := setupTest(t)

// Seed Project
project := seedProject(db, "ToArchiveProject")

// Precondition: should not be archive
require.False(t, project.Archived)

// HTTP request
req := httptest.NewRequest(http.MethodPut, "/projects/"+project.Title+"/archive", nil)
req = mux.SetURLVars(req, map[string]string{"title": project.Title})
rr := httptest.NewRecorder()

// Server request
r.ServeHTTP(rr, req)

// Assertions
require.Equal(t, http.StatusOK, rr.Code)

var archivedProject model.Project
require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &archivedProject))
require.Equal(t, project.Title, archivedProject.Title)
require.True(t, archivedProject.Archived)

// Verify persistence in DB
var dbProject model.Project
require.NoError(t, db.First(&dbProject, archivedProject.ID).Error)
require.True(t, dbProject.Archived)

}

func TestRestoreProject(t *testing.T) {
db, r := setupTest(t)

// Seed project and archive it
project := seedProject(db, "ToRestoreProject")
project.Archive()
require.NoError(t, db.Save(&project).Error)

// HTTP request
req := httptest.NewRequest(http.MethodPut, "/projects/"+project.Title+"/restore", nil)
req = mux.SetURLVars(req, map[string]string{"title": project.Title})
rr := httptest.NewRecorder()

// Server HTTP
r.ServeHTTP(rr, req)

// Assertions
require.Equal(t, http.StatusOK, rr.Code)

// Verify in DB
var dbProject model.Project
err := db.First(&dbProject, project.ID).Error
require.NoError(t, err)
require.False(t, dbProject.Archived)

}