From 4abfd4873f9c0904fe3b1765087a259091e2e684 Mon Sep 17 00:00:00 2001 From: Pavel Date: Sat, 24 May 2025 00:39:56 +0300 Subject: [PATCH 1/2] first-iteration --- main.go | 8 +++++- parcel.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++-- parcel_test.go | 59 ++++++++++++++++++++++++++++++++++---- tracker.db | Bin 61440 -> 61440 bytes 4 files changed, 133 insertions(+), 10 deletions(-) diff --git a/main.go b/main.go index 44c32b3f..987a45e1 100644 --- a/main.go +++ b/main.go @@ -98,8 +98,14 @@ 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..3cfb151b 100644 --- a/parcel.go +++ b/parcel.go @@ -2,6 +2,7 @@ package main import ( "database/sql" + "fmt" ) type ParcelStore struct { @@ -15,17 +16,38 @@ func NewParcelStore(db *sql.DB) ParcelStore { func (s ParcelStore) Add(p Parcel) (int, error) { // реализуйте добавление строки в таблицу parcel, используйте данные из переменной p + res, err := s.db.Exec( + "INSERT INTO parcel (client, status, address, created_at) VALUES (?, ?, ?, ?)", + p.Client, p.Status, p.Address, p.CreatedAt, + ) + if err != nil { + return 0, err + } + + id, err := res.LastInsertId() + if err != nil { + return 0, err + } + // верните идентификатор последней добавленной записи - return 0, nil + return int(id), nil } func (s ParcelStore) Get(number int) (Parcel, error) { // реализуйте чтение строки по заданному number // здесь из таблицы должна вернуться только одна строка - // заполните объект Parcel данными из таблицы + row := s.db.QueryRow( + "SELECT number, client, status, address, created_at FROM parcel WHERE number = ?", + number, + ) p := Parcel{} + err := row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) + if err != nil { + return p, err + } + // заполните объект Parcel данными из таблицы return p, nil } @@ -33,8 +55,24 @@ 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, + ) + if err != nil { + return nil, err + } + defer rows.Close() + // заполните срез Parcel данными из таблицы var res []Parcel + for rows.Next() { + var p Parcel + if err := rows.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt); err != nil { + return nil, err + } + res = append(res, p) + } return res, nil } @@ -42,13 +80,31 @@ func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { func (s ParcelStore) SetStatus(number int, status string) error { // реализуйте обновление статуса в таблице parcel - return nil + _, err := s.db.Exec( + "UPDATE parcel SET status = ? WHERE number = ?", + status, number, + ) + return err } func (s ParcelStore) SetAddress(number int, address string) error { // реализуйте обновление адреса в таблице parcel // менять адрес можно только если значение статуса registered + res, err := s.db.Exec( + "UPDATE parcel SET address = ? WHERE number = ? AND status = ?", + address, number, ParcelStatusRegistered, + ) + if err != nil { + return err + } + affected, err := res.RowsAffected() + if err != nil { + return err + } + if affected == 0 { + return fmt.Errorf("cannot change address: parcel is not in registered status") + } return nil } @@ -56,5 +112,19 @@ func (s ParcelStore) Delete(number int) error { // реализуйте удаление строки из таблицы parcel // удалять строку можно только если значение статуса registered + res, err := s.db.Exec( + "DELETE FROM parcel WHERE number = ? AND status = ?", + number, ParcelStatusRegistered, + ) + if err != nil { + return err + } + affected, err := res.RowsAffected() + if err != nil { + return err + } + if affected == 0 { + return fmt.Errorf("cannot delete: parcel is not in registered status") + } return nil } diff --git a/parcel_test.go b/parcel_test.go index d1b93827..35b7fd6a 100644 --- a/parcel_test.go +++ b/parcel_test.go @@ -31,57 +31,97 @@ func getTestParcel() Parcel { // TestAddGetDelete проверяет добавление, получение и удаление посылки func TestAddGetDelete(t *testing.T) { // prepare - db, err := // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") + require.NoError(t, err) + defer db.Close() store := NewParcelStore(db) parcel := getTestParcel() // add // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + id, err := store.Add(parcel) + require.NoError(t, err) + require.NotZero(t, id) + parcel.Number = id // get // получите только что добавленную посылку, убедитесь в отсутствии ошибки // проверьте, что значения всех полей в полученном объекте совпадают со значениями полей в переменной parcel + got, err := store.Get(id) + require.NoError(t, err) + require.Equal(t, parcel, got) // delete // удалите добавленную посылку, убедитесь в отсутствии ошибки // проверьте, что посылку больше нельзя получить из БД + err = store.Delete(id) + require.NoError(t, err) + + _, err = store.Get(id) + require.Error(t, err) } // TestSetAddress проверяет обновление адреса func TestSetAddress(t *testing.T) { // prepare - db, err := // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") + require.NoError(t, err) + defer db.Close() + store := NewParcelStore(db) // add // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + id, err := store.Add(getTestParcel()) + require.NoError(t, err) + require.NotZero(t, id) // set address // обновите адрес, убедитесь в отсутствии ошибки newAddress := "new test address" + err = store.SetAddress(id, newAddress) + require.NoError(t, err) // check // получите добавленную посылку и убедитесь, что адрес обновился + got, err := store.Get(id) + require.NoError(t, err) + require.Equal(t, newAddress, got.Address) } // TestSetStatus проверяет обновление статуса func TestSetStatus(t *testing.T) { // prepare - db, err := // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") + require.NoError(t, err) + defer db.Close() + store := NewParcelStore(db) // add // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + id, err := store.Add(getTestParcel()) + require.NoError(t, err) + require.NotZero(t, id) // set status // обновите статус, убедитесь в отсутствии ошибки + newStatus := ParcelStatusSent + err = store.SetStatus(id, newStatus) + require.NoError(t, err) // check // получите добавленную посылку и убедитесь, что статус обновился + got, err := store.Get(id) + require.NoError(t, err) + require.Equal(t, newStatus, got.Status) } // TestGetByClient проверяет получение посылок по идентификатору клиента func TestGetByClient(t *testing.T) { // prepare - db, err := // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") + require.NoError(t, err) + defer db.Close() + store := NewParcelStore(db) parcels := []Parcel{ getTestParcel(), @@ -98,7 +138,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.NotZero(t, id) // обновляем идентификатор добавленной у посылки parcels[i].Number = id @@ -108,14 +150,19 @@ func TestGetByClient(t *testing.T) { } // get by client - storedParcels, err := // получите список посылок по идентификатору клиента, сохранённого в переменной client + storedParcels, err := store.GetByClient(client) // убедитесь в отсутствии ошибки + require.NoError(t, err) // убедитесь, что количество полученных посылок совпадает с количеством добавленных + require.Equal(t, len(parcels), len(storedParcels)) // check for _, parcel := range storedParcels { // в parcelMap лежат добавленные посылки, ключ - идентификатор посылки, значение - сама посылка // убедитесь, что все посылки из storedParcels есть в parcelMap // убедитесь, что значения полей полученных посылок заполнены верно + expected, ok := parcelMap[parcel.Number] + require.True(t, ok) + require.Equal(t, expected, parcel) } } diff --git a/tracker.db b/tracker.db index b6ba48a148daa7c8c4727d4bfc1c868229d7fb24..0c28e249dac3fc38051ef7d597d7b5ea9e33eea8 100644 GIT binary patch delta 426 zcmZp8z})bFd4e>f*+dy Date: Mon, 26 May 2025 09:56:50 +0300 Subject: [PATCH 2/2] third-iteration --- parcel.go | 5 ++++- parcel_test.go | 46 +++++++--------------------------------------- 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/parcel.go b/parcel.go index 3cfb151b..42e2c81f 100644 --- a/parcel.go +++ b/parcel.go @@ -44,7 +44,7 @@ func (s ParcelStore) Get(number int) (Parcel, error) { p := Parcel{} err := row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) if err != nil { - return p, err + return Parcel{}, err // исправлено: возвращаем пустую структуру, а не p } // заполните объект Parcel данными из таблицы @@ -73,6 +73,9 @@ func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { } res = append(res, p) } + if err := rows.Err(); err != nil { // исправлено: обработка ошибки rows + return nil, err + } return res, nil } diff --git a/parcel_test.go b/parcel_test.go index 35b7fd6a..bb85020b 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" ) @@ -38,22 +39,17 @@ func TestAddGetDelete(t *testing.T) { parcel := getTestParcel() // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора id, err := store.Add(parcel) require.NoError(t, err) require.NotZero(t, id) parcel.Number = id // get - // получите только что добавленную посылку, убедитесь в отсутствии ошибки - // проверьте, что значения всех полей в полученном объекте совпадают со значениями полей в переменной parcel got, err := store.Get(id) require.NoError(t, err) - require.Equal(t, parcel, got) + assert.Equal(t, parcel, got) // исправлено: assert вместо require // delete - // удалите добавленную посылку, убедитесь в отсутствии ошибки - // проверьте, что посылку больше нельзя получить из БД err = store.Delete(id) require.NoError(t, err) @@ -63,61 +59,46 @@ func TestAddGetDelete(t *testing.T) { // TestSetAddress проверяет обновление адреса func TestSetAddress(t *testing.T) { - // prepare db, err := sql.Open("sqlite", "tracker.db") require.NoError(t, err) defer db.Close() store := NewParcelStore(db) - // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора id, err := store.Add(getTestParcel()) require.NoError(t, err) require.NotZero(t, id) - // set address - // обновите адрес, убедитесь в отсутствии ошибки newAddress := "new test address" err = store.SetAddress(id, newAddress) require.NoError(t, err) - // check - // получите добавленную посылку и убедитесь, что адрес обновился got, err := store.Get(id) require.NoError(t, err) - require.Equal(t, newAddress, got.Address) + assert.Equal(t, newAddress, got.Address) // исправлено: assert вместо require } // TestSetStatus проверяет обновление статуса func TestSetStatus(t *testing.T) { - // prepare db, err := sql.Open("sqlite", "tracker.db") require.NoError(t, err) defer db.Close() store := NewParcelStore(db) - // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора id, err := store.Add(getTestParcel()) require.NoError(t, err) require.NotZero(t, id) - // set status - // обновите статус, убедитесь в отсутствии ошибки newStatus := ParcelStatusSent err = store.SetStatus(id, newStatus) require.NoError(t, err) - // check - // получите добавленную посылку и убедитесь, что статус обновился got, err := store.Get(id) require.NoError(t, err) - require.Equal(t, newStatus, got.Status) + assert.Equal(t, newStatus, got.Status) // исправлено: assert вместо require } // TestGetByClient проверяет получение посылок по идентификатору клиента func TestGetByClient(t *testing.T) { - // prepare db, err := sql.Open("sqlite", "tracker.db") require.NoError(t, err) defer db.Close() @@ -130,39 +111,26 @@ func TestGetByClient(t *testing.T) { } parcelMap := map[int]Parcel{} - // задаём всем посылкам один и тот же идентификатор клиента client := randRange.Intn(10_000_000) parcels[0].Client = client parcels[1].Client = client parcels[2].Client = client - // add for i := 0; i < len(parcels); i++ { id, err := store.Add(parcels[i]) require.NoError(t, err) require.NotZero(t, id) - - // обновляем идентификатор добавленной у посылки parcels[i].Number = id - - // сохраняем добавленную посылку в структуру map, чтобы её можно было легко достать по идентификатору посылки parcelMap[id] = parcels[i] } - // get by client storedParcels, err := store.GetByClient(client) - // убедитесь в отсутствии ошибки require.NoError(t, err) - // убедитесь, что количество полученных посылок совпадает с количеством добавленных - require.Equal(t, len(parcels), len(storedParcels)) + assert.Len(t, storedParcels, len(parcels)) // исправлено: assert.Len вместо require.Equal - // check for _, parcel := range storedParcels { - // в parcelMap лежат добавленные посылки, ключ - идентификатор посылки, значение - сама посылка - // убедитесь, что все посылки из storedParcels есть в parcelMap - // убедитесь, что значения полей полученных посылок заполнены верно expected, ok := parcelMap[parcel.Number] - require.True(t, ok) - require.Equal(t, expected, parcel) + assert.True(t, ok) // исправлено: assert вместо require + assert.Equal(t, expected, parcel) // исправлено: assert вместо require } }