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
8 changes: 6 additions & 2 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() {
// настройте подключение к БД
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)

// регистрация посылки
Expand Down
105 changes: 97 additions & 8 deletions parcel.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package main

import (
"database/sql"
"fmt"
"log"
)

type ParcelStore struct {
Expand All @@ -15,46 +17,133 @@ 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 Parcel{}, 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 nil, 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 nil, 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
}

func (s ParcelStore) SetAddress(number int, address string) error {
// реализуйте обновление адреса в таблице parcel
// менять адрес можно только если значение статуса registered
// Выполняем обновление только для статуса '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("ошибка обновления адреса: %w", err)
}

// Проверяем количество обновленных строк
rowsAffected, err := result.RowsAffected()
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
// Выполняем удаление только для статуса '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("ошибка удаления посылки: %w", err)
}

// Проверяем количество удаленных строк
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("ошибка при получении количества удаленных строк: %w", err)
}

if rowsAffected == 0 {
return fmt.Errorf("нельзя удалить посылку %d: не найдена или статус не '%s'", number, ParcelStatusRegistered)
}

return nil
}
93 changes: 85 additions & 8 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,58 +32,122 @@ 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)

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, "время создания не совпадает")
assert.Equal(t, parcel.Number, res.Number, "номер не совпадает")

// 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)

assert.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)

assert.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(),
Expand All @@ -98,22 +163,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]
assert.True(t, ok, "посылка %d не найдена", parcel.Number)

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)

// в parcelMap лежат добавленные посылки, ключ - идентификатор посылки, значение - сама посылка
// убедитесь, что все посылки из storedParcels есть в parcelMap
// убедитесь, что значения полей полученных посылок заполнены верно
Expand Down
Binary file modified tracker.db
Binary file not shown.
Empty file added traсker.db
Empty file.