Browse Source

Refactor tests and move to test dir

Matthew R Kasun 4 years ago
parent
commit
b08a260a92
4 changed files with 1052 additions and 0 deletions
  1. 197 0
      test/api_test.go
  2. 14 0
      test/config/environments/dev.yaml
  3. 621 0
      test/group_test.go
  4. 220 0
      test/user_test.go

+ 197 - 0
test/api_test.go

@@ -0,0 +1,197 @@
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"sync"
+	"testing"
+	"time"
+
+	controller "github.com/gravitl/netmaker/controllers"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mongoconn"
+	"github.com/stretchr/testify/assert"
+)
+
+type databaseError struct {
+	Inner  *int
+	Errors int
+}
+
+//should be use models.SuccessResponse and models.SuccessfulUserLoginResponse
+//rather than creating new type but having trouble decoding that way
+type Auth struct {
+	Username  string
+	AuthToken string
+}
+type Success struct {
+	Code     int
+	Message  string
+	Response Auth
+}
+
+type AuthorizeTestCase struct {
+	testname      string
+	name          string
+	password      string
+	code          int
+	tokenExpected bool
+	errMessage    string
+}
+
+func TestMain(m *testing.M) {
+	mongoconn.ConnectDatabase()
+	var waitgroup sync.WaitGroup
+	waitgroup.Add(1)
+	go controller.HandleRESTRequests(&waitgroup)
+	//wait for http server to start
+	time.Sleep(time.Second * 1)
+	os.Exit(m.Run())
+}
+
+func adminExists(t *testing.T) bool {
+	response, err := http.Get("http://localhost:8081/users/hasadmin")
+	assert.Nil(t, err, err)
+	assert.Equal(t, http.StatusOK, response.StatusCode)
+	defer response.Body.Close()
+	var body bool
+	json.NewDecoder(response.Body).Decode(&body)
+	return body
+}
+
+func api(t *testing.T, data interface{}, method, url, authorization string) (*http.Response, error) {
+	var request *http.Request
+	var err error
+	if data != "" {
+		payload, err := json.Marshal(data)
+		assert.Nil(t, err, err)
+		request, err = http.NewRequest(method, url, bytes.NewBuffer(payload))
+		assert.Nil(t, err, err)
+		request.Header.Set("Content-Type", "application/json")
+	} else {
+		request, err = http.NewRequest(method, url, nil)
+		assert.Nil(t, err, err)
+	}
+	if authorization != "" {
+		request.Header.Set("Authorization", "Bearer "+authorization)
+	}
+	client := http.Client{}
+	return client.Do(request)
+}
+
+func addAdmin(t *testing.T) {
+	var admin models.User
+	admin.UserName = "admin"
+	admin.Password = "password"
+	response, err := api(t, admin, http.MethodPost, "http://localhost:8081/users/createadmin", "secretkey")
+	assert.Nil(t, err, err)
+	assert.Equal(t, http.StatusOK, response.StatusCode)
+}
+
+func authenticate(t *testing.T) (string, error) {
+	var admin models.User
+	admin.UserName = "admin"
+	admin.Password = "password"
+	response, err := api(t, admin, http.MethodPost, "http://localhost:8081/users/authenticate", "secretkey")
+	assert.Nil(t, err, err)
+
+	var body Success
+	err = json.NewDecoder(response.Body).Decode(&body)
+	assert.Nil(t, err, err)
+	assert.NotEmpty(t, body.Response.AuthToken, "token not returned")
+	assert.Equal(t, "W1R3: Device admin Authorized", body.Message)
+
+	return body.Response.AuthToken, nil
+}
+
+func deleteAdmin(t *testing.T) {
+	if !adminExists(t) {
+		return
+	}
+	token, err := authenticate(t)
+	assert.Nil(t, err, err)
+	_, err = api(t, "", http.MethodDelete, "http://localhost:8081/users/admin", token)
+	assert.Nil(t, err, err)
+}
+
+func createGroup(t *testing.T) {
+	group := models.Group{}
+	group.NameID = "skynet"
+	group.AddressRange = "10.71.0.0/16"
+	response, err := api(t, group, http.MethodPost, "http://localhost:8081/api/groups", "secretkey")
+	assert.Nil(t, err, err)
+	assert.Equal(t, http.StatusOK, response.StatusCode)
+}
+
+func createKey(t *testing.T) {
+	key := models.AccessKey{}
+	key.Name = "skynet"
+	key.Uses = 10
+	response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/groups/skynet/keys", "secretkey")
+	assert.Nil(t, err, err)
+	assert.Equal(t, http.StatusOK, response.StatusCode)
+	defer response.Body.Close()
+	message, err := ioutil.ReadAll(response.Body)
+	assert.Nil(t, err, err)
+	assert.NotNil(t, message, message)
+}
+
+func getKey(t *testing.T, name string) models.AccessKey {
+	response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups/skynet/keys", "secretkey")
+	assert.Nil(t, err, err)
+	assert.Equal(t, http.StatusOK, response.StatusCode)
+	defer response.Body.Close()
+	var keys []models.AccessKey
+	err = json.NewDecoder(response.Body).Decode(&keys)
+	assert.Nil(t, err, err)
+	for _, key := range keys {
+		if key.Name == name {
+			return key
+		}
+	}
+	return models.AccessKey{}
+}
+
+func deleteKey(t *testing.T, key, group string) {
+	response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/groups/"+group+"/keys/"+key, "secretkey")
+	assert.Nil(t, err, err)
+	//api does not return Deleted Count at this time
+	//defer response.Body.Close()
+	//var message mongo.DeleteResult
+	//err = json.NewDecoder(response.Body).Decode(&message)
+	//assert.Nil(t, err, err)
+	assert.Equal(t, http.StatusOK, response.StatusCode)
+	//assert.Equal(t, int64(1), message.DeletedCount)
+}
+
+func groupExists(t *testing.T) bool {
+	response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups", "secretkey")
+	assert.Nil(t, err, err)
+	defer response.Body.Close()
+	assert.Equal(t, http.StatusOK, response.StatusCode)
+	err = json.NewDecoder(response.Body).Decode(&Groups)
+	assert.Nil(t, err, err)
+	if Groups == nil {
+		return false
+	} else {
+		return true
+	}
+}
+
+func deleteGroups(t *testing.T) {
+
+	response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups", "secretkey")
+	assert.Nil(t, err, err)
+	defer response.Body.Close()
+	assert.Equal(t, http.StatusOK, response.StatusCode)
+	err = json.NewDecoder(response.Body).Decode(&Groups)
+	assert.Nil(t, err, err)
+	for _, group := range Groups {
+		name := group.DisplayName
+		_, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/groups/"+name, "secretkey")
+		assert.Nil(t, err, err)
+	}
+}

