diff --git a/07_errors/hsy3418/error.go b/07_errors/hsy3418/error.go new file mode 100644 index 000000000..0b5125a2d --- /dev/null +++ b/07_errors/hsy3418/error.go @@ -0,0 +1,12 @@ +package main + +import "fmt" + +type Error struct { + Message string + Code int +} + +func (e *Error) Error() string { + return fmt.Sprintf("Error code:%d,Error:%s", e.Code, e.Message) +} diff --git a/07_errors/hsy3418/error_test.go b/07_errors/hsy3418/error_test.go new file mode 100644 index 000000000..103c74768 --- /dev/null +++ b/07_errors/hsy3418/error_test.go @@ -0,0 +1,13 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestError(t *testing.T) { + err := Error{Code: -1, Message: "Invalid Error"} + errMessage := err.Error() + assert.Equal(t, "Error code:-1,Error:Invalid Error", errMessage) +} diff --git a/07_errors/hsy3418/main.go b/07_errors/hsy3418/main.go new file mode 100644 index 000000000..820427f1f --- /dev/null +++ b/07_errors/hsy3418/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "io" + "os" +) + +var out io.Writer = os.Stdout + +func main() { + p1 := Puppy{ID: 101, Breed: "Poodle", Colour: "White", Value: 1280.5} + p2 := Puppy{ID: 102, Breed: "Poodle", Colour: "Grey", Value: 1340.5} + + mapStore := NewMapStore() + syncStore := NewSyncStore() + + _ = mapStore.CreatePuppy(p1) + _ = syncStore.CreatePuppy(p2) + + puppy, _ := mapStore.ReadPuppy(101) + puppy2, _ := syncStore.ReadPuppy(102) + fmt.Fprintf(out, "Puppy ID %d is %v", puppy.ID, puppy.Value) + fmt.Fprintf(out, "Puppy ID %d is %v", puppy2.ID, puppy2.Value) + +} diff --git a/07_errors/hsy3418/main_test.go b/07_errors/hsy3418/main_test.go new file mode 100644 index 000000000..95aeb49ae --- /dev/null +++ b/07_errors/hsy3418/main_test.go @@ -0,0 +1,18 @@ +package main + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMain(t *testing.T) { + assert := assert.New(t) + var buf bytes.Buffer + out = &buf + main() + expected := "Puppy ID 101 is 1280.5Puppy ID 102 is 1340.5" + actual := buf.String() + assert.Equal(expected, actual) +} diff --git a/07_errors/hsy3418/mapStore.go b/07_errors/hsy3418/mapStore.go new file mode 100644 index 000000000..e6f334959 --- /dev/null +++ b/07_errors/hsy3418/mapStore.go @@ -0,0 +1,63 @@ +package main + +import "fmt" + +// MapStore is a implementation of storer for the storage of puppies +type MapStore struct { + puppies map[int32]Puppy +} + +func NewMapStore() *MapStore { + return &MapStore{ + puppies: map[int32]Puppy{}, + } +} + +// CreatePuppy adds a nuw puppy to the puppies store +func (m *MapStore) CreatePuppy(puppy Puppy) error { + if puppy.Value < 0 { + err := &Error{ + Message: "The puppy value is invalidate", + Code: -1, + } + return err + } + if _, exists := m.puppies[puppy.ID]; !exists { + m.puppies[puppy.ID] = puppy + return nil + } + return fmt.Errorf("puppy with %d ID already exists", puppy.ID) +} + +// ReadPuppy retrieves the puppy for a given id from puppies store +func (m *MapStore) ReadPuppy(id int32) (Puppy, error) { + if _, exists := m.puppies[id]; !exists { + return Puppy{}, &Error{Message: "puppy ID does not exist", Code: -2} + } + return m.puppies[id], nil +} + +//UpdatePuppy updates the puppy for the given id +func (m *MapStore) UpdatePuppy(puppy Puppy) error { + if puppy.Value < 0 { + err := &Error{ + Message: "The puppy value is invalidate", + Code: -1, + } + return err + } + if _, exists := m.puppies[puppy.ID]; !exists { + return &Error{Message: "puppy ID does not exist", Code: -2} + } + m.puppies[puppy.ID] = puppy + return nil +} + +//DeletePuppy delete the puppy for the given id from puppies store +func (m *MapStore) DeletePuppy(id int32) error { + if _, exists := m.puppies[id]; exists { + delete(m.puppies, id) + return nil + } + return &Error{Message: "puppy ID does not exist", Code: -2} +} diff --git a/07_errors/hsy3418/store_test.go b/07_errors/hsy3418/store_test.go new file mode 100644 index 000000000..c2c201e89 --- /dev/null +++ b/07_errors/hsy3418/store_test.go @@ -0,0 +1,133 @@ +package main + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func (suite *storerTestSuite) SetupTest() { + suite.store = suite.makeStore() + suite.toBeCreatedPuppy = Puppy{ID: 101, Breed: "Poodle", Colour: "White", Value: 1000.5} + suite.existsPuppy = Puppy{ID: 102, Breed: "Poodle", Colour: "White", Value: 1280.5} + suite.toBeUpdatedPuppy = Puppy{ID: 102, Breed: "Poodle", Colour: "White", Value: 2000} + suite.invalidPuppy = Puppy{ID: 103, Breed: "Poodle", Colour: "White", Value: -1000} + suite.invaildError = &Error{ + Message: "The puppy value is invalidate", + Code: -1, + } + suite.invalidIDError = &Error{Message: "puppy ID does not exist", Code: -2} + err := suite.store.CreatePuppy(suite.existsPuppy) + if err != nil { + suite.FailNow("Failed to setup test") + } +} + +type storerTestSuite struct { + suite.Suite + store Storer + makeStore func() Storer + toBeCreatedPuppy Puppy + existsPuppy Puppy + toBeUpdatedPuppy Puppy + invalidPuppy Puppy + invaildError error + invalidIDError error +} + +//successfully create puppy, new puppy, existing puppy, value <0 +func (suite *storerTestSuite) TestCreatePuppy() { + assert := assert.New(suite.T()) + testCases := []struct { + title string + input Puppy + expected error + }{ + {"Create new puppy", suite.toBeCreatedPuppy, nil}, + {"Create existing puppy", suite.toBeCreatedPuppy, fmt.Errorf("puppy with 101 ID already exists")}, + {"Create a invalid puppy", suite.invalidPuppy, suite.invaildError}, + } + for _, tc := range testCases { + tc := tc + suite.T().Run(tc.title, func(t *testing.T) { + err := suite.store.CreatePuppy(tc.input) + assert.Equal(tc.expected, err) + }) + } + +} + +// +func (suite *storerTestSuite) TestUpdatePuppy() { + assert := assert.New(suite.T()) + testCases := []struct { + title string + inputPuppy Puppy + expectedError error + }{ + {"Update puppy successfully", suite.toBeUpdatedPuppy, nil}, + {"Update a invalid puppy", suite.invalidPuppy, suite.invaildError}, + {"Update non-existing puppy", Puppy{}, suite.invalidIDError}, + } + for _, tc := range testCases { + tc := tc + suite.T().Run(tc.title, func(t *testing.T) { + err := suite.store.UpdatePuppy(tc.inputPuppy) + assert.Equal(tc.expectedError, err) + }) + } + +} + +func (suite *storerTestSuite) TestReadPuppy() { + assert := assert.New(suite.T()) + testCases := []struct { + title string + input int32 + expected Puppy + expectedError error + }{ + {"Read puppy successfully", suite.existsPuppy.ID, suite.existsPuppy, nil}, + {"Read non-existing puppy", suite.toBeCreatedPuppy.ID, Puppy{}, suite.invalidIDError}, + } + for _, tc := range testCases { + tc := tc + suite.T().Run(tc.title, func(t *testing.T) { + readPuppy, err := suite.store.ReadPuppy(tc.input) + assert.Equal(tc.expected, readPuppy) + assert.Equal(tc.expectedError, err) + }) + } + +} + +func (suite *storerTestSuite) TestDeletePuppy() { + assert := assert.New(suite.T()) + testCases := []struct { + title string + input int32 + expectedError error + }{ + {"Delete puppy successfully", suite.existsPuppy.ID, nil}, + {"Delete non-existing puppy", suite.toBeCreatedPuppy.ID, suite.invalidIDError}, + } + for _, tc := range testCases { + tc := tc + suite.T().Run(tc.title, func(t *testing.T) { + err := suite.store.DeletePuppy(tc.input) + assert.Equal(tc.expectedError, err) + }) + } + +} + +func TestStorers(t *testing.T) { + suite.Run(t, &storerTestSuite{ + makeStore: func() Storer { return NewMapStore() }, + }) + suite.Run(t, &storerTestSuite{ + makeStore: func() Storer { return NewSyncStore() }, + }) +} diff --git a/07_errors/hsy3418/syncStore.go b/07_errors/hsy3418/syncStore.go new file mode 100644 index 000000000..0c2f7a1aa --- /dev/null +++ b/07_errors/hsy3418/syncStore.go @@ -0,0 +1,71 @@ +package main + +import ( + "fmt" + "sync" +) + +type SyncStore struct { + sync.Mutex + sync.Map +} + +func NewSyncStore() *SyncStore { + return &SyncStore{} +} + +// CreatePuppy adds a nuw puppy to the puppies store +func (m *SyncStore) CreatePuppy(puppy Puppy) error { + m.Lock() + defer m.Unlock() + if puppy.Value < 0 { + err := &Error{ + Message: "The puppy value is invalidate", + Code: -1, + } + return err + } + if _, exists := m.Load(puppy.ID); exists { + return fmt.Errorf("puppy with %d ID already exists", puppy.ID) + } + m.Store(puppy.ID, puppy) + return nil +} + +// ReadPuppy retrieves the puppy for a given id from puppies store +func (m *SyncStore) ReadPuppy(id int32) (Puppy, error) { + if p, exists := m.Load(id); exists { + puppy, _ := p.(Puppy) + return puppy, nil + } + return Puppy{}, &Error{Message: "puppy ID does not exist", Code: -2} +} + +//UpdatePuppy updates the puppy for the given id +func (m *SyncStore) UpdatePuppy(puppy Puppy) error { + m.Lock() + defer m.Unlock() + if puppy.Value < 0 { + err := &Error{ + Message: "The puppy value is invalidate", + Code: -1, + } + return err + } + if _, exists := m.Load(puppy.ID); !exists { + return &Error{Message: "puppy ID does not exist", Code: -2} + } + m.Store(puppy.ID, puppy) + return nil +} + +//DeletePuppy delete the puppy for the given id from puppies store +func (m *SyncStore) DeletePuppy(id int32) error { + m.Lock() + defer m.Unlock() + if _, exists := m.Load(id); exists { + m.Delete(id) + return nil + } + return &Error{Message: "puppy ID does not exist", Code: -2} +} diff --git a/07_errors/hsy3418/types.go b/07_errors/hsy3418/types.go new file mode 100644 index 000000000..fa6bf05d9 --- /dev/null +++ b/07_errors/hsy3418/types.go @@ -0,0 +1,17 @@ +package main + +// Puppy defines the data structure corresponding to a pet +type Puppy struct { + ID int32 `json:"id"` + Value float32 `json:"value"` + Breed string `json:"breed"` + Colour string `json:"colour"` +} + +//Storer define standard CRUD operations for puppys +type Storer interface { + CreatePuppy(Puppy) error + ReadPuppy(ID int32) (Puppy, error) + UpdatePuppy(puppy Puppy) error + DeletePuppy(ID int32) error +}