Skip to content

Commit

Permalink
Implement CreatePuppy in storer
Browse files Browse the repository at this point in the history
Create puupy struct
Implement Init to initialize a puppy and assign a random id
Implement String method to print a puppy struct
At this point there is no check for collision ids
Implement a map backed storer only at this time

Implement ReadStore

Error checking hasnt been implemented.
At this stage nil will be returned if the item cant be found.
This will be addressed in a later lab

Implement DeletePuppy
Fix boolean logic in ReadPuppy

Implement UpdatePuppy
Implement clone method on puppy
Resolve problem with usage of pointers and references

Separate MapStore into its own file
Create a basic main_test

Integrate linter feedback

Rename ID to id where used as parameter

Change mapstore test to use a test suite using the Storer interface
Begin implementing a syncstore version of the Storer interface

Implement all methids for syncstore
Implement all cases for store_tests
Ensure 100% test coverqge

Fix linter errors

Capitals and newlines in error messages
Not assign table tests when ranging across them
Misalignment of structs

Verified lint and code cov passed

Separate types into their own file
  • Loading branch information
Bucknell, Andrew committed Jul 3, 2019
1 parent 25cd3bf commit cca9118
Show file tree
Hide file tree
Showing 7 changed files with 338 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
coverage.txt
06_puppy/undrewb/.vscode/launch.json
48 changes: 48 additions & 0 deletions 06_puppy/undrewb/MapStore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"errors"
"fmt"
)

type MapStore map[uint32]*Puppy

func (msp *MapStore) CreatePuppy(p *Puppy) error {
if _, exist := (*msp)[p.ID]; exist {
return fmt.Errorf("puppy %d already exists", p.ID)
}
(*msp)[p.ID] = p.Clone()
return nil
}

func (msp *MapStore) ReadPuppy(id uint32) (*Puppy, error) {
p, err := (*msp)[id]
if !err {
return nil, errors.New("no such puppy")
}
return p, nil
}

func (msp *MapStore) DeletePuppy(id uint32) (bool, error) {
p, exist := (*msp)[id]
if !exist {
return false, errors.New("no such puppy")
}
delete(*msp, p.ID)
return true, nil
}

func (msp *MapStore) UpdatePuppy(id uint32, puppy *Puppy) error {
if puppy == nil {
return fmt.Errorf("cant update to a nil puppy")
}
p, exist := (*msp)[id]
if !exist {
return errors.New("no such puppy")
}
if p.ID != puppy.ID {
return errors.New("bad puppy")
}
(*msp)[id] = puppy.Clone()
return nil
}
47 changes: 47 additions & 0 deletions 06_puppy/undrewb/SyncStore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package main

import (
"fmt"
"sync"
)

type SyncStore struct {
sync.Map
}

func (store *SyncStore) CreatePuppy(p *Puppy) error {
if _, ok := store.Load(p.ID); !ok {
store.Store(p.ID, p)
return nil
}
return fmt.Errorf("store already has a puppy with id : %d", p.ID)
}

func (store *SyncStore) ReadPuppy(id uint32) (*Puppy, error) {
if p, ok := store.Load(id); ok {
return p.(*Puppy), nil
}
return nil, fmt.Errorf("could not load puppy %d", id)
}

func (store *SyncStore) UpdatePuppy(id uint32, puppy *Puppy) error {
if puppy == nil {
return fmt.Errorf("cant update to a nil puppy")
}
if p, ok := store.Load(id); ok {
if p.(*Puppy).ID == puppy.ID {
store.Store(id, puppy.Clone())
return nil
}
return fmt.Errorf("bad puppy : %d != %d", p.(*Puppy).ID, id)
}
return fmt.Errorf("could not find puppy id = %d", id)
}

func (store *SyncStore) DeletePuppy(id uint32) (bool, error) {
if _, ok := store.Load(id); ok {
store.Delete(id)
return true, nil
}
return false, fmt.Errorf("could not find puppy id = %d", id)
}
16 changes: 16 additions & 0 deletions 06_puppy/undrewb/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

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

var out io.Writer = os.Stdout

func main() {

store := new(MapStore)
fmt.Fprintln(out, store)

}
21 changes: 21 additions & 0 deletions 06_puppy/undrewb/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"bytes"
"strconv"
"testing"
)

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

main()

expected := strconv.Quote("&map[]\n")
actual := strconv.Quote(buf.String())

if expected != actual {
t.Errorf("unexpected result in main() : expected = %s, actual = %s\n", expected, actual)
}
}
177 changes: 177 additions & 0 deletions 06_puppy/undrewb/store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package main