+ 14 - 0
test/config/environments/dev.yaml

@@ -0,0 +1,14 @@
+server:
+  host: "localhost"
+  apiport: "8081"
+  grpcport: "50051"
+  masterkey: "secretkey"
+  allowedorigin: "*"
+  restbackend: true            
+  agentbackend: true
+mongoconn:
+  user: "mongoadmin"
+  pass: "mongopass"
+  host: "localhost"
+  port: "27017"
+  opts: '/?authSource=admin'

+ 621 - 0
test/group_test.go

@@ -0,0 +1,621 @@
+package main
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"testing"
+
+	"github.com/gravitl/netmaker/models"
+	"github.com/stretchr/testify/assert"
+	"go.mongodb.org/mongo-driver/mongo"
+)
+
+var Groups []models.Group
+
+func TestCreateGroup(t *testing.T) {
+	group := models.Group{}
+	group.NameID = "skynet"
+	group.AddressRange = "10.71.0.0/16"
+	deleteGroups(t)
+	t.Run("CreateGroup", func(t *testing.T) {
+		response, err := api(t, group, http.MethodPost, "http://localhost:8081/api/groups", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+	})
+	t.Run("InvalidToken", func(t *testing.T) {
+		response, err := api(t, group, http.MethodPost, "http://localhost:8081/api/groups", "badkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+	})
+	t.Run("BadName", func(t *testing.T) {
+		//issue #42
+		t.Skip()
+	})
+	t.Run("BadAddress", func(t *testing.T) {
+		//issue #42
+		t.Skip()
+	})
+	t.Run("DuplicateGroup", func(t *testing.T) {
+		//issue #42
+		t.Skip()
+	})
+}
+
+func TestGetGroups(t *testing.T) {
+	t.Run("ValidToken", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		err = json.NewDecoder(response.Body).Decode(&Groups)
+		assert.Nil(t, err, err)
+	})
+	t.Run("InvalidToken", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups", "badkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+	})
+}
+
+func TestGetGroup(t *testing.T) {
+	t.Run("ValidToken", func(t *testing.T) {
+		var group models.Group
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		err = json.NewDecoder(response.Body).Decode(&group)
+		assert.Nil(t, err, err)
+		assert.Equal(t, "skynet", group.DisplayName)
+	})
+	t.Run("InvalidToken", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups/skynet", "badkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+	})
+	t.Run("InvalidGroup", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups/badgroup", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, "W1R3: This group does not exist.", message.Message)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
+}
+
+func TestGetGroupNodeNumber(t *testing.T) {
+	t.Run("ValidKey", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups/skynet/numnodes", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message int
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		//assert.Equal(t, "W1R3: This group does not exist.", message.Message)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+	})
+	t.Run("InvalidKey", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups/skynet/numnodes", "badkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+	})
+	t.Run("BadGroup", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups/badgroup/numnodes", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, "W1R3: This group does not exist.", message.Message)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
+}
+
+func TestDeleteGroup(t *testing.T) {
+	t.Run("InvalidKey", func(t *testing.T) {
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/groups/skynet", "badkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+	})
+	t.Run("ValidKey", func(t *testing.T) {
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message mongo.DeleteResult
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		assert.Equal(t, int64(1), message.DeletedCount)
+
+	})
+	t.Run("BadGroup", func(t *testing.T) {
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/groups/badgroup", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, "W1R3: This group does not exist.", message.Message)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
+	t.Run("NodesExist", func(t *testing.T) {
+		t.Skip()
+	})
+	//Create Group for follow-on tests
+	createGroup(t)
+}
+
+func TestCreateAccessKey(t *testing.T) {
+	key := models.AccessKey{}
+	key.Name = "skynet"
+	key.Uses = 10
+	t.Run("MultiUse", func(t *testing.T) {
+		response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/groups/skynet/keys", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		message, err := ioutil.ReadAll(response.Body)
+		assert.Nil(t, err, err)
+		assert.NotNil(t, message, message)
+		returnedkey := getKey(t, key.Name)
+		assert.Equal(t, key.Name, returnedkey.Name)
+		assert.Equal(t, key.Uses, returnedkey.Uses)
+	})
+	deleteKey(t, "skynet", "skynet")
+	t.Run("ZeroUse", func(t *testing.T) {
+		//t.Skip()
+		key.Uses = 0
+		response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/groups/skynet/keys", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		message, err := ioutil.ReadAll(response.Body)
+		assert.Nil(t, err, err)
+		assert.NotNil(t, message, message)
+		returnedkey := getKey(t, key.Name)
+		assert.Equal(t, key.Name, returnedkey.Name)
+		assert.Equal(t, 1, returnedkey.Uses)
+	})
+	t.Run("DuplicateAccessKey", func(t *testing.T) {
+		//t.Skip()
+		//this will fail
+		response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/groups/skynet/keys", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
+		deleteKey(t, key.Name, "skynet")
+	})
+
+	t.Run("InvalidToken", func(t *testing.T) {
+		response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/groups/skynet/keys", "badkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+	})
+	t.Run("BadGroup", func(t *testing.T) {
+		response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/groups/badgroup/keys", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, "W1R3: This group does not exist.", message.Message)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
+}
+
+func TestDeleteKey(t *testing.T) {
+	t.Run("KeyValid", func(t *testing.T) {
+		//fails -- deletecount not returned
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/groups/skynet/keys/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message mongo.DeleteResult
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		assert.Equal(t, int64(1), message.DeletedCount)
+	})
+	t.Run("InValidKey", func(t *testing.T) {
+		//fails -- status message  not returned
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/groups/skynet/keys/badkey", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, "W1R3: This key does not exist.", message.Message)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
+	t.Run("KeyInValidGroup", func(t *testing.T) {
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/groups/badgroup/keys/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, "W1R3: This group does not exist.", message.Message)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
+	t.Run("InvalidCredentials", func(t *testing.T) {
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/groups/skynet/keys/skynet", "badkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+	})
+}
+
+func TestGetKeys(t *testing.T) {
+	createKey(t)
+	t.Run("Valid", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups/skynet/keys", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		var keys []models.AccessKey
+		err = json.NewDecoder(response.Body).Decode(&keys)
+		assert.Nil(t, err, err)
+	})
+	//deletekeys
+	t.Run("InvalidGroup", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups/badgroup/keys", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, "W1R3: This group does not exist.", message.Message)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
+	t.Run("InvalidCredentials", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/groups/skynet/keys", "badkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+	})
+}
+
+func TestUpdateGroup(t *testing.T) {
+	var returnedGroup models.Group
+	t.Run("UpdateNameID", func(t *testing.T) {
+		type Group struct {
+			NameID string
+		}
+		var group Group
+		group.NameID = "wirecat"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, group.NameID, returnedGroup.NameID)
+	})
+	t.Run("NameIDInvalidCredentials", func(t *testing.T) {
+		type Group struct {
+			NameID string
+		}
+		var group Group
+		group.NameID = "wirecat"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "badkey")
+		assert.Nil(t, err, err)
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+	})
+	t.Run("InvalidGroup", func(t *testing.T) {
+		type Group struct {
+			NameID string
+		}
+		var group Group
+		group.NameID = "wirecat"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/badgroup", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusNotFound, message.Code)
+		assert.Equal(t, "W1R3: This group does not exist.", message.Message)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
+	t.Run("UpdateNameIDTooLong", func(t *testing.T) {
+		type Group struct {
+			NameID string
+		}
+		var group Group
+		group.NameID = "wirecat-skynet"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
+	})
+	t.Run("UpdateAddress", func(t *testing.T) {
+		type Group struct {
+			AddressRange string
+		}
+		var group Group
+		group.AddressRange = "10.0.0.1/24"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, group.AddressRange, returnedGroup.AddressRange)
+	})
+	t.Run("UpdateAddressInvalid", func(t *testing.T) {
+		type Group struct {
+			AddressRange string
+		}
+		var group Group
+		group.AddressRange = "10.0.0.1/36"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
+	})
+	t.Run("UpdateDisplayName", func(t *testing.T) {
+		type Group struct {
+			DisplayName string
+		}
+		var group Group
+		group.DisplayName = "wirecat"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, group.DisplayName, returnedGroup.DisplayName)
+
+	})
+	t.Run("UpdateDisplayNameInvalidName", func(t *testing.T) {
+		type Group struct {
+			DisplayName string
+		}
+		var group Group
+		//create name that is longer than 100 chars
+		name := ""
+		for i := 0; i < 101; i++ {
+			name = name + "a"
+		}
+		group.DisplayName = name
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnprocessableEntity, message.Code)
+		assert.Equal(t, "W1R3: Field validation for 'DisplayName' failed.", message.Message)
+		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
+	})
+	t.Run("UpdateInterface", func(t *testing.T) {
+		type Group struct {
+			DefaultInterface string
+		}
+		var group Group
+		group.DefaultInterface = "netmaker"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, group.DefaultInterface, returnedGroup.DefaultInterface)
+
+	})
+	t.Run("UpdateListenPort", func(t *testing.T) {
+		type Group struct {
+			DefaultListenPort int32
+		}
+		var group Group
+		group.DefaultListenPort = 6000
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, group.DefaultListenPort, returnedGroup.DefaultListenPort)
+	})
+	t.Run("UpdateListenPortInvalidPort", func(t *testing.T) {
+		type Group struct {
+			DefaultListenPort int32
+		}
+		var group Group
+		group.DefaultListenPort = 1023
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnprocessableEntity, message.Code)
+		assert.Equal(t, "W1R3: Field validation for 'DefaultListenPort' failed.", message.Message)
+		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
+	})
+	t.Run("UpdatePostUP", func(t *testing.T) {
+		type Group struct {
+			DefaultPostUp string
+		}
+		var group Group
+		group.DefaultPostUp = "sudo wg add-conf wc-netmaker /etc/wireguard/peers/conf"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, group.DefaultPostUp, returnedGroup.DefaultPostUp)
+	})
+	t.Run("UpdatePreUP", func(t *testing.T) {
+		type Group struct {
+			DefaultPreUp string
+		}
+		var group Group
+		group.DefaultPreUp = "test string"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, group.DefaultPreUp, returnedGroup.DefaultPreUp)
+	})
+	t.Run("UpdateKeepAlive", func(t *testing.T) {
+		type Group struct {
+			DefaultKeepalive int32
+		}
+		var group Group
+		group.DefaultKeepalive = 60
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, group.DefaultKeepalive, returnedGroup.DefaultKeepalive)
+	})
+	t.Run("UpdateKeepAliveTooBig", func(t *testing.T) {
+		type Group struct {
+			DefaultKeepAlive int32
+		}
+		var group Group
+		group.DefaultKeepAlive = 1001
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnprocessableEntity, message.Code)
+		assert.Equal(t, "W1R3: Field validation for 'DefaultKeepAlive' failed.", message.Message)
+		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
+	})
+	t.Run("UpdateSaveConfig", func(t *testing.T) {
+		//causes panic
+		t.Skip()
+		type Group struct {
+			DefaultSaveConfig *bool
+		}
+		var group Group
+		value := false
+		group.DefaultSaveConfig = &value
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, *group.DefaultSaveConfig, *returnedGroup.DefaultSaveConfig)
+	})
+	t.Run("UpdateManualSignUP", func(t *testing.T) {
+		t.Skip()
+		type Group struct {
+			AllowManualSignUp *bool
+		}
+		var group Group
+		value := true
+		group.AllowManualSignUp = &value
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, *group.AllowManualSignUp, *returnedGroup.AllowManualSignUp)
+	})
+	t.Run("DefaultCheckInterval", func(t *testing.T) {
+		type Group struct {
+			DefaultCheckInInterval int32
+		}
+		var group Group
+		group.DefaultCheckInInterval = 6000
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, group.DefaultCheckInInterval, returnedGroup.DefaultCheckInInterval)
+	})
+	t.Run("DefaultCheckIntervalTooBig", func(t *testing.T) {
+		type Group struct {
+			DefaultCheckInInterval int32
+		}
+		var group Group
+		group.DefaultCheckInInterval = 100001
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnprocessableEntity, message.Code)
+		assert.Equal(t, "W1R3: Field validation for 'DefaultCheckInInterval' failed.", message.Message)
+		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
+	})
+	t.Run("MultipleFields", func(t *testing.T) {
+		type Group struct {
+			DisplayName       string
+			DefaultListenPort int32
+		}
+		var group Group
+		group.DefaultListenPort = 7777
+		group.DisplayName = "multi"
+		response, err := api(t, group, http.MethodPut, "http://localhost:8081/api/groups/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&returnedGroup)
+		assert.Nil(t, err, err)
+		assert.Equal(t, group.DisplayName, returnedGroup.DisplayName)
+		assert.Equal(t, group.DefaultListenPort, returnedGroup.DefaultListenPort)
+	})
+}

