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

Lab 08 pm #593

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
13 changes: 13 additions & 0 deletions 08_project/patrickmarabeas/cmd/puppy-server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package puppyserver

import (
"fmt"
"io"
"os"
)

var out io.Writer = os.Stdout

func main() {
fmt.Fprint(out, "hello")
}
21 changes: 21 additions & 0 deletions 08_project/patrickmarabeas/cmd/puppy-server/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package puppyserver

import (
"bytes"
"testing"
)

func TestMainOutput(t *testing.T) {
var buf bytes.Buffer
out = &buf

main()

expected := "hello"

got := buf.String()

if expected != got {
t.Errorf("\nExpected: %s\nGot: %s", expected, got)
}
}
32 changes: 32 additions & 0 deletions 08_project/patrickmarabeas/pkg/puppy/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package puppy

import "fmt"

type ErrorCode int

type Error struct {
Code ErrorCode
Message string
}

const (
NegativeValue ErrorCode = iota + 1001
IDNotFound
Unknown = 1999
)

func (e *Error) Error() string {
return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}

// NewError creates a new error with the given enum
func NewError(code ErrorCode) error {
switch code {
case NegativeValue:
return &Error{code, "Puppy value must be greater than 0"}
case IDNotFound:
return &Error{code, "Nonexistent Puppy ID"}
default:
return &Error{Unknown, "Unknown error"}
}
}
22 changes: 22 additions & 0 deletions 08_project/patrickmarabeas/pkg/puppy/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package puppy

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestUnknownError(t *testing.T) {
a := assert.New(t)

error := NewError(123)
a.Equal(error, NewError(Unknown))
}

func TestError(t *testing.T) {
a := assert.New(t)

error := Error{1, "message"}
formattedError := error.Error()
a.Equal(formattedError, "[1] message")
}
67 changes: 67 additions & 0 deletions 08_project/patrickmarabeas/pkg/puppy/store/mapStore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package store

import (
p "github.com/anz-bank/go-course/08_project/patrickmarabeas/pkg/puppy"
)

type MapStore struct {
uuid int
store map[int]p.Puppy
}

// NewMapStore returns a pointer to a new instance of the MapStore struct which implements the Storer interface.
func NewMapStore() Storer {
return &MapStore{
uuid: 0,
store: map[int]p.Puppy{},
}
}

// Create increments the uuid and adds the provided Puppy struct to the store with this identifier.
func (store *MapStore) Create(puppy p.Puppy) (int, error) {
if puppy.Value < 0 {
return -1, p.NewError(p.NegativeValue)
}

puppy.ID = store.uuid
store.store[puppy.ID] = puppy
store.uuid++

return puppy.ID, nil
}

// Read returns the puppy matching the provided uuid.
// An empty Puppy struct is returned if the identifier does not exist.
func (store *MapStore) Read(id int) (p.Puppy, error) {
if _, ok := store.store[id]; ok {
return store.store[id], nil
}

return p.Puppy{}, p.NewError(p.IDNotFound)
}

// Update modifies the puppy matching the provided uuid in the store with the provided Puppy struct.
// It returns a bool whether a matching identifier was modified or not.
func (store *MapStore) Update(id int, puppy p.Puppy) (bool, error) {
if _, ok := store.store[id]; !ok {
return false, p.NewError(p.IDNotFound)
}
if puppy.Value < 0 {
return false, p.NewError(p.NegativeValue)
}

puppy.ID = id
store.store[id] = puppy
return true, nil
}

// Destroy removes the puppy matching the provided uuid from the store.
// It returns a bool whether a matching identifier was deleted or not.
func (store *MapStore) Destroy(id int) (bool, error) {
if _, ok := store.store[id]; !ok {
return false, p.NewError(p.IDNotFound)
}

delete(store.store, id)
return true, nil
}
117 changes: 117 additions & 0 deletions 08_project/patrickmarabeas/pkg/puppy/store/store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package store

import (
"testing"

p "github.com/anz-bank/go-course/08_project/patrickmarabeas/pkg/puppy"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)

type StoreSuite struct {
suite.Suite
store Storer
}

func (suite *StoreSuite) TestCreate() {
a := assert.New(suite.T())

id, error := suite.store.Create(p.Puppy{Breed: "Wolf", Color: "Grey", Value: 450})
a.Equal(id, 0)
a.Equal(error, nil)
}

func (suite *StoreSuite) TestCreateSecond() {
a := assert.New(suite.T())

id, error := suite.store.Create(p.Puppy{Breed: "Boxer", Color: "Brown", Value: 300})
a.Equal(id, 1)
a.Equal(error, nil)
}

func (suite *StoreSuite) TestCreateNegativeNumber() {
a := assert.New(suite.T())

id, error := suite.store.Create(p.Puppy{Breed: "Wolf", Color: "Grey", Value: -100})
a.Equal(id, -1)
a.Equal(error, p.NewError(p.NegativeValue))
}

func (suite *StoreSuite) TestRead() {
a := assert.New(suite.T())

data, error := suite.store.Read(0)
a.Equal(data, p.Puppy{ID: 0, Breed: "Wolf", Color: "Grey", Value: 450})
a.Equal(error, nil)
}

