From 13515ea6194647752b0154c246b6e162752b60f8 Mon Sep 17 00:00:00 2001 From: SHREE Date: Sun, 28 Sep 2025 17:08:00 +0530 Subject: [PATCH 1/2] feat(tests): add CreateProject, GetProject tests and GetAllProjects --- app/handler/projects_test.go | 124 +++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 app/handler/projects_test.go diff --git a/app/handler/projects_test.go b/app/handler/projects_test.go new file mode 100644 index 0000000..9a7b9f2 --- /dev/null +++ b/app/handler/projects_test.go @@ -0,0 +1,124 @@ +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") + + 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) +} From 4c5a537f33a6452d0371add630b87bce1e020cdd Mon Sep 17 00:00:00 2001 From: SHREE Date: Wed, 1 Oct 2025 17:41:49 +0530 Subject: [PATCH 2/2] feat(tests): add tests for Update, Delete, Archive, and Restore project functionalities --- app/handler/projects_test.go | 132 +++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/app/handler/projects_test.go b/app/handler/projects_test.go index 9a7b9f2..3d06d2f 100644 --- a/app/handler/projects_test.go +++ b/app/handler/projects_test.go @@ -35,6 +35,22 @@ func setupTest(t *testing.T) (*gorm.DB, *mux.Router) { 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 } @@ -122,3 +138,119 @@ func TestGetProject(t *testing.T) { 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) + +}