From 605ea31fe1f7b3655713e6eada9a0f312a67ec97 Mon Sep 17 00:00:00 2001 From: bronku Date: Wed, 19 Mar 2025 22:10:43 +0100 Subject: [PATCH] update tests --- handler.go | 22 +++- store.go | 47 ++++---- store_test.go | 313 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 258 insertions(+), 124 deletions(-) diff --git a/handler.go b/handler.go index 1f31884..f129dd2 100644 --- a/handler.go +++ b/handler.go @@ -31,7 +31,13 @@ func (h *handler) form(w http.ResponseWriter, r *http.Request) { Order order Catalogue []cake } - data := formData{o, h.s.getCakes()} + + cakes, err := h.s.getCakes() + if err != nil { + fmt.Fprint(w, "server side error getting available cakes: ", err) + return + } + data := formData{o, cakes} fmt.Println(data) w.Header().Set("content-type", "text/html") @@ -85,7 +91,12 @@ func (h *handler) addOrder(w http.ResponseWriter, r *http.Request) { n.Accepted = time.Now() n.Cakes = make([]cake, 0) - for _, e := range h.s.getCakes() { + cakes, err := h.s.getCakes() + if err != nil { + fmt.Fprint(w, "server side error getting available cakes: ", err) + return + } + for _, e := range cakes { value := r.FormValue(fmt.Sprintf("cake[%d]", e.ID)) if value == "" { continue @@ -109,7 +120,12 @@ func (h *handler) addOrder(w http.ResponseWriter, r *http.Request) { func (h *handler) index(w http.ResponseWriter, r *http.Request) { w.Header().Set("content-type", "text/html") - err := h.tmpl.ExecuteTemplate(w, "index.html", h.s.getOrders()) + orders, err := h.s.getOrders() + if err != nil { + fmt.Fprint(w, "server side error getting orders: ", err) + return + } + err = h.tmpl.ExecuteTemplate(w, "index.html", orders) if err != nil { fmt.Println("error executing the template: ", err) } diff --git a/store.go b/store.go index 1d93138..82afd8a 100644 --- a/store.go +++ b/store.go @@ -1,9 +1,9 @@ +// #todo: check for errors in every rows.Scan() package main import ( "database/sql" _ "embed" - "fmt" "os" "strconv" "time" @@ -37,25 +37,27 @@ func NewStore(filename string) (*store, error) { return &out, nil } -// #todo: should return an error -func (s *store) getCakes() []cake { - // #todo: error handling +func (s *store) close() { + s.db.Close() +} + +func (s *store) getCakes() ([]cake, error) { rows, err := s.db.Query("select id, name, price from cake") - defer rows.Close() if err != nil { - return nil + return nil, err } + defer rows.Close() cakes := make([]cake, 0) for rows.Next() { var c cake - err = rows.Scan(&c.ID, &c.Name, &c.Price) c.Amount = 0 + err = rows.Scan(&c.ID, &c.Name, &c.Price) if err != nil { - fmt.Println("error scanning rows into cake: ", err) + return nil, err } cakes = append(cakes, c) } - return cakes + return cakes, err } func (s *store) saveCake(newCake cake) (int, error) { @@ -74,8 +76,8 @@ func (s *store) saveCake(newCake cake) (int, error) { // #todo: see if next is required or not to get the first element row.Next() - row.Scan(&newCake.ID) - return newCake.ID, nil + err = row.Scan(&newCake.ID) + return newCake.ID, err } // #todo: retrieve and save order contents @@ -98,26 +100,29 @@ func (s *store) getOrder(id int) (order, error) { } // #todo: should return an error -func (s *store) getOrders() []order { +func (s *store) getOrders() ([]order, error) { var out []order - // #todo: error handling - row, err := s.db.Query("select id, name, surname, phone, location, order_date, delivery_date, status, paid from customer_order;") - defer row.Close() + rows, err := s.db.Query("select id, name, surname, phone, location, order_date, delivery_date, status, paid from customer_order;") if err != nil { - return out + return out, err } + defer rows.Close() - for row.Next() { + for rows.Next() { var o order var order_date, delivery_date string - row.Scan(&o.ID, &o.Name, &o.Surname, &o.Phone, &o.Location, &order_date, &delivery_date, &o.Status, &o.Paid) + err = rows.Scan(&o.ID, &o.Name, &o.Surname, &o.Phone, &o.Location, &order_date, &delivery_date, &o.Status, &o.Paid) + if err != nil { + return nil, err + } // #todo: error handling + // figure out what to do with errors, as they shouldn't really crash the app, but there should probably be an indication that something went wrong o.Accepted, _ = time.Parse("2006-01-02 15:04", order_date) o.Date, _ = time.Parse("2006-01-02 15:04", delivery_date) out = append(out, o) } - return out + return out, nil } func (s *store) saveOrder(newOrder order) (int, error) { @@ -139,6 +144,6 @@ func (s *store) saveOrder(newOrder order) (int, error) { // #todo: see if next is required or not to get the first element row.Next() - row.Scan(&newOrder.ID) - return newOrder.ID, nil + err = row.Scan(&newOrder.ID) + return newOrder.ID, err } diff --git a/store_test.go b/store_test.go index d22f74c..7e5a33a 100644 --- a/store_test.go +++ b/store_test.go @@ -5,136 +5,249 @@ import ( "time" ) -func TestCakeOperations(t *testing.T) { +func TestStore(t *testing.T) { s, err := NewStore(":memory:") if err != nil { - t.Fatal("Failed to open database", err) + t.Fatalf("Failed to create store: %v", err) } - defer s.db.Close() + defer s.close() - c := cake{ - ID: -1, - Name: "Test Cake", - Price: 10, - } - - id, err := s.saveCake(c) - if id == -1 || err != nil { - t.Fatal("Failed to save cake", err) - } - - cakes := s.getCakes() - if len(cakes) != 1 { - t.Errorf("Expected 1 cake, got %d", len(cakes)) - } - - if cakes[0].Name != "Test Cake" || cakes[0].Price != 10 { - t.Errorf("Cake data mismatch. Got %+v", cakes[0]) - } - - updatedCake := cake{ - ID: id, - Name: "Updated Cake", - Price: 15, - } - - newID, err := s.saveCake(updatedCake) - if newID != id { - t.Errorf("Update returned different ID. Expected %d, got %d", id, newID) - } - - cakes = s.getCakes() - if cakes[0].Name != "Updated Cake" || cakes[0].Price != 15 { - t.Errorf("Cake update failed. Got %+v", cakes[0]) - } -} - -func TestOrderOperations(t *testing.T) { - s, err := NewStore(":memory:") + // ensure db is empty + cakes, err := s.getCakes() if err != nil { - t.Fatal("Failed to open database", err) + t.Errorf("Error getting cakes: %v", err) + } + if len(cakes) != 0 { + t.Errorf("Expected empty cakes list, got: %v", cakes) + } + orders, err := s.getOrders() + if err != nil { + t.Errorf("Error getting orders: %v", err) + } + if len(orders) != 0 { + t.Errorf("Expected empty orders list, got: %v", orders) } - defer s.db.Close() + // create new cake + newCake := cake{Name: "Chocolate Cake", ID: -1, Price: 2500, Amount: -1} + newCake.ID, err = s.saveCake(newCake) + if err != nil { + t.Fatalf("Failed to save new cake: %v", err) + } + if newCake.ID <= 0 { + t.Errorf("Expected positive cake ID, got: %d", newCake.ID) + } + + // create another cake + anotherCake := cake{Name: "Another Cake", ID: -1, Price: 3000, Amount: -1} + anotherCake.ID, err = s.saveCake(anotherCake) + if err != nil { + t.Fatalf("Failed to save new cake: %v", err) + } + if anotherCake.ID <= 0 { + t.Errorf("Expected positive cake ID, got: %d", anotherCake.ID) + } + + // Update Existing Cake + newCake.Name = "Updated Cake" + newCake.Price = 100 + newID, err := s.saveCake(newCake) + if newID != newCake.ID { + t.Error("wrong id") + } + if err != nil { + t.Error("error updating cake", err) + } + + // Update Non-existing Cake + var updatedCake cake + updatedCake.ID = 10 + _, err = s.saveCake(updatedCake) + if err == nil { + t.Error("did not return an error when attempted to update non existant cake") + } + + // get cakes + cakes, err = s.getCakes() + if err != nil { + t.Fatalf("Failed to get all cakes after creation: %v", err) + } + if len(cakes) != 2 { + t.Error("Expected two cakes returned, got ", len(cakes)) + } + for i, c := range cakes { + if c.ID == newCake.ID { + newCake.Amount = i + } + if c.ID == anotherCake.ID { + anotherCake.Amount = i + } + } + if newCake.Amount == -1 { + t.Error("not found newCake") + } + if cakes[newCake.Amount].ID != newCake.ID || cakes[newCake.Amount].Name != newCake.Name || cakes[newCake.Amount].Price != newCake.Price { + t.Errorf("Want %v\nGot %v", newCake, cakes) + } + if anotherCake.Amount == -1 { + t.Error("not found anotherCake") + } + if cakes[anotherCake.Amount].ID != anotherCake.ID || cakes[anotherCake.Amount].Name != anotherCake.Name || cakes[anotherCake.Amount].Price != anotherCake.Price { + t.Errorf("Want %v\nGot %v", anotherCake, cakes) + } + + // create new order now := time.Now() - delivery := now.Add(24 * time.Hour) - - o := order{ + newOrder := order{ ID: -1, Name: "John", Surname: "Doe", - Phone: "1234567890", - Location: "123 Test St", + Phone: "123-456-7890", + Location: "Some Location", Accepted: now, - Date: delivery, - Status: "pending", - Paid: 0, + Date: now.Add(time.Hour * 24), + Status: "Pending", + Paid: 1000, + Cakes: []cake{{ID: newCake.ID, Amount: 2}, {ID: anotherCake.ID, Amount: 10}}, } - - id, err := s.saveOrder(o) - if id == -1 || err != nil { - t.Fatal("Failed to save order", err) - } - - retrieved, err := s.getOrder(id) + newOrder.ID, err = s.saveOrder(newOrder) if err != nil { - t.Fatalf("Failed to get order: %v", err) + t.Fatalf("Failed to save new order: %v", err) + } + if newOrder.ID <= 0 { + t.Errorf("Expected positive order ID, got: %d", newOrder.ID) } - if retrieved.Name != "John" || retrieved.Surname != "Doe" { - t.Errorf("Order names mismatch. Got %+v", retrieved) + // create another order + anotherOrder := order{ + ID: -1, + Name: "Jane", + Surname: "Doe", + Phone: "123-456-7890", + Location: "New Location", + Accepted: now, + Date: now.Add(time.Hour * 192), + Status: "Accepted", + Paid: 1500, + Cakes: []cake{{ID: newCake.ID, Amount: 100}}, } - - if !timeEqual(retrieved.Date, o.Date) || !timeEqual(retrieved.Accepted, o.Accepted) { - t.Errorf("Order dates mismatch. \nGot %+v\nWant %+v", retrieved, o) - } - - orders := s.getOrders() - if len(orders) != 1 { - t.Errorf("Expected 1 order, got %d", len(orders)) - } - - // Test updating an order - o.ID = id - o.Status = "completed" - - newID, err := s.saveOrder(o) + anotherOrder.ID, err = s.saveOrder(anotherOrder) if err != nil { - t.Fatal("Failed to save order", err) + t.Fatalf("Failed to save new order: %v", err) } - if newID != id { - t.Errorf("Update returned different ID. Expected %d, got %d", id, newID) + if anotherOrder.ID <= 0 { + t.Errorf("Expected positive order ID, got: %d", anotherOrder.ID) } - updated, err := s.getOrder(id) + // update existing order + newOrder.Name = "James" + newOrder.Status = "Done" + newOrder.Cakes = newOrder.Cakes[1:] + newID, err = s.saveOrder(newOrder) + if newID != newOrder.ID { + t.Error("wrong id") + } if err != nil { - t.Fatalf("Failed to get updated order: %v", err) + t.Error("error updating order", err) } - if updated.Status != "completed" { - t.Errorf("Order update failed. Got %+v", updated) + // update non existing order + var updatedOrder order + updatedOrder.ID = 10 + _, err = s.saveOrder(updatedOrder) + if err == nil { + t.Error("did not return an error when attempted to update non existant cake") + } + + // get orders + orders, err = s.getOrders() + if err != nil { + t.Fatalf("Failed to get all orders after creation: %v", err) + } + if len(orders) != 2 { + t.Error("Expected two orders returned, got ", len(orders)) + } + newOrderPos := -1 + anotherOrderPos := -1 + for i, o := range orders { + if o.ID == newOrder.ID { + newOrderPos = i + } + if o.ID == anotherOrder.ID { + anotherOrderPos = i + } + } + if newOrderPos == -1 { + t.Error("not found newOrder") + } + if newOrder.Name != orders[newOrderPos].Name || + newOrder.Surname != orders[newOrderPos].Surname || + newOrder.Phone != orders[newOrderPos].Phone || + newOrder.Status != orders[newOrderPos].Status || + newOrder.Location != orders[newOrderPos].Location || + newOrder.Accepted.Format("2006-01-02 15:04") != newOrder.Accepted.Format("2006-01-02 15:04") || + newOrder.Date.Format("2006-01-02 15:04") != newOrder.Date.Format("2006-01-02 15:04") { + t.Errorf("Want %v\nGot %v", newOrder, orders[newOrderPos]) + } + + if anotherOrderPos == -1 { + t.Error("not found anotherOrder") + } + if anotherOrder.Name != orders[anotherOrderPos].Name || + anotherOrder.Surname != orders[anotherOrderPos].Surname || + anotherOrder.Phone != orders[anotherOrderPos].Phone || + anotherOrder.Status != orders[anotherOrderPos].Status || + anotherOrder.Location != orders[anotherOrderPos].Location || + anotherOrder.Accepted.Format("2006-01-02 15:04") != anotherOrder.Accepted.Format("2006-01-02 15:04") || + anotherOrder.Date.Format("2006-01-02 15:04") != anotherOrder.Date.Format("2006-01-02 15:04") { + t.Errorf("Want %v\nGot %v", anotherOrder, orders[anotherOrderPos]) } } -func TestStoreInitialization(t *testing.T) { - s, err := NewStore(":memory:") - if err != nil { - t.Fatal("Failed to open database", err) - } - defer s.db.Close() - - cakes := s.getCakes() - if len(cakes) != 0 { - t.Errorf("New store should have no cakes, got %d", len(cakes)) +func TestSliceComparison(t *testing.T) { + a := []cake{ + {Name: "ok", Price: 100, ID: 12, Amount: 10}, + {Name: "ok", Price: 11, ID: 13, Amount: 1}, + {Name: "ok", Price: 100, ID: 10, Amount: 11}, } - orders := s.getOrders() - if len(orders) != 0 { - t.Errorf("New store should have no orders, got %d", len(orders)) + b := []cake{ + {Name: "okas", Price: 110, ID: 12, Amount: 10}, + {Name: "oksa", Price: 11, ID: 13, Amount: 1}, + {Name: "oasdk", Price: 101, ID: 10, Amount: 11}, + } + if !areCakeSlicesEqual(a, b) { + t.Error("aren't equal") } } -func timeEqual(t1, t2 time.Time) bool { - format := "2006-01-02 15:04" - return t1.Format(format) == t2.Format(format) +func areCakesEqual(a, b cake) bool { + return a.ID == b.ID && a.Amount == b.Amount +} + +func areCakeSlicesEqual(a, b []cake) bool { + if len(a) != len(b) { + return false + } + + matched := make([]bool, len(a)) + +mainLoop: + for _, e1 := range a { + for i, e2 := range b { + if matched[i] { + continue + } + if areCakesEqual(e1, e2) { + matched[i] = true + continue mainLoop + } + } + } + for _, e := range matched { + if e == false { + return false + } + } + return true }