diff --git a/kadai4/sawadashota/.gitignore b/kadai4/sawadashota/.gitignore new file mode 100644 index 0000000..9cc1a02 --- /dev/null +++ b/kadai4/sawadashota/.gitignore @@ -0,0 +1,2 @@ +profile +profile.html diff --git a/kadai4/sawadashota/Makefile b/kadai4/sawadashota/Makefile new file mode 100644 index 0000000..6cc70b7 --- /dev/null +++ b/kadai4/sawadashota/Makefile @@ -0,0 +1,6 @@ +dev: + go run main.go +test: + go test -v ./... +coverage: + go test -coverprofile=profile ./... && go tool cover -html=profile -o profile.html diff --git a/kadai4/sawadashota/README.md b/kadai4/sawadashota/README.md new file mode 100644 index 0000000..8f79470 --- /dev/null +++ b/kadai4/sawadashota/README.md @@ -0,0 +1,18 @@ +おみくじAPI +=== + +三元日には必ず大吉が出るおみくじAPI + +Run +--- + +``` +$ go run main.go +``` + +Test +--- + +``` +$ go test -v ./... +``` diff --git a/kadai4/sawadashota/fortune/fortune.go b/kadai4/sawadashota/fortune/fortune.go new file mode 100644 index 0000000..55e6563 --- /dev/null +++ b/kadai4/sawadashota/fortune/fortune.go @@ -0,0 +1,93 @@ +package fortune + +import ( + "fmt" + "math/rand" + "time" +) + +type Fortune int + +const ( + AmountItems = 10 + + Daikichi Fortune = iota + Kichi + Chukichi + Shokichi + Suekichi + Kyo + Daikyo +) + +var items = [AmountItems]Fortune{ + Daikichi, + Kichi, + Kichi, + Chukichi, + Chukichi, + Shokichi, + Shokichi, + Suekichi, + Kyo, + Daikyo, +} + +// Result returns fortune +func Result(s rand.Source, t time.Time) Fortune { + if isNewYear(t) { + return Daikichi + } + + return items[rand.New(s).Intn(AmountItems)] +} + +// Parse string to Fortune +func Parse(str string) (Fortune, error) { + switch str { + case "大吉": + return Daikichi, nil + case "吉": + return Kichi, nil + case "中吉": + return Chukichi, nil + case "小吉": + return Shokichi, nil + case "末吉": + return Suekichi, nil + case "凶": + return Kyo, nil + case "大凶": + return Daikyo, nil + default: + return 0, fmt.Errorf("cannot parse %s", str) + } +} + +// String from Fortune +func (f Fortune) String() string { + switch f { + case Daikichi: + return "大吉" + case Kichi: + return "吉" + case Chukichi: + return "中吉" + case Shokichi: + return "小吉" + case Suekichi: + return "末吉" + case Kyo: + return "凶" + default: + return "大凶" + } +} + +func isNewYear(t time.Time) bool { + if t.Month() != time.January { + return false + } + + return 1 <= t.Day() && t.Day() <= 3 +} diff --git a/kadai4/sawadashota/fortune/fortune_test.go b/kadai4/sawadashota/fortune/fortune_test.go new file mode 100644 index 0000000..9ced611 --- /dev/null +++ b/kadai4/sawadashota/fortune/fortune_test.go @@ -0,0 +1,113 @@ +package fortune_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/gopherdojo/dojo2/kadai4/sawadashota/fortune" +) + +var randSource rand.Source + +func init() { + randSource = rand.NewSource(time.Now().UnixNano()) +} + +func TestResult(t *testing.T) { + tokyo, err := time.LoadLocation("Asia/Tokyo") + + if err != nil { + t.Fatal(err) + } + + utc, err := time.LoadLocation("UTC") + + if err != nil { + t.Fatal(err) + } + + cases := map[string]struct { + now time.Time + expect fortune.Fortune + }{ + "tokyo 1.1": { + now: time.Date(2018, 1, 1, 0, 0, 0, 0, tokyo), + expect: fortune.Daikichi, + }, + "utc 1.1": { + now: time.Date(2018, 1, 1, 0, 0, 0, 0, utc), + expect: fortune.Daikichi, + }, + "tokyo 1.3": { + now: time.Date(2020, 1, 3, 23, 59, 59, 59, tokyo), + expect: fortune.Daikichi, + }, + "utc 1.3": { + now: time.Date(2020, 1, 3, 23, 59, 59, 59, utc), + expect: fortune.Daikichi, + }, + } + + for n, c := range cases { + t.Run(n, func(t *testing.T) { + f := fortune.Result(randSource, c.now) + if f != c.expect { + t.Errorf("expect: %s but actual: %s", c.expect, f) + } + }) + } +} + +func TestParse(t *testing.T) { + cases := map[string]struct { + rawStr string + expect fortune.Fortune + hasError bool + }{ + "大吉": { + rawStr: "大吉", + expect: fortune.Daikichi, + hasError: false, + }, + "中吉": { + rawStr: "中吉", + expect: fortune.Chukichi, + hasError: false, + }, + "大凶": { + rawStr: "大凶", + expect: fortune.Daikyo, + hasError: false, + }, + "dummy string": { + rawStr: "てすと", + expect: 0, + hasError: true, + }, + } + + for n, c := range cases { + t.Run(n, func(t *testing.T) { + f, err := fortune.Parse(c.rawStr) + hasError := err != nil + + if hasError != c.hasError { + if hasError { + t.Error(err) + } + + t.Error("expect has error but has no errors") + } + + // Finish test if expects error + if hasError { + return + } + + if f != c.expect { + t.Errorf("expect %s but actual %s", c.expect, f) + } + }) + } +} diff --git a/kadai4/sawadashota/handler/handler.go b/kadai4/sawadashota/handler/handler.go new file mode 100644 index 0000000..5657793 --- /dev/null +++ b/kadai4/sawadashota/handler/handler.go @@ -0,0 +1,37 @@ +package handler + +import ( + "bytes" + "encoding/json" + "fmt" + "math/rand" + "net/http" + "time" + + "github.com/gopherdojo/dojo2/kadai4/sawadashota/fortune" +) + +var randSource rand.Source + +type HandleResponse struct { + Result string `json:"result"` +} + +func init() { + randSource = rand.NewSource(time.Now().UnixNano()) +} + +func Handler(w http.ResponseWriter, r *http.Request) { + f := fortune.Result(randSource, time.Now().Local()) + hr := &HandleResponse{Result: f.String()} + + var buf bytes.Buffer + enc := json.NewEncoder(&buf) + + if err := enc.Encode(hr); err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + fmt.Fprint(w, buf.String()) +} diff --git a/kadai4/sawadashota/handler/handler_test.go b/kadai4/sawadashota/handler/handler_test.go new file mode 100644 index 0000000..9035d53 --- /dev/null +++ b/kadai4/sawadashota/handler/handler_test.go @@ -0,0 +1,43 @@ +package handler_test + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gopherdojo/dojo2/kadai4/sawadashota/fortune" + "github.com/gopherdojo/dojo2/kadai4/sawadashota/handler" +) + +func TestHandler(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/", nil) + + handler.Handler(w, r) + rw := w.Result() + defer rw.Body.Close() + + if rw.StatusCode != http.StatusOK { + t.Errorf("expect: %d but actual: %d", http.StatusOK, rw.StatusCode) + } + + b, err := ioutil.ReadAll(rw.Body) + if err != nil { + t.Fatal(err) + } + + var hr handler.HandleResponse + buf := bytes.NewReader([]byte(b)) + dec := json.NewDecoder(buf) + + if err := dec.Decode(&hr); err != nil { + t.Fatal(err) + } + + if _, err := fortune.Parse(hr.Result); err != nil { + t.Error(err) + } +} diff --git a/kadai4/sawadashota/main.go b/kadai4/sawadashota/main.go new file mode 100644 index 0000000..9aa284b --- /dev/null +++ b/kadai4/sawadashota/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + "net/http" + "os" + + "github.com/gopherdojo/dojo2/kadai4/sawadashota/handler" +) + +const DefaultPort = "8080" + +var port string + +func init() { + port = os.Getenv("PORT") + if port == "" { + port = DefaultPort + } +} + +func main() { + http.HandleFunc("/", handler.Handler) + http.ListenAndServe(fmt.Sprintf(":%s", port), nil) +}