diff --git a/kadai4/tokunaga/main.go b/kadai4/tokunaga/main.go new file mode 100644 index 0000000..497e34b --- /dev/null +++ b/kadai4/tokunaga/main.go @@ -0,0 +1,41 @@ +// handler は、 HTTP リクエストの情報を返します。 +package main + +import ( + crand "crypto/rand" + "log" + "math" + "math/big" + "math/rand" + "net/http" + "time" +) + +func init() { + if err := serRandSeed(); err != nil { + log.Fatal(err) + } +} + +func serRandSeed() error { + seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) + rand.Seed(seed.Int64()) + return err +} + +type timeWrapper struct{} +type randWrapper struct{} + +func (t timeWrapper) Now() time.Time { + return time.Now() +} + +func (r randWrapper) Intn(n int) int { + return rand.Intn(n) +} + +func main() { + omikuji := omikuji{nower: timeWrapper{}, intner: randWrapper{}} + http.HandleFunc("/omikuji", omikuji.open) + log.Fatal(http.ListenAndServe("localhost:8080", nil)) +} diff --git a/kadai4/tokunaga/omikuji.go b/kadai4/tokunaga/omikuji.go new file mode 100644 index 0000000..5025c81 --- /dev/null +++ b/kadai4/tokunaga/omikuji.go @@ -0,0 +1,82 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + "net/http" + "time" +) + +const daikiti = 0 + +var box = map[int]string{ + 0: "大吉", + 1: "中吉", + 2: "小吉", + 3: "凶", + 4: "大凶", +} + +var syougatu = [...]string{ + "01-01", + "01-02", + "01-03", +} + +type nower interface { + Now() time.Time +} + +type intner interface { + Intn(int) int +} + +type response struct { + Result string `json:"result"` +} + +type omikuji struct { + nower + intner + response +} + +func (o *omikuji) open(w http.ResponseWriter, r *http.Request) { + o.pickUp() + buf := o.encodeJson() + fmt.Fprintf(w, buf.String()) +} + +func (o *omikuji) encodeJson() bytes.Buffer { + var buf bytes.Buffer + enc := json.NewEncoder(&buf) + if err := enc.Encode(o.response); err != nil { + log.Fatal(err) + } + return buf +} + +func (o *omikuji) pickUp() { + if isOsyougatu(o.Now()) { + o.Result = getDaikiti() + } else { + o.Result = box[o.Intn(5)] + } + +} + +func isOsyougatu(date time.Time) bool { + day := date.Format("01-02") + for _, sanganichi := range syougatu { + if day == sanganichi { + return true + } + } + return false +} + +func getDaikiti() string { + return box[daikiti] +} diff --git a/kadai4/tokunaga/omikuji_test.go b/kadai4/tokunaga/omikuji_test.go new file mode 100644 index 0000000..29fcd31 --- /dev/null +++ b/kadai4/tokunaga/omikuji_test.go @@ -0,0 +1,105 @@ +package main + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" +) + +type testNower struct { + now time.Time +} + +type testIntner struct { + randInt int +} + +func (t testNower) Now() time.Time { + return t.now +} +func (t testIntner) Intn(_ int) int { + return t.randInt - 1 +} + +func TestOpen(t *testing.T) { + shougatsu := testNower{now: time.Date(2018, 1, 1, 9, 11, 11, 11, time.UTC)} + w := httptest.NewRecorder() + r := httptest.NewRequest("GET", "/omikuji", nil) + o := omikuji{nower: shougatsu, intner: testIntner{randInt: 5}} + o.open(w, r) + rw := w.Result() + defer rw.Body.Close() + if rw.StatusCode != http.StatusOK { + t.Fatal("unexpected status code") + } + b, err := ioutil.ReadAll(rw.Body) + if err != nil { + t.Fatal("unexpected error") + } + expected := `{"result":"大吉"}` + if s := strings.TrimSpace(string(b)); s != expected { + t.Fatalf("want: %s, got: %s", expected, s) + } +} + +func TestEncodeJson(t *testing.T) { + expected := `{"result":"大吉"}` + o := omikuji{response: response{Result: "大吉"}} + actual := o.encodeJson() + if strings.TrimSpace(actual.String()) != expected { + t.Errorf("want: o.encodeJson() = %s, got: %s ", expected, actual) + } +} + +func TestPickUp(t *testing.T) { + nenmatsu := testNower{now: time.Date(2017, 12, 31, 23, 59, 11, 11, time.UTC)} + shougatsu := testNower{now: time.Date(2018, 1, 1, 9, 11, 11, 11, time.UTC)} + cases := []struct { + name string + input omikuji + expected string + }{ + {name: "お正月以外", input: omikuji{nower: nenmatsu, intner: testIntner{randInt: 5}}, expected: "大凶"}, + {name: "お正月", input: omikuji{nower: shougatsu, intner: testIntner{randInt: 5}}, expected: "大吉"}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + c.input.pickUp() + if actual := c.input.Result; actual != c.expected { + t.Errorf("want o.Result = %v, got %v", c.expected, actual) + } + }) + } +} + +func TestIsOsyougatu(t *testing.T) { + cases := []struct { + name string + input time.Time + expected bool + }{ + {name: "12/31", input: time.Date(2017, 12, 31, 23, 59, 11, 11, time.UTC), expected: false}, + {name: "1/1", input: time.Date(2018, 1, 1, 9, 11, 11, 11, time.UTC), expected: true}, + {name: "1/2", input: time.Date(2018, 1, 2, 10, 12, 11, 11, time.UTC), expected: true}, + {name: "1/3", input: time.Date(2018, 1, 3, 11, 12, 11, 11, time.UTC), expected: true}, + {name: "1/4", input: time.Date(2018, 1, 4, 0, 0, 0, 0, time.UTC), expected: false}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + if actual := isOsyougatu(c.input); actual != c.expected { + t.Errorf("want isOsyougatu(%s) = %v, got %v", c.input, c.expected, actual) + } + }) + } +} + +func TestGetDaikiti(t *testing.T) { + expected := "大吉" + actual := getDaikiti() + if actual != expected { + t.Errorf("want: getDaikiti() = %s, got: %s ", expected, actual) + } +}