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 matsuki #60

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
38 changes: 38 additions & 0 deletions kadai4/matsuki/lib/entities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package lib

import (
"time"
)

// FortuneResult は、おみくじを引いた結果
// - JSON返却値
type FortuneResult struct {
Today jsonTime `json:"today"`
Fortune string `json:"fortune"`
IsNewyearMode bool `json:"is_newyear_mode"`
}

// 独自のjsonTimeを作成
// - https://qiita.com/taizo/items/2c3a338f1aeea86ce9e2
type jsonTime struct {
time.Time
}

func (j jsonTime) format() string {
return j.Time.Format("2006-01-02")
}
func (j jsonTime) parse(value string) (time.Time, error) {
return time.Parse(`"2006-01-02"`, value)
}

// MarshalJSON() の実装
func (j jsonTime) MarshalJSON() ([]byte, error) {
return []byte(`"` + j.format() + `"`), nil
}

// UnmarshalJSON() の実装
func (j jsonTime) UnmarshalJSON(data []byte) error {
tm, err := j.parse(string(data))
j.Time = tm
return err
}
62 changes: 62 additions & 0 deletions kadai4/matsuki/lib/fortune.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package lib

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

// Verbos は、ログ出力レベルを制御
var Verbos bool

// Fortune は、おみくじに関する型
type Fortune struct {
fortuneList map[uint]string
luckiest string
today time.Time
}

// NewFortune は、おみくじの初期化
func NewFortune(now time.Time) Fortune {
// おみくじのタネを初期化
rand.Seed(time.Now().UnixNano())

return Fortune{
fortuneList: map[uint]string{
0: "大吉",
1: "中吉",
2: "吉",
3: "凶",
4: "大凶",
},
luckiest: "大吉",
today: now,
}
}

func (f Fortune) isNewyearsDay() bool {
if Verbos {
log.Printf("[DEBUG] today is %v %v", f.today.Day(), f.today.Month())
}

// Month is defined here: https://golang.org/pkg/time/#Month
if f.today.Month().String() == "January" {
switch f.today.Day() {
case 1, 2, 3:
return true
}
}
return false
}

// DrawFortuneSlip は、おみくじを引く関数
// return ( 運勢, is正月 )
func (f Fortune) DrawFortuneSlip() (string, bool) {
// 正月には大吉を引く
if f.isNewyearsDay() {
return f.luckiest, true
}
// 等確率でひく
r := rand.Intn(len(f.fortuneList))
return f.fortuneList[uint(r)], false
}
56 changes: 56 additions & 0 deletions kadai4/matsuki/lib/fortune_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package lib

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

"github.com/gopherdojo/dojo1/kadai4/matsuki/lib"
)

// TestMain called first
func TestMain(t *testing.T) {

beforeTest()

// test here
// TestIndex(t)

}

func beforeTest() {
rand.Seed(time.Now().UnixNano())
lib.Verbos = true
}

func TestDrawFortuneSlip(t *testing.T) {
f := lib.NewFortune(time.Now())
slip, _ := f.DrawFortuneSlip()
switch slip {
case "大吉", "中吉", "吉", "凶", "大凶":
log.Print("draw valid slip")
default:
t.Errorf("invalid slip=%v", slip)
}
}

func TestDrawWhenNewyear(t *testing.T) {
tm, err := time.Parse("2006-01-02", "2013-01-03")
log.Println("test date=", tm, err)

f := lib.NewFortune(tm)
slip, isNd := f.DrawFortuneSlip()
switch slip {
case "大吉":
log.Print("draw valid slip")
case "中吉", "吉", "凶", "大凶":
t.Errorf("today is newYear. invalid slip=%v", slip)
default:
t.Errorf("invalid slip=%v", slip)
}

if isNd != true {
t.Errorf("want isNewYearDay is true but %v", isNd)
}
}
81 changes: 81 additions & 0 deletions kadai4/matsuki/lib/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package lib

import (
"encoding/json"
"log"
"net/http"
"time"
)

var drawer Drawer

// Drawer は、おみくじを引くinterface
type Drawer interface {
DrawFortuneSlip() (string, bool)
}

