diff --git a/go.mod b/go.mod index 20eb2205..70415fa1 100644 --- a/go.mod +++ b/go.mod @@ -29,4 +29,4 @@ require ( modernc.org/opt v0.1.3 // indirect modernc.org/strutil v1.1.3 // indirect modernc.org/token v1.0.1 // indirect -) +) \ No newline at end of file diff --git a/go.sum b/go.sum index cc7fd35d..f9b0cf0d 100644 --- a/go.sum +++ b/go.sum @@ -78,4 +78,4 @@ modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= -modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= +modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= \ No newline at end of file diff --git a/main.go b/main.go index 44c32b3f..8153cd32 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..69e255e9 100644 --- a/parcel.go +++ b/parcel.go @@ -2,6 +2,9 @@ package main import ( "database/sql" + "fmt" + + _ "modernc.org/sqlite" ) type ParcelStore struct { @@ -13,48 +16,97 @@ func NewParcelStore(db *sql.DB) ParcelStore { } func (s ParcelStore) Add(p Parcel) (int, error) { - // реализуйте добавление строки в таблицу parcel, используйте данные из переменной p - - // верните идентификатор последней добавленной записи - return 0, nil + query := "INSERT INTO parcel (client, status, address, created_at) VALUES (?, ?, ?, ?)" + output, err := s.db.Exec(query, p.Client, p.Status, p.Address, p.CreatedAt) + if err != nil { + fmt.Println(err) + return 0, err + } + + id, err := output.LastInsertId() + if err != nil { + fmt.Println(err) + return 0, err + } + + return int(id), nil } func (s ParcelStore) Get(number int) (Parcel, error) { - // реализуйте чтение строки по заданному number - // здесь из таблицы должна вернуться только одна строка + query := "SELECT * FROM parcel WHERE number = ?" + output := s.db.QueryRow(query, number) - // заполните объект Parcel данными из таблицы p := Parcel{} + err := output.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) + if err != nil { + fmt.Println(err) + return Parcel{}, err + } return p, nil } func (s ParcelStore) GetByClient(client int) ([]Parcel, error) { - // реализуйте чтение строк из таблицы parcel по заданному client - // здесь из таблицы может вернуться несколько строк - - // заполните срез Parcel данными из таблицы - var res []Parcel - - return res, nil + query := "SELECT * FROM parcel WHERE client = ?" + output, err := s.db.Query(query, client) + if err != nil { + fmt.Println(err) + return nil, err + } + defer output.Close() + + var result []Parcel + for output.Next() { + var p Parcel + err := output.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt) + if err != nil { + fmt.Println(err) + return nil, err + } + result = append(result, p) + } + + err = output.Err() + if err != nil { + fmt.Println(err) + return nil, err + } + + return result, nil } func (s ParcelStore) SetStatus(number int, status string) error { - // реализуйте обновление статуса в таблице parcel + + query := "UPDATE parcel SET status = ? WHERE number = ?" + _, err := s.db.Exec(query, status, number) + if err != nil { + fmt.Println(err) + return err + } return nil } func (s ParcelStore) SetAddress(number int, address string) error { - // реализуйте обновление адреса в таблице parcel - // менять адрес можно только если значение статуса registered + + query := "UPDATE parcel SET address = ? WHERE number = ? AND status = ?" + _, err := s.db.Exec(query, address, number, ParcelStatusRegistered) + if err != nil { + fmt.Println(err) + return err + } return nil } func (s ParcelStore) Delete(number int) error { - // реализуйте удаление строки из таблицы parcel - // удалять строку можно только если значение статуса registered + + query := "DELETE FROM parcel WHERE number = ? AND status = ?" + _, err := s.db.Exec(query, number, ParcelStatusRegistered) + if err != nil { + fmt.Println(err) + return err + } return nil } diff --git a/parcel_test.go b/parcel_test.go index d1b93827..59c6bc6f 100644 --- a/parcel_test.go +++ b/parcel_test.go @@ -6,11 +6,12 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + _ "modernc.org/sqlite" ) var ( - // randSource источник псевдо случайных чисел. // Для повышения уникальности в качестве seed // используется текущее время в unix формате (в виде числа) randSource = rand.NewSource(time.Now().UnixNano()) @@ -31,58 +32,92 @@ 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) + assert.GreaterOrEqual(t, id, 0) + + parcel.Number = id // get - // получите только что добавленную посылку, убедитесь в отсутствии ошибки - // проверьте, что значения всех полей в полученном объекте совпадают со значениями полей в переменной parcel + output, err := store.Get(id) + require.NoError(t, err) + assert.Equal(t, output, parcel) // delete - // удалите добавленную посылку, убедитесь в отсутствии ошибки - // проверьте, что посылку больше нельзя получить из БД + err = store.Delete(id) + require.NoError(t, err) + output, err = store.Get(parcel.Number) + require.ErrorIs(t, err, sql.ErrNoRows) + require.Empty(t, output) } // 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) + parcel := getTestParcel() // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + id, err := store.Add(parcel) + require.NoError(t, err) + assert.GreaterOrEqual(t, id, 0) // set address - // обновите адрес, убедитесь в отсутствии ошибки newAddress := "new test address" + err = store.SetAddress(id, newAddress) + require.NoError(t, err) // check - // получите добавленную посылку и убедитесь, что адрес обновился + output, err := store.Get(id) + require.NoError(t, err) + require.Equal(t, newAddress, output.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) + parcel := getTestParcel() // add - // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора + id, err := store.Add(parcel) + require.NoError(t, err) + assert.GreaterOrEqual(t, id, 0) // set status - // обновите статус, убедитесь в отсутствии ошибки + err = store.SetStatus(id, ParcelStatusDelivered) + require.NoError(t, err) // check - // получите добавленную посылку и убедитесь, что статус обновился + output, err := store.Get(id) + require.NoError(t, err) + require.Equal(t, ParcelStatusDelivered, output.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,7 +133,8 @@ 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) // обновляем идентификатор добавленной у посылки parcels[i].Number = id @@ -108,14 +144,14 @@ 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 - // убедитесь, что значения полей полученных посылок заполнены верно + output, ok := parcelMap[parcel.Number] + require.True(t, ok) + require.Equal(t, output, parcel) } } diff --git a/tracker.db b/tracker.db index b6ba48a1..d293881c 100644 Binary files a/tracker.db and b/tracker.db differ