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

WIP: UC Testing 2 #11

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
7 changes: 6 additions & 1 deletion domains/usecases/authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,21 @@ func (uc AuthenticateUsecase) Build(params AuthenticateParam) domains.Unauthoriz
uc.OutputPort.Raise(err)
return
}
uc.Logger.Info(fmt.Sprintf("New session stored: %s", session.ID()))

event := UserAuthenticatedEvent{
UserID: auth.User().ID().String(),
CreatedAt: time.Now(),
}

// Eventのpublishに失敗しても意図的にエラーはレスポンスせずErrorのレポートのみとしておく
// 非同期処理のエラーは別途手動で復旧作業を行う
if err := uc.Publisher.Publish(event); err.NotNil() {
uc.Logger.Errorf(uc.Ctx, "Failed publishing event: %s", err.Error())
} else {
uc.Logger.Infof(uc.Ctx, "Event published: %s", event.ID())
}

uc.Logger.Infof(uc.Ctx, "Event published: %s", event.ID())
uc.OutputPort.Write(session)
})
}
Expand Down
89 changes: 89 additions & 0 deletions domains/usecases/authenticate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package usecases

import (
"go-cleanarchitecture/domains"
"go-cleanarchitecture/domains/errors"
"go-cleanarchitecture/domains/models"
"go-cleanarchitecture/domains/models/authentication"
"go-cleanarchitecture/domains/models/user"
"go-cleanarchitecture/testing/adapters"
"go-cleanarchitecture/testing/adapters/dao"
"testing"
"time"

"golang.org/x/xerrors"
)

type mockAuthenticateOutputPort struct {
Error errors.Domain
Result models.Session
}

func (m *mockAuthenticateOutputPort) Raise(err errors.Domain) {
m.Error = err
}

func (m *mockAuthenticateOutputPort) Write(session models.Session) {
m.Result = session
}

type mockAuthenticatePublisher struct {
event UserAuthenticatedEvent
}

func (p *mockAuthenticatePublisher) Publish(event domains.Event) errors.Domain {
p.event = event.(UserAuthenticatedEvent)
return errors.None
}

func TestAuthenticateUsecase(t *testing.T) {
newUsecase := func(
authDao domains.AuthenticationRepository,
sessionDao domains.SessionRepository,
op *mockAuthenticateOutputPort,
p *mockAuthenticatePublisher,
) AuthenticateUsecase {
return AuthenticateUsecase{
OutputPort: op,
AuthenticationDao: authDao,
SessionDao: sessionDao,
Logger: adapters.MockLogger{T: t},
Publisher: p,
}
}

t.Run("Validation", func(t *testing.T) {
// todo
})

t.Run("Store", func(t *testing.T) {
aDao := dao.NewMockAuthenticationDao()
aDao.GetByEmailResult = func() (models.Authentication, errors.Domain, bool) {
email, _ := authentication.NewEmail("test@example.com")
hash := authentication.NewHash("password")
a := models.NewAuthentication(email, hash, user.Name{})
return a, errors.None, true
}

op := &mockAuthenticateOutputPort{}
p := &mockAuthenticatePublisher{}
newUsecase(aDao, dao.NewMockSessionDao(), op, p).
Build(AuthenticateParam{
Email: "test@example.com",
Password: "password",
}).
Run()

if !xerrors.Is(op.Error, errors.None) {
t.Errorf("Unexpected error raised: %s", op.Error)
}

if op.Result.CreatedAt().Value() == (time.Time{}) {
t.Error("Error: invalid result")
}

if p.event == (UserAuthenticatedEvent{}) {
t.Error("Error: event not published")
}
})
}
6 changes: 5 additions & 1 deletion domains/usecases/create_todo.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,15 @@ func (uc CreateTodoUsecase) Build(params CreateTodoParam) domains.AuthorizedUsec
Description: newTodo.Description().Value(),
CreatedAt: time.Now(),
}

// Eventのpublishに失敗しても意図的にエラーはレスポンスせずErrorのレポートのみとしておく
// 非同期処理のエラーは別途手動で復旧作業を行う
if err := uc.Publisher.Publish(event); err.NotNil() {
uc.Logger.Errorf(uc.Ctx, "Failed publishing event: %s", err.Error())
} else {
uc.Logger.Infof(uc.Ctx, "Event published: %s", event.ID())
}

