From 4749d2ed7f35071798edfd2061b42167b733a338 Mon Sep 17 00:00:00 2001 From: xails-git Date: Fri, 20 Jun 2025 17:31:33 +0300 Subject: [PATCH 1/3] first commit --- main.go | 8 +++- parcel.go | 91 ++++++++++++++++++++++++++++++++++++++++++-- parcel_test.go | 91 ++++++++++++++++++++++++++++++++++++++++---- tracker.db | Bin 61440 -> 61440 bytes "tra\321\201ker.db" | 0 5 files changed, 176 insertions(+), 14 deletions(-) create mode 100644 "tra\321\201ker.db" diff --git a/main.go b/main.go index 44c32b3f..c6dab40e 100644 --- a/main.go +++ b/main.go @@ -97,9 +97,13 @@ func (s ParcelService) Delete(number int) error { } func main() { - // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") + if err != nil { + fmt.Println(err) + return + } - store := // создайте объект ParcelStore функцией NewParcelStore + store := NewParcelStore(db) // создайте объект ParcelStore функцией NewParcelStore service := NewParcelService(store) // регистрация посылки diff --git a/parcel.go b/parcel.go index db6c815d..f091fba1 100644 --- a/parcel.go +++ b/parcel.go @@ -2,6 +2,8 @@ package main import ( "database/sql" + "fmt" + "log" ) type ParcelStore struct { @@ -15,32 +17,79 @@ 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 (:client, :status, :address, :created_at)", + sql.Named("client", p.Client), + sql.Named("status", p.Status), + sql.Named("address", p.Address), + sql.Named("created_at", p.CreatedAt)) + + if err != nil { + return 0, fmt.Errorf("ошибка при добавлении значений в таблицу функцией Add") + } + + id, err := res.LastInsertId() + if err != nil { + return 0, fmt.Errorf("ошибка получения идентификатора, функция Add") + } + // верните идентификатор последней добавленной записи - return 0, nil + 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)) + // заполните объект Parcel данными из таблицы + err := row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) + if err != nil { + return p, fmt.Errorf("ошибка сканирования: %w", err) + } return p, nil } func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { // реализуйте чтение строк из таблицы parcel по заданному client // здесь из таблицы может вернуться несколько строк + var res []Parcel + + row, err := s.db.Query("SELECT number, client, status, address, created_at FROM parcel WHERE client = :client", sql.Named("client", client)) + if err != nil { + return res, fmt.Errorf("ошибка получения строк из бд") + } + + defer row.Close() + + for row.Next() { + var p Parcel + + err = row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) + if err != nil { + return res, err + } + res = append(res, p) + } + if err := row.Err(); err != nil { + log.Fatal(err) + } // заполните срез Parcel данными из таблицы - var res []Parcel 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 fmt.Errorf("ошибка обновления статуса") + } return nil } @@ -49,6 +98,23 @@ func (s ParcelStore) SetAddress(number int, address string) error { // реализуйте обновление адреса в таблице parcel // менять адрес можно только если значение статуса registered + var status string + + row := s.db.QueryRow("SELECT status FROM parcel WHERE number = :number", sql.Named("number", number)) + err := row.Scan(&status) + if err != nil { + return fmt.Errorf("ошибка сканирования") + } + + if status != ParcelStatusRegistered { + return fmt.Errorf("нельзя изменить адрес для статуса %s", status) + } + + _, err = s.db.Exec("UPDATE parcel SET address = :address WHERE number = :number", sql.Named("address", address), sql.Named("number", number)) + if err != nil { + return fmt.Errorf("ошибка обновления адреса %w", err) + } + return nil } @@ -56,5 +122,22 @@ func (s ParcelStore) Delete(number int) error { // реализуйте удаление строки из таблицы parcel // удалять строку можно только если значение статуса registered + var status string + + row := s.db.QueryRow("SELECT status FROM parcel WHERE number = :number", sql.Named("number", number)) + err := row.Scan(&status) + if err != nil { + return fmt.Errorf("ошибка сканирования") + } + + if status != ParcelStatusRegistered { + return fmt.Errorf("нельзя изменить адрес для статуса %s", status) + } + + _, err = s.db.Exec("DELETE FROM parcel WHERE number = :number", sql.Named("number", number)) + if err != nil { + return fmt.Errorf("ошибка удаления строчки") + } + return nil } diff --git a/parcel_test.go b/parcel_test.go index d1b93827..b52d1727 100644 --- a/parcel_test.go +++ b/parcel_test.go @@ -31,58 +31,121 @@ 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, "идентификатор отсутствует") + // get // получите только что добавленную посылку, убедитесь в отсутствии ошибки // проверьте, что значения всех полей в полученном объекте совпадают со значениями полей в переменной parcel + res, err := store.Get(id) + + require.NoError(t, err) + require.Equal(t, parcel.Client, res.Client, "клиент не совпадает") + require.Equal(t, parcel.Status, res.Status, "статус не совпадает") + require.Equal(t, parcel.Address, res.Address, "адрес не совпадает") + require.Equal(t, parcel.CreatedAt, res.CreatedAt, "время создания не совпадает") + // delete // удалите добавленную посылку, убедитесь в отсутствии ошибки // проверьте, что посылку больше нельзя получить из БД + + err = store.Delete(id) + require.NoError(t, err) + + _, err = store.Get(id) + require.ErrorIs(t, err, sql.ErrNoRows, "после удаления посылка не должна быть найдена") + } // TestSetAddress проверяет обновление адреса func TestSetAddress(t *testing.T) { // prepare - db, err := // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") // настройте подключение к БД + require.NoError(t, err, "ошибка подключения к bd") + defer db.Close() // add // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + store := NewParcelStore(db) + parcel := getTestParcel() + + id, err := store.Add(parcel) + require.NoError(t, err) + require.NotZero(t, id, "идентификатор отсутствует") // set address // обновите адрес, убедитесь в отсутствии ошибки newAddress := "new test address" + err = store.SetAddress(id, newAddress) + require.NoError(t, err) + // check // получите добавленную посылку и убедитесь, что адрес обновился + + res, err := store.Get(id) + require.NoError(t, err) + + require.Equal(t, newAddress, res.Address, "не совпадение нового добавленного адреса") } // TestSetStatus проверяет обновление статуса func TestSetStatus(t *testing.T) { // prepare - db, err := // настройте подключение к БД + db, err := sql.Open("sqlite", "tracker.db") // настройте подключение к БД + require.NoError(t, err) + defer db.Close() // add // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + store := NewParcelStore(db) + parcel := getTestParcel() + + id, err := store.Add(parcel) + require.NoError(t, err) + + require.NotZero(t, id, "идентификатор отсутствует") + // set status // обновите статус, убедитесь в отсутствии ошибки + err = store.SetStatus(id, ParcelStatusSent) + require.NoError(t, err) + // check // получите добавленную посылку и убедитесь, что статус обновился + + res, err := store.Get(id) + require.NoError(t, err) + + require.Equal(t, ParcelStatusSent, res.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(), getTestParcel(), @@ -98,22 +161,34 @@ 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 - // сохраняем добавленную посылку в структуру map, чтобы её можно было легко достать по идентификатору посылки parcelMap[id] = parcels[i] } // get by client - storedParcels, err := // получите список посылок по идентификатору клиента, сохранённого в переменной client + storedParcels, err := store.GetByClient(client) // получите список посылок по идентификатору клиента, сохранённого в переменной client + require.NoError(t, err) + // убедитесь в отсутствии ошибки // убедитесь, что количество полученных посылок совпадает с количеством добавленных + require.Len(t, storedParcels, len(parcels)) + // check for _, parcel := range storedParcels { + expected, ok := parcelMap[parcel.Number] + require.True(t, ok, "посылка %d не найдена", parcel.Number) + + require.Equal(t, expected.Client, parcel.Client) + require.Equal(t, expected.Status, parcel.Status) + require.Equal(t, expected.Address, parcel.Address) + require.Equal(t, expected.CreatedAt, parcel.CreatedAt) + // в parcelMap лежат добавленные посылки, ключ - идентификатор посылки, значение - сама посылка // убедитесь, что все посылки из storedParcels есть в parcelMap // убедитесь, что значения полей полученных посылок заполнены верно diff --git a/tracker.db b/tracker.db index b6ba48a148daa7c8c4727d4bfc1c868229d7fb24..082699ee965edd2577118367b51f205bedae7deb 100644 GIT binary patch delta 763 zcmZp8z})bFd4e>f^F$eER%Zsiv{xHb7RhsbVc`G5e}n(VWL-uePg#SDIS z22RmNdDG(5ypjtGFKoEj0K`of_FdSdqi|u1p2CGm7q(t(xY&AO?}Z%~wklkheqkR_ z$qpTbi_I5y1EpsHm2SVV8>HdF28b3TQzHW-Q(XfyT_b}KLlY}w11kgLC~ZDA24+Q3 zQ`INuic-@vi%U}Li&9fcQj1GaRU27Y8Jb6F^P;Q9ryaY~=m-PLC~X! Date: Tue, 8 Jul 2025 17:18:15 +0300 Subject: [PATCH 2/3] new commit --- parcel.go | 64 +++++++++++++++++++++++++++----------------------- parcel_test.go | 21 +++++++++-------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/parcel.go b/parcel.go index f091fba1..786f7a3e 100644 --- a/parcel.go +++ b/parcel.go @@ -47,7 +47,7 @@ func (s ParcelStore) Get(number int) (Parcel, error) { if err != nil { return p, fmt.Errorf("ошибка сканирования: %w", err) } - return p, nil + return Parcel{}, nil } func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { @@ -57,7 +57,7 @@ func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { row, err := s.db.Query("SELECT number, client, status, address, created_at FROM parcel WHERE client = :client", sql.Named("client", client)) if err != nil { - return res, fmt.Errorf("ошибка получения строк из бд") + return nil, fmt.Errorf("ошибка получения строк из бд") } defer row.Close() @@ -67,7 +67,7 @@ func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { err = row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) if err != nil { - return res, err + return nil, err } res = append(res, p) } @@ -95,48 +95,54 @@ func (s ParcelStore) SetStatus(number int, status string) error { } func (s ParcelStore) SetAddress(number int, address string) error { - // реализуйте обновление адреса в таблице parcel - // менять адрес можно только если значение статуса registered - - var status string - - row := s.db.QueryRow("SELECT status FROM parcel WHERE number = :number", sql.Named("number", number)) - err := row.Scan(&status) + // Выполняем обновление только для статуса 'registered' + result, err := s.db.Exec(` + UPDATE parcel + SET address = :address + WHERE number = :number AND status = :status + `, + sql.Named("address", address), + sql.Named("number", number), + sql.Named("status", ParcelStatusRegistered), + ) if err != nil { - return fmt.Errorf("ошибка сканирования") + return fmt.Errorf("ошибка обновления адреса: %w", err) } - if status != ParcelStatusRegistered { - return fmt.Errorf("нельзя изменить адрес для статуса %s", status) + // Проверяем количество обновленных строк + rowsAffected, err := result.RowsAffected() + if err != nil { + return fmt.Errorf("ошибка при получении количества обновленных строк: %w", err) } - _, err = s.db.Exec("UPDATE parcel SET address = :address WHERE number = :number", sql.Named("address", address), sql.Named("number", number)) - if err != nil { - return fmt.Errorf("ошибка обновления адреса %w", err) + if rowsAffected == 0 { + return fmt.Errorf("нельзя изменить адрес для посылки %d: не найдена или статус не '%s'", number, ParcelStatusRegistered) } return nil } func (s ParcelStore) Delete(number int) error { - // реализуйте удаление строки из таблицы parcel - // удалять строку можно только если значение статуса registered - - var status string - - row := s.db.QueryRow("SELECT status FROM parcel WHERE number = :number", sql.Named("number", number)) - err := row.Scan(&status) + // Выполняем удаление только для статуса 'registered' + result, err := s.db.Exec(` + DELETE FROM parcel + WHERE number = :number AND status = :status + `, + sql.Named("number", number), + sql.Named("status", ParcelStatusRegistered), + ) if err != nil { - return fmt.Errorf("ошибка сканирования") + return fmt.Errorf("ошибка удаления посылки: %w", err) } - if status != ParcelStatusRegistered { - return fmt.Errorf("нельзя изменить адрес для статуса %s", status) + // Проверяем количество удаленных строк + rowsAffected, err := result.RowsAffected() + if err != nil { + return fmt.Errorf("ошибка при получении количества удаленных строк: %w", err) } - _, err = s.db.Exec("DELETE FROM parcel WHERE number = :number", sql.Named("number", number)) - if err != nil { - return fmt.Errorf("ошибка удаления строчки") + if rowsAffected == 0 { + return fmt.Errorf("нельзя удалить посылку %d: не найдена или статус не '%s'", number, ParcelStatusRegistered) } return nil diff --git a/parcel_test.go b/parcel_test.go index b52d1727..7293280f 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" ) @@ -53,11 +54,11 @@ func TestAddGetDelete(t *testing.T) { res, err := store.Get(id) - require.NoError(t, err) - require.Equal(t, parcel.Client, res.Client, "клиент не совпадает") - require.Equal(t, parcel.Status, res.Status, "статус не совпадает") - require.Equal(t, parcel.Address, res.Address, "адрес не совпадает") - require.Equal(t, parcel.CreatedAt, res.CreatedAt, "время создания не совпадает") + assert.NoError(t, err) + assert.Equal(t, parcel.Client, res.Client, "клиент не совпадает") + assert.Equal(t, parcel.Status, res.Status, "статус не совпадает") + assert.Equal(t, parcel.Address, res.Address, "адрес не совпадает") + assert.Equal(t, parcel.CreatedAt, res.CreatedAt, "время создания не совпадает") // delete // удалите добавленную посылку, убедитесь в отсутствии ошибки @@ -98,7 +99,7 @@ func TestSetAddress(t *testing.T) { // получите добавленную посылку и убедитесь, что адрес обновился res, err := store.Get(id) - require.NoError(t, err) + assert.NoError(t, err) require.Equal(t, newAddress, res.Address, "не совпадение нового добавленного адреса") } @@ -131,9 +132,9 @@ func TestSetStatus(t *testing.T) { // получите добавленную посылку и убедитесь, что статус обновился res, err := store.Get(id) - require.NoError(t, err) + assert.NoError(t, err) - require.Equal(t, ParcelStatusSent, res.Status, "не совпадения статуса с обновленным") + assert.Equal(t, ParcelStatusSent, res.Status, "не совпадения статуса с обновленным") } @@ -172,7 +173,7 @@ func TestGetByClient(t *testing.T) { // get by client storedParcels, err := store.GetByClient(client) // получите список посылок по идентификатору клиента, сохранённого в переменной client - require.NoError(t, err) + assert.NoError(t, err) // убедитесь в отсутствии ошибки // убедитесь, что количество полученных посылок совпадает с количеством добавленных @@ -182,7 +183,7 @@ func TestGetByClient(t *testing.T) { // check for _, parcel := range storedParcels { expected, ok := parcelMap[parcel.Number] - require.True(t, ok, "посылка %d не найдена", parcel.Number) + assert.True(t, ok, "посылка %d не найдена", parcel.Number) require.Equal(t, expected.Client, parcel.Client) require.Equal(t, expected.Status, parcel.Status) From 5a73ce73f3423663e6e976acd0fb91a016dd5aa3 Mon Sep 17 00:00:00 2001 From: xails-git Date: Wed, 9 Jul 2025 18:08:39 +0300 Subject: [PATCH 3/3] new commit 2 --- parcel.go | 4 ++-- parcel_test.go | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/parcel.go b/parcel.go index 786f7a3e..8ee75684 100644 --- a/parcel.go +++ b/parcel.go @@ -45,9 +45,9 @@ func (s ParcelStore) Get(number int) (Parcel, error) { // заполните объект Parcel данными из таблицы err := row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) if err != nil { - return p, fmt.Errorf("ошибка сканирования: %w", err) + return Parcel{}, fmt.Errorf("ошибка сканирования: %w", err) } - return Parcel{}, nil + return p, nil } func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { diff --git a/parcel_test.go b/parcel_test.go index 7293280f..73c998eb 100644 --- a/parcel_test.go +++ b/parcel_test.go @@ -59,6 +59,7 @@ func TestAddGetDelete(t *testing.T) { assert.Equal(t, parcel.Status, res.Status, "статус не совпадает") assert.Equal(t, parcel.Address, res.Address, "адрес не совпадает") assert.Equal(t, parcel.CreatedAt, res.CreatedAt, "время создания не совпадает") + assert.Equal(t, parcel.Number, res.Number, "номер не совпадает") // delete // удалите добавленную посылку, убедитесь в отсутствии ошибки @@ -99,9 +100,9 @@ func TestSetAddress(t *testing.T) { // получите добавленную посылку и убедитесь, что адрес обновился res, err := store.Get(id) - assert.NoError(t, err) + require.NoError(t, err) - require.Equal(t, newAddress, res.Address, "не совпадение нового добавленного адреса") + assert.Equal(t, newAddress, res.Address, "не совпадение нового добавленного адреса") } // TestSetStatus проверяет обновление статуса @@ -132,7 +133,7 @@ func TestSetStatus(t *testing.T) { // получите добавленную посылку и убедитесь, что статус обновился res, err := store.Get(id) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, ParcelStatusSent, res.Status, "не совпадения статуса с обновленным") @@ -173,7 +174,7 @@ func TestGetByClient(t *testing.T) { // get by client storedParcels, err := store.GetByClient(client) // получите список посылок по идентификатору клиента, сохранённого в переменной client - assert.NoError(t, err) + require.NoError(t, err) // убедитесь в отсутствии ошибки // убедитесь, что количество полученных посылок совпадает с количеством добавленных @@ -185,7 +186,7 @@ func TestGetByClient(t *testing.T) { expected, ok := parcelMap[parcel.Number] assert.True(t, ok, "посылка %d не найдена", parcel.Number) - require.Equal(t, expected.Client, parcel.Client) + assert.Equal(t, expected.Client, parcel.Client) require.Equal(t, expected.Status, parcel.Status) require.Equal(t, expected.Address, parcel.Address) require.Equal(t, expected.CreatedAt, parcel.CreatedAt)