diff --git a/.vscode/launch.json b/.vscode/launch.json index b3d3cce..5448ce6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "go", "request": "launch", "mode": "auto", - "program": "${workspaceFolder}/", + "program": "${workspaceFolder}/cmd/rove-server/main.go", "cwd": "${workspaceFolder}", "env": {}, "args": [], diff --git a/pkg/server/api.go b/pkg/rove/api.go similarity index 73% rename from pkg/server/api.go rename to pkg/rove/api.go index 5e40393..5d1b8a1 100644 --- a/pkg/server/api.go +++ b/pkg/rove/api.go @@ -1,10 +1,17 @@ -package server +package rove -import "github.com/mdiluz/rove/pkg/game" +import ( + "github.com/mdiluz/rove/pkg/game" +) // ============================== // API: /status method: GET -// Queries the status of the server + +// Status queries the status of the server +func (s Server) Status() (r StatusResponse, err error) { + s.GET("status", &r) + return +} // StatusResponse is a struct that contains information on the status of the server type StatusResponse struct { @@ -14,8 +21,13 @@ type StatusResponse struct { // ============================== // API: /register method: POST -// Registers a user account by name + +// Register registers a user account by name // Responds with a unique ID for that account to be used in future requests +func (s Server) Register(d RegisterData) (r RegisterResponse, err error) { + err = s.POST("register", d, &r) + return +} // RegisterData describes the data to send when registering type RegisterData struct { @@ -32,8 +44,13 @@ type RegisterResponse struct { // ============================== // API: /spawn method: POST -// Spawns the rover for an account + +// Spawn spawns the rover for an account // Responds with the position of said rover +func (s Server) Spawn(d SpawnData) (r SpawnResponse, err error) { + err = s.POST("spawn", d, &r) + return +} // SpawnData is the data to be sent for the spawn command type SpawnData struct { @@ -51,7 +68,12 @@ type SpawnResponse struct { // ============================== // API: /commands method: POST -// Issues a set of commands from the user + +// Commands issues a set of commands from the user +func (s Server) Commands(d CommandsData) (r CommandsResponse, err error) { + err = s.POST("commands", d, &r) + return +} // CommandsData is a set of commands to execute in order type CommandsData struct { @@ -70,17 +92,6 @@ const ( CommandMove = "move" ) -const ( - BearingNorth = "North" - BearingNorthEast - BearingEast - BearingSouthEast - BearingSouth - BearingSouthWest - BearingWest - BearingNorthWest -) - // Command describes a single command to execute // it contains the type, and then any members used for each command type type Command struct { @@ -94,7 +105,13 @@ type Command struct { // ================ // API: /radar POST -// Queries the current radar for the user + +// Radar queries the current radar for the user +// Commands issues a set of commands from the user +func (s Server) Radar(d RadarData) (r RadarResponse, err error) { + err = s.POST("radar", d, &r) + return +} // RadarData describes the input data to request an accounts current radar type RadarData struct { diff --git a/pkg/rove/http.go b/pkg/rove/http.go new file mode 100644 index 0000000..30e4aa5 --- /dev/null +++ b/pkg/rove/http.go @@ -0,0 +1,64 @@ +package rove + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" +) + +// Server is a simple wrapper to a server path +type Server string + +// GET performs a GET request +func (s Server) GET(path string, out interface{}) error { + url := url.URL{ + Scheme: "http", + Host: string(s), + Path: path, + } + if resp, err := http.Get(url.String()); err != nil { + return err + + } else if resp.StatusCode != http.StatusOK { + return fmt.Errorf("http.Get returned status %d: %s", resp.StatusCode, resp.Status) + + } else { + return json.NewDecoder(resp.Body).Decode(out) + } +} + +// POST performs a POST request +func (s Server) POST(path string, in interface{}, out interface{}) error { + url := url.URL{ + Scheme: "http", + Host: string(s), + Path: path, + } + client := &http.Client{} + + // Marshal the input + marshalled, err := json.Marshal(in) + if err != nil { + return err + } + + // Set up the request + req, err := http.NewRequest("POST", url.String(), bytes.NewReader(marshalled)) + if err != nil { + return err + } + + // Do the POST + req.Header.Set("Content-Type", "application/json") + if resp, err := client.Do(req); err != nil { + return err + + } else if resp.StatusCode != http.StatusOK { + return fmt.Errorf("http returned status %d", resp.StatusCode) + + } else { + return json.NewDecoder(resp.Body).Decode(out) + } +} diff --git a/pkg/rove/integration_test.go b/pkg/rove/integration_test.go new file mode 100644 index 0000000..cec57f1 --- /dev/null +++ b/pkg/rove/integration_test.go @@ -0,0 +1,39 @@ +package rove + +import ( + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" +) + +var server Server = "localhost:80" + +func TestStatus(t *testing.T) { + status, err := server.Status() + assert.NoError(t, err, "Status must not return error") + assert.True(t, status.Ready, "Server must return ready") + assert.NotZero(t, len(status.Version), "Version must not be empty") +} + +func TestRegister(t *testing.T) { + d1 := RegisterData{ + Name: uuid.New().String(), + } + r1, err := server.Register(d1) + assert.NoError(t, err, "Register must not return error") + assert.True(t, r1.Success, "Register must return success") + assert.NotZero(t, len(r1.Id), "Register must return registration ID") + + d2 := RegisterData{ + Name: uuid.New().String(), + } + r2, err := server.Register(d2) + assert.NoError(t, err, "Register must not return error") + assert.True(t, r2.Success, "Register must return success") + assert.NotZero(t, len(r2.Id), "Register must return registration ID") + + r3, err := server.Register(d1) + assert.NoError(t, err, "Register must not return error") + assert.False(t, r3.Success, "Register must return fail for duplicate registration") +} diff --git a/pkg/server/integration_test.go b/pkg/server/integration_test.go deleted file mode 100644 index 42ea5a0..0000000 --- a/pkg/server/integration_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// +build integration - -package server - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "net/url" - "testing" - - "github.com/google/uuid" - "github.com/stretchr/testify/assert" -) - -var serverUrl = "localhost:80" - -func TestStatus(t *testing.T) { - url := url.URL{ - Scheme: "http", - Host: serverUrl, - Path: "status", - } - resp, err := http.Get(url.String()) - assert.NoError(t, err, "http.Get must not return error") - assert.Equal(t, http.StatusOK, resp.StatusCode, "http.Get must return StatusOK") - - var status StatusResponse - err = json.NewDecoder(resp.Body).Decode(&status) - assert.NoError(t, err, "json decode must not return error") - - assert.NoError(t, err, "Status must not return error") - assert.True(t, status.Ready, "Server must return ready") - assert.NotZero(t, len(status.Version), "Version must not be empty") -} - -// helper for register test -func register(name string) (register RegisterResponse, err error) { - url := url.URL{ - Scheme: "http", - Host: serverUrl, - Path: "register", - } - - // Marshal the register data struct - data := RegisterData{Name: name} - marshalled, err := json.Marshal(data) - - // Set up the request - req, err := http.NewRequest("POST", url.String(), bytes.NewReader(marshalled)) - req.Header.Set("Content-Type", "application/json") - - // Do the request - client := &http.Client{} - if resp, err := client.Do(req); err != nil { - return RegisterResponse{}, err - } else { - defer resp.Body.Close() - - // Handle any errors - if resp.StatusCode != http.StatusOK { - return RegisterResponse{}, fmt.Errorf("Status request returned %d", resp.StatusCode) - } else { - // Decode the reply - err = json.NewDecoder(resp.Body).Decode(®ister) - } - } - - return -} - -func TestRegister(t *testing.T) { - a := uuid.New().String() - reg1, err := register(a) - assert.NoError(t, err, "Register must not return error") - assert.True(t, reg1.Success, "Register must return success") - assert.NotZero(t, len(reg1.Id), "Register must return registration ID") - - b := uuid.New().String() - reg2, err := register(b) - assert.NoError(t, err, "Register must not return error") - assert.True(t, reg2.Success, "Register must return success") - assert.NotZero(t, len(reg2.Id), "Register must return registration ID") - - reg2, err = register(a) - assert.NoError(t, err, "Register must not return error") - assert.False(t, reg2.Success, "Register must return fail for duplicate registration") -}