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 uobikiemukot #61

Open
wants to merge 4 commits 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
18 changes: 18 additions & 0 deletions kadai4/uobikiemukot/fortune/cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package main

import (
"net/http"
"time"

"github.com/gopherdojo/dojo4/kadai4/uobikiemukot/fortune"
)

func main() {
clock := fortune.ClockFunc(func() time.Time {
return time.Now()
})
f := fortune.Fortune{Clock: clock}

http.HandleFunc("/", f.Handler)
http.ListenAndServe(":8080", nil)
}
96 changes: 96 additions & 0 deletions kadai4/uobikiemukot/fortune/fortune.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package fortune

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

// index of values
const (
excellent = iota
great
good
bad
)

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

// Clock return current time
type Clock interface {
Now() time.Time
}

// ClockFunc is an adapter to implement Clock interface
type ClockFunc func() time.Time

// Now calls f()
func (f ClockFunc) Now() time.Time {
return f()
}

// Fortune implement Clock interfate
type Fortune struct {
Clock Clock
Err error
}

// Result is type for JSON encode/decode
type Result struct {
Fortune string `json:"fortune"`
}

// Encode converts Result into JSON string
func Encode(r Result) (string, error) {
b := bytes.Buffer{}
e := json.NewEncoder(&b)

err := e.Encode(&r)
if err != nil {
return "", err
}

return string(b.Bytes()), nil
}

// Decode converts JSON string into Result
func Decode(s string) (Result, error) {
b := bytes.NewBufferString(s)
d := json.NewDecoder(b)

var r Result
err := d.Decode(&r)
if err != nil {
return r, err
}

return r, nil
}

// Handler http.HandleFunc implementation
func (f *Fortune) Handler(w http.ResponseWriter, r *http.Request) {
var res Result
now := f.Clock.Now()

if now.Month() == time.January && (1 <= now.Day() && now.Day() <= 3) {
// always return excellent
res.Fortune = values[excellent]
} else {
// random pick up
rand.Seed(now.Unix())
i := rand.Int() % len(values)
res.Fortune = values[i]
}

json, err := Encode(res)
if err != nil {
f.Err = err
} else {
fmt.Fprintf(w, json)
}
}
128 changes: 128 additions & 0 deletions kadai4/uobikiemukot/fortune/fortune_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package fortune_test

import (
"bytes"
"net/http"
"testing"
"time"

"github.com/gopherdojo/dojo4/kadai4/uobikiemukot/fortune"
)

// dummyWriter implement http.ResponseWriter
type dummyWriter struct {
buf bytes.Buffer
}

func (d *dummyWriter) Header() http.Header {
return nil
}

func (d *dummyWriter) Write(b []byte) (int, error) {
return d.buf.Write(b)
}

func (d *dummyWriter) WriteHeader(code int) {
}

// mockWriter creates dummyWriter from bytes.Buffer
func mockWriter(t *testing.T, b bytes.Buffer) dummyWriter {
t.Helper()
return dummyWriter{buf: b}
}

// mockClock creates fortune.ClockFunc from date string
func mockClock(t *testing.T, s string) fortune.ClockFunc {
t.Helper()

now, err := time.Parse("2006/01/02", s)
if err != nil {
t.Fatal(err)
}

return fortune.ClockFunc(func() time.Time {
return now
})
}

func parse(t *testing.T, s string) string {
t.Helper()

r, err := fortune.Decode(s)
if err != nil {
t.Fatal(err)
}

return r.Fortune
}

func TestHandler_Success(t *testing.T) {
w := mockWriter(t, bytes.Buffer{})

cases := map[string]struct {
writer *dummyWriter
clock fortune.Clock
}{
"1900/01/01": {
writer: &w,
clock: mockClock(t, "1900/01/01"),
},
"2000/02/29": {
writer: &w,
clock: mockClock(t, "2000/02/29"),
},
"2020/12/19": {
writer: &w,
clock: mockClock(t, "2020/12/19"),
},
}

for _, c := range cases {
c.writer.buf.Reset()

f := &fortune.Fortune{Clock: c.clock}
f.Handler(c.writer, nil)

if f.Err != nil {
t.Errorf("error occurred: %s", f.Err)
}
}
}

func TestHandler_SpecialDate(t *testing.T) {
w := mockWriter(t, bytes.Buffer{})

cases := map[string]struct {
writer *dummyWriter
clock fortune.Clock
msg string
}{
"2019/01/01": {
writer: &w,
clock: mockClock(t, "2019/01/01"),
msg: "大吉",
},
"2020/01/02": {
writer: &w,
clock: mockClock(t, "2020/01/02"),
msg: "大吉",
},
"2021/01/03": {
writer: &w,
clock: mockClock(t, "2021/01/03"),
msg: "大吉",
},
}

for _, c := range cases {
c.writer.buf.Reset()

f := &fortune.Fortune{Clock: c.clock}
f.Handler(c.writer, nil)

r := parse(t, c.writer.buf.String())
if r != c.msg {
t.Errorf("want:%s but got:%s\n", c.msg, r)
}
}
}
1 change: 1 addition & 0 deletions kadai4/uobikiemukot/fortune/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module github.com/gopherdojo/dojo4/kadai4/uobikiemukot/fortune