From d7955a8104d14a9700f2993ab45afea3acaa9294 Mon Sep 17 00:00:00 2001 From: Ekaterina Deribina Date: Wed, 20 Nov 2024 16:01:24 +0800 Subject: [PATCH 1/2] first try --- main.go | 9 ++++- parcel.go | 99 +++++++++++++++++++++++++++++++++++++++++++------ parcel_test.go | 90 ++++++++++++++++++++++++++++++++------------ tracker.db | Bin 61440 -> 61440 bytes 4 files changed, 161 insertions(+), 37 deletions(-) diff --git a/main.go b/main.go index 44c32b3f..9e5ffe63 100644 --- a/main.go +++ b/main.go @@ -98,8 +98,15 @@ func (s ParcelService) Delete(number int) error { func main() { // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") + if err != nil { + fmt.Println(err) + return + + } + defer db.Close() - store := // создайте объект ParcelStore функцией NewParcelStore + store := NewParcelStore(db) // создайте объект ParcelStore функцией NewParcelStore service := NewParcelService(store) // регистрация посылки diff --git a/parcel.go b/parcel.go index db6c815d..308c422e 100644 --- a/parcel.go +++ b/parcel.go @@ -13,48 +13,123 @@ func NewParcelStore(db *sql.DB) ParcelStore { } func (s ParcelStore) Add(p Parcel) (int, error) { - // реализуйте добавление строки в таблицу parcel, используйте данные из переменной p - // верните идентификатор последней добавленной записи - return 0, nil + res, err := s.db.Exec("INSERT INTO parcel (client, status, address, created_at) VALUES ( :client, :status, :address, :createdAt)", + sql.Named("client", p.Client), + sql.Named("status", ParcelStatusRegistered), + sql.Named("address", p.Address), + sql.Named("createdAt", p.CreatedAt)) + if err != nil { + return 0, err + } + + id, err := res.LastInsertId() + if err != nil { + return 0, err + } + + return int(id), nil + } func (s ParcelStore) Get(number int) (Parcel, error) { - // реализуйте чтение строки по заданному number - // здесь из таблицы должна вернуться только одна строка - // заполните объект Parcel данными из таблицы p := Parcel{} + row := s.db.QueryRow("SELECT number, client, status, address, created_at FROM parcel WHERE number = :number", sql.Named("number", number)) + err := row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) + if err != nil { + return p, err + } return p, nil } func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { - // реализуйте чтение строк из таблицы parcel по заданному client - // здесь из таблицы может вернуться несколько строк + rows, err := s.db.Query(`SELECT number, client, status, address, created_at FROM parcel WHERE client = :client`, sql.Named("client", client)) + if err != nil { + return nil, err + } + defer rows.Close() - // заполните срез Parcel данными из таблицы var res []Parcel + for rows.Next() { + p := Parcel{} + + err := rows.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) + if err != nil { + + return res, err + } + + res = append(res, p) + } + + if err := rows.Err(); err != nil { + + return nil, err + } + return res, nil } func (s ParcelStore) SetStatus(number int, status string) error { // реализуйте обновление статуса в таблице parcel - + _, err := s.db.Exec("UPDATE parcel SET status = :status WHERE number = :number", + sql.Named("status", status), + sql.Named("number", number)) + if err != nil { + return err + } return nil } +func (s ParcelStore) GetStatus(number int) (string, error) { + // не хочу повторять код в двух других функциях, поэтому отдельно реализовала получение статуса + p := Parcel{} + row := s.db.QueryRow("SELECT status FROM parcel WHERE number = :number", sql.Named("number", number)) + err := row.Scan(&p.Status) + if err != nil { + return "", err + } + + return p.Status, nil + +} func (s ParcelStore) SetAddress(number int, address string) error { // реализуйте обновление адреса в таблице parcel // менять адрес можно только если значение статуса registered - + status, err := s.GetStatus(number) + if err != nil { + return err + } + + if status != ParcelStatusRegistered { + return nil // не знаю что возвратить + } + _, err = s.db.Exec("UPDATE parcel SET address = :address WHERE number = :number", + sql.Named("address", address), + sql.Named("number", number)) + if err != nil { + return err + } return nil + } func (s ParcelStore) Delete(number int) error { // реализуйте удаление строки из таблицы parcel // удалять строку можно только если значение статуса registered + status, err := s.GetStatus(number) + if err != nil { + return err + } + + if status != ParcelStatusRegistered { + return nil // не знаю что возвратить + } + + _, err = s.db.Exec("DELETE FROM parcel WHERE number = :number", sql.Named("number", number)) + return err - return nil } diff --git a/parcel_test.go b/parcel_test.go index d1b93827..386c1662 100644 --- a/parcel_test.go +++ b/parcel_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -31,57 +32,94 @@ func getTestParcel() Parcel { // TestAddGetDelete проверяет добавление, получение и удаление посылки func TestAddGetDelete(t *testing.T) { // prepare - db, err := // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") // настройте подключение к БД + require.NoError(t, err) store := NewParcelStore(db) parcel := getTestParcel() - // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + number, err := store.Add(parcel) + require.NoError(t, err) + require.NotEmpty(t, number) - // get - // получите только что добавленную посылку, убедитесь в отсутствии ошибки - // проверьте, что значения всех полей в полученном объекте совпадают со значениями полей в переменной parcel + getParcel, err := store.Get(number) + require.NoError(t, err) + //не можем проверить через assert.Equal(t, getParcel, parcel) тк функция Add не меняет 'number' у 'parcel' + assert.Equal(t, getParcel.Address, parcel.Address) + assert.Equal(t, getParcel.Client, parcel.Client) + assert.Equal(t, getParcel.CreatedAt, parcel.CreatedAt) + assert.Equal(t, getParcel.Status, parcel.Status) + + err = store.Delete(number) + require.NoError(t, err) + + _, err = store.Get(number) + require.Error(t, err) - // delete - // удалите добавленную посылку, убедитесь в отсутствии ошибки - // проверьте, что посылку больше нельзя получить из БД } // TestSetAddress проверяет обновление адреса func TestSetAddress(t *testing.T) { // prepare - db, err := // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") // настройте подключение к БД + require.NoError(t, err) + store := NewParcelStore(db) + parcel := getTestParcel() // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + number, err := store.Add(parcel) + require.NoError(t, err) + require.NotEmpty(t, number) // set address - // обновите адрес, убедитесь в отсутствии ошибки newAddress := "new test address" + err = store.SetAddress(number, newAddress) + require.NoError(t, err) // check - // получите добавленную посылку и убедитесь, что адрес обновился + getParcel, err := store.Get(number) + require.NoError(t, err) + require.Equal(t, getParcel.Address, newAddress) + + // delete + err = store.Delete(number) + require.NoError(t, err) + } // TestSetStatus проверяет обновление статуса func TestSetStatus(t *testing.T) { // prepare - db, err := // настройте подключение к БД - + db, err := sql.Open("sqlite", "tracker.db") + require.NoError(t, err) // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + + store := NewParcelStore(db) + parcel := getTestParcel() + + number, err := store.Add(parcel) + require.NoError(t, err) + require.NotEmpty(t, number) // set status - // обновите статус, убедитесь в отсутствии ошибки + err = store.SetStatus(number, ParcelStatusDelivered) + require.NoError(t, err) // check - // получите добавленную посылку и убедитесь, что статус обновился + getParcel, err := store.Get(number) + require.NoError(t, err) + require.Equal(t, getParcel.Status, ParcelStatusDelivered) + // deete + err = store.Delete(number) + require.NoError(t, err) + } // TestGetByClient проверяет получение посылок по идентификатору клиента func TestGetByClient(t *testing.T) { // prepare - db, err := // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") + require.NoError(t, err) + store := NewParcelStore(db) parcels := []Parcel{ getTestParcel(), @@ -98,8 +136,9 @@ func TestGetByClient(t *testing.T) { // add for i := 0; i < len(parcels); i++ { - id, err := // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора - + id, err := store.Add(parcels[i]) // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + require.NoError(t, err) + require.NotEmpty(t, id) // обновляем идентификатор добавленной у посылки parcels[i].Number = id @@ -108,14 +147,17 @@ func TestGetByClient(t *testing.T) { } // get by client - storedParcels, err := // получите список посылок по идентификатору клиента, сохранённого в переменной client - // убедитесь в отсутствии ошибки - // убедитесь, что количество полученных посылок совпадает с количеством добавленных + storedParcels, err := store.GetByClient(client) + require.NoError(t, err) + require.Equal(t, len(storedParcels), 3) // check for _, parcel := range storedParcels { // в parcelMap лежат добавленные посылки, ключ - идентификатор посылки, значение - сама посылка // убедитесь, что все посылки из storedParcels есть в parcelMap + require.NotEmpty(t, parcelMap[parcel.Number]) // убедитесь, что значения полей полученных посылок заполнены верно + require.Equal(t, parcel, parcelMap[parcel.Number]) } + } diff --git a/tracker.db b/tracker.db index b6ba48a148daa7c8c4727d4bfc1c868229d7fb24..2c5e5cbdf4db550d653fded395d3d6146c2c0553 100644 GIT binary patch delta 492 zcmZp8z})bFd4e>f?L--8R$B(Wv{xHb7RhsbVc`G5e}n(VW44GndT3_=XdtxQd<3@xLyxk*vXMTFvdn9-V?Yz$2DqNdC* zQc`m=%ka8_4PvliWez?kn^;;I8%Jrg;#FJ^^B`0;3trWziZz*ua5BVPEk>YHU??-c QKy$G$N+?4VFLKxb0KsF3n*aa+ delta 60 zcmV-C0K@-)-~)i*1CSd5Dv=yR1u6h8Zn&{zq8|wL01x#K*bmsV5fI7`lh`jD3}FcY S0~rW#VRB<-Y@!elqCl{GdK7*D From 1f6236ccf6a8b2b6ec3d9f43c58bea65e2901022 Mon Sep 17 00:00:00 2001 From: Ekaterina Deribina Date: Thu, 21 Nov 2024 12:19:22 +0800 Subject: [PATCH 2/2] wraped errors --- parcel.go | 31 +++++++++++++++---------------- parcel_test.go | 2 +- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/parcel.go b/parcel.go index 308c422e..463553bf 100644 --- a/parcel.go +++ b/parcel.go @@ -2,6 +2,7 @@ package main import ( "database/sql" + "fmt" ) type ParcelStore struct { @@ -16,16 +17,16 @@ func (s ParcelStore) Add(p Parcel) (int, error) { res, err := s.db.Exec("INSERT INTO parcel (client, status, address, created_at) VALUES ( :client, :status, :address, :createdAt)", sql.Named("client", p.Client), - sql.Named("status", ParcelStatusRegistered), + sql.Named("status", p.Status), sql.Named("address", p.Address), sql.Named("createdAt", p.CreatedAt)) if err != nil { - return 0, err + return 0, fmt.Errorf("insert failed: %w", err) } id, err := res.LastInsertId() if err != nil { - return 0, err + return 0, fmt.Errorf("scan id failed: %w", err) } return int(id), nil @@ -38,7 +39,7 @@ func (s ParcelStore) Get(number int) (Parcel, error) { row := s.db.QueryRow("SELECT number, client, status, address, created_at FROM parcel WHERE number = :number", sql.Named("number", number)) err := row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) if err != nil { - return p, err + return Parcel{}, fmt.Errorf("select error: %w", err) } return p, nil @@ -47,7 +48,7 @@ func (s ParcelStore) Get(number int) (Parcel, error) { func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { rows, err := s.db.Query(`SELECT number, client, status, address, created_at FROM parcel WHERE client = :client`, sql.Named("client", client)) if err != nil { - return nil, err + return nil, fmt.Errorf("select failed: %w", err) } defer rows.Close() @@ -59,15 +60,13 @@ func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { err := rows.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) if err != nil { - return res, err + return res, fmt.Errorf("scan failed: %w", err) } res = append(res, p) } - if err := rows.Err(); err != nil { - - return nil, err + return nil, fmt.Errorf("rows unpacking failed: %w", err) } return res, nil @@ -79,7 +78,7 @@ func (s ParcelStore) SetStatus(number int, status string) error { sql.Named("status", status), sql.Named("number", number)) if err != nil { - return err + return fmt.Errorf("update failed: %w", err) } return nil } @@ -90,7 +89,7 @@ func (s ParcelStore) GetStatus(number int) (string, error) { row := s.db.QueryRow("SELECT status FROM parcel WHERE number = :number", sql.Named("number", number)) err := row.Scan(&p.Status) if err != nil { - return "", err + return "", fmt.Errorf("select failed: %w", err) } return p.Status, nil @@ -101,17 +100,17 @@ func (s ParcelStore) SetAddress(number int, address string) error { // менять адрес можно только если значение статуса registered status, err := s.GetStatus(number) if err != nil { - return err + return fmt.Errorf("getStatus failed: %w", err) } if status != ParcelStatusRegistered { - return nil // не знаю что возвратить + return fmt.Errorf("address shouldn't be updated") // не знаю что возвратить } _, err = s.db.Exec("UPDATE parcel SET address = :address WHERE number = :number", sql.Named("address", address), sql.Named("number", number)) if err != nil { - return err + return fmt.Errorf("update failed: %w", err) } return nil @@ -126,10 +125,10 @@ func (s ParcelStore) Delete(number int) error { } if status != ParcelStatusRegistered { - return nil // не знаю что возвратить + return fmt.Errorf("parcel shouldn't be deleted") } _, err = s.db.Exec("DELETE FROM parcel WHERE number = :number", sql.Named("number", number)) - return err + return fmt.Errorf("delete failed: %w", err) } diff --git a/parcel_test.go b/parcel_test.go index 386c1662..6871099a 100644 --- a/parcel_test.go +++ b/parcel_test.go @@ -53,7 +53,7 @@ func TestAddGetDelete(t *testing.T) { require.NoError(t, err) _, err = store.Get(number) - require.Error(t, err) + require.Equal(t, sql.ErrNoRows, err) }