[hw-3] loms service

This commit is contained in:
Никита Шубин
2025-06-20 10:11:59 +00:00
parent c8e056bc99
commit b88dfe6db5
73 changed files with 8837 additions and 52 deletions

View File

@@ -0,0 +1,118 @@
package repository
import (
"context"
"sync"
"route256/cart/internal/domain/entity"
"route256/cart/internal/domain/model"
)
type storage = map[entity.UID]*entity.Cart
type InMemoryRepository struct {
storage storage
mx sync.RWMutex
}
func NewInMemoryRepository(cap int) *InMemoryRepository {
return &InMemoryRepository{
storage: make(storage, cap),
}
}
func (r *InMemoryRepository) AddItem(_ context.Context, userID entity.UID, item *model.Item) error {
r.mx.Lock()
defer r.mx.Unlock()
cart, ok := r.storage[userID]
if !ok {
cart = &entity.Cart{
UserID: userID,
Items: []entity.Sku{},
ItemCount: map[entity.Sku]uint32{},
}
r.storage[userID] = cart
}
if _, ok := cart.ItemCount[item.Product.Sku]; !ok {
cart.Items = append(cart.Items, item.Product.Sku)
}
cart.ItemCount[item.Product.Sku] += item.Count
return nil
}
func (r *InMemoryRepository) GetItemsByUserID(_ context.Context, userID entity.UID) (entity.Cart, error) {
r.mx.Lock()
defer r.mx.Unlock()
cart, ok := r.storage[userID]
if !ok {
return entity.Cart{}, nil
}
resultCart := entity.Cart{
UserID: userID,
Items: make([]entity.Sku, len(cart.Items)),
ItemCount: make(map[entity.Sku]uint32, len(cart.ItemCount)),
}
for i, sku := range cart.Items {
resultCart.Items[i] = cart.Items[i]
resultCart.ItemCount[sku] = cart.ItemCount[sku]
}
return resultCart, nil
}
func (r *InMemoryRepository) DeleteItem(_ context.Context, userID entity.UID, sku entity.Sku) error {
r.mx.Lock()
defer r.mx.Unlock()
cart, ok := r.storage[userID]
if !ok {
return model.ErrCartNotFound
}
if len(cart.Items) == 0 {
return nil
}
delete(cart.ItemCount, sku)
i := 0
found := false
for _, v := range cart.Items {
if v == sku {
found = true
break
}
i++
}
if !found {
return model.ErrItemNotFoundInCart
}
cart.Items[i] = cart.Items[len(cart.Items)-1]
cart.Items = cart.Items[:len(cart.Items)-1]
return nil
}
func (r *InMemoryRepository) DeleteItemsByUserID(_ context.Context, userID entity.UID) error {
r.mx.Lock()
defer r.mx.Unlock()
_, ok := r.storage[userID]
if ok {
delete(r.storage, userID)
}
return nil
}

View File

@@ -0,0 +1,59 @@
package repository
import (
"context"
"testing"
"route256/cart/internal/domain/entity"
"route256/cart/internal/domain/model"
)
var (
ctx = context.Background()
testItem = &model.Item{
Product: &model.Product{
Sku: 1,
Name: "bench",
Price: 100,
},
Count: 1,
}
)
func BenchmarkInMemoryRepository_AddItem(b *testing.B) {
repo := NewInMemoryRepository(1024)
b.ResetTimer()
for i := 1; i < b.N; i++ {
uid := entity.UID(i % 1000000)
_ = repo.AddItem(ctx, uid, testItem)
}
}
func BenchmarkInMemoryRepository_AddItemParallel(b *testing.B) {
repo := NewInMemoryRepository(1024)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
var i int
for pb.Next() {
uid := entity.UID(i % 1000000)
_ = repo.AddItem(ctx, uid, testItem)
i++
}
})
}
func BenchmarkInMemoryRepository_DeleteItem(b *testing.B) {
repo := NewInMemoryRepository(1024)
for i := 1; i < 1_000; i++ {
_ = repo.AddItem(ctx, entity.UID(i), testItem)
}
b.ResetTimer()
for i := 1; i < b.N; i++ {
uid := entity.UID(i % 1000000)
_ = repo.DeleteItem(ctx, uid, entity.Sku(testItem.Product.Sku))
}
}

View File

