Skip to content
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

Kadai4 by @int128 #50

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
63 changes: 63 additions & 0 deletions kadai4/int128/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# kadai4

> - JSON形式でおみくじの結果を返す
> - 結果は毎回ランダムに変わるようにする
> - 正月(1/1-1/3)だけ大吉にする
> - ハンドラのテストを書いてみる

## 実行例

```
% go run main.go
2018/07/09 10:24:36 Listening on :8000
2018/07/09 10:24:57 GET /
2018/07/09 10:25:09 GET /api/omikuji
2018/07/09 10:25:14 GET /api/omikuji
2018/07/09 10:25:16 GET /api/omikuji
```

```
% curl -v http://localhost:8000/api/omikuji
> GET /api/omikuji HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Mon, 09 Jul 2018 01:25:09 GMT
< Content-Length: 32
<
{"description":"凶","value":1}
```

```
% curl -v http://localhost:8000/api/omikuji
> GET /api/omikuji HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Mon, 09 Jul 2018 01:25:16 GMT
< Content-Length: 35
<
{"description":"中吉","value":3}
```

```
% curl -v http://localhost:8000/
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Mon, 09 Jul 2018 01:24:57 GMT
< Content-Length: 19
<
404 page not found
```
24 changes: 24 additions & 0 deletions kadai4/int128/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

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

"github.com/gopherdojo/dojo2/kadai4/int128/routes"
)

func main() {
addr := ":8000"
if port := os.Getenv("PORT"); port != "" {
addr = ":" + port
}
s := http.Server{
Addr: addr,
Handler: routes.New(),
}
log.Printf("Listening on %s", s.Addr)
if err := s.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
57 changes: 57 additions & 0 deletions kadai4/int128/omikuji/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package omikuji

import (
"math/rand"
"time"
)

// Service provides omikuji.
type Service interface {
Hiku() Omikuji
}

// New returns a new Service.
func New() Service {
return &DefaultService{
random: rand.New(rand.NewSource(time.Now().UnixNano())),
}
}

// DefaultService is a default implementation of Service.
type DefaultService struct {
random *rand.Rand // Random generator
time func() time.Time // Time provider (optional)
}

// Hiku peforms おみくじを引く.
// If the current date is shogatsu, it always returns the DaiKichi.
func (s *DefaultService) Hiku() Omikuji {
if s.isShogatsu() {
return DaiKichi
}
n := s.random.Intn(6)
switch n {
default:
return Kyo
case 2, 3:
return ShoKichi
case 4:
return ChuKichi
case 5:
return DaiKichi
}
}

func (s *DefaultService) isShogatsu() bool {
now := time.Now()
if s.time != nil {
now = s.time()
}
if now.Month() == time.January {
switch now.Day() {
case 1, 2, 3:
return true
}
}
return false
}
39 changes: 39 additions & 0 deletions kadai4/int128/omikuji/service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package omikuji

import (
"math/rand"
"testing"
"time"
)

func TestDefaultService_IsShogatsu(t *testing.T) {
tokyoTime, err := time.LoadLocation("Asia/Tokyo")
if err != nil {
t.Fatal(err)
}
matrix := []struct {
now time.Time
expected bool
}{
{time.Date(2017, 12, 31, 0, 0, 0, 0, tokyoTime), false},
{time.Date(2017, 12, 31, 23, 59, 59, 0, tokyoTime), false},
{time.Date(2018, 1, 1, 0, 0, 0, 0, tokyoTime), true},
{time.Date(2018, 1, 2, 0, 0, 0, 0, tokyoTime), true},
{time.Date(2018, 1, 3, 0, 0, 0, 0, tokyoTime), true},
{time.Date(2018, 1, 3, 23, 59, 59, 0, tokyoTime), true},
{time.Date(2018, 1, 4, 0, 0, 0, 0, tokyoTime), false},
{time.Date(2018, 2, 1, 0, 0, 0, 0, tokyoTime), false},
}
for _, m := range matrix {
t.Run(m.now.String(), func(t *testing.T) {
s := &DefaultService{
random: rand.New(rand.NewSource(0)),
time: func() time.Time { return m.now },
}
actual := s.isShogatsu()
if m.expected != actual {
t.Errorf("shogatsu wants %v but %v", m.expected, actual)
}
})
}
}
31 changes: 31 additions & 0 deletions kadai4/int128/omikuji/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package omikuji

// Omikuji represents a result of omikuji.
type Omikuji int

const (
_ Omikuji = iota
// Kyo means 凶.
Kyo
// ShoKichi means 小吉.
ShoKichi
// ChuKichi means 中吉.
ChuKichi
// DaiKichi means 大吉.
DaiKichi
)

func (o Omikuji) String() string {
switch o {
case Kyo:
return "凶"
case ShoKichi:
return "小吉"
case ChuKichi:
return "中吉"
case DaiKichi:
return "大吉"
default:
return ""
}
}
36 changes: 36 additions & 0 deletions kadai4/int128/routes/omikuji.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package routes

import (
"encoding/json"
"net/http"

"github.com/gopherdojo/dojo2/kadai4/int128/omikuji"
)

// OmikujiHandler is a HTTP handler for GET omikuji
type OmikujiHandler struct {
service omikuji.Service
}

// OmikujiGetResponse is a GET response of OmikujiHandler.
type OmikujiGetResponse struct {
Description string `json:"description"`
Value int `json:"value"`
}

func (h *OmikujiHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
switch req.Method {
case "GET":
w.Header().Add("Content-Type", "application/json")
o := h.service.Hiku()
e := json.NewEncoder(w)
res := OmikujiGetResponse{o.String(), int(o)}
if err := e.Encode(res); err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
default:
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}
}
39 changes: 39 additions & 0 deletions kadai4/int128/routes/omikuji_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package routes

import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

"github.com/gopherdojo/dojo2/kadai4/int128/omikuji"
)

func TestOmikujiHandler_GET(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/api/omikuji", nil)
h := &OmikujiHandler{omikuji.New()}
h.ServeHTTP(w, r)
res := w.Result()
defer res.Body.Close()
if http.StatusOK != res.StatusCode {
t.Fatalf("res.StatusCode wants %d but %d", http.StatusOK, res.StatusCode)
}
d := json.NewDecoder(res.Body)
var json OmikujiGetResponse
if err := d.Decode(&json); err != nil {
t.Fatal(err)
}
}

func TestOmikujiHandler_POST(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest("POST", "/api/omikuji", nil)
h := &OmikujiHandler{omikuji.New()}
h.ServeHTTP(w, r)
res := w.Result()
defer res.Body.Close()
if http.StatusMethodNotAllowed != res.StatusCode {
t.Fatalf("res.StatusCode wants %d but %d", http.StatusMethodNotAllowed, res.StatusCode)
}
}
22 changes: 22 additions & 0 deletions kadai4/int128/routes/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package routes

import (
"log"
"net/http"

"github.com/gopherdojo/dojo2/kadai4/int128/omikuji"
)

// New returns a handler with application routes.
func New() http.Handler {
m := http.NewServeMux()
m.Handle("/api/omikuji", &OmikujiHandler{omikuji.New()})
return withLogging(m)
}

func withLogging(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s", r.Method, r.RequestURI)
h.ServeHTTP(w, r)
})
}