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
10 changes: 7 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,13 @@ 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)

// регистрация посылки
Expand Down
69 changes: 58 additions & 11 deletions parcel.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,52 +9,99 @@ type ParcelStore struct {
}

func NewParcelStore(db *sql.DB) ParcelStore {

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()
if err != nil {
return 0, err
}

return int(id), nil

}

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

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

err := s.db.QueryRow("SELECT client, status, address, created_at FROM parcel WHERE number = :number",
sql.Named("number", number)).
Scan(&p.Client, &p.Status, &p.Address, &p.CreatedAt)
if err != nil {
return Parcel{}, err
}
p.Number = number
return p, nil
}

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

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

rows, 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, err
}
defer rows.Close()

for rows.Next() {
// заполните объект Parcel данными из таблицы
p := Parcel{}
err := rows.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt)
if err != nil {
return nil, err
}
res = append(res, p)
}
if err := rows.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
_, 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 err
}

return nil
}

func (s ParcelStore) Delete(number int) error {
// реализуйте удаление строки из таблицы parcel
// удалять строку можно только если значение статуса registered
_, 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 err
}

return nil
}
143 changes: 73 additions & 70 deletions parcel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,120 +2,123 @@ package main

import (
"database/sql"
"math/rand"
"testing"
"time"

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

var (
// randSource источник псевдо случайных чисел.
// Для повышения уникальности в качестве seed
// используется текущее время в unix формате (в виде числа)
randSource = rand.NewSource(time.Now().UnixNano())
// randRange использует randSource для генерации случайных чисел
randRange = rand.New(randSource)
)

// getTestParcel возвращает тестовую посылку
// getTestParcel возвращает тестовую посылку с фиксированными значениями
func getTestParcel() Parcel {
return Parcel{
Client: 1000,
Client: 42,
Status: ParcelStatusRegistered,
Address: "test",
Address: "Test Address",
CreatedAt: time.Now().UTC().Format(time.RFC3339),
}
}

// TestAddGetDelete проверяет добавление, получение и удаление посылки
// getTestStore создаёт ParcelStore с временной БД в памяти
func getTestStore(t *testing.T) ParcelStore {
db, err := sql.Open("sqlite", "file::memory:?cache=shared")
require.NoError(t, err)

_, err = db.Exec(`DROP TABLE IF EXISTS parcel`)
require.NoError(t, err)

_, err = db.Exec(`CREATE TABLE parcel (
number INTEGER PRIMARY KEY AUTOINCREMENT,
client INTEGER NOT NULL,
status TEXT NOT NULL,
address TEXT NOT NULL,
created_at TEXT NOT NULL
)`)
require.NoError(t, err)

return NewParcelStore(db)
}

func TestAddGetDelete(t *testing.T) {
// prepare
db, err := // настройте подключение к БД
store := NewParcelStore(db)
store := getTestStore(t)
parcel := getTestParcel()

// add
// добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора
id, err := store.Add(parcel)
require.NoError(t, err)
require.NotZero(t, id)

stored, err := store.Get(id)
require.NoError(t, err)
assert.Equal(t, parcel.Client, stored.Client)
assert.Equal(t, parcel.Status, stored.Status)
assert.Equal(t, parcel.Address, stored.Address)

// get
// получите только что добавленную посылку, убедитесь в отсутствии ошибки
// проверьте, что значения всех полей в полученном объекте совпадают со значениями полей в переменной parcel
err = store.Delete(id)
require.NoError(t, err)

// delete
// удалите добавленную посылку, убедитесь в отсутствии ошибки
// проверьте, что посылку больше нельзя получить из БД
_, err = store.Get(id)
require.Error(t, err)
}

// TestSetAddress проверяет обновление адреса
func TestSetAddress(t *testing.T) {
// prepare
db, err := // настройте подключение к БД
store := getTestStore(t)
parcel := getTestParcel()

// add
// добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора
id, err := store.Add(parcel)
require.NoError(t, err)

// set address
// обновите адрес, убедитесь в отсутствии ошибки
newAddress := "new test address"
newAddress := "Updated Address"
err = store.SetAddress(id, newAddress)
require.NoError(t, err)

// check
// получите добавленную посылку и убедитесь, что адрес обновился
stored, err := store.Get(id)
require.NoError(t, err)
assert.Equal(t, newAddress, stored.Address)
}

// TestSetStatus проверяет обновление статуса
func TestSetStatus(t *testing.T) {
// prepare
db, err := // настройте подключение к БД
store := getTestStore(t)
parcel := getTestParcel()

// add
// добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора
id, err := store.Add(parcel)
require.NoError(t, err)

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

// check
// получите добавленную посылку и убедитесь, что статус обновился
stored, err := store.Get(id)
require.NoError(t, err)
assert.Equal(t, newStatus, stored.Status)
}

// TestGetByClient проверяет получение посылок по идентификатору клиента
func TestGetByClient(t *testing.T) {
// prepare
db, err := // настройте подключение к БД
store := getTestStore(t)
clientID := 42

parcels := []Parcel{
getTestParcel(),
getTestParcel(),
getTestParcel(),
}
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 := // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора

// обновляем идентификатор добавленной у посылки
parcelMap := map[int]Parcel{}
for i := range parcels {
parcels[i].Client = clientID
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
// убедитесь в отсутствии ошибки
// убедитесь, что количество полученных посылок совпадает с количеством добавленных
stored, err := store.GetByClient(clientID)
require.NoError(t, err)
require.Len(t, stored, len(parcels))

// check
for _, parcel := range storedParcels {
// в parcelMap лежат добавленные посылки, ключ - идентификатор посылки, значение - сама посылка
// убедитесь, что все посылки из storedParcels есть в parcelMap
// убедитесь, что значения полей полученных посылок заполнены верно
for _, s := range stored {
original := parcelMap[s.Number]
assert.Equal(t, original.Address, s.Address)
assert.Equal(t, original.Status, s.Status)
assert.Equal(t, original.Client, s.Client)
}
}