func (suite *StoreSuite) TestReadNonExistent() {
a := assert.New(suite.T())

success, error := suite.store.Read(100)
a.Equal(success, p.Puppy{})
a.Equal(error, p.NewError(p.IDNotFound))
}

func (suite *StoreSuite) TestUpdate() {
a := assert.New(suite.T())

success, error := suite.store.Update(0, p.Puppy{Breed: "Doberman", Color: "Black", Value: 500})
a.Equal(success, true)
a.Equal(error, nil)

data, error := suite.store.Read(0)
a.Equal(data, p.Puppy{ID: 0, Breed: "Doberman", Color: "Black", Value: 500})
a.Equal(error, nil)
}

func (suite *StoreSuite) TestUpdateNonExistent() {
a := assert.New(suite.T())

success, error := suite.store.Update(100, p.Puppy{Breed: "Doberman", Color: "Black", Value: 500})
a.Equal(success, false)
a.Equal(error, p.NewError(p.IDNotFound))
}

func (suite *StoreSuite) TestUpdateNegativeNumber() {
a := assert.New(suite.T())

success, error := suite.store.Update(0, p.Puppy{Breed: "Doberman", Color: "Black", Value: -500})
a.Equal(success, false)
a.Equal(error, p.NewError(p.NegativeValue))
}

func (suite *StoreSuite) TestDestroy() {
a := assert.New(suite.T())

success, error := suite.store.Destroy(1)
a.Equal(success, true)
a.Equal(error, nil)

data, error := suite.store.Read(1)
a.Equal(data, p.Puppy{})
a.Equal(error, p.NewError(p.IDNotFound))
}

func (suite *StoreSuite) TestDestroyNonExistent() {
a := assert.New(suite.T())

success, error := suite.store.Destroy(100)
a.Equal(success, false)
a.Equal(error, p.NewError(p.IDNotFound))
}

func (suite *StoreSuite) TestIdIncrementOnDelete() {
a := assert.New(suite.T())
id, _ := suite.store.Create(p.Puppy{Breed: "Greyhound", Color: "Light Brown", Value: 700})
success, _ := suite.store.Destroy(id)
a.Equal(success, true)

newID, _ := suite.store.Create(p.Puppy{Breed: "Greyhound", Color: "Light Brown", Value: 700})
a.Equal(newID, 3)
}

func TestStore(t *testing.T) {
suite.Run(t, &StoreSuite{store: NewMapStore()})
suite.Run(t, &StoreSuite{store: NewSyncStore()})
}
12 changes: 12 additions & 0 deletions 08_project/patrickmarabeas/pkg/puppy/store/storer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package store

import (
p "github.com/anz-bank/go-course/08_project/patrickmarabeas/pkg/puppy"
)

type Storer interface {
Create(puppy p.Puppy) (int, error)
Read(ID int) (p.Puppy, error)
Update(ID int, puppy p.Puppy) (bool, error)
Destroy(ID int) (bool, error)
}
77 changes: 77 additions & 0 deletions 08_project/patrickmarabeas/pkg/puppy/store/syncStore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package store

import (
"sync"

p "github.com/anz-bank/go-course/08_project/patrickmarabeas/pkg/puppy"
)

type SyncStore struct {
uuid int
sync.Map
sync.RWMutex
}

// NewSyncStore returns a pointer to a new instance of the SyncStore struct which implements the Storer interface.
func NewSyncStore() Storer {
return &SyncStore{
uuid: 0,
}
}

// Create increments the uuid and adds the provided Puppy struct to the store with this identifier.
func (store *SyncStore) Create(puppy p.Puppy) (int, error) {
if puppy.Value < 0 {
return -1, p.NewError(p.NegativeValue)
}

store.Lock()
puppy.ID = store.uuid
store.Store(puppy.ID, puppy)
store.uuid++
store.Unlock()

return puppy.ID, nil
}

// Read returns the puppy matching the provided uuid.
// An empty Puppy struct is returned if the identifier does not exist.
func (store *SyncStore) Read(id int) (p.Puppy, error) {
store.RLock()
if value, ok := store.Load(id); ok {
return value.(p.Puppy), nil
}
store.RUnlock()

return p.Puppy{}, p.NewError(p.IDNotFound)
}

// Update modifies the puppy matching the provided uuid in the store with the provided Puppy struct.
// It returns a bool whether a matching identifier was modified or not.
func (store *SyncStore) Update(id int, puppy p.Puppy) (bool, error) {
if _, ok := store.Load(id); !ok {
return false, p.NewError(p.IDNotFound)
}
if puppy.Value < 0 {
return false, p.NewError(p.NegativeValue)
}

puppy.ID = id
store.Store(id, puppy)

return true, nil
}

// Destroy removes the puppy matching the provided uuid from the store.
// It returns a bool whether a matching identifier was deleted or not.
func (store *SyncStore) Destroy(id int) (bool, error) {
if _, ok := store.Load(id); !ok {
return false, p.NewError(p.IDNotFound)
}

store.Lock()
store.Delete(id)
store.Unlock()

return true, nil
}
8 changes: 8 additions & 0 deletions 08_project/patrickmarabeas/pkg/puppy/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package puppy

type Puppy struct {
ID int
Breed string
Color string
Value int // cents
}