@@ -0,0 +1,269 @@
package repository
import (
"context"
"testing"
"route256/cart/internal/domain/entity"
"route256/cart/internal/domain/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAddItem(t *testing.T) {
t.Parallel()
var (
repo = NewInMemoryRepository(10)
uid = entity.UID(1337)
sku = entity.Sku(123)
item = &model.Item{
Product: &model.Product{
Name: "",
Price: 0,
Sku: sku,
},
Count: 2,
}
ctx = context.Background()
)
err := repo.AddItem(ctx, uid, item)
require.NoError(t, err, "check add item error")
assert.Len(t, repo.storage, 1, "check map length")
assert.Len(t, repo.storage[uid].Items, 1, "check list length")
assert.Equal(t, uint32(2), repo.storage[uid].ItemCount[sku], "check count value")
assert.Equal(t, uid, repo.storage[uid].UserID, "check uid")
}
func TestAddItemMultiple(t *testing.T) {
t.Parallel()
var (
repo = NewInMemoryRepository(10)
uid = entity.UID(1337)
item1 = &model.Item{
Product: &model.Product{
Name: "",
Price: 0,
Sku: 1,
},
Count: 1,
}
item2 = &model.Item{
Product: &model.Product{
Name: "",
Price: 0,
Sku: 2,
},
Count: 2,
}
ctx = context.Background()
)
err := repo.AddItem(ctx, uid, item1)
require.NoError(t, err, "check add item error")
err = repo.AddItem(ctx, uid, item2)
require.NoError(t, err, "check add item error")
assert.Len(t, repo.storage, 1, "check map length")
assert.Len(t, repo.storage[uid].Items, 2, "check list length")
assert.Equal(t, uint32(1), repo.storage[uid].ItemCount[1], "check count value of item 1")
assert.Equal(t, uint32(2), repo.storage[uid].ItemCount[2], "check count value of item 2")
assert.Equal(t, uid, repo.storage[uid].UserID, "check uid")
}
func TestGetItemsByUserID(t *testing.T) {
t.Parallel()
var (
repo = NewInMemoryRepository(10)
uid = entity.UID(1337)
item1 = &model.Item{
Product: &model.Product{
Name: "",
Price: 0,
Sku: 1,
},
Count: 1,
}
item2 = &model.Item{
Product: &model.Product{
Name: "",
Price: 0,
Sku: 2,
},
Count: 2,
}
ctx = context.Background()
)
err := repo.AddItem(ctx, uid, item1)
require.NoError(t, err, "check add item error")
err = repo.AddItem(ctx, uid, item2)
require.NoError(t, err, "check add item error")
cart, err := repo.GetItemsByUserID(ctx, uid)
require.NoError(t, err, "check get items error")
require.Equal(t, uid, cart.UserID, "check cart uid")
require.Len(t, cart.Items, 2, "check cart len")
require.Equal(t, uint32(1), cart.ItemCount[item1.Product.Sku], "check item 1 count")
require.Equal(t, uint32(2), cart.ItemCount[item2.Product.Sku], "check item 2 count")
}
func TestGetItemsByUserIDNoCart(t *testing.T) {
t.Parallel()
var (
repo = NewInMemoryRepository(10)
uid = entity.UID(1337)
ctx = context.Background()
)
cart, err := repo.GetItemsByUserID(ctx, uid)
require.NoError(t, err, "check get items error")
require.Equal(t, entity.Cart{}, cart, "check cart")
}
func TestDeleteItem(t *testing.T) {
t.Parallel()
var (
repo = NewInMemoryRepository(10)
uid = entity.UID(1337)
sku = entity.Sku(123)
item = &model.Item{
Product: &model.Product{
Name: "",
Price: 0,
Sku: sku,
},
Count: 2,
}
ctx = context.Background()
)
err := repo.AddItem(ctx, uid, item)
require.NoError(t, err, "check add item error")
assert.Len(t, repo.storage, 1, "check map length")
assert.Len(t, repo.storage[uid].Items, 1, "check list length")
err = repo.DeleteItem(ctx, uid, item.Product.Sku)
require.NoError(t, err, "check delete item error")
assert.Len(t, repo.storage[uid].Items, 0, "check list length")
}
func TestDeleteItemUnknownUser(t *testing.T) {
t.Parallel()
var (
repo = NewInMemoryRepository(10)
uid = entity.UID(1337)
sku = entity.Sku(123)
ctx = context.Background()
)
err := repo.DeleteItem(ctx, uid, sku)
require.ErrorIs(t, err, model.ErrCartNotFound, "check cart not found err")
}
func TestDeleteItemUnknownItem(t *testing.T) {
t.Parallel()
var (
repo = NewInMemoryRepository(10)
uid = entity.UID(1337)
sku = entity.Sku(123)
item = &model.Item{
Product: &model.Product{
Name: "",
Price: 0,
Sku: sku,
},
Count: 2,
}
ctx = context.Background()
)
err := repo.AddItem(ctx, uid, item)
require.NoError(t, err, "check add item error")
assert.Len(t, repo.storage, 1, "check map length")
assert.Len(t, repo.storage[uid].Items, 1, "check list length")
err = repo.DeleteItem(ctx, uid, 1)
require.ErrorIs(t, err, model.ErrItemNotFoundInCart, "check delete item error")
assert.Len(t, repo.storage[uid].Items, 1, "check list length")
}
func TestDeleteItemEmptyCart(t *testing.T) {
t.Parallel()
var (
repo = NewInMemoryRepository(10)
uid = entity.UID(1337)
sku = entity.Sku(123)
item = &model.Item{
Product: &model.Product{
Name: "",
Price: 0,
Sku: sku,
},
Count: 2,
}
ctx = context.Background()
)
err := repo.AddItem(ctx, uid, item)
require.NoError(t, err, "check add item error")
assert.Len(t, repo.storage, 1, "check map length")
assert.Len(t, repo.storage[uid].Items, 1, "check list length")
err = repo.DeleteItem(ctx, uid, item.Product.Sku)
require.NoError(t, err, "check delete item error")
assert.Len(t, repo.storage[uid].Items, 0, "check list length")
err = repo.DeleteItem(ctx, uid, item.Product.Sku)
require.NoError(t, err, "check empty cart no err")
}
func TestDeleteItemsByUserID(t *testing.T) {
t.Parallel()
var (
repo = NewInMemoryRepository(10)
uid = entity.UID(1337)
sku = entity.Sku(123)
item = &model.Item{
Product: &model.Product{
Name: "",
Price: 0,
Sku: sku,
},
Count: 2,
}
ctx = context.Background()
)
err := repo.AddItem(ctx, uid, item)
require.NoError(t, err, "check add item error")
assert.Len(t, repo.storage, 1, "check map length")
assert.Len(t, repo.storage[uid].Items, 1, "check list length")
err = repo.DeleteItemsByUserID(ctx, uid)
require.NoError(t, err, "check delete items error")
assert.Len(t, repo.storage, 0, "check storage length")
}