uc.Logger.Infof(uc.Ctx, "Event published: %s", event.ID())
uc.OutputPort.Write(newTodo)
})
}
Expand Down
108 changes: 78 additions & 30 deletions domains/usecases/create_todo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,65 +15,113 @@ import (
)

type mockCreateTodoOutputPort struct {
Result errors.Domain
Error errors.Domain
Result models.Todo
}

func (m *mockCreateTodoOutputPort) Raise(err errors.Domain) {
m.Result = err
m.Error = err
}

func (_ *mockCreateTodoOutputPort) Write(todo models.Todo) {}
func (m *mockCreateTodoOutputPort) Write(todo models.Todo) {
m.Result = todo
}

type mockCreateTodoPublisher struct {
event TodoCreatedEvent
}

func (p *mockCreateTodoPublisher) Publish(event domains.Event) errors.Domain {
p.event = event.(TodoCreatedEvent)
return errors.None
}

func TestCreateTodoUsecase(t *testing.T) {
newUsecase := func(
todoDao domains.TodoRepository,
todosDao domains.TodosRepository,
op *mockCreateTodoOutputPort,
p *mockCreateTodoPublisher,
) CreateTodoUsecase {
return CreateTodoUsecase{
Ctx: context.Background(),
OutputPort: op,
TodoDao: todoDao,
TodosDao: todosDao,
Logger: adapters.MockLogger{T: t},
Publisher: adapters.MockPublisher{},
Publisher: p,
}
}

t.Run(uc_TODO_NAME_NOT_UNIQUE.Reason(), func(t *testing.T) {
todoDao := dao.NewMockTodoDao()
todoDao.GetByNameResult = func() (models.Todo, errors.Domain, bool) {
name, _ := todo.NewName("testing todo")
description, _ := todo.NewDescription("this is a testing todo")
userID, _ := user.NewID("user_id")
todo := models.NewTodo(name, description, userID)
return todo, errors.None, true
}
t.Run("Validation", func(t *testing.T) {
t.Run(uc_TODO_NAME_NOT_UNIQUE.Reason(), func(t *testing.T) {
todoDao := dao.NewMockTodoDao()
todoDao.GetByNameResult = func() (models.Todo, errors.Domain, bool) {
name, _ := todo.NewName("testing todo")
description, _ := todo.NewDescription("this is a testing todo")
userID, _ := user.NewID("user_id")
todo := models.NewTodo(name, description, userID)
return todo, errors.None, true
}

op := &mockCreateTodoOutputPort{}
newUsecase(todoDao, dao.NewMockTodosDao(), op).
Build(CreateTodoParam{Name: "testing todo", Description: "this is a testing todo"}).
Run(adapters.MockAuthorizer{})
op := &mockCreateTodoOutputPort{}
p := &mockCreateTodoPublisher{}
newUsecase(todoDao, dao.NewMockTodosDao(), op, p).
Build(CreateTodoParam{Name: "testing todo", Description: "this is a testing todo"}).
Run(adapters.MockAuthorizer{})

if !xerrors.Is(op.Result, uc_TODO_NAME_NOT_UNIQUE) {
t.Errorf("Unexpected validation error (caught: %s)", op.Result.Error())
}
if !xerrors.Is(op.Error, uc_TODO_NAME_NOT_UNIQUE) {
t.Errorf("Unexpected validation error (caught: %s)", op.Error)
}

if p.event != (TodoCreatedEvent{}) {
t.Error("Error: event published")
}
})

t.Run(uc_MAXIMUM_TODOS_REACHED.Reason(), func(t *testing.T) {
todosDao := dao.NewMockTodosDao()
todosDao.GetByUserIDResult = func() (models.Todos, errors.Domain) {
todos := make([]models.Todo, 100)
return models.NewTodos(todos), errors.None
}

op := &mockCreateTodoOutputPort{}
p := &mockCreateTodoPublisher{}
newUsecase(dao.NewMockTodoDao(), todosDao, op, p).
Build(CreateTodoParam{Name: "testing todo", Description: "this is a testing todo"}).
Run(adapters.MockAuthorizer{})

if !xerrors.Is(op.Error, uc_MAXIMUM_TODOS_REACHED) {
t.Errorf("Unexpected validation error (caught: %s)", op.Error)
}

if p.event != (TodoCreatedEvent{}) {
t.Error("Error: event published")
}
})
})

t.Run(uc_MAXIMUM_TODOS_REACHED.Reason(), func(t *testing.T) {
todosDao := dao.NewMockTodosDao()
todosDao.GetByUserIDResult = func() (models.Todos, errors.Domain) {
todos := make([]models.Todo, 100)
return models.NewTodos(todos), errors.None
}
t.Run("Store", func(t *testing.T) {
name, _ := todo.NewName("testing todo")
description, _ := todo.NewDescription("this is a testing todo")

op := &mockCreateTodoOutputPort{}
newUsecase(dao.NewMockTodoDao(), todosDao, op).
Build(CreateTodoParam{Name: "testing todo", Description: "this is a testing todo"}).
p := &mockCreateTodoPublisher{}
newUsecase(dao.NewMockTodoDao(), dao.NewMockTodosDao(), op, p).
Build(CreateTodoParam{Name: name.Value(), Description: description.Value()}).
Run(adapters.MockAuthorizer{})

if !xerrors.Is(op.Result, uc_MAXIMUM_TODOS_REACHED) {
t.Errorf("Unexpected validation error (caught: %s)", op.Result.Error())
if !xerrors.Is(op.Error, errors.None) {
t.Errorf("Unexpected error raised: %s", op.Error)
}

if op.Result.Name() != name || op.Result.Description() != description {
t.Error("Error: invalid result")
}

if p.event == (TodoCreatedEvent{}) {
t.Error("Error: event not published")
}
})
}
8 changes: 7 additions & 1 deletion domains/usecases/signup.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,24 @@ func (uc SignupUsecase) Build(params SignupParam) domains.UnauthorizedUsecase {
uc.OutputPort.Raise(err)
return
}
uc.Logger.Info(fmt.Sprintf("New authentication created: %s", auth.Email().Value()))
uc.Logger.Info(fmt.Sprintf("Created UserID: %s", auth.User().ID()))

event := UserSignedUpEvent{
UserID: auth.User().ID().String(),
Email: auth.Email().Value(),
Name_: auth.User().Name().Value(),
CreatedAt: time.Now(),
}

// Eventのpublishに失敗しても意図的にエラーはレスポンスせずErrorのレポートのみとしておく
// 非同期処理のエラーは別途手動で復旧作業を行う
if err := uc.Publisher.Publish(event); err.NotNil() {
uc.Logger.Errorf(uc.Ctx, "Failed publishing event: %s", err.Error())
} else {
uc.Logger.Infof(uc.Ctx, "Event published: %s", event.ID())
}

uc.Logger.Infof(uc.Ctx, "Event published: %s", event.ID())
uc.OutputPort.Write(auth)
})
}
Expand Down
78 changes: 78 additions & 0 deletions domains/usecases/signup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package usecases

import (
"go-cleanarchitecture/domains"
"go-cleanarchitecture/domains/errors"
"go-cleanarchitecture/domains/models"
"go-cleanarchitecture/testing/adapters"
"go-cleanarchitecture/testing/adapters/dao"
"testing"
"time"

"golang.org/x/xerrors"
)

type mockSignupOutputPort struct {
Error errors.Domain
Result models.Authentication
}

func (m *mockSignupOutputPort) Raise(err errors.Domain) {
m.Error = err
}

func (m *mockSignupOutputPort) Write(auth models.Authentication) {
m.Result = auth
}

type mockSignupPublisher struct {
event UserSignedUpEvent
}

func (p *mockSignupPublisher) Publish(event domains.Event) errors.Domain {
p.event = event.(UserSignedUpEvent)
return errors.None
}

func TestSignupUsecase(t *testing.T) {
newUsecase := func(
authDao domains.AuthenticationRepository,
op *mockSignupOutputPort,
p *mockSignupPublisher,
) SignupUsecase {
return SignupUsecase{
OutputPort: op,
AuthenticationDao: authDao,
Logger: adapters.MockLogger{T: t},
Publisher: p,
}
}

t.Run("Validation", func(t *testing.T) {
// todo
})

t.Run("Store", func(t *testing.T) {
op := &mockSignupOutputPort{}
p := &mockSignupPublisher{}
newUsecase(dao.NewMockAuthenticationDao(), op, p).
Build(SignupParam{
Email: "test@example.com",
Password: "password",
UserName: "test",
}).
Run()

if !xerrors.Is(op.Error, errors.None) {
t.Errorf("Unexpected error raised: %s", op.Error)
}

if op.Result.CreatedAt().Value() == (time.Time{}) {
t.Error("Error: invalid result")
}

if p.event == (UserSignedUpEvent{}) {
t.Error("Error: event not published")
}
})
}
Loading