+ 220 - 0
test/user_test.go

@@ -0,0 +1,220 @@
+package main
+
+import (
+	"encoding/json"
+	"net/http"
+	"testing"
+
+	"github.com/gravitl/netmaker/models"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAdminCreation(t *testing.T) {
+	var admin models.UserAuthParams
+	var user models.User
+	admin.UserName = "admin"
+	admin.Password = "password"
+	t.Run("AdminCreationSuccess", func(t *testing.T) {
+		if adminExists(t) {
+			deleteAdmin(t)
+		}
+		response, err := api(t, admin, http.MethodPost, "http://localhost:8081/users/createadmin", "")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&user)
+		assert.Nil(t, err, err)
+		assert.Equal(t, admin.UserName, user.UserName)
+		assert.Equal(t, true, user.IsAdmin)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		assert.True(t, adminExists(t), "Admin creation failed")
+	})
+	t.Run("AdminCreationFailure", func(t *testing.T) {
+		if !adminExists(t) {
+			addAdmin(t)
+		}
+		response, err := api(t, admin, http.MethodPost, "http://localhost:8081/users/createadmin", "")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "W1R3: Admin already exists! ", message.Message)
+	})
+
+}
+
+func TestGetUser(t *testing.T) {
+
+	//ensure admin exists
+	if !adminExists(t) {
+		addAdmin(t)
+	}
+	//authenticate
+	t.Run("GetUserWithValidToken", func(t *testing.T) {
+		token, err := authenticate(t)
+		assert.Nil(t, err, err)
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/users/admin", token)
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var user models.User
+		json.NewDecoder(response.Body).Decode(&user)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		assert.Equal(t, "admin", user.UserName)
+		assert.Equal(t, true, user.IsAdmin)
+	})
+	t.Run("GetUserWithInvalidToken", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/users/admin", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		t.Log(response.Body)
+	})
+}
+
+func TestUpdateUser(t *testing.T) {
+	if !adminExists(t) {
+		addAdmin(t)
+	}
+	token, err := authenticate(t)
+	assert.Nil(t, err, err)
+	var admin models.UserAuthParams
+	var user models.User
+	var message models.ErrorResponse
+	t.Run("UpdateWrongToken", func(t *testing.T) {
+		admin.UserName = "admin"
+		admin.Password = "admin"
+		response, err := api(t, admin, http.MethodPut, "http://localhost:8081/users/admin", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, "W1R3: Error Verifying Auth Token.", message.Message)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+	})
+	t.Run("UpdateSuccess", func(t *testing.T) {
+		admin.UserName = "admin"
+		admin.Password = "password"
+		response, err := api(t, admin, http.MethodPut, "http://localhost:8081/users/admin", token)
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&user)
+		assert.Nil(t, err, err)
+		assert.Equal(t, admin.UserName, user.UserName)
+		assert.Equal(t, true, user.IsAdmin)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+	})
+
+}
+
+func TestDeleteUser(t *testing.T) {
+	if !adminExists(t) {
+		addAdmin(t)
+	}
+	token, err := authenticate(t)
+	assert.Nil(t, err, err)
+	t.Run("DeleteUser-WongAdmin", func(t *testing.T) {
+		//skip for now ... shouldn't panic
+		t.Skip()
+		function := func() {
+			_, _ = api(t, "", http.MethodDelete, "http://localhost:8081/users/xxxx", token)
+		}
+		assert.Panics(t, function, "")
+	})
+	t.Run("DeleteUser-InvalidCredentials", func(t *testing.T) {
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/users/admin", "secretkey")
+		assert.Nil(t, err, err)
+		var message models.ErrorResponse
+		json.NewDecoder(response.Body).Decode(&message)
+		assert.Equal(t, "W1R3: Error Verifying Auth Token.", message.Message)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+	})
+	t.Run("DeleteUser-ValidCredentials", func(t *testing.T) {
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/users/admin", token)
+		assert.Nil(t, err, err)
+		var body string
+		json.NewDecoder(response.Body).Decode(&body)
+		assert.Equal(t, "admin deleted.", body)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+	})
+	t.Run("DeleteUser-NoAdmin", func(t *testing.T) {
+		//skip for now ... shouldn't panic
+		t.Skip()
+		function := func() {
+			_, _ = api(t, "", http.MethodDelete, "http://localhost:8081/users/admin", token)
+		}
+		assert.Panics(t, function, "")
+	})
+	addAdmin(t)
+}
+
+func TestAuthenticateUser(t *testing.T) {
+	cases := []AuthorizeTestCase{
+		AuthorizeTestCase{
+			testname:      "Invalid User",
+			name:          "invaliduser",
+			password:      "password",
+			code:          http.StatusBadRequest,
+			tokenExpected: false,
+			errMessage:    "W1R3: User invaliduser not found.",
+		},
+		AuthorizeTestCase{
+			testname:      "empty user",
+			name:          "",
+			password:      "password",
+			code:          http.StatusBadRequest,
+			tokenExpected: false,
+			errMessage:    "W1R3: Username can't be empty",
+		},
+		AuthorizeTestCase{
+			testname:      "empty password",
+			name:          "admin",
+			password:      "",
+			code:          http.StatusBadRequest,
+			tokenExpected: false,
+			errMessage:    "W1R3: Password can't be empty",
+		},
+		AuthorizeTestCase{
+			testname:      "Invalid Password",
+			name:          "admin",
+			password:      "xxxxxxx",
+			code:          http.StatusUnauthorized,
+			tokenExpected: false,
+			errMessage:    "W1R3: Wrong Password.",
+		},
+		AuthorizeTestCase{
+			testname:      "Valid User",
+			name:          "admin",
+			password:      "password",
+			code:          http.StatusOK,
+			tokenExpected: true,
+			errMessage:    "W1R3: Device Admin Authorized",
+		},
+	}
+
+	if !adminExists(t) {
+		addAdmin(t)
+	}
+	for _, tc := range cases {
+		t.Run(tc.testname, func(t *testing.T) {
+			var admin models.User
+			admin.UserName = tc.name
+			admin.Password = tc.password
+			response, err := api(t, admin, http.MethodPost, "http://localhost:8081/users/authenticate", "secretkey")
+			assert.Nil(t, err, err)
+			defer response.Body.Close()
+			if tc.tokenExpected {
+				var body Success
+				err = json.NewDecoder(response.Body).Decode(&body)
+				assert.Nil(t, err, err)
+				assert.NotEmpty(t, body.Response.AuthToken, "token not returned")
+				assert.Equal(t, "W1R3: Device admin Authorized", body.Message)
+			} else {
+				var bad models.ErrorResponse
+				json.NewDecoder(response.Body).Decode(&bad)
+				assert.Equal(t, tc.errMessage, bad.Message)
+			}
+			assert.Equal(t, tc.code, response.StatusCode)
+		})
+	}
+}