import (
"reflect"
"testing"

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

type storerSuite struct {
suite.Suite
store Storer
storerType func() Storer
}

func TestStorers(t *testing.T) {
suite.Run(t, &storerSuite{
storerType: func() Storer { return &MapStore{} },
})
suite.Run(t, &storerSuite{
storerType: func() Storer { return &SyncStore{} },
})
}

func (s *storerSuite) SetupTest() {
s.store = s.storerType()

corgi := &Puppy{1000, "corgi", "orange and white", "$$$$$$$"}
_ = s.store.CreatePuppy(corgi)
spaniel := &Puppy{2000, "spaniel", "brown", "$$$$$$"}
_ = s.store.CreatePuppy(spaniel)
cujo := &Puppy{2500, "cujo", "tan", "$"}
_ = s.store.CreatePuppy(cujo)
terrier := &Puppy{3000, "terrier", "black", "$$$"}
_ = s.store.CreatePuppy(terrier)
bulldog := &Puppy{4000, "bulldog", "white", "$$"}
_ = s.store.CreatePuppy(bulldog)
}

func (s *storerSuite) TestMapStore_CreatePuppy() {

corgi := &Puppy{10000, "new corgi", "even brighter orange and white", "$$$$$$$"}

tests := []struct {
name string
puppy *Puppy
wantErr bool
}{
{name: "New corgi",
puppy: corgi,
wantErr: false},
{name: "Existing corgi",
puppy: corgi,
wantErr: true},
}
for _, tt := range tests {
tt := tt
s.T().Run(tt.name, func(t *testing.T) {
if err := s.store.CreatePuppy(tt.puppy); (err != nil) != tt.wantErr {
t.Errorf("CreatePuppy() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

func (s *storerSuite) TestMapStore_ReadPuppy() {

corgi := &Puppy{1000, "corgi", "orange and white", "$$$$$$$"}
_ = s.store.CreatePuppy(corgi)

tests := []struct {
name string
id uint32
wantErr bool
want *Puppy
}{
{name: "lookup existing puppy",
id: 1000,
want: corgi,
wantErr: false,
},
{name: "lookup non-existing puppy",
id: 1001,
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
s.T().Run(tt.name, func(t *testing.T) {
got, err := s.store.ReadPuppy(tt.id)
if (err != nil) != tt.wantErr {
t.Errorf("ReadPuppy() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ReadPuppy() = %v, want %v", got, tt.want)
}
})
}
}

func (s *storerSuite) TestMapStore_DeletePuppy() {
tests := []struct {
name string
id uint32
want bool
wantErr bool
}{
{name: "delete existing corgi",
id: 1000,
want: true,
wantErr: false,
},
{name: "delete non-existent corgi",
id: 1000,
want: false,
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
s.T().Run(tt.name, func(t *testing.T) {
got, err := s.store.DeletePuppy(tt.id)
if (err != nil) != tt.wantErr {
t.Errorf("DeletePuppy() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("DeletePuppy() = %v, want %v", got, tt.want)
}
})
}
}

func (s *storerSuite) TestMapStore_UpdatePuppy() {
albinoCorgi := &Puppy{1000, "corgi", "white", "$$$$$$$"}
corruptCorgi := &Puppy{1009, "corgi", "white", "$$$$$$$"}
// _ = s.store.CreatePuppy(albinoCorgi)

tests := []struct {
name string
id uint32
wantErr bool
puppy *Puppy
}{
{name: "update existing puppy",
id: 1000,
puppy: albinoCorgi,
wantErr: false,
},
{name: "update non-existing puppy",
id: 1001,
puppy: albinoCorgi,
wantErr: true,
},
{name: "update with an empty puppy",
id: 1000,
puppy: nil,
wantErr: true,
},
{name: "update with a corrupt puppy",
id: 1000,
puppy: corruptCorgi,
wantErr: true,
},
}

for _, tt := range tests {
tt := tt
s.T().Run(tt.name, func(t *testing.T) {
if err := s.store.UpdatePuppy(tt.id, tt.puppy); (err != nil) != tt.wantErr {
t.Errorf("UpdatePuppy() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
28 changes: 28 additions & 0 deletions 06_puppy/undrewb/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import(

)

type Puppy struct {
ID uint32 `json:"ID"`
Breed string `json:"breed"`
Colour string `json:"colour"`
Value string `json:"value"`
}

func (p *Puppy) Clone() *Puppy {
puppy := new(Puppy)
puppy.ID = p.ID
puppy.Breed = p.Breed
puppy.Colour = p.Colour
puppy.Value = p.Value
return puppy
}

type Storer interface {
CreatePuppy(*Puppy) error
ReadPuppy(id uint32) (*Puppy, error)
UpdatePuppy(id uint32, puppy *Puppy) error
DeletePuppy(id uint32) (bool, error)
}

0 comments on commit cca9118

Please sign in to comment.