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

Kadai4 simady #35

Open
wants to merge 5 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
17 changes: 17 additions & 0 deletions kadai4/simady/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# 課題4

## 内容
おみくじAPI
- JSON形式でおみくじの結果を返す
- 正月 (1/1-1/3) だけ大吉にする
- ハンドラのテストを書いてみる

## 実行方法

```
go run cmd/main.go
```

## 所感
アーキテクチャを少しだけ考慮してみました。
Middleware等のテストをどのように実施するのがいいか悩みました。
9 changes: 9 additions & 0 deletions kadai4/simady/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package main

import (
"omikuji-app/pkg/api"
)

func main() {
api.Serve(":8081")
}
3 changes: 3 additions & 0 deletions kadai4/simady/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module omikuji-app

go 1.12
28 changes: 28 additions & 0 deletions kadai4/simady/pkg/api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package api

import (
"fmt"
"net/http"

"os"

omikujiHandler "omikuji-app/pkg/api/app/handler/omikuji"
omikujiInteractor "omikuji-app/pkg/api/app/interactor/omikuji"
"omikuji-app/pkg/api/app/middleware"
"omikuji-app/pkg/api/app/presenter"
omikujiService "omikuji-app/pkg/api/domain/service/omikuji"
)

func Serve(addr string) {
jsonPresenter := presenter.New()
service := omikujiService.New()
interactor := omikujiInteractor.New(jsonPresenter, service)
handler := omikujiHandler.New(interactor)
http.Handle("/", middleware.With(handler, middleware.ContextMiddleWare{}, middleware.ResponseHeaderMiddleWare{}))
http.Handle("/favicon.ico", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Favicon is not set.", http.StatusNotFound)
}))
if err := http.ListenAndServe(addr, nil); err != nil {
fmt.Fprintf(os.Stderr, "Failed to serve. err = %v\n", err)
}
}
27 changes: 27 additions & 0 deletions kadai4/simady/pkg/api/app/handler/omikuji/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package omikuji

import (
"fmt"
"net/http"
"os"

interactor "omikuji-app/pkg/api/app/interactor/omikuji"
)

type omikujiHandler struct {
omikujiInteractor interactor.OmikujiInteractor
}

func New(i interactor.OmikujiInteractor) http.Handler {
return &omikujiHandler{omikujiInteractor: i}
}

func (h *omikujiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
res, err := h.omikujiInteractor.Draw(r.Context())
if err != nil {
fmt.Fprintf(os.Stderr, "抽選に失敗しました. err = %v\n", err)
http.Error(w, "抽選に失敗しました.", http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "%s", res)
}
103 changes: 103 additions & 0 deletions kadai4/simady/pkg/api/app/handler/omikuji/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package omikuji

import (
"context"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"

interactor "omikuji-app/pkg/api/app/interactor/omikuji"
mockInteractor "omikuji-app/pkg/api/app/interactor/omikuji/mock"
"omikuji-app/pkg/api/ocontext"
)

func TestNew(t *testing.T) {
type args struct {
i interactor.OmikujiInteractor
}
tests := []struct {
name string
args args
want http.Handler
}{
{
name: "omikujiHandlerの生成",
args: args{
i: mockInteractor.New(),
},
want: &omikujiHandler{omikujiInteractor: mockInteractor.New()},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := New(tt.args.i); !reflect.DeepEqual(got, tt.want) {
t.Errorf("New() = %v, want %v", got, tt.want)
}
})
}
}

func Test_omikujiHandler_ServeHTTP(t *testing.T) {
type fields struct {
omikujiInteractor interactor.OmikujiInteractor
}
type args struct {
w http.ResponseWriter
r *http.Request
}
tests := []struct {
name string
fields fields
args args
want string
wantCode int
}{
{
name: "リクエスト成功",
fields: fields{
omikujiInteractor: mockInteractor.New(),
},
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest("GET", "/", nil).WithContext(ocontext.SetAccessTime(context.Background(), time.Now())),
},
want: "{\"id\":4,\"ruck\":\"吉\",\"message\":\"吉です!良い運勢ですね!\"}\n",
wantCode: http.StatusOK,
},
{
name: "リクエスト失敗",
fields: fields{
omikujiInteractor: mockInteractor.NewError(),
},
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest("GET", "/", nil).WithContext(ocontext.SetAccessTime(context.Background(), time.Now())),
},
want: "抽選に失敗しました.\n",
wantCode: http.StatusInternalServerError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := &omikujiHandler{
omikujiInteractor: tt.fields.omikujiInteractor,
}
h.ServeHTTP(tt.args.w, tt.args.r)
rw := tt.args.w.(*httptest.ResponseRecorder).Result()
defer rw.Body.Close()
if rw.StatusCode != tt.wantCode {
t.Errorf("unexpected status code: %v, want %v", rw.StatusCode, tt.wantCode)
}
b, err := ioutil.ReadAll(rw.Body)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if s := string(b); s != tt.want {
t.Errorf("unexpected response: %v, want %v", s, tt.want)
}
})
}
}
30 changes: 30 additions & 0 deletions kadai4/simady/pkg/api/app/interactor/omikuji/interactor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package interactor

