diff --git a/kadai4/translucens/README.md b/kadai4/translucens/README.md new file mode 100644 index 0000000..a5df0ec --- /dev/null +++ b/kadai4/translucens/README.md @@ -0,0 +1,22 @@ +# 課題4 + +おみくじAPIを作ってみよう。 + +* JSON形式でおみくじの結果を返す + * `result`に結果が入るようにしました +* 正月(1/1-1/3)だけ大吉にする + * 正月の前後で境界値試験を記述しました +* ハンドラのテストを書いてみる + * `TestHTTPHandler`に記述しました + +使い方 + +```sh +$ go run omikujiserver.go +# In another terminal +$ curl --silent localhost:8080 | jq . +{ + "result": "吉", + "date": "2018-05-25 02:35:02.0177204 +0900 DST m=+106.863404001" +} +``` diff --git a/kadai4/translucens/omikuji/omikuji.go b/kadai4/translucens/omikuji/omikuji.go new file mode 100644 index 0000000..6e71747 --- /dev/null +++ b/kadai4/translucens/omikuji/omikuji.go @@ -0,0 +1,61 @@ +package omikuji + +import ( + "encoding/json" + "log" + "math/rand" + "net/http" + "time" +) + +var nowFunc = time.Now + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +// Response is the type of omikuji result +type Response struct { + Result string `json:"result"` + Date string `json:"date"` +} + +// HTTPHandler writes omikuji into HTTP response +func HTTPHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + res := Response{ + Result: omikuji(), + Date: nowFunc().String(), + } + + if err := json.NewEncoder(w).Encode(res); err != nil { + log.Println("Error in JSON Encoder: ", err) + } +} + +func omikuji() string { + + now := nowFunc() + + if 1 <= now.YearDay() && now.YearDay() <= 3 { + return "大吉!" + } + + i := rand.Intn(100) + switch { + case i < 17: + return "大吉" + case i < 17+30: + return "凶" + case i < 17+30+36: + return "吉" + case i < 17+30+36+6: + return "末吉" + case i < 17+30+36+6+3: + return "末小吉" + case i < 17+30+36+6+3+4: + return "半吉" + default: + return "小吉" + } +} diff --git a/kadai4/translucens/omikuji/omikuji_test.go b/kadai4/translucens/omikuji/omikuji_test.go new file mode 100644 index 0000000..bad28dd --- /dev/null +++ b/kadai4/translucens/omikuji/omikuji_test.go @@ -0,0 +1,129 @@ +package omikuji + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func Test_omikujiNewYear(t *testing.T) { + + nowFunc = func() time.Time { + return time.Date(2018, time.January, 1, 0, 0, 0, 0, time.Local) + } + + result := omikuji() + if result != "大吉!" { + t.Errorf("omikuji() = %s", result) + } + +} + +func Test_omikujiNewYear2(t *testing.T) { + + nowFunc = func() time.Time { + return time.Date(2018, time.January, 3, 23, 59, 59, 0, time.Local) + } + + result := omikuji() + if result != "大吉!" { + t.Errorf("omikuji() = %s", result) + } + +} + +func Test_omikujiNotNewYear(t *testing.T) { + + nowFunc = func() time.Time { + return time.Date(2018, time.January, 4, 0, 0, 0, 0, time.Local) + } + + switch result := omikuji(); result { + case "大吉", "凶", "吉", "末吉", "末小吉", "半吉", "小吉": + t.Logf("omikuji() = %s", result) + default: + t.Errorf("omikuji() = %s", result) + } +} + +func Test_omikujiNotNewYear2(t *testing.T) { + + nowFunc = func() time.Time { + return time.Date(2017, time.December, 31, 23, 59, 59, 0, time.Local) + } + + switch result := omikuji(); result { + case "大吉", "凶", "吉", "末吉", "末小吉", "半吉", "小吉": + t.Logf("omikuji() = %s", result) + default: + t.Errorf("omikuji() = %s", result) + } +} + +func TestHTTPHandler(t *testing.T) { + + w := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + HTTPHandler(w, req) + result := w.Result() + defer result.Body.Close() + + if result.StatusCode != http.StatusOK { + t.Fatalf("Unexpected Status Code: %d", result.StatusCode) + } + + body, err := ioutil.ReadAll(result.Body) + if err != nil { + t.Fatal("Cannot read response body ", err) + } + + decoder := json.NewDecoder(bytes.NewBuffer(body)) + var res Response + err = decoder.Decode(&res) + if err != nil { + t.Fatal("Decode error: ", err) + } + + switch res.Result { + case "大吉", "凶", "吉", "末吉", "末小吉", "半吉", "小吉": + t.Logf("result = %s", res) + default: + t.Errorf("result = %s", res) + } +} + +func testResultCount(t *testing.T, actualCount, expectedCount int) { + t.Helper() + + diff := 100 + + if actualCount < expectedCount-diff { + t.Errorf("The actual number is too small, actual: %d, expected: %d", actualCount, expectedCount) + } else if expectedCount+diff < actualCount { + t.Errorf("The actual number is too large, actual: %d, expected: %d", actualCount, expectedCount) + } else { + t.Logf("The actual and expected numbers are %d, %d", actualCount, expectedCount) + } +} + +func Test_omikuji10000times(t *testing.T) { + + resultCount := make(map[string]int) + + for i := 0; i < 10000; i++ { + resultCount[omikuji()]++ + } + + testResultCount(t, resultCount["大吉"], 1700) + testResultCount(t, resultCount["凶"], 3000) + testResultCount(t, resultCount["吉"], 3600) + testResultCount(t, resultCount["末吉"], 600) + testResultCount(t, resultCount["末小吉"], 300) + testResultCount(t, resultCount["半吉"], 400) + testResultCount(t, resultCount["小吉"], 300) +} diff --git a/kadai4/translucens/omikujiserver.go b/kadai4/translucens/omikujiserver.go new file mode 100644 index 0000000..13f4c61 --- /dev/null +++ b/kadai4/translucens/omikujiserver.go @@ -0,0 +1,15 @@ +package main + +import ( + "log" + "net/http" + + "github.com/translucens/dojo1/kadai4/translucens/omikuji" +) + +func main() { + http.HandleFunc("/", omikuji.HTTPHandler) + if err := http.ListenAndServe(":8080", nil); err != nil { + log.Println(err) + } +}