-
Notifications
You must be signed in to change notification settings - Fork 164
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 6 - CRUD puppy with interface #533
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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] | ||
undrewb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if !exist { | ||
return errors.New("no such puppy") | ||
} | ||
if p.ID != puppy.ID { | ||
return errors.New("bad puppy") | ||
} | ||
(*msp)[id] = puppy.Clone() | ||
return nil | ||
} |
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) | ||
} |
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) | ||
|
||
} |
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) | ||
} | ||
} |
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) | ||
undrewb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just a comment. I have written my test cases as unit test cases, as each one test a specific condition. Rather than bundle all the create operations into one. Not saying it's right or wrong. But I think test cases should be atomic. And it will eliminate the need for struct, and making test files shorter. |
||
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, | ||
undrewb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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) { | ||
undrewb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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) | ||
undrewb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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) | ||
} | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package main | ||
|
||
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 | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the idea of immutability. |
||
type Storer interface { | ||
CreatePuppy(*Puppy) error | ||
ReadPuppy(id uint32) (*Puppy, error) | ||
UpdatePuppy(id uint32, puppy *Puppy) error | ||
DeletePuppy(id uint32) (bool, error) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, this file should have not been committed.