Skip to content

Commit 5f7bde2

Browse files
author
hitakats
committed
kadai4 tk3fftk
1 parent 1153a9d commit 5f7bde2

File tree

6 files changed

+275
-0
lines changed

6 files changed

+275
-0
lines changed

kadai4/tk3fftk/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## おみくじAPIを作ってみよう
2+
### 制約
3+
- [x] JSON形式でおみくじの結果を返す
4+
- [x] 正月(1/1-1/3)だけ大吉にする
5+
- テストにて動作確認しています
6+
- [x] ハンドラのテストを書いてみる
7+
8+
### 使い方
9+
```
10+
$ go build -o lot
11+
$ ./lot &
12+
$ curl localhost:8080/lot
13+
{"result":"大凶"}
14+
```

kadai4/tk3fftk/main.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package main
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/gopherdojo/dojo3/kadai4/tk3fftk/omikuji"
7+
)
8+
9+
func main() {
10+
handler := omikuji.New(nil)
11+
http.Handle("/lot", &handler)
12+
http.ListenAndServe(":8080", nil)
13+
}

kadai4/tk3fftk/omikuji/handler.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package omikuji
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
"time"
9+
)
10+
11+
const bigLuckey = "大吉"
12+
13+
type response struct {
14+
Result string `json:"result"`
15+
}
16+
17+
type Clock interface {
18+
Now() time.Time
19+
}
20+
21+
type ClockFunc func() time.Time
22+
23+
func (f ClockFunc) Now() time.Time {
24+
return f()
25+
}
26+
27+
type OmikujiHandler struct {
28+
Clock Clock
29+
Omikuji Omikuji
30+
}
31+
32+
func New(clock Clock) OmikujiHandler {
33+
o := NewOmikuji()
34+
return OmikujiHandler{
35+
Clock: clock,
36+
Omikuji: o,
37+
}
38+
}
39+
40+
func (o *OmikujiHandler) createResponseJSON(result string) (string, error) {
41+
res := &response{Result: result}
42+
var buf bytes.Buffer
43+
enc := json.NewEncoder(&buf)
44+
if err := enc.Encode(res); err != nil {
45+
return "", err
46+
}
47+
return buf.String(), nil
48+
}
49+
50+
func (o *OmikujiHandler) handlerFunc(w http.ResponseWriter, r *http.Request) {
51+
w.Header().Set("Content-Type", "application/json; charset=utf-8")
52+
53+
var res string
54+
var err error
55+
56+
if o.isNewYear() {
57+
res, err = o.createResponseJSON(bigLuckey)
58+
if err != nil {
59+
http.Error(w, err.Error(), http.StatusInternalServerError)
60+
}
61+
} else {
62+
lot := o.Omikuji.Do([]string{})
63+
res, err = o.createResponseJSON(lot)
64+
if err != nil {
65+
http.Error(w, err.Error(), http.StatusInternalServerError)
66+
}
67+
}
68+
fmt.Fprintf(w, res)
69+
}
70+
71+
func (o *OmikujiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
72+
o.handlerFunc(w, r)
73+
}
74+
75+
func (o *OmikujiHandler) now() time.Time {
76+
if o.Clock == nil {
77+
return time.Now()
78+
}
79+
return o.Clock.Now()
80+
}
81+
82+
func (o *OmikujiHandler) isNewYear() bool {
83+
_, m, d := o.now().Date()
84+
if m == 1 && d <= 3 {
85+
return true
86+
}
87+
return false
88+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package omikuji
2+
3+
import (
4+
"io/ioutil"
5+
"net/http"
6+
"net/http/httptest"
7+
"strings"
8+
"testing"
9+
"time"
10+
)
11+
12+
func TestIsNewYear(t *testing.T) {
13+
cases := map[string]struct {
14+
month int
15+
day int
16+
expected bool
17+
}{
18+
"December31st": {
19+
12,
20+
31,
21+
false,
22+
},
23+
"January1st": {
24+
1,
25+
1,
26+
true,
27+
},
28+
"January2nd": {
29+
1, 2, true,
30+
},
31+
"January3rd": {
32+
1, 3, true,
33+
},
34+
"January4th": {
35+
1, 4, false,
36+
},
37+
}
38+
39+
for k, v := range cases {
40+
k := k
41+
v := v
42+
43+
t.Run(k, func(t *testing.T) {
44+
t.Helper()
45+
handler := New(
46+
ClockFunc(func() time.Time {
47+
return time.Date(2018, time.Month(v.month), v.day, 6, 0, 0, 0, time.Local)
48+
}),
49+
)
50+
51+
if handler.isNewYear() != v.expected {
52+
t.Errorf("expected=%v, actual=%v", v.expected, !v.expected)
53+
}
54+
})
55+
}
56+
}
57+
58+
func TestCreateResponseJSON(t *testing.T) {
59+
handler := New(nil)
60+
result := "test"
61+
expected := "{\"result\":\"test\"}\n"
62+
json, err := handler.createResponseJSON(result)
63+
if err != nil {
64+
t.Fatal("should not come here")
65+
}
66+
if strings.Compare(json, expected) != 0 {
67+
t.Errorf("expected='%v', actual='%v'", expected, json)
68+
}
69+
}
70+
71+
func TestHandler(t *testing.T) {
72+
handler := New(ClockFunc(func() time.Time {
73+
return time.Date(2018, 1, 1, 6, 0, 0, 0, time.Local)
74+
}))
75+
76+
w := httptest.NewRecorder()
77+
r := httptest.NewRequest("GET", "/", nil)
78+
handler.handlerFunc(w, r)
79+
rw := w.Result()
80+
defer rw.Body.Close()
81+
82+
if rw.StatusCode != http.StatusOK {
83+
t.Fatal("unexpected status code")
84+
}
85+
86+
b, err := ioutil.ReadAll(rw.Body)
87+
if err != nil {
88+
t.Fatal("unexpected error")
89+
}
90+
expected := "{\"result\":\"大吉\"}\n"
91+
92+
if s := string(b); s != expected {
93+
t.Errorf("expected='%v', actual='%v'", expected, s)
94+
}
95+
}

kadai4/tk3fftk/omikuji/omikuji.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package omikuji
2+
3+
import (
4+
"math/rand"
5+
"time"
6+
)
7+
8+
var defaultLots = []string{
9+
"大吉",
10+
"吉",
11+
"中吉",
12+
"小吉",
13+
"凶",
14+
"大凶",
15+
}
16+
17+
type Omikuji struct {
18+
rand *rand.Rand
19+
}
20+
21+
func NewOmikuji() Omikuji {
22+
r := rand.New(rand.NewSource(time.Now().UnixNano()))
23+
return Omikuji{
24+
rand: r,
25+
}
26+
}
27+
28+
func (o *Omikuji) Do(lots []string) string {
29+
l := len(lots)
30+
if l != 0 {
31+
return lots[rand.Intn(l)]
32+
}
33+
34+
return defaultLots[rand.Intn(len(defaultLots))]
35+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package omikuji
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestOmikuji_Do(t *testing.T) {
9+
cases := map[string][]string{
10+
"default": defaultLots,
11+
"one": {"大吉"},
12+
"two": {"大吉", "大凶"},
13+
}
14+
15+
o := NewOmikuji()
16+
17+
for c := range cases {
18+
c := c
19+
t.Run(c, func(t *testing.T) {
20+
lots := cases[c]
21+
lot := o.Do(lots)
22+
for _, l := range lots {
23+
if strings.Compare(lot, l) == 0 {
24+
return
25+
}
26+
}
27+
t.Errorf("%v should be included in %v", lot, lots)
28+
})
29+
}
30+
}

0 commit comments

Comments
 (0)