Merge pull request #12 from davidlick/produce-service
Implement produce service
This commit was merged in pull request #12.
This commit is contained in:
@@ -3,6 +3,7 @@ module github.com/davidlick/supermarket-api
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/Rhymond/go-money v1.0.2 // indirect
|
||||
github.com/go-chi/chi v1.5.4 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
github.com/Rhymond/go-money v1.0.2 h1:KklB66H3VlpNMkm8T5BH/MROK88o7q9CCn1hl853TzI=
|
||||
github.com/Rhymond/go-money v1.0.2/go.mod h1:iHvCuIvitxu2JIlAlhF0g9jHqjRSr+rpdOs7Omqlupg=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package interfaces
|
||||
|
||||
import "github.com/davidlick/supermarket-api/pkg/ramdb"
|
||||
|
||||
type RamDB interface {
|
||||
Get(column, key string) (r *ramdb.Record, err error)
|
||||
Select(column string) (rr []*ramdb.Record, err error)
|
||||
Insert(r *ramdb.Record) error
|
||||
Delete(r *ramdb.Record) error
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: ./internal/interfaces/ramdb.go
|
||||
|
||||
// Package mocks is a generated GoMock package.
|
||||
package mocks
|
||||
|
||||
import (
|
||||
ramdb "github.com/davidlick/supermarket-api/pkg/ramdb"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockRamDB is a mock of RamDB interface
|
||||
type MockRamDB struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockRamDBMockRecorder
|
||||
}
|
||||
|
||||
// MockRamDBMockRecorder is the mock recorder for MockRamDB
|
||||
type MockRamDBMockRecorder struct {
|
||||
mock *MockRamDB
|
||||
}
|
||||
|
||||
// NewMockRamDB creates a new mock instance
|
||||
func NewMockRamDB(ctrl *gomock.Controller) *MockRamDB {
|
||||
mock := &MockRamDB{ctrl: ctrl}
|
||||
mock.recorder = &MockRamDBMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockRamDB) EXPECT() *MockRamDBMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Get mocks base method
|
||||
func (m *MockRamDB) Get(column, key string) (*ramdb.Record, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Get", column, key)
|
||||
ret0, _ := ret[0].(*ramdb.Record)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Get indicates an expected call of Get
|
||||
func (mr *MockRamDBMockRecorder) Get(column, key interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRamDB)(nil).Get), column, key)
|
||||
}
|
||||
|
||||
// Select mocks base method
|
||||
func (m *MockRamDB) Select(column string) ([]*ramdb.Record, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Select", column)
|
||||
ret0, _ := ret[0].([]*ramdb.Record)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Select indicates an expected call of Select
|
||||
func (mr *MockRamDBMockRecorder) Select(column interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockRamDB)(nil).Select), column)
|
||||
}
|
||||
|
||||
// Insert mocks base method
|
||||
func (m *MockRamDB) Insert(r *ramdb.Record) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Insert", r)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Insert indicates an expected call of Insert
|
||||
func (mr *MockRamDBMockRecorder) Insert(r interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockRamDB)(nil).Insert), r)
|
||||
}
|
||||
|
||||
// Delete mocks base method
|
||||
func (m *MockRamDB) Delete(r *ramdb.Record) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Delete", r)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Delete indicates an expected call of Delete
|
||||
func (mr *MockRamDBMockRecorder) Delete(r interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRamDB)(nil).Delete), r)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# Produce Service
|
||||
|
||||
This produce service allows persisting produce in a database. It supports adding multiple produce items, removing a produce item, getting a produce item by produce code, and selecting all produce items from the database.
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
// Create database and table.
|
||||
db := ramdb.NewDatabase()
|
||||
_ = db.CreateTable("produce", KeyProduceCode)
|
||||
|
||||
produceSvc := produce.NewService(db)
|
||||
|
||||
produceItem := produce.Item{
|
||||
Code: "A12T-4GH7-QPL9-3N4M",
|
||||
Name: "Lettuce",
|
||||
Price: money.New(346, "USD"),
|
||||
}
|
||||
|
||||
_ = produceSvc.Add([]Item{produceItem})
|
||||
_, _ = produceSvc.Get(produceItem.Code)
|
||||
_, _ = produceSvc.All()
|
||||
_ = produceSvc.Remove(produceItem)
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
package produce
|
||||
|
||||
const (
|
||||
KeyProduceCode = "produce_code"
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package produce
|
||||
|
||||
import "github.com/Rhymond/go-money"
|
||||
|
||||
// Item models a produce item.
|
||||
type Item struct {
|
||||
Code string
|
||||
Name string
|
||||
Price *money.Money
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package produce
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/davidlick/supermarket-api/internal/interfaces"
|
||||
"github.com/davidlick/supermarket-api/pkg/ramdb"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
db interfaces.RamDB
|
||||
}
|
||||
|
||||
// NewService creates a new produce service for storing produce items.
|
||||
func NewService(db interfaces.RamDB) *service {
|
||||
return &service{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds the Items to the database.
|
||||
func (s *service) Add(items []Item) error {
|
||||
for _, item := range items {
|
||||
rec, err := ramdb.NewRecord(strings.ToLower(item.Code), KeyProduceCode, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.db.Insert(rec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the item from the database.
|
||||
func (s *service) Remove(item Item) error {
|
||||
rec, err := ramdb.NewRecord(strings.ToLower(item.Code), KeyProduceCode, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.db.Delete(rec)
|
||||
}
|
||||
|
||||
// Get fetches the produceCode from the database.
|
||||
func (s *service) Get(produceCode string) (item Item, err error) {
|
||||
rec, err := s.db.Get(KeyProduceCode, strings.ToLower(produceCode))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = rec.Deserialize(&item)
|
||||
return
|
||||
}
|
||||
|
||||
// All returns all produce items stored in the database.
|
||||
func (s *service) All() (items []Item, err error) {
|
||||
recs, err := s.db.Select(KeyProduceCode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, rec := range recs {
|
||||
var item Item
|
||||
err = rec.Deserialize(&item)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
package produce
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/Rhymond/go-money"
|
||||
"github.com/davidlick/supermarket-api/internal/mocks"
|
||||
"github.com/davidlick/supermarket-api/pkg/ramdb"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestService_Add(t *testing.T) {
|
||||
tests := []struct {
|
||||
test string
|
||||
expectFunc func(t *testing.T, mockRamDB *mocks.MockRamDB) []Item
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
test: "it should add all Items",
|
||||
expectFunc: func(t *testing.T, mockRamDB *mocks.MockRamDB) []Item {
|
||||
items := []Item{
|
||||
{Code: "code-1", Name: "name-1", Price: money.New(101, "USD")},
|
||||
{Code: "code-2", Name: "name-2", Price: money.New(202, "USD")},
|
||||
{Code: "code-3", Name: "name-3", Price: money.New(303, "USD")},
|
||||
{Code: "code-4", Name: "name-4", Price: money.New(404, "USD")},
|
||||
{Code: "code-5", Name: "name-5", Price: money.New(505, "USD")},
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
rec, err := ramdb.NewRecord(item.Code, KeyProduceCode, item)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
mockRamDB.EXPECT().Insert(rec).Return(nil)
|
||||
}
|
||||
|
||||
return items
|
||||
},
|
||||
},
|
||||
{
|
||||
test: "it should return an error from ramdb",
|
||||
expectFunc: func(t *testing.T, mockRamDB *mocks.MockRamDB) []Item {
|
||||
mockRamDB.EXPECT().Insert(gomock.Any()).Return(errors.New("test error"))
|
||||
return []Item{
|
||||
{Code: "code-1", Name: "name-1", Price: money.New(101, "USD")},
|
||||
}
|
||||
},
|
||||
expectedError: errors.New("test error"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.test, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
mockRamDB := mocks.NewMockRamDB(ctrl)
|
||||
|
||||
items := tc.expectFunc(t, mockRamDB)
|
||||
|
||||
svc := NewService(mockRamDB)
|
||||
err := svc.Add(items)
|
||||
|
||||
assert.Equal(t, tc.expectedError, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_Add_Lowercased(t *testing.T) {
|
||||
t.Run("it should store produce codes lowercased", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
item := Item{
|
||||
Code: "TEST_CODE",
|
||||
Name: "Name",
|
||||
Price: money.New(101, "USD"),
|
||||
}
|
||||
|
||||
mockRamDB := mocks.NewMockRamDB(ctrl)
|
||||
rec, err := ramdb.NewRecord("test_code", KeyProduceCode, item)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
mockRamDB.EXPECT().Insert(rec).Return(nil)
|
||||
|
||||
svc := NewService(mockRamDB)
|
||||
err = svc.Add([]Item{item})
|
||||
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_Remove(t *testing.T) {
|
||||
tests := []struct {
|
||||
test string
|
||||
expectFunc func(t *testing.T, mockRamDB *mocks.MockRamDB) Item
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
test: "it should successfully remove an item",
|
||||
expectFunc: func(t *testing.T, mockRamDB *mocks.MockRamDB) Item {
|
||||
item := Item{Code: "code-1", Name: "name-1", Price: money.New(101, "USD")}
|
||||
rec, err := ramdb.NewRecord(item.Code, KeyProduceCode, item)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
mockRamDB.EXPECT().Delete(rec).Return(nil)
|
||||
return item
|
||||
},
|
||||
},
|
||||
{
|
||||
test: "it should return an error from ramdb",
|
||||
expectFunc: func(t *testing.T, mockRamDB *mocks.MockRamDB) Item {
|
||||
mockRamDB.EXPECT().Delete(gomock.Any()).Return(errors.New("test error"))
|
||||
return Item{}
|
||||
},
|
||||
expectedError: errors.New("test error"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.test, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
mockRamDB := mocks.NewMockRamDB(ctrl)
|
||||
|
||||
item := tc.expectFunc(t, mockRamDB)
|
||||
|
||||
svc := NewService(mockRamDB)
|
||||
err := svc.Remove(item)
|
||||
|
||||
assert.Equal(t, tc.expectedError, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_Get(t *testing.T) {
|
||||
tests := []struct {
|
||||
test string
|
||||
expectFunc func(t *testing.T, mockRamDB *mocks.MockRamDB) Item
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
test: "it should return the item successfully",
|
||||
expectFunc: func(t *testing.T, mockRamDB *mocks.MockRamDB) Item {
|
||||
item := Item{Code: "produce_code", Name: "name-1", Price: money.New(101, "USD")}
|
||||
rec, err := ramdb.NewRecord(item.Code, KeyProduceCode, item)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
mockRamDB.EXPECT().Get(KeyProduceCode, "test_code").Return(rec, nil)
|
||||
return item
|
||||
},
|
||||
},
|
||||
{
|
||||
test: "it should return an error from ramdb",
|
||||
expectFunc: func(t *testing.T, mockRamDB *mocks.MockRamDB) Item {
|
||||
mockRamDB.EXPECT().Get(KeyProduceCode, "test_code").Return(nil, errors.New("test error"))
|
||||
return Item{}
|
||||
},
|
||||
expectedError: errors.New("test error"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.test, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
mockRamDB := mocks.NewMockRamDB(ctrl)
|
||||
|
||||
expectedItem := tc.expectFunc(t, mockRamDB)
|
||||
|
||||
svc := NewService(mockRamDB)
|
||||
item, err := svc.Get("test_code")
|
||||
|
||||
assert.Equal(t, expectedItem, item)
|
||||
assert.Equal(t, tc.expectedError, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_Get_CaseInsensitive(t *testing.T) {
|
||||
t.Run("it should lowercase produce codes before getting", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
searchItem := Item{
|
||||
Code: "TEST_CODE",
|
||||
}
|
||||
|
||||
expectedItem := Item{
|
||||
Code: "test_code",
|
||||
}
|
||||
|
||||
rec, err := ramdb.NewRecord("test_code", KeyProduceCode, expectedItem)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
mockRamDB := mocks.NewMockRamDB(ctrl)
|
||||
mockRamDB.EXPECT().Get(KeyProduceCode, "test_code").Return(rec, nil)
|
||||
|
||||
svc := NewService(mockRamDB)
|
||||
|
||||
item, err := svc.Get(searchItem.Code)
|
||||
|
||||
assert.Equal(t, item, expectedItem)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_All(t *testing.T) {
|
||||
tests := []struct {
|
||||
test string
|
||||
expectFunc func(t *testing.T, mockRamDB *mocks.MockRamDB) []Item
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
test: "it should return all items",
|
||||
expectFunc: func(t *testing.T, mockRamDB *mocks.MockRamDB) []Item {
|
||||
items := []Item{
|
||||
{Code: "code-1", Name: "name-1", Price: money.New(101, "USD")},
|
||||
{Code: "code-2", Name: "name-2", Price: money.New(202, "USD")},
|
||||
{Code: "code-3", Name: "name-3", Price: money.New(303, "USD")},
|
||||
{Code: "code-4", Name: "name-4", Price: money.New(404, "USD")},
|
||||
{Code: "code-5", Name: "name-5", Price: money.New(505, "USD")},
|
||||
}
|
||||
|
||||
var recs []*ramdb.Record
|
||||
for _, item := range items {
|
||||
rec, err := ramdb.NewRecord(item.Code, KeyProduceCode, item)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
recs = append(recs, rec)
|
||||
}
|
||||
|
||||
mockRamDB.EXPECT().Select(KeyProduceCode).Return(recs, nil)
|
||||
|
||||
return items
|
||||
},
|
||||
},
|
||||
{
|
||||
test: "it should return a ramdb error",
|
||||
expectFunc: func(t *testing.T, mockRamDB *mocks.MockRamDB) []Item {
|
||||
mockRamDB.EXPECT().Select(gomock.Any()).Return(nil, errors.New("test error"))
|
||||
return nil
|
||||
},
|
||||
expectedError: errors.New("test error"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.test, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
mockRamDB := mocks.NewMockRamDB(ctrl)
|
||||
|
||||
expectedItems := tc.expectFunc(t, mockRamDB)
|
||||
|
||||
svc := NewService(mockRamDB)
|
||||
items, err := svc.All()
|
||||
|
||||
assert.Equal(t, expectedItems, items)
|
||||
assert.Equal(t, tc.expectedError, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -27,7 +27,7 @@ indog := HotDog{
|
||||
Brat: true,
|
||||
}
|
||||
|
||||
// Create a new record and insert.
|
||||
// Create a new Record and insert.
|
||||
rec, _ := ramdb.NewRecord("1", "frank_id", indog)
|
||||
_ = db.From("hotdogs").Insert(rec)
|
||||
|
||||
|
||||
+11
-11
@@ -3,7 +3,7 @@ package ramdb
|
||||
import "github.com/google/btree"
|
||||
|
||||
// Get validates that the table and index for the given column exists and searches for the given key in the tree.
|
||||
func (t *table) Get(column, key string) (r *record, err error) {
|
||||
func (t *table) Get(column, key string) (r *Record, err error) {
|
||||
if !t.exists {
|
||||
return nil, ErrNoTable
|
||||
}
|
||||
@@ -16,18 +16,18 @@ func (t *table) Get(column, key string) (r *record, err error) {
|
||||
}
|
||||
|
||||
// keyLookup tries to find the given key in the tree. It returns ErrNoRecord if not found.
|
||||
func (t *table) keyLookup(key string, index *index) (r *record, err error) {
|
||||
item := &record{id: keyHash(key)}
|
||||
func (t *table) keyLookup(key string, index *index) (r *Record, err error) {
|
||||
item := &Record{id: keyHash(key)}
|
||||
result := index.tree.Get(item)
|
||||
if result == nil {
|
||||
return nil, ErrNoRecord
|
||||
}
|
||||
|
||||
return result.(*record), nil
|
||||
return result.(*Record), nil
|
||||
}
|
||||
|
||||
// Select returns all of the records in the database sorted in ascending order by id.
|
||||
func (t *table) Select(column string) (rr []*record, err error) {
|
||||
// Select returns all of the Records in the database sorted in ascending order by id.
|
||||
func (t *table) Select(column string) (rr []*Record, err error) {
|
||||
if !t.exists {
|
||||
return nil, ErrNoTable
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func (t *table) Select(column string) (rr []*record, err error) {
|
||||
}
|
||||
|
||||
t.indexes[column].tree.Ascend(func(item btree.Item) bool {
|
||||
r := item.(*record)
|
||||
r := item.(*Record)
|
||||
rr = append(rr, r)
|
||||
return true
|
||||
})
|
||||
@@ -45,8 +45,8 @@ func (t *table) Select(column string) (rr []*record, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Insert adds the record to the database. It returns ErrRecordExists if the record already exists. Insert is thread safe.
|
||||
func (t *table) Insert(r *record) error {
|
||||
// Insert adds the Record to the database. It returns ErrRecordExists if the Record already exists. Insert is thread safe.
|
||||
func (t *table) Insert(r *Record) error {
|
||||
if !t.exists {
|
||||
return ErrNoTable
|
||||
}
|
||||
@@ -69,8 +69,8 @@ func (t *table) Insert(r *record) error {
|
||||
|
||||
}
|
||||
|
||||
// Delete removes the item from the database. It returns ErrNoRecord if the record does not exist and ErrNotDeleted if the removal fails. Delete is thread safe.
|
||||
func (t *table) Delete(r *record) error {
|
||||
// Delete removes the item from the database. It returns ErrNoRecord if the Record does not exist and ErrNotDeleted if the removal fails. Delete is thread safe.
|
||||
func (t *table) Delete(r *Record) error {
|
||||
if !t.exists {
|
||||
return ErrNoTable
|
||||
}
|
||||
|
||||
+15
-15
@@ -14,7 +14,7 @@ func TestTable_Get(t *testing.T) {
|
||||
tests := []struct {
|
||||
test string
|
||||
tableConfig func() *table
|
||||
expectedRecord *record
|
||||
expectedRecord *Record
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
@@ -34,7 +34,7 @@ func TestTable_Get(t *testing.T) {
|
||||
expectedError: ErrNoIndex,
|
||||
},
|
||||
{
|
||||
test: "it should return ErrNoRecord if no record was found for key",
|
||||
test: "it should return ErrNoRecord if no Record was found for key",
|
||||
tableConfig: func() *table {
|
||||
return &table{
|
||||
exists: true,
|
||||
@@ -48,7 +48,7 @@ func TestTable_Get(t *testing.T) {
|
||||
expectedError: ErrNoRecord,
|
||||
},
|
||||
{
|
||||
test: "it should return a record when one is found",
|
||||
test: "it should return a Record when one is found",
|
||||
tableConfig: func() *table {
|
||||
tbl := &table{
|
||||
exists: true,
|
||||
@@ -65,7 +65,7 @@ func TestTable_Get(t *testing.T) {
|
||||
tbl.indexes["test_column"].tree.ReplaceOrInsert(rec)
|
||||
return tbl
|
||||
},
|
||||
expectedRecord: &record{
|
||||
expectedRecord: &Record{
|
||||
serialized: []uint8{0x7b, 0x7d},
|
||||
key: "test_key",
|
||||
keyColumn: "test_column",
|
||||
@@ -78,9 +78,9 @@ func TestTable_Get(t *testing.T) {
|
||||
t.Run(tc.test, func(t *testing.T) {
|
||||
tbl := tc.tableConfig()
|
||||
|
||||
record, err := tbl.Get("test_column", "test_key")
|
||||
Record, err := tbl.Get("test_column", "test_key")
|
||||
|
||||
assert.Equal(t, tc.expectedRecord, record)
|
||||
assert.Equal(t, tc.expectedRecord, Record)
|
||||
assert.Equal(t, tc.expectedError, err)
|
||||
})
|
||||
}
|
||||
@@ -89,13 +89,13 @@ func TestTable_Get(t *testing.T) {
|
||||
func TestTable_Select(t *testing.T) {
|
||||
tests := []struct {
|
||||
test string
|
||||
tableConfig func() (*table, []*record)
|
||||
expectedRecords []*record
|
||||
tableConfig func() (*table, []*Record)
|
||||
expectedRecords []*Record
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
test: "it should return ErrNoTable if an invalid table is supplied",
|
||||
tableConfig: func() (*table, []*record) {
|
||||
tableConfig: func() (*table, []*Record) {
|
||||
return &table{
|
||||
mutex: &sync.Mutex{},
|
||||
}, nil
|
||||
@@ -104,7 +104,7 @@ func TestTable_Select(t *testing.T) {
|
||||
},
|
||||
{
|
||||
test: "it should return ErrNoIndex if no index exists for column",
|
||||
tableConfig: func() (*table, []*record) {
|
||||
tableConfig: func() (*table, []*Record) {
|
||||
return &table{
|
||||
exists: true,
|
||||
mutex: &sync.Mutex{},
|
||||
@@ -114,8 +114,8 @@ func TestTable_Select(t *testing.T) {
|
||||
expectedError: ErrNoIndex,
|
||||
},
|
||||
{
|
||||
test: "it should return all records in the database",
|
||||
tableConfig: func() (*table, []*record) {
|
||||
test: "it should return all Records in the database",
|
||||
tableConfig: func() (*table, []*Record) {
|
||||
tbl := &table{
|
||||
exists: true,
|
||||
mutex: &sync.Mutex{},
|
||||
@@ -126,7 +126,7 @@ func TestTable_Select(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
expectedRecords := make([]*record, 0)
|
||||
expectedRecords := make([]*Record, 0)
|
||||
for i := 0; i < 10; i++ {
|
||||
key := fmt.Sprintf("key-%d", i)
|
||||
rec, err := NewRecord(key, "test_column", struct{}{})
|
||||
@@ -185,7 +185,7 @@ func TestTable_Insert(t *testing.T) {
|
||||
expectedError: ErrNoIndex,
|
||||
},
|
||||
{
|
||||
test: "it should return ErrRecordExists if a record with key exists",
|
||||
test: "it should return ErrRecordExists if a Record with key exists",
|
||||
tableConfig: func() *table {
|
||||
tbl := &table{
|
||||
exists: true,
|
||||
@@ -265,7 +265,7 @@ func TestTable_Delete(t *testing.T) {
|
||||
expectedError: ErrNoIndex,
|
||||
},
|
||||
{
|
||||
test: "it should return ErrNoRecord if the record does not exist",
|
||||
test: "it should return ErrNoRecord if the Record does not exist",
|
||||
tableConfig: func() *table {
|
||||
return &table{
|
||||
exists: true,
|
||||
|
||||
+8
-8
@@ -8,21 +8,21 @@ import (
|
||||
"github.com/google/btree"
|
||||
)
|
||||
|
||||
type record struct {
|
||||
type Record struct {
|
||||
serialized []byte
|
||||
keyColumn string
|
||||
key string
|
||||
id uint64
|
||||
}
|
||||
|
||||
// NewRecord returns a pointer to a record populated with data, key, and a hash of the key used for ordering in the tree.
|
||||
func NewRecord(key, keyColumn string, data interface{}) (*record, error) {
|
||||
// NewRecord returns a pointer to a Record populated with data, key, and a hash of the key used for ordering in the tree.
|
||||
func NewRecord(key, keyColumn string, data interface{}) (*Record, error) {
|
||||
serialized, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var r record
|
||||
var r Record
|
||||
r.serialized = serialized
|
||||
r.keyColumn = keyColumn
|
||||
r.key = key
|
||||
@@ -39,13 +39,13 @@ func keyHash(s string) uint64 {
|
||||
}
|
||||
|
||||
// Deserialize unmarshals the serialized data into `into`.
|
||||
func (r *record) Deserialize(into interface{}) error {
|
||||
func (r *Record) Deserialize(into interface{}) error {
|
||||
err := json.Unmarshal(r.serialized, &into)
|
||||
return err
|
||||
}
|
||||
|
||||
// Less is used to order items and for looking up records in the tree.
|
||||
func (r *record) Less(than btree.Item) bool {
|
||||
re := than.(*record)
|
||||
// Less is used to order items and for looking up Records in the tree.
|
||||
func (r *Record) Less(than btree.Item) bool {
|
||||
re := than.(*Record)
|
||||
return r.id < re.id
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ func TestRecord_NewRecord(t *testing.T) {
|
||||
key string
|
||||
keyColumn string
|
||||
data interface{}
|
||||
expectedRecord *record
|
||||
expectedRecord *Record
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
@@ -29,7 +29,7 @@ func TestRecord_NewRecord(t *testing.T) {
|
||||
key: "test-record",
|
||||
keyColumn: "test-column",
|
||||
data: struct{}{},
|
||||
expectedRecord: &record{
|
||||
expectedRecord: &Record{
|
||||
serialized: []byte("{}"),
|
||||
key: "test-record",
|
||||
keyColumn: "test-column",
|
||||
|
||||
Reference in New Issue
Block a user