From 003db3ad20d68eb3fb3e5a7d705e89e3601bebf7 Mon Sep 17 00:00:00 2001 From: Matteo Vaccari Date: Sun, 11 Feb 2024 19:09:28 +0100 Subject: [PATCH] feat: now deletion goes to the database --- db/repository.go | 8 ++++++++ db/repository_test.go | 19 +++++++++++++++++++ server.go | 2 +- web/handlers.go | 25 ++++++++++++++++++++++--- web/handlers_test.go | 19 ++++++++++++++----- 5 files changed, 64 insertions(+), 9 deletions(-) diff --git a/db/repository.go b/db/repository.go index 2a55bd0..8ff71be 100644 --- a/db/repository.go +++ b/db/repository.go @@ -20,6 +20,7 @@ type TodoRepository interface { Find(todo.ItemId) (*todo.Item, bool, error) Save(item todo.Item) (todo.ItemId, error) FindList() (*todo.List, error) + Destroy(id todo.ItemId) error } type todoRepository struct { @@ -125,3 +126,10 @@ values (?, ?)` } return newId, nil } + +//goland:noinspection SqlNoDataSourceInspection +func (t todoRepository) Destroy(id todo.ItemId) error { + destroySql := `delete from todo_items where id = ?` + _, err := t.db.Exec(destroySql, id) + return err +} diff --git a/db/repository_test.go b/db/repository_test.go index 4c56997..65bc54a 100644 --- a/db/repository_test.go +++ b/db/repository_test.go @@ -89,6 +89,25 @@ func Test_findAll(t *testing.T) { assert.Equal(id1, all[1].Id) } +func Test_destroy_ok(t *testing.T) { + assert := assert.New(t) + db := initTestDb() + repo := NewTodoRepository(db) + id0, err := repo.Save(todo.Item{Title: "first", IsDone: false}) + require.NoError(t, err) + id1, err := repo.Save(todo.Item{Title: "second", IsDone: true}) + require.NoError(t, err) + + err = repo.Destroy(id1) + require.NoError(t, err) + + list, err := repo.FindList() + require.NoError(t, err) + + assert.Equal(1, len(list.Items)) + assert.Equal("first", list.Items[id0].Title) +} + //goland:noinspection SqlNoDataSourceInspection func initTestDb() *sql.DB { db, err := sql.Open("sqlite", "test.db") diff --git a/server.go b/server.go index 0851c1e..e840746 100644 --- a/server.go +++ b/server.go @@ -57,7 +57,7 @@ func main() { web.Metrics("destroy", web.Logging( web.POSTonly( - web.DestroyHandler(templ, model))))) + web.DestroyHandler(templ, repository, repository))))) http.Handle("/metrics", promhttp.Handler()) diff --git a/web/handlers.go b/web/handlers.go index f654850..1f2045a 100644 --- a/web/handlers.go +++ b/web/handlers.go @@ -3,6 +3,7 @@ package web import ( "github.com/xpmatteo/todomvc-golang/todo" "html/template" + "log" "net/http" ) @@ -17,6 +18,10 @@ type ListFinder interface { FindList() (*todo.List, error) } +type Destroyer interface { + Destroy(id todo.ItemId) error +} + func IndexHandler(templ *template.Template, repo ListFinder) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" && r.URL.Path != pathActive && r.URL.Path != pathCompleted { @@ -25,7 +30,7 @@ func IndexHandler(templ *template.Template, repo ListFinder) http.Handler { } model, err := repo.FindList() if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + internalServerError(w, err) return } print(model.Items) @@ -100,7 +105,7 @@ func EditHandler(templ *template.Template, model *todo.List) http.Handler { }) } -func DestroyHandler(templ *template.Template, model *todo.List) http.Handler { +func DestroyHandler(templ *template.Template, finder ListFinder, destroyer Destroyer) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { @@ -112,13 +117,27 @@ func DestroyHandler(templ *template.Template, model *todo.List) http.Handler { badRequest(w, err) return } - model.Destroy(id) + if err := destroyer.Destroy(id); err != nil { + internalServerError(w, err) + return + } + + model, err := finder.FindList() + if err != nil { + internalServerError(w, err) + return + } vm := viewModel(model, r) render(w, r, templ, vm) }) } +func internalServerError(w http.ResponseWriter, err error) { + log.Printf("Finder: %s", err.Error()) + http.Error(w, "Internal server error", http.StatusInternalServerError) +} + func badRequest(w http.ResponseWriter, err error) { http.Error(w, err.Error(), http.StatusBadRequest) } diff --git a/web/handlers_test.go b/web/handlers_test.go index 24a7665..26ad4ae 100644 --- a/web/handlers_test.go +++ b/web/handlers_test.go @@ -97,16 +97,25 @@ func Test_editHandler_textIsEmpty(t *testing.T) { assert.Equal(0, len(model.Items)) } +type DestroyerMock struct { + ids []todo.ItemId +} + +func (d *DestroyerMock) Destroy(id todo.ItemId) error { + d.ids = append(d.ids, id) + return nil +} + func Test_destroyHandler_ok(t *testing.T) { assert := assert.New(t) - w, r := httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/", strings.NewReader("todoItemId=0")) + w, r := httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/", strings.NewReader("todoItemId=123")) r.Header.Set("Content-Type", "application/x-www-form-urlencoded") - model := todo.NewList() - model.Add("foo") + testFinder := ListFinderStub{todo.NewList()} + destroyer := &DestroyerMock{} - DestroyHandler(templ, model).ServeHTTP(w, r) + DestroyHandler(templ, testFinder, destroyer).ServeHTTP(w, r) assert.Equal(http.StatusOK, w.Code) assert.Equal("

[]

", w.Body.String()) - assert.Equal(0, len(model.Items)) + assert.Contains(destroyer.ids, todo.MustNewItemId("123")) }