From 885078c64928871d25b7965971eb06e4d9976506 Mon Sep 17 00:00:00 2001 From: Adrian Zankich Date: Tue, 15 Apr 2014 17:59:44 -0700 Subject: [PATCH] WIP api tests --- api.go | 160 ++++++++++++++++++++++++++++++++----------------- api_test.go | 112 ++++++++++++++++++++++++++++++++++ robot_test.go | 4 ++ test_helper.go | 22 +++++-- 4 files changed, 239 insertions(+), 59 deletions(-) create mode 100644 api_test.go diff --git a/api.go b/api.go index 6ba42321..dfe6c9cb 100644 --- a/api.go +++ b/api.go @@ -3,11 +3,14 @@ package gobot import ( "encoding/json" "github.com/go-martini/martini" + "io/ioutil" "net/http" "reflect" ) -type api struct{} +type api struct { + master *Master +} type jsonRobot struct { Name string `json:"name"` @@ -29,68 +32,103 @@ type jsonConnection struct { Adaptor string `json:"adaptor"` } -func Api(bot *Master) { +var startApi = func(m *martini.ClassicMartini) { + go m.Run() +} + +func Api(bot *Master) *api { a := new(api) + a.master = bot m := martini.Classic() m.Use(martini.Static("robeaux")) - m.Get("/robots", func() string { - jsonRobots := make([]*jsonRobot, 0) - for _, robot := range bot.Robots { - jsonRobots = append(jsonRobots, a.formatJsonRobot(robot)) - } - return toJson(jsonRobots) + m.Get("/robots", func(res http.ResponseWriter, req *http.Request) { + a.robots(res, req) }) - m.Get("/robots/:robotname", func(params martini.Params) string { - return toJson(a.formatJsonRobot(bot.FindRobot(params["robotname"]))) + m.Get("/robots/:robotname", func(params martini.Params, res http.ResponseWriter, req *http.Request) { + a.robot(params["robotname"], res, req) }) - m.Get("/robots/:robotname/commands", func(params martini.Params) string { - return toJson(bot.FindRobot(params["robotname"]).RobotCommands) + m.Get("/robots/:robotname/commands", func(params martini.Params, res http.ResponseWriter, req *http.Request) { + a.robot_commands(params["robotname"], res, req) }) robot_command_route := "/robots/:robotname/commands/:command" - m.Get(robot_command_route, func(params martini.Params, res http.ResponseWriter, req *http.Request) string { - decoder := json.NewDecoder(req.Body) - var body map[string]interface{} - decoder.Decode(&body) - if len(body) == 0 { - body = map[string]interface{}{} - } - body["robotname"] = params["robotname"] - return a.executeRobotCommand(bot, params, body) + m.Get(robot_command_route, func(params martini.Params, res http.ResponseWriter, req *http.Request) { + a.executeRobotCommand(bot, params, res, req) }) - m.Get("/robots/:robotname/devices", func(params martini.Params) string { - devices := bot.FindRobot(params["robotname"]).GetDevices() - jsonDevices := make([]*jsonDevice, 0) - for _, device := range devices { - jsonDevices = append(jsonDevices, a.formatJsonDevice(device)) - } - return toJson(jsonDevices) + m.Get("/robots/:robotname/devices", func(params martini.Params, res http.ResponseWriter, req *http.Request) { + a.robot_devices(params["robotname"], res, req) }) - m.Get("/robots/:robotname/devices/:devicename", func(params martini.Params) string { - return toJson(a.formatJsonDevice(bot.FindRobotDevice(params["robotname"], params["devicename"]))) + m.Get("/robots/:robotname/devices/:devicename", func(params martini.Params, res http.ResponseWriter, req *http.Request) { + a.robot_device(params["robotname"], params["devicename"], res, req) }) - m.Get("/robots/:robotname/devices/:devicename/commands", func(params martini.Params) string { - return toJson(bot.FindRobotDevice(params["robotname"], params["devicename"]).Commands()) + m.Get("/robots/:robotname/devices/:devicename/commands", func(params martini.Params, res http.ResponseWriter, req *http.Request) { + a.robot_device_commands(params["robotname"], params["devicename"], res, req) }) command_route := "/robots/:robotname/devices/:devicename/commands/:command" - m.Get(command_route, func(params martini.Params, res http.ResponseWriter, req *http.Request) string { - return a.executeCommand(bot, params, res, req) + m.Get(command_route, func(params martini.Params, res http.ResponseWriter, req *http.Request) { + a.executeCommand(params["robotname"], params["devicename"], params["command"], res, req) }) - m.Post(command_route, func(params martini.Params, res http.ResponseWriter, req *http.Request) string { - return a.executeCommand(bot, params, res, req) + m.Post(command_route, func(params martini.Params, res http.ResponseWriter, req *http.Request) { + a.executeCommand(params["robotname"], params["devicename"], params["command"], res, req) }) - go m.Run() + startApi(m) + return a +} + +func (me *api) robots(res http.ResponseWriter, req *http.Request) { + jsonRobots := make([]*jsonRobot, 0) + for _, robot := range me.master.Robots { + jsonRobots = append(jsonRobots, me.formatJsonRobot(robot)) + } + data, _ := json.Marshal(jsonRobots) + res.Header().Set("Content-Type", "application/json; charset=utf-8") + res.Write(data) +} + +func (me *api) robot(name string, res http.ResponseWriter, req *http.Request) { + data, _ := json.Marshal(me.formatJsonRobot(me.master.FindRobot(name))) + res.Header().Set("Content-Type", "application/json; charset=utf-8") + res.Write(data) +} + +func (me *api) robot_commands(name string, res http.ResponseWriter, req *http.Request) { + data, _ := json.Marshal(me.master.FindRobot(name).RobotCommands) + res.Header().Set("Content-Type", "application/json; charset=utf-8") + res.Write(data) +} + +func (me *api) robot_devices(name string, res http.ResponseWriter, req *http.Request) { + devices := me.master.FindRobot(name).GetDevices() + jsonDevices := make([]*jsonDevice, 0) + for _, device := range devices { + jsonDevices = append(jsonDevices, me.formatJsonDevice(device)) + } + data, _ := json.Marshal(jsonDevices) + res.Header().Set("Content-Type", "application/json; charset=utf-8") + res.Write(data) +} + +func (me *api) robot_device(robot string, device string, res http.ResponseWriter, req *http.Request) { + data, _ := json.Marshal(me.formatJsonDevice(me.master.FindRobotDevice(robot, device))) + res.Header().Set("Content-Type", "application/json; charset=utf-8") + res.Write(data) +} + +func (me *api) robot_device_commands(robot string, device string, res http.ResponseWriter, req *http.Request) { + data, _ := json.Marshal(me.master.FindRobotDevice(robot, device).Commands()) + res.Header().Set("Content-Type", "application/json; charset=utf-8") + res.Write(data) } func (a *api) formatJsonRobot(robot *Robot) *jsonRobot { @@ -118,27 +156,39 @@ func (a *api) formatJsonDevice(device *device) *jsonDevice { return jsonDevice } -func (a *api) executeCommand(bot *Master, params martini.Params, res http.ResponseWriter, req *http.Request) string { +func (a *api) executeCommand(robotname string, devicename string, commandname string, res http.ResponseWriter, req *http.Request) { + data, _ := ioutil.ReadAll(req.Body) + var body map[string]interface{} + json.Unmarshal(data, &body) + robot := a.master.FindRobotDevice(robotname, devicename) + commands := robot.Commands().([]string) + for command := range commands { + if commands[command] == commandname { + ret := Call(robot.Driver, commandname, body) + data, _ = json.Marshal(map[string]interface{}{"result": ret[0].Interface()}) + res.Header().Set("Content-Type", "application/json; charset=utf-8") + res.Write(data) + return + } + } + data, _ = json.Marshal(map[string]interface{}{"result": "Unknown Command"}) + res.Header().Set("Content-Type", "application/json; charset=utf-8") + res.Write(data) +} + +func (a *api) executeRobotCommand(bot *Master, params martini.Params, res http.ResponseWriter, req *http.Request) string { decoder := json.NewDecoder(req.Body) var body map[string]interface{} decoder.Decode(&body) - robot := bot.FindRobotDevice(params["robotname"], params["devicename"]) - commands := robot.Commands().([]string) - for command := range commands { - if commands[command] == params["command"] { - ret := Call(robot.Driver, params["command"], body) - return toJson(map[string]interface{}{"results": ret}) - } + if len(body) == 0 { + body = map[string]interface{}{} } - return toJson(map[string]interface{}{"results": "Unknown Command"}) -} - -func (a *api) executeRobotCommand(bot *Master, m_params martini.Params, params ...interface{}) string { - robot := bot.FindRobot(m_params["robotname"]) + body["robotname"] = params["robotname"] + robot := bot.FindRobot(params["robotname"]) in := make([]reflect.Value, len(params)) - for k, param := range params { - in[k] = reflect.ValueOf(param) - } - ret := reflect.ValueOf(robot.Commands[m_params["command"]]).Call(in) - return toJson(map[string]interface{}{"results": ret[0].Interface()}) + //for k, param := range params { + // in[k] = reflect.ValueOf(param) + //} + ret := reflect.ValueOf(robot.Commands[params["command"]]).Call(in) + return toJson(map[string]interface{}{"result": ret[0].Interface()}) } diff --git a/api_test.go b/api_test.go new file mode 100644 index 00000000..6201236c --- /dev/null +++ b/api_test.go @@ -0,0 +1,112 @@ +package gobot + +import ( + "bytes" + "encoding/json" + "github.com/go-martini/martini" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "io/ioutil" + "net/http" + "net/http/httptest" + "os" +) + +var _ = Describe("Master", func() { + var ( + myMaster *Master + a *api + ) + + BeforeEach(func() { + myMaster = GobotMaster() + startApi = func(m *martini.ClassicMartini) {} + a = Api(myMaster) + myMaster.Robots = []*Robot{ + newTestRobot("Robot 1"), + newTestRobot("Robot 2"), + newTestRobot("Robot 3"), + } + trap = func(c chan os.Signal) { + c <- os.Interrupt + } + myMaster.Start() + }) + + Context("when valid", func() { + It("should return all robots", func() { + request, _ := http.NewRequest("GET", "/robots", nil) + response := httptest.NewRecorder() + a.robots(response, request) + body, _ := ioutil.ReadAll(response.Body) + var i []map[string]interface{} + json.Unmarshal(body, &i) + Expect(len(i)).To(Equal(3)) + }) + It("should return robot", func() { + request, _ := http.NewRequest("GET", "/robots/Robot%201", nil) + response := httptest.NewRecorder() + a.robot("Robot 1", response, request) + body, _ := ioutil.ReadAll(response.Body) + var i map[string]interface{} + json.Unmarshal(body, &i) + Expect(i["name"].(string)).To(Equal("Robot 1")) + }) + It("should return robot commands", func() { + request, _ := http.NewRequest("GET", "/robots/Robot%201/commands", nil) + response := httptest.NewRecorder() + a.robot_commands("Robot 1", response, request) + body, _ := ioutil.ReadAll(response.Body) + var i []string + json.Unmarshal(body, &i) + Expect(i).To(Equal([]string{"Command1", "Command2"})) + }) + It("should return all robot devices", func() { + request, _ := http.NewRequest("GET", "/robots/Robot%201/devices", nil) + response := httptest.NewRecorder() + a.robot_devices("Robot 1", response, request) + body, _ := ioutil.ReadAll(response.Body) + var i []map[string]interface{} + json.Unmarshal(body, &i) + Expect(len(i)).To(Equal(3)) + }) + It("should return robot device", func() { + request, _ := http.NewRequest("GET", "/robots/Robot%201/devices/Device%201", nil) + response := httptest.NewRecorder() + a.robot_device("Robot 1", "Device 1", response, request) + body, _ := ioutil.ReadAll(response.Body) + var i map[string]interface{} + json.Unmarshal(body, &i) + Expect(i["name"].(string)).To(Equal("Device 1")) + }) + It("should return device commands", func() { + request, _ := http.NewRequest("GET", "/robots/Robot%201/devices/Device%201/commands", nil) + response := httptest.NewRecorder() + a.robot_device_commands("Robot 1", "Device 1", response, request) + body, _ := ioutil.ReadAll(response.Body) + var i []string + json.Unmarshal(body, &i) + Expect(i).To(Equal([]string{"DriverCommand1", "DriverCommand2", "DriverCommand3"})) + }) + It("should execute device command", func() { + request, _ := http.NewRequest("GET", "/robots/Robot%201/devices/Device%201/commands/Command1", bytes.NewBufferString(`{"name":"human"}`)) + request.Header.Add("Content-Type", "application/json") + response := httptest.NewRecorder() + a.executeCommand("Robot 1", "Device 1", "DriverCommand1", response, request) + body, _ := ioutil.ReadAll(response.Body) + var i map[string]interface{} + json.Unmarshal(body, &i) + Expect(i["result"]).To(Equal("hello human")) + }) + It("should not execute unknown device command", func() { + request, _ := http.NewRequest("GET", "/robots/Robot%201/devices/Device%201/commands/Command1", bytes.NewBufferString(`{"name":"human"}`)) + request.Header.Add("Content-Type", "application/json") + response := httptest.NewRecorder() + a.executeCommand("Robot 1", "Device 1", "DriverCommand4", response, request) + body, _ := ioutil.ReadAll(response.Body) + var i map[string]interface{} + json.Unmarshal(body, &i) + Expect(i["result"]).To(Equal("Unknown Command")) + }) + }) +}) diff --git a/robot_test.go b/robot_test.go index 643c64d4..78b8fe0d 100644 --- a/robot_test.go +++ b/robot_test.go @@ -3,6 +3,7 @@ package gobot import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "os" ) var _ = Describe("Robot", func() { @@ -14,6 +15,9 @@ var _ = Describe("Robot", func() { Context("when valid", func() { BeforeEach(func() { someRobot = newTestRobot("") + trap = func(c chan os.Signal) { + c <- os.Interrupt + } someRobot.Start() }) diff --git a/test_helper.go b/test_helper.go index 643e96da..c18d743f 100644 --- a/test_helper.go +++ b/test_helper.go @@ -1,5 +1,7 @@ package gobot +import "fmt" + type null struct{} func (null) Write(p []byte) (int, error) { @@ -8,11 +10,16 @@ func (null) Write(p []byte) (int, error) { type testDriver struct { Driver + Adaptor *testAdaptor } func (me *testDriver) Init() bool { return true } func (me *testDriver) Start() bool { return true } func (me *testDriver) Halt() bool { return true } +func (me *testDriver) DriverCommand1(params map[string]interface{}) string { + name := params["name"].(string) + return fmt.Sprintf("hello %v", name) +} type testAdaptor struct { Adaptor @@ -23,9 +30,10 @@ func (me *testAdaptor) Connect() bool { return true } func (me *testAdaptor) Disconnect() bool { return true } func (me *testAdaptor) Reconnect() bool { return true } -func newTestDriver(name string) *testDriver { +func newTestDriver(name string, adaptor *testAdaptor) *testDriver { d := new(testDriver) d.Name = name + d.Adaptor = adaptor d.Commands = []string{ "DriverCommand1", "DriverCommand2", @@ -45,13 +53,19 @@ func newTestAdaptor(name string) *testAdaptor { } func newTestRobot(name string) *Robot { + adaptor1 := newTestAdaptor("Connection 1") + adaptor2 := newTestAdaptor("Connection 2") + adaptor3 := newTestAdaptor("Connection 3") + driver1 := newTestDriver("Device 1", adaptor1) + driver2 := newTestDriver("Device 2", adaptor2) + driver3 := newTestDriver("Device 3", adaptor3) return &Robot{ Name: name, - Connections: []Connection{newTestAdaptor("Connection 1"), newTestAdaptor("Connection 2"), newTestAdaptor("Connection 3")}, - Devices: []Device{newTestDriver("Device 1"), newTestDriver("Device 2"), newTestDriver("Device 3")}, + Connections: []Connection{adaptor1, adaptor2, adaptor3}, + Devices: []Device{driver1, driver2, driver3}, Work: func() {}, Commands: map[string]interface{}{ - "Command1": func() {}, + "Command1": func() { fmt.Println("hi") }, "Command2": func() {}, }, }