Skip to content

Commit

Permalink
test: add bots unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
Fog3211 committed Feb 29, 2024
1 parent 8a7957b commit 50005b1
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 17 deletions.
36 changes: 24 additions & 12 deletions handlers/bots.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,19 @@ import (
"github.com/stakwork/sphinx-tribes/db"
)

func CreateOrEditBot(w http.ResponseWriter, r *http.Request) {
type botHandler struct {
db db.Database
verifyTribeUUID func(uuid string, checkTimestamp bool) (string, error)
}

func NewBotHandler(db db.Database) *botHandler {
return &botHandler{
db: db,
verifyTribeUUID: auth.VerifyTribeUUID,
}
}

func (h *botHandler) CreateOrEditBot(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)

Expand All @@ -36,7 +48,7 @@ func CreateOrEditBot(w http.ResponseWriter, r *http.Request) {

now := time.Now()

extractedPubkey, err := auth.VerifyTribeUUID(bot.UUID, false)
extractedPubkey, err := h.verifyTribeUUID(bot.UUID, false)
if err != nil {
fmt.Println(err)
w.WriteHeader(http.StatusUnauthorized)
Expand All @@ -54,9 +66,9 @@ func CreateOrEditBot(w http.ResponseWriter, r *http.Request) {

bot.OwnerPubKey = extractedPubkey
bot.Updated = &now
bot.UniqueName, _ = BotUniqueNameFromName(bot.Name)
bot.UniqueName, _ = h.BotUniqueNameFromName(bot.Name)

_, err = db.DB.CreateOrEditBot(bot)
_, err = h.db.CreateOrEditBot(bot)
if err != nil {
fmt.Println("=> ERR createOrEditBot", err)
w.WriteHeader(http.StatusBadRequest)
Expand All @@ -67,22 +79,22 @@ func CreateOrEditBot(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(bot)
}

func GetListedBots(w http.ResponseWriter, r *http.Request) {
bots := db.DB.GetListedBots(r)
func (h *botHandler) GetListedBots(w http.ResponseWriter, r *http.Request) {
bots := h.db.GetListedBots(r)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(bots)
}

func GetBot(w http.ResponseWriter, r *http.Request) {
func (h *botHandler) GetBot(w http.ResponseWriter, r *http.Request) {
uuid := chi.URLParam(r, "uuid")
bot := db.DB.GetBot(uuid)
bot := h.db.GetBot(uuid)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(bot)
}

func GetBotByUniqueName(w http.ResponseWriter, r *http.Request) {
func (h *botHandler) GetBotByUniqueName(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
bot := db.DB.GetBotByUniqueName(name)
bot := h.db.GetBotByUniqueName(name)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(bot)
}
Expand Down Expand Up @@ -141,7 +153,7 @@ func DeleteBot(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(true)
}

func BotUniqueNameFromName(name string) (string, error) {
func (h *botHandler) BotUniqueNameFromName(name string) (string, error) {
pathOne := strings.ToLower(strings.Join(strings.Fields(name), ""))
reg, err := regexp.Compile("[^a-zA-Z0-9]+")
if err != nil {
Expand All @@ -154,7 +166,7 @@ func BotUniqueNameFromName(name string) (string, error) {
if n > 0 {
uniquepath = path + strconv.Itoa(n)
}
existing := db.DB.GetBotByUniqueName(uniquepath)
existing := h.db.GetBotByUniqueName(uniquepath)
if existing.UUID != "" {
n = n + 1
} else {
Expand Down
259 changes: 259 additions & 0 deletions handlers/bots_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
package handlers

import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

"github.com/go-chi/chi"
"github.com/stakwork/sphinx-tribes/auth"
"github.com/stakwork/sphinx-tribes/db"
mocks "github.com/stakwork/sphinx-tribes/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestCreateOrEditBot(t *testing.T) {
mockDb := mocks.NewDatabase(t)
bHandler := NewBotHandler(mockDb)

t.Run("should test that a 401 error is returned during bot creation if there is no bot uuid", func(t *testing.T) {
mockUUID := "valid_uuid"

requestBody := map[string]interface{}{
"UUID": mockUUID,
}

requestBodyBytes, err := json.Marshal(requestBody)
if err != nil {
t.Fatal(err)
}

req, err := http.NewRequest("POST", "/", bytes.NewBuffer(requestBodyBytes))
if err != nil {
t.Fatal(err)
}

rr := httptest.NewRecorder()
handler := http.HandlerFunc(bHandler.CreateOrEditBot)

handler.ServeHTTP(rr, req)

assert.Equal(t, http.StatusUnauthorized, rr.Code)
})

t.Run("should test that a 401 error is returned if the user public key can't be verified during bot creation", func(t *testing.T) {
ctx := context.WithValue(context.Background(), auth.ContextKey, "pubkey")
mockPubKey := "valid_pubkey"
mockUUID := "valid_uuid"

requestBody := map[string]interface{}{
"UUID": mockUUID,
}

requestBodyBytes, err := json.Marshal(requestBody)
if err != nil {
t.Fatal(err)
}

mockVerifyTribeUUID := func(uuid string, checkTimestamp bool) (string, error) {
return mockPubKey, nil
}

bHandler.verifyTribeUUID = mockVerifyTribeUUID

rr := httptest.NewRecorder()
handler := http.HandlerFunc(bHandler.CreateOrEditBot)

req, err := http.NewRequestWithContext(ctx, "POST", "/", bytes.NewBuffer(requestBodyBytes))
if err != nil {
t.Fatal(err)
}

chiCtx := chi.NewRouteContext()
chiCtx.URLParams.Add("uuid", mockUUID)

req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, chiCtx))
handler.ServeHTTP(rr, req)

assert.Equal(t, http.StatusUnauthorized, rr.Code)
})

t.Run("should test that a bot gets created successfully if an authenticated user sends the right data", func(t *testing.T) {
mockPubKey := "valid_pubkey"
mockUUID := "valid_uuid"
mockName := "Test Bot"
mockUniqueName := "unique test bot"

mockVerifyTribeUUID := func(uuid string, checkTimestamp bool) (string, error) {
return mockPubKey, nil
}

bHandler.verifyTribeUUID = mockVerifyTribeUUID

requestBody := map[string]interface{}{
"UUID": mockUUID,
"Name": mockName,
}
requestBodyBytes, err := json.Marshal(requestBody)
if err != nil {
t.Fatal(err)
}

mockDb.On("GetBotByUniqueName", mock.Anything).Return(db.Bot{
UniqueName: mockUniqueName,
}, nil)

mockDb.On("CreateOrEditBot", mock.Anything).Return(db.Bot{
UUID: mockUUID,
}, nil)

req, err := http.NewRequest("POST", "/", bytes.NewBuffer(requestBodyBytes))
if err != nil {
t.Fatal(err)
}

ctx := context.WithValue(req.Context(), auth.ContextKey, mockPubKey)
req = req.WithContext(ctx)

rr := httptest.NewRecorder()
handler := http.HandlerFunc(bHandler.CreateOrEditBot)
handler.ServeHTTP(rr, req)

assert.Equal(t, http.StatusOK, rr.Code)
var responseData map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &responseData)
if err != nil {
t.Fatalf("Error decoding JSON response: %s", err)
}
assert.Equal(t, mockUUID, responseData["uuid"])
})

t.Run("should test that an existing bot gets updated when passed to POST bots", func(t *testing.T) {
mockPubKey := "valid_pubkey"
mockUUID := "valid_uuid"
mockName := "Updated Test Bot"
mockUniqueName := "unique test bot"

mockVerifyTribeUUID := func(uuid string, checkTimestamp bool) (string, error) {
return mockPubKey, nil
}
bHandler.verifyTribeUUID = mockVerifyTribeUUID

requestBody := map[string]interface{}{
"UUID": mockUUID,
"Name": mockName,
}
requestBodyBytes, err := json.Marshal(requestBody)
if err != nil {
t.Fatal(err)
}

mockDb.On("GetBotByUniqueName", mock.Anything).Return(db.Bot{
UUID: mockUUID,
UniqueName: mockUniqueName,
Name: "Original Test Bot",
}, nil)

mockDb.On("CreateOrEditBot", mock.Anything).Return(db.Bot{
UUID: mockUUID,
Name: mockName,
}, nil)

req, err := http.NewRequest("POST", "/", bytes.NewBuffer(requestBodyBytes))
if err != nil {
t.Fatal(err)
}
ctx := context.WithValue(req.Context(), auth.ContextKey, mockPubKey)
req = req.WithContext(ctx)

rr := httptest.NewRecorder()
handler := http.HandlerFunc(bHandler.CreateOrEditBot)
handler.ServeHTTP(rr, req)

assert.Equal(t, http.StatusOK, rr.Code)
var responseData map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &responseData)
if err != nil {
t.Fatalf("Error decoding JSON response: %s", err)
}
assert.Equal(t, mockUUID, responseData["uuid"])
assert.Equal(t, mockName, responseData["name"])
})

}

func TestGetBot(t *testing.T) {
mockDb := mocks.NewDatabase(t)
bHandler := NewBotHandler(mockDb)

t.Run("should test that a bot can be fetched with its uuid", func(t *testing.T) {

mockUUID := "valid_uuid"
mockBot := db.Bot{UUID: mockUUID, Name: "Test Bot"}
mockDb.On("GetBot", mock.Anything).Return(mockBot).Once()

rr := httptest.NewRecorder()
rctx := chi.NewRouteContext()
rctx.URLParams.Add("uuid", mockUUID)
req, err := http.NewRequestWithContext(context.WithValue(context.Background(), chi.RouteCtxKey, rctx), http.MethodGet, "/"+mockUUID, nil)

if err != nil {
t.Fatal(err)
}

handler := http.HandlerFunc(bHandler.GetBot)
handler.ServeHTTP(rr, req)

assert.Equal(t, http.StatusOK, rr.Code)
var returnedBot db.Bot
json.Unmarshal(rr.Body.Bytes(), &returnedBot)
assert.Equal(t, mockBot, returnedBot)
})
}

func TestGetListedBots(t *testing.T) {
mockDb := mocks.NewDatabase(t)
bHandler := NewBotHandler(mockDb)

t.Run("should test that all bots that are not unlisted or deleted get listed", func(t *testing.T) {
rr := httptest.NewRecorder()
handler := http.HandlerFunc(bHandler.GetListedBots)

allBots := []db.Bot{
{UUID: "uuid1", Name: "Bot1", Unlisted: false, Deleted: false},
{UUID: "uuid2", Name: "Bot2", Unlisted: false, Deleted: true},
{UUID: "uuid3", Name: "Bot3", Unlisted: true, Deleted: false},
{UUID: "uuid4", Name: "Bot4", Unlisted: true, Deleted: true},
}

expectedBots := []db.Bot{
{UUID: "uuid1", Name: "Bot1", Unlisted: false, Deleted: false},
}

rctx := chi.NewRouteContext()
req, err := http.NewRequestWithContext(context.WithValue(context.Background(), chi.RouteCtxKey, rctx), http.MethodGet, "/", nil)
assert.NoError(t, err)

mockDb.On("GetListedBots", mock.Anything).Return(allBots)
handler.ServeHTTP(rr, req)
var returnedBots []db.Bot
err = json.Unmarshal(rr.Body.Bytes(), &returnedBots)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rr.Code)

var filteredBots []db.Bot
for _, bot := range returnedBots {
if !bot.Deleted && !bot.Unlisted {
filteredBots = append(filteredBots, bot)
}
}

assert.ElementsMatch(t, expectedBots, filteredBots)
mockDb.AssertExpectations(t)
})

}
6 changes: 4 additions & 2 deletions routes/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ package routes
import (
"github.com/go-chi/chi"
"github.com/stakwork/sphinx-tribes/auth"
"github.com/stakwork/sphinx-tribes/db"
"github.com/stakwork/sphinx-tribes/handlers"
)

func BotRoutes() chi.Router {
r := chi.NewRouter()
botHandler := handlers.NewBotHandler(db.DB)
r.Group(func(r chi.Router) {
r.Get("/{name}", handlers.GetBotByUniqueName)
r.Get("/{name}", botHandler.GetBotByUniqueName)
})
r.Group(func(r chi.Router) {
r.Use(auth.PubKeyContext)

r.Put("/", handlers.CreateOrEditBot)
r.Put("/", botHandler.CreateOrEditBot)
r.Delete("/{uuid}", handlers.DeleteBot)
})
return r
Expand Down
9 changes: 6 additions & 3 deletions routes/bots.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ package routes

import (
"github.com/go-chi/chi"
"github.com/stakwork/sphinx-tribes/db"
"github.com/stakwork/sphinx-tribes/handlers"
)

func BotsRoutes() chi.Router {
r := chi.NewRouter()
botHandler := handlers.NewBotHandler(db.DB)

r.Group(func(r chi.Router) {
r.Post("/", handlers.CreateOrEditBot)
r.Get("/", handlers.GetListedBots)
r.Post("/", botHandler.CreateOrEditBot)
r.Get("/", botHandler.GetListedBots)
r.Get("/owner/{pubkey}", handlers.GetBotsByOwner)
r.Get("/{uuid}", handlers.GetBot)
r.Get("/{uuid}", botHandler.GetBot)
})
return r
}

0 comments on commit 50005b1

Please sign in to comment.