import (
"context"

"omikuji-app/pkg/api/app/presenter"
service "omikuji-app/pkg/api/domain/service/omikuji"
)

type OmikujiInteractor interface {
Draw(ctx context.Context) (string, error)
}

type omikujiInteractor struct {
presenter presenter.Presenter
omikujiService service.OmikujiService
}

func New(p presenter.Presenter, s service.OmikujiService) OmikujiInteractor {
return &omikujiInteractor{presenter: p, omikujiService: s}
}

func (i *omikujiInteractor) Draw(ctx context.Context) (string, error) {
rs := i.omikujiService.Draw(ctx)
output, err := i.presenter.Output(rs)
if err != nil {
return "", err
}
return output, nil
}
104 changes: 104 additions & 0 deletions kadai4/simady/pkg/api/app/interactor/omikuji/interactor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package interactor

import (
"context"
"fmt"
"reflect"
"testing"

"omikuji-app/pkg/api/app/presenter"
mockPresenter "omikuji-app/pkg/api/app/presenter/mock"
entity "omikuji-app/pkg/api/domain/entity/omikuji"
service "omikuji-app/pkg/api/domain/service/omikuji"
mockService "omikuji-app/pkg/api/domain/service/omikuji/mock"
)

func TestNew(t *testing.T) {
type args struct {
p presenter.Presenter
s service.OmikujiService
}
tests := []struct {
name string
args args
want OmikujiInteractor
}{
{
name: "omikujiInteractorの生成",
args: args{
p: mockPresenter.New(),
s: mockService.New(),
},
want: &omikujiInteractor{presenter: mockPresenter.New(), omikujiService: mockService.New()},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := New(tt.args.p, tt.args.s); !reflect.DeepEqual(got, tt.want) {
t.Errorf("New() = %v, want %v", got, tt.want)
}
})
}
}

func Test_omikujiInteractor_Draw(t *testing.T) {
type fields struct {
presenter presenter.Presenter
omikujiService service.OmikujiService
}
type args struct {
ctx context.Context
}
tests := []struct {
name string
fields fields
args args
want string
wantErr bool
}{
{
name: "正常終了",
fields: fields{
presenter: mockPresenter.New(),
omikujiService: mockService.New(),
},
args: args{
ctx: context.Background(),
},
want: fmt.Sprintf("output: %v", entity.OmikujiResult{
ID: 4,
Ruck: "吉",
Message: "吉です!良い運勢ですね!",
}),
wantErr: false,
},
{
name: "エラー発生",
fields: fields{
presenter: mockPresenter.NewError(),
omikujiService: mockService.New(),
},
args: args{
ctx: context.Background(),
},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
i := &omikujiInteractor{
presenter: tt.fields.presenter,
omikujiService: tt.fields.omikujiService,
}
got, err := i.Draw(tt.args.ctx)
if (err != nil) != tt.wantErr {
t.Errorf("omikujiInteractor.Draw() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("omikujiInteractor.Draw() = %v, want %v", got, tt.want)
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package mock

import (
"context"

interactor "omikuji-app/pkg/api/app/interactor/omikuji"
)

type mockInteractor struct {
}

func New() interactor.OmikujiInteractor {
return &mockInteractor{}
}

func (i *mockInteractor) Draw(ctx context.Context) (string, error) {
return "{\"id\":4,\"ruck\":\"吉\",\"message\":\"吉です!良い運勢ですね!\"}\n", nil
}

type mockErrorInteractor struct {
}

func NewError() interactor.OmikujiInteractor {
return &mockErrorInteractor{}
}

func (i *mockErrorInteractor) Draw(ctx context.Context) (string, error) {
return "", &MockError{}
}

type MockError struct{}

func (e *MockError) Error() string {
return "mock interactor error."
}
Loading