Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

first commit #135

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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.24 // 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.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/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
8 changes: 7 additions & 1 deletion 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
}

store := // создайте объект ParcelStore функцией NewParcelStore
store := NewParcelStore(db)
//creare obj
service := NewParcelService(store)

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

import (
"database/sql"
"fmt"
)

type ParcelStore struct {
db *sql.DB
}

// NewParcelStore инициализирует новый ParcelStore
func NewParcelStore(db *sql.DB) ParcelStore {
return ParcelStore{db: db}
}

// Add добавляет новую посылку в базу данных
func (s ParcelStore) Add(p Parcel) (int, error) {
// реализуйте добавление строки в таблицу parcel, используйте данные из переменной p

// верните идентификатор последней добавленной записи
return 0, nil
}

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

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

return p, 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, fmt.Errorf("failed to add parcel: %w", err)
}
id, err := res.LastInsertId()
if err != nil {
return 0, fmt.Errorf("failed to get last insert id: %w", err)
}
return int(id), nil
}

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

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

return res, nil
// Чтение строк из таблицы по client
rows, err := s.db.Query(`SELECT number, client, status, address, created_at FROM parcel WHERE client = ?`, client)
if err != nil {
return nil, fmt.Errorf("failed to get parcels by client: %w", err)
}
defer rows.Close()

// Заполнение среза Parcel данными из таблицы
var parcels []Parcel
for rows.Next() {
var p Parcel
if err := rows.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt); err != nil {
return nil, fmt.Errorf("failed to scan parcel: %w", err)
}
parcels = append(parcels, p)
}

if err = rows.Err(); err != nil {
return nil, fmt.Errorf("error during rows iteration: %w", err)
}

return parcels, nil
}

func (s ParcelStore) SetStatus(number int, status string) error {
// реализуйте обновление статуса в таблице parcel

return nil
// Delete удаляет посылку, если она в статусе "registered"
func (s ParcelStore) Delete(number int) error {
parcel, err := s.Get(number)
if err != nil {
return err
}
if parcel.Status != ParcelStatusRegistered {
return fmt.Errorf("can only delete registered parcel")
}
_, err = s.db.Exec(`DELETE FROM parcel WHERE number = ?`, number)
return err
}

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

parcel, err := s.Get(number)
if err != nil {
return fmt.Errorf("failed to get parcel for update: %w", err)
}
if parcel.Status != ParcelStatusRegistered {
return fmt.Errorf("can only change address of registered parcel")
}
_, err = s.db.Exec(`UPDATE parcel SET address = ? WHERE number = ?`, address, number)
if err != nil {
return fmt.Errorf("failed to update address: %w", err)
}
return nil
}

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

// SetStatus обновляет статус посылки
func (s ParcelStore) SetStatus(number int, status string) error {
_, err := s.db.Exec(`UPDATE parcel SET status = ? WHERE number = ?`, status, number)
if err != nil {
return fmt.Errorf("failed to update status: %w", err)
}
return nil
}

// Get возвращает посылку по её номеру
func (s ParcelStore) Get(number int) (Parcel, error) {
row := s.db.QueryRow(`SELECT number, client, status, address, created_at FROM parcel WHERE number = ?`, number)
var p Parcel
if err := row.Scan(&p.Number, &p.Client, &p.Status, &p.Address, &p.CreatedAt); err != nil {
if err == sql.ErrNoRows {
return Parcel{}, fmt.Errorf("parcel not found: %w", err)
}
return Parcel{}, fmt.Errorf("failed to scan parcel: %w", err)
}
return p, nil
}
148 changes: 71 additions & 77 deletions parcel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,120 +2,114 @@ package main

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

_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/require"
)

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

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

// TestAddGetDelete проверяет добавление, получение и удаление посылки
func TestAddGetDelete(t *testing.T) {
// prepare
db, err := // настройте подключение к БД
// Подготовка
db, err := sql.Open("sqlite3", "tracker.db")
require.NoError(t, err)

store := NewParcelStore(db)
parcel := getTestParcel()

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

// get
// получите только что добавленную посылку, убедитесь в отсутствии ошибки
// проверьте, что значения всех полей в полученном объекте совпадают со значениями полей в переменной parcel

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

// Получение
storedParcel, err := store.Get(id)
require.NoError(t, err)
require.Equal(t, parcel.Client, storedParcel.Client)
require.Equal(t, parcel.Status, storedParcel.Status)
require.Equal(t, parcel.Address, storedParcel.Address)

// Удаление
err = store.Delete(id)
require.NoError(t, err)

// Проверка, что посылка удалена
_, err = store.Get(id)
require.Error(t, err)
}

// TestSetAddress проверяет обновление адреса
func TestSetAddress(t *testing.T) {
// prepare
db, err := // настройте подключение к БД
// Подготовка
db, err := sql.Open("sqlite3", "tracker.db")
require.NoError(t, err)

// add
// добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора
store := NewParcelStore(db)
parcel := getTestParcel()

// Добавление
id, err := store.Add(parcel)
require.NoError(t, err)
require.Greater(t, id, 0)

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

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

// Очистка
err = store.Delete(id)
require.NoError(t, err)
}

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

// add
// добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора
// Подготовка
db, err := sql.Open("sqlite3", "tracker.db")
require.NoError(t, err)

// set status
// обновите статус, убедитесь в отсутствии ошибки

// check
// получите добавленную посылку и убедитесь, что статус обновился
}

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

parcels := []Parcel{
getTestParcel(),
getTestParcel(),
getTestParcel(),
}
parcelMap := map[int]Parcel{}
store := NewParcelStore(db)
parcel := getTestParcel()

// задаём всем посылкам один и тот же идентификатор клиента
client := randRange.Intn(10_000_000)
parcels[0].Client = client
parcels[1].Client = client
parcels[2].Client = client
// Добавляем посылку
id, err := store.Add(parcel)
require.NoError(t, err)
require.Greater(t, id, 0)

// add
for i := 0; i < len(parcels); i++ {
id, err := // добавьте новую посылку в БД, убедитесь в отсутствии ошибки и наличии идентификатора
// Изменяем статус посылки
err = store.SetStatus(id, ParcelStatusSent)
require.NoError(t, err)

// обновляем идентификатор добавленной у посылки
parcels[i].Number = id
// Проверяем новый статус
storedParcel, err := store.Get(id)
require.NoError(t, err)
require.Equal(t, ParcelStatusSent, storedParcel.Status)

// сохраняем добавленную посылку в структуру map, чтобы её можно было легко достать по идентификатору посылки
parcelMap[id] = parcels[i]
}
// Пытаемся удалить посылку
err = store.Delete(id)
require.Error(t, err) // ожидаем ошибку, т.к. статус не "registered"

// get by client
storedParcels, err := // получите список посылок по идентификатору клиента, сохранённого в переменной client
// убедитесь в отсутствии ошибки
// убедитесь, что количество полученных посылок совпадает с количеством добавленных
// Возвращаем статус в "registered", чтобы можно было удалить
err = store.SetStatus(id, ParcelStatusRegistered)
require.NoError(t, err)

// check
for _, parcel := range storedParcels {
// в parcelMap лежат добавленные посылки, ключ - идентификатор посылки, значение - сама посылка
// убедитесь, что все посылки из storedParcels есть в parcelMap
// убедитесь, что значения полей полученных посылок заполнены верно
}
// Теперь удаляем посылку
err = store.Delete(id)
require.NoError(t, err)
}
Binary file modified tracker.db
Binary file not shown.