Skip to content

Commit c246917

Browse files
committed
add omikuji API server
1 parent 5416925 commit c246917

File tree

6 files changed

+257
-0
lines changed

6 files changed

+257
-0
lines changed

kadai4/zaneli/omikuji/README.md

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
## 課題4
2+
3+
おみくじAPI
4+
5+
### 使い方
6+
7+
ポートを指定しないで起動する。(デフォルト8080ポートが使用される。)
8+
9+
```sh
10+
go run main.go
11+
```
12+
13+
ポートを指定して起動する。
14+
15+
```sh
16+
go run main.go 8081
17+
```
18+
19+
アクセスする。
20+
21+
```
22+
> curl --dump-header - "http://localhost:8080/"
23+
HTTP/1.1 200 OK
24+
Content-Type: application/json; charset=UTF-8
25+
Date: Sat, 02 Jun 2018 17:52:03 GMT
26+
Content-Length: 52
27+
28+
{"result":"吉","date":"2018-06-03T02:52:03+09:00"}
29+
```
30+
31+
テストを実行する。
32+
33+
```
34+
> go test ./omikuji
35+
ok _/Users/zaneli/ws/dojo1/kadai4/zaneli/omikuji/omikuji 0.026s
36+
```
37+
38+
```
39+
> go test ./omikuji --cover
40+
ok _/Users/zaneli/ws/dojo1/kadai4/zaneli/omikuji/omikuji 0.026s coverage: 76.2% of statements
41+
```

