Skip to content

kadai 4 by zaneli #63

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

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

おみくじAPI

### 使い方

ポートを指定しないで起動する。(デフォルト8080ポートが使用される。)

```sh
go run main.go
```

ポートを指定して起動する。

```sh
go run main.go 8081
```

アクセスする。

```
> curl --dump-header - "http://localhost:8080/"
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Date: Sat, 02 Jun 2018 17:52:03 GMT
Content-Length: 52

{"result":"吉","date":"2018-06-03T02:52:03+09:00"}
```

テストを実行する。

```
> go test ./omikuji
ok _/Users/zaneli/ws/dojo1/kadai4/zaneli/omikuji/omikuji 0.026s
```

```
> go test ./omikuji --cover
ok _/Users/zaneli/ws/dojo1/kadai4/zaneli/omikuji/omikuji 0.026s coverage: 76.2% of statements
```
26 changes: 26 additions & 0 deletions kadai4/zaneli/omikuji/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

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

"./omikuji"
)

func main() {
port := 8080
if len(os.Args) > 1 {
p, err := strconv.Atoi(os.Args[1])
if err != nil {
log.Fatal(err)
}
port = p
}

mux := http.NewServeMux()
mux.Handle("/", omikuji.AddCurrentDateTime(http.HandlerFunc(omikuji.Handler)))
http.ListenAndServe(fmt.Sprintf(":%d", port), mux)
}
21 changes: 21 additions & 0 deletions kadai4/zaneli/omikuji/omikuji/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package omikuji

import (
"context"
"net/http"
"time"
)

type contextKey string

const datetimeContextKey contextKey = "datetime"

// AddCurrentDateTime は Context に現在日時を設定する。
// refer: https://gocodecloud.com/blog/2016/11/15/simple-golang-http-request-context-example/
func AddCurrentDateTime(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
date := time.Now().In(time.FixedZone("Asia/Tokyo", 9*60*60))
ctx := context.WithValue(r.Context(), datetimeContextKey, date)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
Binary file added kadai4/zaneli/omikuji/omikuji/debug.test
Binary file not shown.
51 changes: 51 additions & 0 deletions kadai4/zaneli/omikuji/omikuji/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package omikuji

import (
"encoding/json"
"math/rand"
"net/http"
"time"
)

var 大吉 = "大吉"
var 中吉 = "中吉"
var 吉 = "吉"
var 小吉 = "小吉"
var 凶 = "凶"
var 結果 = []string{大吉, 中吉, 吉, 小吉, 凶}

// Result はレスポンスJSONの構造を表す。
type Result struct {
Result string `json:"result"`
Date string `json:"date"`
}

// Handler はおみくじの結果を返す。
func Handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
v := ctx.Value(datetimeContextKey)
if v == nil {
v = time.Now()
}
date, ok := v.(time.Time)
if !ok {
date = time.Now()
}

var result string
if is三が日(date) {
result = 大吉
} else {
rand.Shuffle(len(結果), func(i, j int) {
結果[i], 結果[j] = 結果[j], 結果[i]
})
result = 結果[0]
}
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
json.NewEncoder(w).Encode(Result{Result: result, Date: date.Format(time.RFC3339)})
}

func is三が日(date time.Time) bool {
_, m, d := date.Date()
return m == time.January && (d == 1 || d == 2 || d == 3)
}
110 changes: 110 additions & 0 deletions kadai4/zaneli/omikuji/omikuji/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package omikuji

import (
"context"
"encoding/json"
"io/ioutil"
"math/rand"
"net/http"
"net/http/httptest"
"testing"
"time"
)

var jst = time.FixedZone("Asia/Tokyo", 9*60*60)

func contains(expecteds []string, actual string) bool {
for _, expected := range expecteds {
if expected == actual {
return true
}
}
return false
}

func Testランダムに結果を返す(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(Handler))
defer s.Close()

for i := 0; i < 100; i++ {
res, err := http.Get(s.URL)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
t.Errorf("unexpected status code: %d", res.StatusCode)
}

contentType := res.Header.Get("Content-Type")
if contentType != "application/json; charset=UTF-8" {
t.Errorf("unexpected content type: %s", contentType)
}

body, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

var result Result
if err := json.Unmarshal(body, &result); err != nil {
t.Errorf("unexpected error: %v", err)
}

if !(contains(結果, result.Result)) {
t.Errorf("unexpected contents: %s", result.Result)
}

if _, err := time.Parse(time.RFC3339, result.Date); err != nil {
t.Errorf("unexpected error: %v", err)
}
}
}

// refer: https://blog.questionable.services/article/testing-http-handlers-go/
func Test三が日は大吉を返す(t *testing.T) {
recorder := httptest.NewRecorder()
handler := http.HandlerFunc(Handler)
rand.Seed(time.Now().UnixNano())

days := []int{1, 2, 3}
for _, day := range days {
for i := 0; i < 100; i++ {
date := time.Date(2018, time.January, day, rand.Intn(24), rand.Intn(60), rand.Intn(60), rand.Intn(1e9), jst)

req, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
ctx := context.WithValue(req.Context(), datetimeContextKey, date)
handler.ServeHTTP(recorder, req.WithContext(ctx))

if recorder.Code != http.StatusOK {
t.Errorf("unexpected status code: %d", recorder.Code)
}
contentType := recorder.Header().Get("Content-Type")
if contentType != "application/json; charset=UTF-8" {
t.Errorf("unexpected content type: %s", contentType)
}

body, err := ioutil.ReadAll(recorder.Body)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

var result Result
if err := json.Unmarshal(body, &result); err != nil {
t.Errorf("unexpected error: %v", err)
}

if !(contains(結果, result.Result)) {
t.Errorf("unexpected contents: %s", result.Result)
}

if result.Date != date.Format(time.RFC3339) {
t.Errorf("unexpected date: %v", result.Date)
}
}
}
}