Skip to content

Commit

Permalink
new style error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
CreatCodeBuild committed Oct 16, 2018
1 parent 04d7011 commit 910ffec
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 10 deletions.
File renamed without changes.
23 changes: 23 additions & 0 deletions core/adapter/experiment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package adapter

import (
"fmt"

"Growth/core/entity"
)

type ExperimentStore interface {
Save(entity.Experiment) entity.Experiment
FetchByID(id entity.ID) entity.Experiment
ErrNotFound() *ErrExperimentNotFound
ErrOther() error
}

type ErrExperimentNotFound struct {
ID entity.ID
Stack string
}

func (e *ErrExperimentNotFound) Error() string {
return fmt.Sprintf("experiment:%d not found", e.ID)
}
File renamed without changes.
61 changes: 61 additions & 0 deletions core/adapter/testadapter/test_experiment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package testadapter

import (
"runtime/debug"

"Growth/core/adapter"
"Growth/core/entity"
)

type FakeExperimentStore struct {
experiments []entity.Experiment
errNotFound *adapter.ErrExperimentNotFound
errOther error
}

func (store *FakeExperimentStore) Save(e entity.Experiment) entity.Experiment {
store.init()
e.ID = entity.ID(len(store.experiments) + 1)
store.experiments = append(store.experiments, e)
return e
}

func (store *FakeExperimentStore) FetchByID(id entity.ID) entity.Experiment {
for _, e := range store.experiments {
if e.ID == id {
return e
}
}
store.errNotFound =&adapter.ErrExperimentNotFound{ID: id, Stack: string(debug.Stack())}
return entity.Experiment{}
}

func (store *FakeExperimentStore) ErrNotFound() *adapter.ErrExperimentNotFound {
defer func() {
store.errNotFound = nil
}()
return store.errNotFound
}

func (store *FakeExperimentStore) ErrNotFoundTrace() *adapter.ErrExperimentNotFound {
defer func() {
store.errNotFound = nil
}()
return store.errNotFound
}

func (store *FakeExperimentStore) ErrOther() error {
defer func() {
store.errOther = nil
}()
return store.errOther
}

func (store *FakeExperimentStore) init() {
if store == nil {
*store = FakeExperimentStore{}
}
if store.experiments == nil {
store.experiments = make([]entity.Experiment, 0)
}
}
86 changes: 86 additions & 0 deletions core/usecase/evenet_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package usecase

import (
"testing"

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

type EventType int

type Event struct {
Type EventType
Data interface{}
}

type EventBus struct {
qs map[EventType][]chan Out
}

func (bus *EventBus) init() {
if bus.qs == nil {
bus.qs = make(map[EventType][]chan Out)
}
}

func (bus *EventBus) Emit(e Event) error {
bus.init()
for _, out := range bus.qs[e.Type] {
go func(out chan Out) {
out <- Out{
Data: e.Data,
Error: nil,
}
}(out)
}
return nil
}

type Out struct {
Data interface{}
Error error
}

func (bus *EventBus) On(et EventType) chan Out {
bus.init()
// register a queue for this listener
outs := bus.qs[et]
out := make(chan Out)
bus.qs[et] = append(outs, out)
return out
}

const (
EventX EventType = iota
EventY
EventZ
)

func TestX(t *testing.T) {
defer func() {
recover()
}()

bus := &EventBus{}

xChan := bus.On(EventX)
xChan2 := bus.On(EventX)

bus.Emit(Event{
Type: EventType(EventX),
Data: "1",
})
bus.Emit(Event{
Type: EventType(EventX),
Data: "1",
})

out := <-xChan
require.Equal(t, "1", out.Data)
require.Equal(t, nil, out.Error)

out = <-xChan2
require.Equal(t, "1", out.Data)
require.Equal(t, nil, out.Error)

}
2 changes: 1 addition & 1 deletion core/usecase/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestFetchEventById(t *testing.T) {

t.Run("event not found when store is empty", func(t *testing.T) {
e, err := FetchEventByID(1, eventStore)
require.IsType(t, &adapter.ErrEventNotFound{}, err)
require.IsType(t, &adapter.ErrEventNotFound{}, err) // todo: an error leak here, should define use case level error for it
require.Equal(t, "event:1 not found", err.Error())
require.Equal(t, entity.Event{}, e)
})
Expand Down
42 changes: 39 additions & 3 deletions core/usecase/experiment.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
package usecase

import "Growth/core/entity"
import (
"Growth/core/adapter"
"Growth/core/entity"
)

func CreateExperiment(userID entity.ID) (entity.Experiment, error) {
return entity.Experiment{Owner: userID}, nil
func NewExperiment(store adapter.ExperimentStore) *Experiment {
return &Experiment{store: store}
}

type Experiment struct {
store adapter.ExperimentStore
errExperimentNotFound *adapter.ErrExperimentNotFound
errOther error
}

func (e *Experiment) CreateExperiment(userID entity.ID) (entity.Experiment) {
exp := e.store.Save(entity.Experiment{Owner: userID})
e.errOther = e.store.ErrOther()
return exp
}

func (e *Experiment) FetchExperimentByID(id entity.ID) entity.Experiment {
exp := e.store.FetchByID(id)
e.errExperimentNotFound = e.store.ErrNotFound()
e.errOther = e.store.ErrOther()
return exp
}

func (e *Experiment) ErrExperimentNotFound() *adapter.ErrExperimentNotFound {
defer func() {
e.errExperimentNotFound = nil
}()
return e.errExperimentNotFound
}

func (e *Experiment) ErrOther() error {
defer func() {
e.errOther = nil
}()
return e.errOther
}
27 changes: 21 additions & 6 deletions core/usecase/experiment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,34 @@ import (
"testing"
"time"

"Growth/core/adapter/testadapter"
"Growth/core/entity"

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

func TestCreateExperiment(t *testing.T) {
t.Run("the owner of an experiment is correctly set", func(t *testing.T) {
func TestExperiment(t *testing.T) {
e := NewExperiment(&testadapter.FakeExperimentStore{})

t.Run("no data when the store is empty", func(t *testing.T) {
e.FetchExperimentByID(entity.ID(1))
require.Equal(t, entity.ID(1), e.ErrExperimentNotFound().ID)
})

t.Run("can find the same experiment which has been created", func(t *testing.T) {
rand.Seed(time.Now().Unix())
for i := 0; i < 10; i++ {
id := entity.ID(rand.Int63())
exp, err := CreateExperiment(id)
require.NoError(t, err)
require.Equal(t, id, exp.Owner)
userID := entity.ID(rand.Int63())
exp := e.CreateExperiment(userID)
require.NoError(t, e.ErrOther())
require.Equal(t, userID, exp.Owner)

exp2 := e.FetchExperimentByID(exp.ID)
require.Nil(t, e.ErrExperimentNotFound())
require.NoError(t, e.ErrOther())

require.Equal(t, exp2.ID, exp.ID, entity.ID(i+1))
require.Equal(t, userID, exp2.Owner)
}
})
}
1 change: 1 addition & 0 deletions dep/dep.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func (d *Dep) RelayHandler() http.Handler {
EventStore: d.EventStore,
},
}

h, err := server.RelayHandler(schema, r)
d.Err = errors.WithStack(err)
return h
Expand Down

0 comments on commit 910ffec

Please sign in to comment.