Refactor and implement the api functions

This commit is contained in:
Marc Di Luzio 2020-06-05 17:59:14 +01:00
parent 3474e6ca8c
commit f82565bf22
5 changed files with 139 additions and 108 deletions

2
.vscode/launch.json vendored
View file

@ -9,7 +9,7 @@
"type": "go", "type": "go",
"request": "launch", "request": "launch",
"mode": "auto", "mode": "auto",
"program": "${workspaceFolder}/", "program": "${workspaceFolder}/cmd/rove-server/main.go",
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"env": {}, "env": {},
"args": [], "args": [],

View file

@ -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 // 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 // StatusResponse is a struct that contains information on the status of the server
type StatusResponse struct { type StatusResponse struct {
@ -14,8 +21,13 @@ type StatusResponse struct {
// ============================== // ==============================
// API: /register method: POST // 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 // 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 // RegisterData describes the data to send when registering
type RegisterData struct { type RegisterData struct {
@ -32,8 +44,13 @@ type RegisterResponse struct {
// ============================== // ==============================
// API: /spawn method: POST // API: /spawn method: POST
// Spawns the rover for an account
// Spawn spawns the rover for an account
// Responds with the position of said rover // 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 // SpawnData is the data to be sent for the spawn command
type SpawnData struct { type SpawnData struct {
@ -51,7 +68,12 @@ type SpawnResponse struct {
// ============================== // ==============================
// API: /commands method: POST // 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 // CommandsData is a set of commands to execute in order
type CommandsData struct { type CommandsData struct {
@ -70,17 +92,6 @@ const (
CommandMove = "move" CommandMove = "move"
) )
const (
BearingNorth = "North"
BearingNorthEast
BearingEast
BearingSouthEast
BearingSouth
BearingSouthWest
BearingWest
BearingNorthWest
)
// Command describes a single command to execute // Command describes a single command to execute
// it contains the type, and then any members used for each command type // it contains the type, and then any members used for each command type
type Command struct { type Command struct {
@ -94,7 +105,13 @@ type Command struct {
// ================ // ================
// API: /radar POST // 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 // RadarData describes the input data to request an accounts current radar
type RadarData struct { type RadarData struct {

64
pkg/rove/http.go Normal file
View file

@ -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)
}
}

View file

@ -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")
}

View file

@ -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(&register)
}
}
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")
}