diff --git a/06_puppy/alfredxiao/main.go b/06_puppy/alfredxiao/main.go new file mode 100644 index 000000000..13cd90a00 --- /dev/null +++ b/06_puppy/alfredxiao/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "io" + "os" +) + +var out io.Writer = os.Stdout + +func main() { + store := NewMapStore() + id := store.CreatePuppy(Puppy{ + Colour: "Red", + }) + + puppy, _ := store.ReadPuppy(id) + fmt.Fprint(out, puppy.Colour) +} diff --git a/06_puppy/alfredxiao/main_test.go b/06_puppy/alfredxiao/main_test.go new file mode 100644 index 000000000..aad093632 --- /dev/null +++ b/06_puppy/alfredxiao/main_test.go @@ -0,0 +1,16 @@ +package main + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMainOutput(t *testing.T) { + var buf bytes.Buffer + out = &buf + main() + + assert.Equal(t, "Red", buf.String()) +} diff --git a/06_puppy/alfredxiao/mapstore.go b/06_puppy/alfredxiao/mapstore.go new file mode 100644 index 000000000..555a7adea --- /dev/null +++ b/06_puppy/alfredxiao/mapstore.go @@ -0,0 +1,58 @@ +package main + +import ( + "fmt" + + "github.com/google/uuid" +) + +type mapStore struct { + data map[string]Puppy +} + +func (s *mapStore) CreatePuppy(p Puppy) string { + id := uuid.New().String() + + p.ID = id + s.data[id] = p + return id +} + +func (s *mapStore) ReadPuppy(id string) (Puppy, error) { + p, ok := s.data[id] + if !ok { + return Puppy{}, fmt.Errorf("puppy with ID[%s] does not exists", p.ID) + } + + return p, nil +} + +func (s *mapStore) UpdatePuppy(id string, p Puppy) error { + if id != p.ID { + return fmt.Errorf("bad update request, two IDs (%s, %s) do not match", + id, p.ID) + } + + _, ok := s.data[id] + if !ok { + return fmt.Errorf("puppy with ID[%s] does not exists", p.ID) + } + + s.data[id] = p + return nil +} + +func (s *mapStore) DeletePuppy(id string) error { + p, ok := s.data[id] + if !ok { + return fmt.Errorf("puppy with ID[%s] does not exists", p.ID) + } + delete(s.data, id) + return nil +} + +func NewMapStore() Storer { + return &mapStore{ + data: make(map[string]Puppy), + } +} diff --git a/06_puppy/alfredxiao/store_test.go b/06_puppy/alfredxiao/store_test.go new file mode 100644 index 000000000..067621f52 --- /dev/null +++ b/06_puppy/alfredxiao/store_test.go @@ -0,0 +1,82 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type storerSuite struct { + suite.Suite + store Storer + storeFactory func() Storer +} + +func (s *storerSuite) SetupTest() { + s.store = s.storeFactory() +} + +func (s *storerSuite) TestCreatePuppyHappyCase() { + puppy := Puppy{Colour: "Black"} + id := s.store.CreatePuppy(puppy) + puppyRead, err := s.store.ReadPuppy(id) + s.Require().NoError(err) + + puppy.ID = id + s.Equal(puppy, puppyRead) +} + +func (s *storerSuite) TestReadPuppyHappyCase() { + id := s.store.CreatePuppy(Puppy{Colour: "Blue"}) + p, err := s.store.ReadPuppy(id) + s.Require().NoError(err) + s.Equal("Blue", p.Colour) +} + +func (s *storerSuite) TestReadPuppyNonExisting() { + _, err := s.store.ReadPuppy("id_that_does_not_exist") + s.Error(err) +} + +func (s *storerSuite) TestUpdatePuppyHappyCase() { + id := s.store.CreatePuppy(Puppy{Colour: "Brown"}) + err := s.store.UpdatePuppy(id, Puppy{ID: id, Colour: "Green"}) + s.Require().NoError(err) + p, err := s.store.ReadPuppy(id) + s.Require().NoError(err) + s.Equal("Green", p.Colour) +} + +func (s *storerSuite) TestUpdatePuppyMismatchIDs() { + err := s.store.UpdatePuppy("id1", Puppy{ID: "id2"}) + s.Error(err) +} + +func (s *storerSuite) TestUpdatePuppyNonExisting() { + id := "id_that_does_not_exist_either" + err := s.store.UpdatePuppy(id, Puppy{ID: id}) + s.Error(err) +} + +func (s *storerSuite) TestDeletePuppyHappyCase() { + id := s.store.CreatePuppy(Puppy{Colour: "Brown"}) + err := s.store.DeletePuppy(id) + s.Require().NoError(err) + + _, err = s.store.ReadPuppy(id) + s.Error(err, "Puppy should be gone after deletion") +} + +func (s *storerSuite) TestDeletePuppyNonExisting() { + err := s.store.DeletePuppy("id_that_does_not_exist_again") + s.Require().Error(err) +} + +func TestStorers(t *testing.T) { + suite.Run(t, &storerSuite{ + storeFactory: NewMapStore, + }) + suite.Run(t, &storerSuite{ + storeFactory: NewSyncStore, + }) +} diff --git a/06_puppy/alfredxiao/syncstore.go b/06_puppy/alfredxiao/syncstore.go new file mode 100644 index 000000000..0b5b222f3 --- /dev/null +++ b/06_puppy/alfredxiao/syncstore.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "sync" + + "github.com/google/uuid" +) + +type syncStore struct { + data sync.Map +} + +func (s *syncStore) CreatePuppy(p Puppy) string { + id := uuid.New().String() + + p.ID = id + s.data.Store(id, p) + return id +} + +func (s *syncStore) ReadPuppy(id string) (Puppy, error) { + p, ok := s.data.Load(id) + if !ok { + return Puppy{}, fmt.Errorf("puppy with ID[%s] does not exists", id) + } + + return p.(Puppy), nil +} + +func (s *syncStore) UpdatePuppy(id string, p Puppy) error { + if id != p.ID { + return fmt.Errorf("bad update request, two IDs (%s, %s) do not match", + id, p.ID) + } + + _, ok := s.data.Load(id) + if !ok { + return fmt.Errorf("puppy with ID[%s] does not exists", p.ID) + } + + s.data.Store(id, p) + return nil +} + +func (s *syncStore) DeletePuppy(id string) error { + _, ok := s.data.Load(id) + if !ok { + return fmt.Errorf("puppy with ID[%s] does not exists", id) + } + s.data.Delete(id) + return nil +} + +func NewSyncStore() Storer { + return &syncStore{} +} diff --git a/06_puppy/alfredxiao/types.go b/06_puppy/alfredxiao/types.go new file mode 100644 index 000000000..5d04abe8d --- /dev/null +++ b/06_puppy/alfredxiao/types.go @@ -0,0 +1,15 @@ +package main + +type Puppy struct { + ID string + Breed string + Colour string + Value string +} + +type Storer interface { + CreatePuppy(p Puppy) string + ReadPuppy(ID string) (Puppy, error) + UpdatePuppy(ID string, p Puppy) error + DeletePuppy(ID string) error +}