Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/google/uuid v1.3.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-sqlite3 v1.14.28 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/mod v0.3.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peK
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
Expand Down
10 changes: 8 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
service := NewParcelService(store)

// регистрация посылки
Expand Down Expand Up @@ -140,7 +146,7 @@ func main() {
return
}

// вывод посылок клиента
// вывод посылок клиента "Привет"
// предыдущая посылка не должна удалиться, т.к. её статус НЕ «зарегистрирована»
err = service.PrintClientParcels(client)
if err != nil {
Expand Down
95 changes: 93 additions & 2 deletions parcel.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package main

import (
"fmt"

"database/sql"
)

Expand All @@ -15,39 +17,108 @@ func NewParcelStore(db *sql.DB) ParcelStore {
func (s ParcelStore) Add(p Parcel) (int, error) {
// реализуйте добавление строки в таблицу parcel, используйте данные из переменной p

// верните идентификатор последней добавленной записи
return 0, nil
result, err := s.db.Exec("INSERT INTO parcel (client, status, address, created_at) VALUES (:client, :status, :address, :created_at)",
sql.Named("number", p.Number),
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, err
}

id, err := result.LastInsertId()
if err != nil {
return 0, err
}

return int(id), nil
}

func (s ParcelStore) Get(number int) (Parcel, error) {
// реализуйте чтение строки по заданному number
// здесь из таблицы должна вернуться только одна строка

result := s.db.QueryRow("SELECT number, client, status, address, created_at FROM parcel WHERE number = ?", number)

// заполните объект Parcel данными из таблицы
p := Parcel{}

err := result.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt)

if err != nil {
return Parcel{}, err
}

return p, nil
}

func (s ParcelStore) GetByClient(client int) ([]Parcel, error) {
// реализуйте чтение строк из таблицы parcel по заданному client
// здесь из таблицы может вернуться несколько строк

result, err := s.db.Query(
`SELECT number, client, status, address, created_at FROM parcel WHERE client = ?`,
client,
)
if err != nil {
return nil, err
}
defer result.Close()

// заполните срез Parcel данными из таблицы
var res []Parcel

for result.Next() {
var p Parcel
err := result.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt)
if err != nil {
return nil, err
}
res = append(res, p)
}
if err := result.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) SetAddress(number int, address string) error {
// реализуйте обновление адреса в таблице parcel
// менять адрес можно только если значение статуса registered
var status string

err := s.db.QueryRow(
`SELECT status FROM parcel WHERE number = ?`,
number,
).Scan(&status)

if err != nil {
return err
}

if status != "registered" {
return fmt.Errorf("address can only be updated if status is 'registered'")
}

_, err = s.db.Exec("UPDATE parcel SET address = ? WHERE number = ?", address, number)
if err != nil {
return err
}

return nil
}
Expand All @@ -56,5 +127,25 @@ func (s ParcelStore) Delete(number int) error {
// реализуйте удаление строки из таблицы parcel
// удалять строку можно только если значение статуса registered

var status string

err := s.db.QueryRow(
`SELECT status FROM parcel WHERE number = ?`,
number,
).Scan(&status)

if err != nil {
return err
}

if status != "registered" {
return fmt.Errorf("cannot delete: status is not 'registered'")
}

_, err = s.db.Exec("DELETE FROM parcel WHERE number = ?", number)
if err != nil {
return err
}

return nil
}
78 changes: 67 additions & 11 deletions parcel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand All @@ -31,57 +32,107 @@ func getTestParcel() Parcel {
// TestAddGetDelete проверяет добавление, получение и удаление посылки
func TestAddGetDelete(t *testing.T) {
// prepare
db, err := // настройте подключение к БД
db, err := sql.Open("sqlite", "tracker.db")
if err != nil {
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
got, err := store.Get(id)
require.NoError(t, err)
assert.Equal(t, parcel.Client, got.Client)
assert.Equal(t, parcel.Status, got.Status)
assert.Equal(t, parcel.Address, got.Address)
assert.Equal(t, parcel.CreatedAt, got.CreatedAt)

// delete
// удалите добавленную посылку, убедитесь в отсутствии ошибки
// проверьте, что посылку больше нельзя получить из БД

err = store.Delete(id)
require.NoError(t, err)

// get again - should fail
_, err = store.Get(id)
assert.Equal(t, sql.ErrNoRows, 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)
parcel := getTestParcel()
// add
// добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора
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
// получите добавленную посылку и убедитесь, что адрес обновился
updated, err := store.Get(id)
require.NoError(t, err)
assert.Equal(t, newAddress, updated.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)
require.NotZero(t, id)

// set status
// обновите статус, убедитесь в отсутствии ошибки
newStatus := "delivered"
err = store.SetStatus(id, newStatus)
require.NoError(t, err)

// check
// получите добавленную посылку и убедитесь, что статус обновился
updated, err := store.Get(id)
require.NoError(t, err)
assert.Equal(t, newStatus, updated.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(),
Expand All @@ -98,24 +149,29 @@ 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)
require.NoError(t, err)
require.Equal(t, len(parcels), len(storedParcels))
// убедитесь в отсутствии ошибки
// убедитесь, что количество полученных посылок совпадает с количеством добавленных

// check
for _, parcel := range storedParcels {
// в parcelMap лежат добавленные посылки, ключ - идентификатор посылки, значение - сама посылка
// убедитесь, что все посылки из storedParcels есть в parcelMap
// убедитесь, что значения полей полученных посылок заполнены верно
expected, exists := parcelMap[parcel.Number]
assert.True(t, exists, "unexpected parcel with number %d", parcel.Number)
assert.Equal(t, expected.Client, parcel.Client)
assert.Equal(t, expected.Status, parcel.Status)
assert.Equal(t, expected.Address, parcel.Address)
assert.Equal(t, expected.CreatedAt, parcel.CreatedAt)
}
}
Binary file modified tracker.db
Binary file not shown.