From ed40fa58ed5b52d646ad75c1e6510e014a0633cc Mon Sep 17 00:00:00 2001 From: Ramil Safiullin Date: Thu, 22 May 2025 22:21:26 +0300 Subject: [PATCH 1/2] testik: Added --- main.go | 64 ++++++------------------ parcel.go | 83 +++++++++++++++++++++---------- parcel_test.go | 132 ++++++++++++++++++++----------------------------- tracker.db | Bin 61440 -> 61440 bytes 4 files changed, 126 insertions(+), 153 deletions(-) diff --git a/main.go b/main.go index 44c32b3f..2b145555 100644 --- a/main.go +++ b/main.go @@ -37,17 +37,13 @@ func (s ParcelService) Register(client int, address string) (Parcel, error) { Address: address, CreatedAt: time.Now().UTC().Format(time.RFC3339), } - id, err := s.store.Add(parcel) if err != nil { return parcel, err } - parcel.Number = id - fmt.Printf("Новая посылка № %d на адрес %s от клиента с идентификатором %d зарегистрирована %s\n", parcel.Number, parcel.Address, parcel.Client, parcel.CreatedAt) - return parcel, nil } @@ -56,14 +52,12 @@ func (s ParcelService) PrintClientParcels(client int) error { if err != nil { return err } - fmt.Printf("Посылки клиента %d:\n", client) for _, parcel := range parcels { fmt.Printf("Посылка № %d на адрес %s от клиента с идентификатором %d зарегистрирована %s, статус %s\n", parcel.Number, parcel.Address, parcel.Client, parcel.CreatedAt, parcel.Status) } fmt.Println() - return nil } @@ -72,19 +66,16 @@ func (s ParcelService) NextStatus(number int) error { if err != nil { return err } - var nextStatus string switch parcel.Status { case ParcelStatusRegistered: nextStatus = ParcelStatusSent case ParcelStatusSent: nextStatus = ParcelStatusDelivered - case ParcelStatusDelivered: + default: return nil } - fmt.Printf("У посылки № %d новый статус: %s\n", number, nextStatus) - return s.store.SetStatus(number, nextStatus) } @@ -97,12 +88,14 @@ func (s ParcelService) Delete(number int) error { } func main() { - // настройте подключение к БД - - store := // создайте объект ParcelStore функцией NewParcelStore + db, err := sql.Open("sqlite", "tracker.db") + if err != nil { + fmt.Println(err) + return + } + defer db.Close() + store := NewParcelStore(db) service := NewParcelService(store) - - // регистрация посылки client := 1 address := "Псков, д. Пушкина, ул. Колотушкина, д. 5" p, err := service.Register(client, address) @@ -110,63 +103,36 @@ func main() { fmt.Println(err) return } - - // изменение адреса newAddress := "Саратов, д. Верхние Зори, ул. Козлова, д. 25" - err = service.ChangeAddress(p.Number, newAddress) - if err != nil { + if err := service.ChangeAddress(p.Number, newAddress); err != nil { fmt.Println(err) return } - - // изменение статуса - err = service.NextStatus(p.Number) - if err != nil { + if err := service.NextStatus(p.Number); err != nil { fmt.Println(err) return } - - // вывод посылок клиента - err = service.PrintClientParcels(client) - if err != nil { + if err := service.PrintClientParcels(client); err != nil { fmt.Println(err) return } - - // попытка удаления отправленной посылки - err = service.Delete(p.Number) - if err != nil { + if err := service.Delete(p.Number); err != nil { fmt.Println(err) - return } - - // вывод посылок клиента - // предыдущая посылка не должна удалиться, т.к. её статус НЕ «зарегистрирована» - err = service.PrintClientParcels(client) - if err != nil { + if err := service.PrintClientParcels(client); err != nil { fmt.Println(err) return } - - // регистрация новой посылки p, err = service.Register(client, address) if err != nil { fmt.Println(err) return } - - // удаление новой посылки - err = service.Delete(p.Number) - if err != nil { + if err := service.Delete(p.Number); err != nil { fmt.Println(err) return } - - // вывод посылок клиента - // здесь не должно быть последней посылки, т.к. она должна была успешно удалиться - err = service.PrintClientParcels(client) - if err != nil { + if err := service.PrintClientParcels(client); err != nil { fmt.Println(err) - return } } diff --git a/parcel.go b/parcel.go index db6c815d..3a914f6e 100644 --- a/parcel.go +++ b/parcel.go @@ -2,6 +2,7 @@ package main import ( "database/sql" + "fmt" ) type ParcelStore struct { @@ -9,52 +10,84 @@ type ParcelStore struct { } func NewParcelStore(db *sql.DB) ParcelStore { + _, _ = db.Exec(`CREATE TABLE IF NOT EXISTS parcel( + number INTEGER PRIMARY KEY AUTOINCREMENT, + client INTEGER NOT NULL, + status TEXT NOT NULL, + address TEXT NOT NULL, + created_at TEXT NOT NULL + )`) return ParcelStore{db: db} } 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(?,?,?,?)`, + p.Client, p.Status, p.Address, p.CreatedAt) + if err != nil { + return 0, err + } + id, err := res.LastInsertId() + return int(id), err } func (s ParcelStore) Get(number int) (Parcel, error) { - // реализуйте чтение строки по заданному number - // здесь из таблицы должна вернуться только одна строка - - // заполните объект Parcel данными из таблицы - p := Parcel{} - - return p, nil + row := s.db.QueryRow(`SELECT number,client,status,address,created_at FROM parcel WHERE number=?`, number) + var p Parcel + err := row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) + return p, err } func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { - // реализуйте чтение строк из таблицы parcel по заданному client - // здесь из таблицы может вернуться несколько строк - - // заполните срез Parcel данными из таблицы + 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() var res []Parcel - - return res, nil + 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, rows.Err() } func (s ParcelStore) SetStatus(number int, status string) error { - // реализуйте обновление статуса в таблице parcel - + res, err := s.db.Exec(`UPDATE parcel SET status=? WHERE number=?`, status, number) + if err != nil { + return err + } + aff, _ := res.RowsAffected() + if aff == 0 { + return fmt.Errorf("parcel %d not found", number) + } return nil } 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 + } + aff, _ := res.RowsAffected() + if aff == 0 { + return fmt.Errorf("cannot change address") + } return nil } 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 + } + aff, _ := res.RowsAffected() + if aff == 0 { + return fmt.Errorf("cannot delete parcel") + } return nil } diff --git a/parcel_test.go b/parcel_test.go index d1b93827..a6892e48 100644 --- a/parcel_test.go +++ b/parcel_test.go @@ -7,18 +7,14 @@ import ( "time" "github.com/stretchr/testify/require" + _ "modernc.org/sqlite" ) var ( - // randSource источник псевдо случайных чисел. - // Для повышения уникальности в качестве seed - // используется текущее время в unix формате (в виде числа) randSource = rand.NewSource(time.Now().UnixNano()) - // randRange использует randSource для генерации случайных чисел - randRange = rand.New(randSource) + randRange = rand.New(randSource) ) -// getTestParcel возвращает тестовую посылку func getTestParcel() Parcel { return Parcel{ Client: 1000, @@ -28,94 +24,72 @@ func getTestParcel() Parcel { } } -// TestAddGetDelete проверяет добавление, получение и удаление посылки func TestAddGetDelete(t *testing.T) { - // prepare - db, err := // настройте подключение к БД + db, err := sql.Open("sqlite", ":memory:") + require.NoError(t, err) store := NewParcelStore(db) parcel := getTestParcel() - - // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора - - // get - // получите только что добавленную посылку, убедитесь в отсутствии ошибки - // проверьте, что значения всех полей в полученном объекте совпадают со значениями полей в переменной parcel - - // delete - // удалите добавленную посылку, убедитесь в отсутствии ошибки - // проверьте, что посылку больше нельзя получить из БД + id, err := store.Add(parcel) + require.NoError(t, err) + require.NotZero(t, id) + parcel.Number = id + stored, err := store.Get(id) + require.NoError(t, err) + require.Equal(t, parcel, stored) + require.NoError(t, store.Delete(id)) + _, err = store.Get(id) + require.ErrorIs(t, err, sql.ErrNoRows) } -// TestSetAddress проверяет обновление адреса func TestSetAddress(t *testing.T) { - // prepare - db, err := // настройте подключение к БД - - // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора - - // set address - // обновите адрес, убедитесь в отсутствии ошибки + db, err := sql.Open("sqlite", ":memory:") + require.NoError(t, err) + store := NewParcelStore(db) + p := getTestParcel() + id, err := store.Add(p) + require.NoError(t, err) newAddress := "new test address" - - // check - // получите добавленную посылку и убедитесь, что адрес обновился + require.NoError(t, store.SetAddress(id, newAddress)) + stored, err := store.Get(id) + require.NoError(t, err) + require.Equal(t, newAddress, stored.Address) } -// TestSetStatus проверяет обновление статуса func TestSetStatus(t *testing.T) { - // prepare - db, err := // настройте подключение к БД - - // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора - - // set status - // обновите статус, убедитесь в отсутствии ошибки - - // check - // получите добавленную посылку и убедитесь, что статус обновился + db, err := sql.Open("sqlite", ":memory:") + require.NoError(t, err) + store := NewParcelStore(db) + p := getTestParcel() + id, err := store.Add(p) + require.NoError(t, err) + require.NoError(t, store.SetStatus(id, ParcelStatusSent)) + stored, err := store.Get(id) + require.NoError(t, err) + require.Equal(t, ParcelStatusSent, stored.Status) } -// TestGetByClient проверяет получение посылок по идентификатору клиента func TestGetByClient(t *testing.T) { - // prepare - db, err := // настройте подключение к БД - - parcels := []Parcel{ - getTestParcel(), - getTestParcel(), - getTestParcel(), - } - parcelMap := map[int]Parcel{} - - // задаём всем посылкам один и тот же идентификатор клиента + db, err := sql.Open("sqlite", ":memory:") + require.NoError(t, err) + store := NewParcelStore(db) + parcels := []Parcel{getTestParcel(), getTestParcel(), getTestParcel()} 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 := // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора - - // обновляем идентификатор добавленной у посылки + for i := range parcels { + parcels[i].Client = client + id, err := store.Add(parcels[i]) + require.NoError(t, err) parcels[i].Number = id - - // сохраняем добавленную посылку в структуру map, чтобы её можно было легко достать по идентификатору посылки - parcelMap[id] = parcels[i] } - - // get by client - storedParcels, err := // получите список посылок по идентификатору клиента, сохранённого в переменной client - // убедитесь в отсутствии ошибки - // убедитесь, что количество полученных посылок совпадает с количеством добавленных - - // check - for _, parcel := range storedParcels { - // в parcelMap лежат добавленные посылки, ключ - идентификатор посылки, значение - сама посылка - // убедитесь, что все посылки из storedParcels есть в parcelMap - // убедитесь, что значения полей полученных посылок заполнены верно + stored, err := store.GetByClient(client) + require.NoError(t, err) + require.Len(t, stored, len(parcels)) + m := map[int]Parcel{} + for _, p := range parcels { + m[p.Number] = p + } + for _, p := range stored { + exp, ok := m[p.Number] + require.True(t, ok) + require.Equal(t, exp, p) } } diff --git a/tracker.db b/tracker.db index b6ba48a148daa7c8c4727d4bfc1c868229d7fb24..899e66c7eb118835cbc864fdba943f2b1b5f4821 100644 GIT binary patch delta 285 zcmZp8z})bFd4e>f{zMsPR(%G&v{xHb7RhsbVc`G5e}n(VWO#i`gRY+96>o>^RyT9le{VgAL&3%f4tyRb<| z;ldU@1t7oqVh2!U$A!HYHs~l^Y`(A?C^`$MV)uo87n@LJK{`y042(>54NP^7j6w`8 zt$@hLG%ACUoq4QlP+w%*l@8GXw8lbTNN%$2b$h+ ZVF!wh+d+0-*aUJ1$R;DKRxNVa006fye%b&4 delta 60 zcmV-C0K@-)-~)i*1CSd5Dv=yR1u6h8Zn&{zq8|wL01x#K*bmsV5fI7`lh`jD3}FcY S0~rW#VRB<-Y@!elqCl{GdK7*D From c766abc2257096c3fd999f56c0114238e72d948c Mon Sep 17 00:00:00 2001 From: Ramil Safiullin Date: Sun, 25 May 2025 12:29:12 +0300 Subject: [PATCH 2/2] testik: Fixed --- main.go | 11 ++++++++- parcel.go | 64 ++++++++++++++++++++++++++++++++++--------------- parcel_test.go | 44 ++++++++++++++++++++++------------ tracker.db | Bin 61440 -> 61440 bytes 4 files changed, 84 insertions(+), 35 deletions(-) diff --git a/main.go b/main.go index 2b145555..76afbc20 100644 --- a/main.go +++ b/main.go @@ -96,6 +96,7 @@ func main() { defer db.Close() store := NewParcelStore(db) service := NewParcelService(store) + client := 1 address := "Псков, д. Пушкина, ул. Колотушкина, д. 5" p, err := service.Register(client, address) @@ -103,35 +104,43 @@ func main() { fmt.Println(err) return } + newAddress := "Саратов, д. Верхние Зори, ул. Козлова, д. 25" if err := service.ChangeAddress(p.Number, newAddress); err != nil { fmt.Println(err) return } + if err := service.NextStatus(p.Number); err != nil { fmt.Println(err) return } + if err := service.PrintClientParcels(client); err != nil { fmt.Println(err) return } + if err := service.Delete(p.Number); err != nil { - fmt.Println(err) + fmt.Println(err) // ожидаемая ошибка } + if err := service.PrintClientParcels(client); err != nil { fmt.Println(err) return } + p, err = service.Register(client, address) if err != nil { fmt.Println(err) return } + if err := service.Delete(p.Number); err != nil { fmt.Println(err) return } + if err := service.PrintClientParcels(client); err != nil { fmt.Println(err) } diff --git a/parcel.go b/parcel.go index 3a914f6e..8da86210 100644 --- a/parcel.go +++ b/parcel.go @@ -11,18 +11,20 @@ type ParcelStore struct { func NewParcelStore(db *sql.DB) ParcelStore { _, _ = db.Exec(`CREATE TABLE IF NOT EXISTS parcel( - number INTEGER PRIMARY KEY AUTOINCREMENT, - client INTEGER NOT NULL, - status TEXT NOT NULL, - address TEXT NOT NULL, - created_at TEXT NOT NULL + number INTEGER PRIMARY KEY AUTOINCREMENT, + client INTEGER NOT NULL, + status TEXT NOT NULL, + address TEXT NOT NULL, + created_at TEXT NOT NULL )`) return ParcelStore{db: db} } func (s ParcelStore) Add(p Parcel) (int, error) { - res, err := s.db.Exec(`INSERT INTO parcel(client,status,address,created_at) VALUES(?,?,?,?)`, - p.Client, p.Status, p.Address, p.CreatedAt) + 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 } @@ -31,14 +33,22 @@ func (s ParcelStore) Add(p Parcel) (int, error) { } func (s ParcelStore) Get(number int) (Parcel, error) { - row := s.db.QueryRow(`SELECT number,client,status,address,created_at FROM parcel WHERE number=?`, number) + row := s.db.QueryRow( + `SELECT number,client,status,address,created_at FROM parcel WHERE number=?`, + number, + ) var p Parcel - err := row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) - return p, err + if err := row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt); err != nil { + return p, err + } + return p, nil } 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) + rows, err := s.db.Query( + `SELECT number,client,status,address,created_at FROM parcel WHERE client=?`, + client, + ) if err != nil { return nil, err } @@ -51,7 +61,10 @@ func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { } res = append(res, p) } - return res, rows.Err() + if err := rows.Err(); err != nil { + return nil, err + } + return res, nil } func (s ParcelStore) SetStatus(number int, status string) error { @@ -59,7 +72,10 @@ func (s ParcelStore) SetStatus(number int, status string) error { if err != nil { return err } - aff, _ := res.RowsAffected() + aff, err := res.RowsAffected() + if err != nil { + return err + } if aff == 0 { return fmt.Errorf("parcel %d not found", number) } @@ -67,12 +83,17 @@ func (s ParcelStore) SetStatus(number int, status string) error { } func (s ParcelStore) SetAddress(number int, address string) error { - res, err := s.db.Exec(`UPDATE parcel SET address=? WHERE number=? AND status=?`, - address, number, ParcelStatusRegistered) + res, err := s.db.Exec( + `UPDATE parcel SET address=? WHERE number=? AND status=?`, + address, number, ParcelStatusRegistered, + ) + if err != nil { + return err + } + aff, err := res.RowsAffected() if err != nil { return err } - aff, _ := res.RowsAffected() if aff == 0 { return fmt.Errorf("cannot change address") } @@ -80,12 +101,17 @@ func (s ParcelStore) SetAddress(number int, address string) error { } func (s ParcelStore) Delete(number int) error { - res, err := s.db.Exec(`DELETE FROM parcel WHERE number=? AND status=?`, - number, ParcelStatusRegistered) + res, err := s.db.Exec( + `DELETE FROM parcel WHERE number=? AND status=?`, + number, ParcelStatusRegistered, + ) + if err != nil { + return err + } + aff, err := res.RowsAffected() if err != nil { return err } - aff, _ := res.RowsAffected() if aff == 0 { return fmt.Errorf("cannot delete parcel") } diff --git a/parcel_test.go b/parcel_test.go index a6892e48..5a1d2f4b 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" _ "modernc.org/sqlite" ) @@ -28,14 +29,17 @@ func TestAddGetDelete(t *testing.T) { db, err := sql.Open("sqlite", ":memory:") require.NoError(t, err) store := NewParcelStore(db) - parcel := getTestParcel() - id, err := store.Add(parcel) + + p := getTestParcel() + id, err := store.Add(p) require.NoError(t, err) require.NotZero(t, id) - parcel.Number = id - stored, err := store.Get(id) + + p.Number = id + got, err := store.Get(id) require.NoError(t, err) - require.Equal(t, parcel, stored) + assert.Equal(t, p, got) + require.NoError(t, store.Delete(id)) _, err = store.Get(id) require.ErrorIs(t, err, sql.ErrNoRows) @@ -45,51 +49,61 @@ func TestSetAddress(t *testing.T) { db, err := sql.Open("sqlite", ":memory:") require.NoError(t, err) store := NewParcelStore(db) + p := getTestParcel() id, err := store.Add(p) require.NoError(t, err) + newAddress := "new test address" require.NoError(t, store.SetAddress(id, newAddress)) - stored, err := store.Get(id) + + got, err := store.Get(id) require.NoError(t, err) - require.Equal(t, newAddress, stored.Address) + assert.Equal(t, newAddress, got.Address) } func TestSetStatus(t *testing.T) { db, err := sql.Open("sqlite", ":memory:") require.NoError(t, err) store := NewParcelStore(db) + p := getTestParcel() id, err := store.Add(p) require.NoError(t, err) + require.NoError(t, store.SetStatus(id, ParcelStatusSent)) - stored, err := store.Get(id) + + got, err := store.Get(id) require.NoError(t, err) - require.Equal(t, ParcelStatusSent, stored.Status) + assert.Equal(t, ParcelStatusSent, got.Status) } func TestGetByClient(t *testing.T) { db, err := sql.Open("sqlite", ":memory:") require.NoError(t, err) store := NewParcelStore(db) + parcels := []Parcel{getTestParcel(), getTestParcel(), getTestParcel()} client := randRange.Intn(10_000_000) + for i := range parcels { parcels[i].Client = client id, err := store.Add(parcels[i]) require.NoError(t, err) parcels[i].Number = id } + stored, err := store.GetByClient(client) require.NoError(t, err) require.Len(t, stored, len(parcels)) - m := map[int]Parcel{} - for _, p := range parcels { - m[p.Number] = p - } + + storedMap := map[int]Parcel{} for _, p := range stored { - exp, ok := m[p.Number] + storedMap[p.Number] = p + } + for _, exp := range parcels { + got, ok := storedMap[exp.Number] require.True(t, ok) - require.Equal(t, exp, p) + assert.Equal(t, exp, got) } } diff --git a/tracker.db b/tracker.db index 899e66c7eb118835cbc864fdba943f2b1b5f4821..8c16ddab60785d37e144f2af139bca03b2b47aba 100644 GIT binary patch delta 78 zcmZp8z})bFd4e>f$wV1vMw5*R^Y!^zco`TNnD})V_^0vf@EzJLD6pMxvc7!`uW5*Z brInGTm4R7Q2J_?@_OVvo`<$I+zt< delta 65 zcmZp8z})bFd4e>f{zMsPM*WQm^YwX|co`TN82P6$@K58{*(@la%s=_4T@0^Lh@qtw U5E+?9WiU?Gvya{U)t-9;07{t>KL7v#