From 1358c73e4e4381e0aaf235d4287e114a1386303b Mon Sep 17 00:00:00 2001 From: Patrick Marabeas Date: Mon, 19 Aug 2019 15:34:20 +1000 Subject: [PATCH 1/6] Copy from upstream/06_puppy/patrickmarabeas Lab 07 (#589) --- 07_errors/patrickmarabeas/main.go | 5 ++ 07_errors/patrickmarabeas/main_test.go | 25 +++++++ 07_errors/patrickmarabeas/mapStore.go | 51 +++++++++++++++ 07_errors/patrickmarabeas/store_test.go | 87 +++++++++++++++++++++++++ 07_errors/patrickmarabeas/syncStore.go | 50 ++++++++++++++ 07_errors/patrickmarabeas/types.go | 29 +++++++++ 6 files changed, 247 insertions(+) create mode 100644 07_errors/patrickmarabeas/main.go create mode 100644 07_errors/patrickmarabeas/main_test.go create mode 100644 07_errors/patrickmarabeas/mapStore.go create mode 100644 07_errors/patrickmarabeas/store_test.go create mode 100644 07_errors/patrickmarabeas/syncStore.go create mode 100644 07_errors/patrickmarabeas/types.go diff --git a/07_errors/patrickmarabeas/main.go b/07_errors/patrickmarabeas/main.go new file mode 100644 index 000000000..790580777 --- /dev/null +++ b/07_errors/patrickmarabeas/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + +} diff --git a/07_errors/patrickmarabeas/main_test.go b/07_errors/patrickmarabeas/main_test.go new file mode 100644 index 000000000..c98b5b046 --- /dev/null +++ b/07_errors/patrickmarabeas/main_test.go @@ -0,0 +1,25 @@ +package main + +import ( + "bytes" + "io" + "os" + "testing" +) + +var out io.Writer = os.Stdout + +func TestMainOutput(t *testing.T) { + var buf bytes.Buffer + out = &buf + + main() + + expected := "" + + got := buf.String() + + if expected != got { + t.Errorf("\nExpected: %s\nGot: %s", expected, got) + } +} diff --git a/07_errors/patrickmarabeas/mapStore.go b/07_errors/patrickmarabeas/mapStore.go new file mode 100644 index 000000000..187bb55d3 --- /dev/null +++ b/07_errors/patrickmarabeas/mapStore.go @@ -0,0 +1,51 @@ +package main + +// 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]Puppy{}, + } +} + +// Create increments the uuid and adds the provided Puppy struct to the store with this identifier. +func (store *MapStore) Create(puppy Puppy) int { + puppy.ID = store.uuid + store.store[puppy.ID] = puppy + store.uuid++ + + return puppy.ID +} + +// 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) Puppy { + if _, ok := store.store[id]; ok { + return store.store[id] + } + + return Puppy{} +} + +// 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 Puppy) bool { + if _, ok := store.store[id]; !ok { + return false + } + + puppy.ID = id + store.store[id] = puppy + return true +} + +// 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 { + if _, ok := store.store[id]; !ok { + return false + } + + delete(store.store, id) + return true +} diff --git a/07_errors/patrickmarabeas/store_test.go b/07_errors/patrickmarabeas/store_test.go new file mode 100644 index 000000000..bb9899ed1 --- /dev/null +++ b/07_errors/patrickmarabeas/store_test.go @@ -0,0 +1,87 @@ +package main + +import ( + "testing" + + "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 := suite.store.Create(Puppy{Breed: "Wolf", Color: "Grey", Value: "450"}) + + a.Equal(id, 0) +} + +func (suite *StoreSuite) TestCreateSecond() { + a := assert.New(suite.T()) + id := suite.store.Create(Puppy{Breed: "Boxer", Color: "Brown", Value: "300"}) + + a.Equal(id, 1) +} + +func (suite *StoreSuite) TestRead() { + a := assert.New(suite.T()) + data := suite.store.Read(0) + + a.Equal(data, Puppy{ID: 0, Breed: "Wolf", Color: "Grey", Value: "450"}) +} + +func (suite *StoreSuite) TestReadNonExistent() { + a := assert.New(suite.T()) + success := suite.store.Read(100) + + a.Equal(success, Puppy{}) +} + +func (suite *StoreSuite) TestUpdate() { + a := assert.New(suite.T()) + success := suite.store.Update(0, Puppy{Breed: "Doberman", Color: "Black", Value: "500"}) + data := suite.store.Read(0) + + a.Equal(success, true) + a.Equal(data, Puppy{ID: 0, Breed: "Doberman", Color: "Black", Value: "500"}) +} + +func (suite *StoreSuite) TestUpdateNonExistent() { + a := assert.New(suite.T()) + success := suite.store.Update(100, Puppy{Breed: "Doberman", Color: "Black", Value: "500"}) + + a.Equal(success, false) +} + +func (suite *StoreSuite) TestDestroy() { + a := assert.New(suite.T()) + success := suite.store.Destroy(1) + data := suite.store.Read(1) + + a.Equal(success, true) + a.Equal(data, Puppy{}) +} + +func (suite *StoreSuite) TestDestroyNonExistent() { + a := assert.New(suite.T()) + success := suite.store.Destroy(100) + + a.Equal(success, false) +} + +func (suite *StoreSuite) TestIdIncrementOnDelete() { + a := assert.New(suite.T()) + id := suite.store.Create(Puppy{Breed: "Greyhound", Color: "Light Brown", Value: "700"}) + suite.store.Destroy(id) + newID := suite.store.Create(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()}) +} diff --git a/07_errors/patrickmarabeas/syncStore.go b/07_errors/patrickmarabeas/syncStore.go new file mode 100644 index 000000000..47dc770f5 --- /dev/null +++ b/07_errors/patrickmarabeas/syncStore.go @@ -0,0 +1,50 @@ +package main + +// 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 Puppy) int { + puppy.ID = store.uuid + store.Store(puppy.ID, puppy) + store.uuid++ + + return puppy.ID +} + +// 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) Puppy { + if value, ok := store.Load(id); ok { + return value.(Puppy) + } + + return Puppy{} +} + +// 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 Puppy) bool { + if _, ok := store.Load(id); !ok { + return false + } + + puppy.ID = id + store.Store(id, puppy) + return true +} + +// 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 { + if _, ok := store.Load(id); !ok { + return false + } + + store.Delete(id) + return true +} diff --git a/07_errors/patrickmarabeas/types.go b/07_errors/patrickmarabeas/types.go new file mode 100644 index 000000000..bbf6907be --- /dev/null +++ b/07_errors/patrickmarabeas/types.go @@ -0,0 +1,29 @@ +package main + +import ( + "sync" +) + +type Puppy struct { + ID int + Breed string + Color string + Value string +} + +type Storer interface { + Create(puppy Puppy) int + Read(ID int) Puppy + Update(ID int, puppy Puppy) bool + Destroy(ID int) bool +} + +type MapStore struct { + uuid int + store map[int]Puppy +} + +type SyncStore struct { + uuid int + sync.Map +} From bb43faf4d7ad142e5690ad3003dc227fe09501be Mon Sep 17 00:00:00 2001 From: Patrick Marabeas Date: Mon, 19 Aug 2019 18:15:46 +1000 Subject: [PATCH 2/6] Add custom error handling Adds a NewError function which creates a new error of the custom error type `Error` (contraining fields: `Message` and `Code`) with the given enum. Error enums for the package begin at 1001. Errors being handled: - Puppy value < 0 - Puppy ID not found Lab 07 (#589) --- 07_errors/patrickmarabeas/errors.go | 32 ++++++++++++++++++++++++ 07_errors/patrickmarabeas/errors_test.go | 22 ++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 07_errors/patrickmarabeas/errors.go create mode 100644 07_errors/patrickmarabeas/errors_test.go diff --git a/07_errors/patrickmarabeas/errors.go b/07_errors/patrickmarabeas/errors.go new file mode 100644 index 000000000..ad2869182 --- /dev/null +++ b/07_errors/patrickmarabeas/errors.go @@ -0,0 +1,32 @@ +package main + +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"} + } +} diff --git a/07_errors/patrickmarabeas/errors_test.go b/07_errors/patrickmarabeas/errors_test.go new file mode 100644 index 000000000..baec25eac --- /dev/null +++ b/07_errors/patrickmarabeas/errors_test.go @@ -0,0 +1,22 @@ +package main + +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") +} From c52f22a410e592891c784a704dfa9949c7dd4d44 Mon Sep 17 00:00:00 2001 From: Patrick Marabeas Date: Mon, 19 Aug 2019 18:20:30 +1000 Subject: [PATCH 3/6] Extends Storer interface for all methods to also return an error In order to implement the Storer interface, all methods must now return and error. The value property in Puppy has had its type changed to an int to represent a numerical cent figure so that it may be properly throw an error if the value supplied is less than 0. Lab 07 (#589) --- 07_errors/patrickmarabeas/types.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/07_errors/patrickmarabeas/types.go b/07_errors/patrickmarabeas/types.go index bbf6907be..e157b1ece 100644 --- a/07_errors/patrickmarabeas/types.go +++ b/07_errors/patrickmarabeas/types.go @@ -1,21 +1,19 @@ package main -import ( - "sync" -) +import "sync" type Puppy struct { ID int Breed string Color string - Value string + Value int // cents } type Storer interface { - Create(puppy Puppy) int - Read(ID int) Puppy - Update(ID int, puppy Puppy) bool - Destroy(ID int) bool + Create(puppy Puppy) (int, error) + Read(ID int) (Puppy, error) + Update(ID int, puppy Puppy) (bool, error) + Destroy(ID int) (bool, error) } type MapStore struct { From 5fa43117e7112c71c37032d7efd83b30b4b7abc2 Mon Sep 17 00:00:00 2001 From: Patrick Marabeas Date: Mon, 19 Aug 2019 18:23:59 +1000 Subject: [PATCH 4/6] Implement custom error handling - Value < 0 in Create and Update - ID not found in Read, Update and Delete Lab 07 (#589) --- 07_errors/patrickmarabeas/mapStore.go | 29 +++++++----- 07_errors/patrickmarabeas/store_test.go | 59 ++++++++++++++++++------- 07_errors/patrickmarabeas/syncStore.go | 29 +++++++----- 3 files changed, 80 insertions(+), 37 deletions(-) diff --git a/07_errors/patrickmarabeas/mapStore.go b/07_errors/patrickmarabeas/mapStore.go index 187bb55d3..59b9aa312 100644 --- a/07_errors/patrickmarabeas/mapStore.go +++ b/07_errors/patrickmarabeas/mapStore.go @@ -9,43 +9,50 @@ func NewMapStore() Storer { } // Create increments the uuid and adds the provided Puppy struct to the store with this identifier. -func (store *MapStore) Create(puppy Puppy) int { +func (store *MapStore) Create(puppy Puppy) (int, error) { + if puppy.Value < 0 { + return -1, NewError(NegativeValue) + } + puppy.ID = store.uuid store.store[puppy.ID] = puppy store.uuid++ - return puppy.ID + 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) Puppy { +func (store *MapStore) Read(id int) (Puppy, error) { if _, ok := store.store[id]; ok { - return store.store[id] + return store.store[id], nil } - return Puppy{} + return Puppy{}, NewError(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 Puppy) bool { +func (store *MapStore) Update(id int, puppy Puppy) (bool, error) { if _, ok := store.store[id]; !ok { - return false + return false, NewError(IDNotFound) + } + if puppy.Value < 0 { + return false, NewError(NegativeValue) } puppy.ID = id store.store[id] = puppy - return true + 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 { +func (store *MapStore) Destroy(id int) (bool, error) { if _, ok := store.store[id]; !ok { - return false + return false, NewError(IDNotFound) } delete(store.store, id) - return true + return true, nil } diff --git a/07_errors/patrickmarabeas/store_test.go b/07_errors/patrickmarabeas/store_test.go index bb9899ed1..22a1c7a2e 100644 --- a/07_errors/patrickmarabeas/store_test.go +++ b/07_errors/patrickmarabeas/store_test.go @@ -14,70 +14,99 @@ type StoreSuite struct { func (suite *StoreSuite) TestCreate() { a := assert.New(suite.T()) - id := suite.store.Create(Puppy{Breed: "Wolf", Color: "Grey", Value: "450"}) + id, error := suite.store.Create(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 := suite.store.Create(Puppy{Breed: "Boxer", Color: "Brown", Value: "300"}) + id, error := suite.store.Create(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(Puppy{Breed: "Wolf", Color: "Grey", Value: -100}) + a.Equal(id, -1) + a.Equal(error, NewError(NegativeValue)) } func (suite *StoreSuite) TestRead() { a := assert.New(suite.T()) - data := suite.store.Read(0) - a.Equal(data, Puppy{ID: 0, Breed: "Wolf", Color: "Grey", Value: "450"}) + data, error := suite.store.Read(0) + a.Equal(data, Puppy{ID: 0, Breed: "Wolf", Color: "Grey", Value: 450}) + a.Equal(error, nil) } func (suite *StoreSuite) TestReadNonExistent() { a := assert.New(suite.T()) - success := suite.store.Read(100) + success, error := suite.store.Read(100) a.Equal(success, Puppy{}) + a.Equal(error, NewError(IDNotFound)) } func (suite *StoreSuite) TestUpdate() { a := assert.New(suite.T()) - success := suite.store.Update(0, Puppy{Breed: "Doberman", Color: "Black", Value: "500"}) - data := suite.store.Read(0) + success, error := suite.store.Update(0, Puppy{Breed: "Doberman", Color: "Black", Value: 500}) a.Equal(success, true) - a.Equal(data, Puppy{ID: 0, Breed: "Doberman", Color: "Black", Value: "500"}) + a.Equal(error, nil) + + data, error := suite.store.Read(0) + a.Equal(data, Puppy{ID: 0, Breed: "Doberman", Color: "Black", Value: 500}) + a.Equal(error, nil) } func (suite *StoreSuite) TestUpdateNonExistent() { a := assert.New(suite.T()) - success := suite.store.Update(100, Puppy{Breed: "Doberman", Color: "Black", Value: "500"}) + success, error := suite.store.Update(100, Puppy{Breed: "Doberman", Color: "Black", Value: 500}) + a.Equal(success, false) + a.Equal(error, NewError(IDNotFound)) +} + +func (suite *StoreSuite) TestUpdateNegativeNumber() { + a := assert.New(suite.T()) + + success, error := suite.store.Update(0, Puppy{Breed: "Doberman", Color: "Black", Value: -500}) a.Equal(success, false) + a.Equal(error, NewError(NegativeValue)) } func (suite *StoreSuite) TestDestroy() { a := assert.New(suite.T()) - success := suite.store.Destroy(1) - data := suite.store.Read(1) + success, error := suite.store.Destroy(1) a.Equal(success, true) + a.Equal(error, nil) + + data, error := suite.store.Read(1) a.Equal(data, Puppy{}) + a.Equal(error, NewError(IDNotFound)) } func (suite *StoreSuite) TestDestroyNonExistent() { a := assert.New(suite.T()) - success := suite.store.Destroy(100) + success, error := suite.store.Destroy(100) a.Equal(success, false) + a.Equal(error, NewError(IDNotFound)) } func (suite *StoreSuite) TestIdIncrementOnDelete() { a := assert.New(suite.T()) - id := suite.store.Create(Puppy{Breed: "Greyhound", Color: "Light Brown", Value: "700"}) - suite.store.Destroy(id) - newID := suite.store.Create(Puppy{Breed: "Greyhound", Color: "Light Brown", Value: "700"}) + id, _ := suite.store.Create(Puppy{Breed: "Greyhound", Color: "Light Brown", Value: 700}) + success, _ := suite.store.Destroy(id) + a.Equal(success, true) + newID, _ := suite.store.Create(Puppy{Breed: "Greyhound", Color: "Light Brown", Value: 700}) a.Equal(newID, 3) } diff --git a/07_errors/patrickmarabeas/syncStore.go b/07_errors/patrickmarabeas/syncStore.go index 47dc770f5..12f1a529a 100644 --- a/07_errors/patrickmarabeas/syncStore.go +++ b/07_errors/patrickmarabeas/syncStore.go @@ -8,43 +8,50 @@ func NewSyncStore() Storer { } // Create increments the uuid and adds the provided Puppy struct to the store with this identifier. -func (store *SyncStore) Create(puppy Puppy) int { +func (store *SyncStore) Create(puppy Puppy) (int, error) { + if puppy.Value < 0 { + return -1, NewError(NegativeValue) + } + puppy.ID = store.uuid store.Store(puppy.ID, puppy) store.uuid++ - return puppy.ID + 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) Puppy { +func (store *SyncStore) Read(id int) (Puppy, error) { if value, ok := store.Load(id); ok { - return value.(Puppy) + return value.(Puppy), nil } - return Puppy{} + return Puppy{}, NewError(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 Puppy) bool { +func (store *SyncStore) Update(id int, puppy Puppy) (bool, error) { if _, ok := store.Load(id); !ok { - return false + return false, NewError(IDNotFound) + } + if puppy.Value < 0 { + return false, NewError(NegativeValue) } puppy.ID = id store.Store(id, puppy) - return true + 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 { +func (store *SyncStore) Destroy(id int) (bool, error) { if _, ok := store.Load(id); !ok { - return false + return false, NewError(IDNotFound) } store.Delete(id) - return true + return true, nil } From 8efff56b4d8a646ec742ff2020b85d3d6e775dd2 Mon Sep 17 00:00:00 2001 From: Patrick Marabeas Date: Mon, 19 Aug 2019 19:09:16 +1000 Subject: [PATCH 5/6] Add locking to use of sync.Map --- 07_errors/patrickmarabeas/syncStore.go | 8 ++++++++ 07_errors/patrickmarabeas/types.go | 1 + 2 files changed, 9 insertions(+) diff --git a/07_errors/patrickmarabeas/syncStore.go b/07_errors/patrickmarabeas/syncStore.go index 12f1a529a..c1015ae08 100644 --- a/07_errors/patrickmarabeas/syncStore.go +++ b/07_errors/patrickmarabeas/syncStore.go @@ -13,9 +13,11 @@ func (store *SyncStore) Create(puppy Puppy) (int, error) { return -1, NewError(NegativeValue) } + store.Lock() puppy.ID = store.uuid store.Store(puppy.ID, puppy) store.uuid++ + store.Unlock() return puppy.ID, nil } @@ -23,9 +25,11 @@ func (store *SyncStore) Create(puppy Puppy) (int, error) { // 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) (Puppy, error) { + store.RLock() if value, ok := store.Load(id); ok { return value.(Puppy), nil } + store.RUnlock() return Puppy{}, NewError(IDNotFound) } @@ -42,6 +46,7 @@ func (store *SyncStore) Update(id int, puppy Puppy) (bool, error) { puppy.ID = id store.Store(id, puppy) + return true, nil } @@ -52,6 +57,9 @@ func (store *SyncStore) Destroy(id int) (bool, error) { return false, NewError(IDNotFound) } + store.Lock() store.Delete(id) + store.Unlock() + return true, nil } diff --git a/07_errors/patrickmarabeas/types.go b/07_errors/patrickmarabeas/types.go index e157b1ece..a4a4079cd 100644 --- a/07_errors/patrickmarabeas/types.go +++ b/07_errors/patrickmarabeas/types.go @@ -24,4 +24,5 @@ type MapStore struct { type SyncStore struct { uuid int sync.Map + sync.RWMutex } From e6dbc8dbc43a87d975ce357902e866845d2f2bdd Mon Sep 17 00:00:00 2001 From: Patrick Marabeas Date: Mon, 19 Aug 2019 19:21:04 +1000 Subject: [PATCH 6/6] Add dummy output to main Empty main has a code coverage of 0%, unlike lab-06. --- 07_errors/patrickmarabeas/main.go | 10 +++++++++- 07_errors/patrickmarabeas/main_test.go | 6 +----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/07_errors/patrickmarabeas/main.go b/07_errors/patrickmarabeas/main.go index 790580777..13eff90e6 100644 --- a/07_errors/patrickmarabeas/main.go +++ b/07_errors/patrickmarabeas/main.go @@ -1,5 +1,13 @@ package main -func main() { +import ( + "fmt" + "io" + "os" +) + +var out io.Writer = os.Stdout +func main() { + fmt.Fprint(out, "hello") } diff --git a/07_errors/patrickmarabeas/main_test.go b/07_errors/patrickmarabeas/main_test.go index c98b5b046..23ac60697 100644 --- a/07_errors/patrickmarabeas/main_test.go +++ b/07_errors/patrickmarabeas/main_test.go @@ -2,20 +2,16 @@ package main import ( "bytes" - "io" - "os" "testing" ) -var out io.Writer = os.Stdout - func TestMainOutput(t *testing.T) { var buf bytes.Buffer out = &buf main() - expected := "" + expected := "hello" got := buf.String()