Skip to content

Commit b2c5528

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

File tree

6 files changed

+249
-0
lines changed

6 files changed

+249
-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,110 @@
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+
func contains(expecteds []string, actual string) bool {
17+
for _, expected := range expecteds {
18+
if expected == actual {
19+
return true
20+
}
21+
}
22+
return false
23+
}
24+
25+
func Testランダムに結果を返す(t *testing.T) {
26+
s := httptest.NewServer(http.HandlerFunc(Handler))
27+
defer s.Close()
28+
29+
for i := 0; i < 100; i++ {
30+
res, err := http.Get(s.URL)
31+
if err != nil {
32+
t.Errorf("unexpected error: %v", err)
33+
}
34+
defer res.Body.Close()
35+
36+
if res.StatusCode != http.StatusOK {
37+
t.Errorf("unexpected status code: %d", res.StatusCode)
38+
}
39+
40+
contentType := res.Header.Get("Content-Type")
41+
if contentType != "application/json; charset=UTF-8" {
42+
t.Errorf("unexpected content type: %s", contentType)
43+
}
44+
45+
body, err := ioutil.ReadAll(res.Body)
46+
if err != nil {
47+
t.Errorf("unexpected error: %v", err)
48+
}
49+
50+
var result Result
51+
if err := json.Unmarshal(body, &result); err != nil {
52+
t.Errorf("unexpected error: %v", err)
53+
}
54+
55+
if !(contains(結果, result.Result)) {
56+
t.Errorf("unexpected contents: %s", result.Result)
57+
}
58+
59+
if _, err := time.Parse(time.RFC3339, result.Date); err != nil {
60+
t.Errorf("unexpected error: %v", err)
61+
}
62+
}
63+
}
64+
65+
// refer: https://blog.questionable.services/article/testing-http-handlers-go/
66+
func Test三が日は大吉を返す(t *testing.T) {
67+
recorder := httptest.NewRecorder()
68+
handler := http.HandlerFunc(Handler)
69+
rand.Seed(time.Now().UnixNano())
70+
71+
days := []int{1, 2, 3}
72+
for _, day := range days {
73+
for i := 0; i < 100; i++ {
74+
date := time.Date(2018, time.January, day, rand.Intn(24), rand.Intn(60), rand.Intn(60), rand.Intn(1e9), jst)
75+
76+
req, err := http.NewRequest("GET", "/", nil)
77+
if err != nil {
78+
t.Errorf("unexpected error: %v", err)
79+
}
80+
ctx := context.WithValue(req.Context(), datetimeContextKey, date)
81+
handler.ServeHTTP(recorder, req.WithContext(ctx))
82+
83+
if recorder.Code != http.StatusOK {
84+
t.Errorf("unexpected status code: %d", recorder.Code)
85+
}
86+
contentType := recorder.Header().Get("Content-Type")
87+
if contentType != "application/json; charset=UTF-8" {
88+
t.Errorf("unexpected content type: %s", contentType)
89+
}
90+
91+
body, err := ioutil.ReadAll(recorder.Body)
92+
if err != nil {
93+
t.Errorf("unexpected error: %v", err)
94+
}
95+
96+
var result Result
97+
if err := json.Unmarshal(body, &result); err != nil {
98+
t.Errorf("unexpected error: %v", err)
99+
}
100+
101+
if !(contains(結果, result.Result)) {
102+
t.Errorf("unexpected contents: %s", result.Result)
103+
}
104+
105+
if result.Date != date.Format(time.RFC3339) {
106+
t.Errorf("unexpected date: %v", result.Date)
107+
}
108+
}
109+
}
110+
}

0 commit comments

Comments
 (0)