// NewServer is constoructor
// Drawer interfaceを使うことでunitTestしやすくする
func NewServer(d Drawer) http.Server {
SetDrawer(d) // パッケージ内部グローバル変数にセット

m := http.NewServeMux()
// Routing
m.Handle("/twice", http.HandlerFunc(Twice))
m.Handle("/", http.HandlerFunc(Index))

server := http.Server{
Addr: ":8080",
Handler: m,
}
log.Printf("[INFO] server start")
return server
}

// SetDrawer is setter
func SetDrawer(d Drawer) {
drawer = d
}

// Index では、おみくじを引き、結果をJSONで返却
func Index(w http.ResponseWriter, req *http.Request) {
if req.URL.Path != "/" {
log.Printf("[INFO] index() with path = %v", req.URL.Path)
http.NotFound(w, req)
return
}

fortuneRes, isNewyearDay := drawer.DrawFortuneSlip()
log.Printf("[INFO] index() with fourtune=%v, isNewyearDay=%v", fortuneRes, isNewyearDay)
// w.Write([]byte("Hello world = " + fortuneRes))
res := FortuneResult{
Today: jsonTime{time.Now()},
Fortune: fortuneRes,
IsNewyearMode: isNewyearDay,
}
if err := json.NewEncoder(w).Encode(res); err != nil {
panic(err)
}
}

// Twice では、おみくじを2回連続で引き、JSONで返却
func Twice(w http.ResponseWriter, req *http.Request) {
fFirst, isNdFist := drawer.DrawFortuneSlip()
fSecond, isNdSecond := drawer.DrawFortuneSlip()

res := []FortuneResult{
FortuneResult{
Today: jsonTime{time.Now()},
Fortune: fFirst,
IsNewyearMode: isNdFist,
},
FortuneResult{
Today: jsonTime{time.Now()},
Fortune: fSecond,
IsNewyearMode: isNdSecond,
},
}
if err := json.NewEncoder(w).Encode(res); err != nil {
panic(err)
}
}
94 changes: 94 additions & 0 deletions kadai4/matsuki/lib/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package lib

import (
"encoding/json"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"testing"

"github.com/gopherdojo/dojo1/kadai4/matsuki/lib"
)

type MockDrawer struct {
}

// DrawFortuneSlip is Mock
func (m MockDrawer) DrawFortuneSlip() (string, bool) {
return "大吉", false
}

// TestMain called first
func TestMain(t *testing.T) {

beforeTest()

// test here
// TestIndex(t)

}

func beforeTest() {
lib.SetDrawer(MockDrawer{})
}

func TestIndex(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(lib.Index))
defer ts.Close()

res, err := http.Get(ts.URL)
if err != nil {
t.Error("unexpected")
return
}

if res.StatusCode != 200 {
t.Error("Status code error")
return
}

//レスポンスBODY取得
body, _ := ioutil.ReadAll(res.Body)
have := string(body)
log.Print(have)

fortuneRes := lib.FortuneResult{}
if err := json.Unmarshal([]byte(have), &fortuneRes); err != nil {
t.Errorf("response body cannot unmarshal. err=%v, body=%v", err, have)
}

// now := time.Now()
// today := now.Format("2006-01-02")
// want := fmt.Sprint(`{"today":"`, today, `","fortune":"大吉","is_newyear_mode":false}`)
//
// if have != want {
// t.Errorf("response body error, want %v but have %v", want, have)
// }
}

func TestTwice(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(lib.Twice))
defer ts.Close()

res, err := http.Get(ts.URL)
if err != nil {
t.Error("unexpected")
return
}

if res.StatusCode != 200 {
t.Error("Status code error")
return
}

//レスポンスBODY取得 & JSON復元できるか
body, _ := ioutil.ReadAll(res.Body)
have := string(body)
log.Print(have)

fortuneRess := []lib.FortuneResult{}
if err := json.Unmarshal([]byte(have), &fortuneRess); err != nil {
t.Errorf("response body cannot unmarshal. err=%v, body=%v", err, have)
}
}
18 changes: 18 additions & 0 deletions kadai4/matsuki/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package main

import (
"time"

"github.com/gopherdojo/dojo1/kadai4/matsuki/lib"
)

var verbos bool

func main() {
lib.Verbos = true

f := lib.NewFortune(time.Now())
// server
s := lib.NewServer(f)
s.ListenAndServe()
}