kadai4/zaneli/omikuji/main.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net/http"
7+
"os"
8+
"strconv"
9+
10+
"./omikuji"
11+
)
12+
13+
func main() {
14+
port := 8080
15+
if len(os.Args) > 1 {
16+
p, err := strconv.Atoi(os.Args[1])
17+
if err != nil {
18+
log.Fatal(err)
19+
}
20+
port = p
21+
}
22+
23+
mux := http.NewServeMux()
24+
mux.Handle("/", omikuji.AddCurrentDateTime(http.HandlerFunc(omikuji.Handler)))
25+
http.ListenAndServe(fmt.Sprintf(":%d", port), mux)
26+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package omikuji
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"time"
7+
)
8+
9+
type contextKey string
10+
11+
const datetimeContextKey contextKey = "datetime"
12+
13+
// AddCurrentDateTime は Context に現在日時を設定する。
14+
// refer: https://gocodecloud.com/blog/2016/11/15/simple-golang-http-request-context-example/
15+
func AddCurrentDateTime(next http.Handler) http.Handler {
16+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
17+
date := time.Now().In(time.FixedZone("Asia/Tokyo", 9*60*60))
18+
ctx := context.WithValue(r.Context(), datetimeContextKey, date)
19+
next.ServeHTTP(w, r.WithContext(ctx))
20+
})
21+
}
8.24 MB
Binary file not shown.
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package omikuji
2+
3+
import (
4+
"encoding/json"
5+
"math/rand"
6+
"net/http"
7+
"time"
8+
)
9+
10+
var 大吉 = "大吉"
11+
var 中吉 = "中吉"
12+
var = "吉"
13+
var 小吉 = "小吉"
14+
var = "凶"
15+
var 結果 = []string{大吉, 中吉, , 小吉, }
16+
17+
// Result はレスポンスJSONの構造を表す。
18+
type Result struct {
19+
Result string `json:"result"`
20+
Date string `json:"date"`
21+
}
22+
23+
// Handler はおみくじの結果を返す。
24+
func Handler(w http.ResponseWriter, r *http.Request) {
25+
ctx := r.Context()
26+
v := ctx.Value(datetimeContextKey)
27+
if v == nil {
28+
v = time.Now()
29+
}
30+
date, ok := v.(time.Time)
31+
if !ok {
32+
date = time.Now()
33+
}
34+
35+
var result string
36+
if is三が日(date) {
37+
result = 大吉
38+
} else {
39+
rand.Shuffle(len(結果), func(i, j int) {
40+
結果[i], 結果[j] = 結果[j], 結果[i]
41+
})
42+
result = 結果[0]
43+
}
44+
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
45+
json.NewEncoder(w).Encode(Result{Result: result, Date: date.Format(time.RFC3339)})
46+
}
47+
48+
func is三が日(date time.Time) bool {
49+
_, m, d := date.Date()
50+
return m == time.January && (d == 1 || d == 2 || d == 3)
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package omikuji
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"io/ioutil"
7+
"math/rand"
8+
"net/http"
9+
"net/http/httptest"
10+
"testing"
11+
"time"
12+
)
13+
14+
var jst = time.FixedZone("Asia/Tokyo", 9*60*60)
15+
16+
var validContents = []string{
17+
"あなたの運勢は大吉です!!!",
18+
"あなたの運勢は中吉です!!",
19+
"あなたの運勢は吉です!",
20+
"あなたの運勢は小吉です。",
21+
"あなたの運勢は凶です…",
22+
}
23+
24+
func contains(expecteds []string, actual string) bool {
25+
for _, expected := range expecteds {
26+
if expected == actual {
27+
return true
28+
}
29+
}
30+
return false
31+
}
32+
33+
func Testランダムに結果を返す(t *testing.T) {
34+
s := httptest.NewServer(http.HandlerFunc(Handler))
35+
defer s.Close()
36+
37+
for i := 0; i < 100; i++ {
38+
res, err := http.Get(s.URL)
39+
if err != nil {
40+
t.Errorf("unexpected error: %v", err)
41+
}
42+
defer res.Body.Close()
43+
44+
if res.StatusCode != http.StatusOK {
45+
t.Errorf("unexpected status code: %d", res.StatusCode)
46+
}
47+
48+
contentType := res.Header.Get("Content-Type")
49+
if contentType != "application/json; charset=UTF-8" {
50+
t.Errorf("unexpected content type: %s", contentType)
51+
}
52+
53+
body, err := ioutil.ReadAll(res.Body)
54+
if err != nil {
55+
t.Errorf("unexpected error: %v", err)
56+
}
57+
58+
var result Result
59+
if err := json.Unmarshal(body, &result); err != nil {
60+
t.Errorf("unexpected error: %v", err)
61+
}
62+
63+
if !(contains(結果, result.Result)) {
64+
t.Errorf("unexpected contents: %s", result.Result)
65+
}
66+
67+
if _, err := time.Parse(time.RFC3339, result.Date); err != nil {
68+
t.Errorf("unexpected error: %v", err)
69+
}
70+
}
71+
}
72+
73+
// refer: https://blog.questionable.services/article/testing-http-handlers-go/
74+
func Test三が日は大吉を返す(t *testing.T) {
75+
recorder := httptest.NewRecorder()
76+
handler := http.HandlerFunc(Handler)
77+
rand.Seed(time.Now().UnixNano())
78+
79+
days := []int{1, 2, 3}
80+
for _, day := range days {
81+
for i := 0; i < 100; i++ {
82+
date := time.Date(2018, time.January, day, rand.Intn(24), rand.Intn(60), rand.Intn(60), rand.Intn(1e9), jst)
83+
84+
req, err := http.NewRequest("GET", "/", nil)
85+
if err != nil {
86+
t.Errorf("unexpected error: %v", err)
87+
}
88+
ctx := context.WithValue(req.Context(), datetimeContextKey, date)
89+
handler.ServeHTTP(recorder, req.WithContext(ctx))
90+
91+
if recorder.Code != http.StatusOK {
92+
t.Errorf("unexpected status code: %d", recorder.Code)
93+
}
94+
contentType := recorder.Header().Get("Content-Type")
95+
if contentType != "application/json; charset=UTF-8" {
96+
t.Errorf("unexpected content type: %s", contentType)
97+
}
98+
99+
body, err := ioutil.ReadAll(recorder.Body)
100+
if err != nil {
101+
t.Errorf("unexpected error: %v", err)
102+
}
103+
104+
var result Result
105+
if err := json.Unmarshal(body, &result); err != nil {
106+
t.Errorf("unexpected error: %v", err)
107+
}
108+
109+
if !(contains(結果, result.Result)) {
110+
t.Errorf("unexpected contents: %s", result.Result)
111+
}
112+
113+
if result.Date != date.Format(time.RFC3339) {
114+
t.Errorf("unexpected date: %v", result.Date)
115+
}
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)