Skip to content

Commit

Permalink
Merge pull request stakwork#1522 from AbdulWahab3181/organizations-ha…
Browse files Browse the repository at this point in the history
…ndler-UT

Backend [Integration Test] organizations.go handlers GetOrganizationBounties, GetOrganizationBudget, & GetOrganizationBudgetHistory
  • Loading branch information
elraphty authored Feb 16, 2024
2 parents fe0bbed + 45def3b commit 7bb3085
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 14 deletions.
11 changes: 11 additions & 0 deletions db/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,17 @@ func UserHasAccess(pubKeyFromAuth string, uuid string, role string) bool {
return true
}

func (db database) UserHasAccess(pubKeyFromAuth string, uuid string, role string) bool {
org := DB.GetOrganizationByUuid(uuid)
var hasRole bool = false
if pubKeyFromAuth != org.OwnerPubKey {
userRoles := DB.GetUserRoles(uuid, pubKeyFromAuth)
hasRole = RolesCheck(userRoles, role)
return hasRole
}
return true
}

func (db database) UserHasManageBountyRoles(pubKeyFromAuth string, uuid string) bool {
var manageRolesCount = len(ManageBountiesGroup)
org := DB.GetOrganizationByUuid(uuid)
Expand Down
1 change: 1 addition & 0 deletions db/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,5 @@ type Database interface {
GetBountiesByDateRangeCount(r PaymentDateRange, re *http.Request) int64
PersonUniqueNameFromName(name string) (string, error)
ProcessAlerts(p Person)
UserHasAccess(pubKeyFromAuth string, uuid string, role string) bool
}
166 changes: 166 additions & 0 deletions handlers/organization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,169 @@ func TestDeleteOrganization(t *testing.T) {
mockDb.AssertExpectations(t)
})
}

func TestGetOrganizationBounties(t *testing.T) {
ctx := context.WithValue(context.Background(), auth.ContextKey, "test-key")
mockDb := mocks.NewDatabase(t)
mockGenerateBountyHandler := func(bounties []db.Bounty) []db.BountyResponse {
return []db.BountyResponse{} // Mocked response
}
oHandler := NewOrganizationHandler(mockDb)

t.Run("Should test that an organization's bounties can be listed without authentication", func(t *testing.T) {
orgUUID := "valid-uuid"
oHandler.generateBountyHandler = mockGenerateBountyHandler

expectedBounties := []db.Bounty{{}, {}} // Mocked response
mockDb.On("GetOrganizationBounties", mock.AnythingOfType("*http.Request"), orgUUID).Return(expectedBounties).Once()

rr := httptest.NewRecorder()
handler := http.HandlerFunc(oHandler.GetOrganizationBounties)

rctx := chi.NewRouteContext()
rctx.URLParams.Add("uuid", orgUUID)
req, err := http.NewRequestWithContext(context.WithValue(context.Background(), chi.RouteCtxKey, rctx), http.MethodGet, "/bounties/"+orgUUID, nil)
if err != nil {
t.Fatal(err)
}

handler.ServeHTTP(rr, req)

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

t.Run("should return empty array when wrong organization UUID is passed", func(t *testing.T) {
orgUUID := "wrong-uuid"

mockDb.On("GetOrganizationBounties", mock.AnythingOfType("*http.Request"), orgUUID).Return([]db.Bounty{}).Once()

rr := httptest.NewRecorder()
handler := http.HandlerFunc(oHandler.GetOrganizationBounties)

rctx := chi.NewRouteContext()
rctx.URLParams.Add("uuid", orgUUID)
req, err := http.NewRequestWithContext(context.WithValue(ctx, chi.RouteCtxKey, rctx), http.MethodGet, "/bounties/"+orgUUID+"?limit=10&sortBy=created&search=test&page=1&resetPage=true", nil)
if err != nil {
t.Fatal(err)
}

handler.ServeHTTP(rr, req)

// Assert that the response status code is as expected
assert.Equal(t, http.StatusOK, rr.Code)

// Assert that the response body is an empty array
assert.Equal(t, "[]\n", rr.Body.String())
})
}

func TestGetOrganizationBudget(t *testing.T) {
ctx := context.WithValue(context.Background(), auth.ContextKey, "test-key")
mockDb := mocks.NewDatabase(t)
oHandler := NewOrganizationHandler(mockDb)

t.Run("Should test that a 401 is returned when trying to view an organization's budget without a token", func(t *testing.T) {
orgUUID := "valid-uuid"

rctx := chi.NewRouteContext()
rctx.URLParams.Add("uuid", orgUUID)
req, err := http.NewRequestWithContext(context.WithValue(context.Background(), chi.RouteCtxKey, rctx), http.MethodGet, "/budget/"+orgUUID, nil)
if err != nil {
t.Fatal(err)
}

rr := httptest.NewRecorder()
http.HandlerFunc(oHandler.GetOrganizationBudget).ServeHTTP(rr, req)

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

t.Run("Should test that the right organization budget is returned, if the user is the organization admin or has the ViewReport role", func(t *testing.T) {
orgUUID := "valid-uuid"
expectedBudget := db.BountyBudget{
ID: 1,
OrgUuid: orgUUID,
TotalBudget: 1000,
Created: nil,
Updated: nil,
}

mockDb.On("UserHasAccess", "test-key", orgUUID, "VIEW REPORT").Return(true).Once()
mockDb.On("GetOrganizationBudget", orgUUID).Return(expectedBudget).Once()

rctx := chi.NewRouteContext()
rctx.URLParams.Add("uuid", orgUUID)
req, err := http.NewRequestWithContext(context.WithValue(ctx, chi.RouteCtxKey, rctx), http.MethodGet, "/budget/"+orgUUID, nil)
if err != nil {
t.Fatal(err)
}

rr := httptest.NewRecorder()
http.HandlerFunc(oHandler.GetOrganizationBudget).ServeHTTP(rr, req)

assert.Equal(t, http.StatusOK, rr.Code)

var responseBudget db.BountyBudget
err = json.Unmarshal(rr.Body.Bytes(), &responseBudget)
if err != nil {
t.Fatal(err)
}

assert.Equal(t, expectedBudget, responseBudget)
})
}

func TestGetOrganizationBudgetHistory(t *testing.T) {
ctx := context.WithValue(context.Background(), auth.ContextKey, "test-key")
mockDb := mocks.NewDatabase(t)
oHandler := NewOrganizationHandler(mockDb)

t.Run("Should test that a 401 is returned when trying to view an organization's budget history without a token", func(t *testing.T) {
orgUUID := "valid-uuid"

mockDb.On("UserHasAccess", "", orgUUID, "VIEW REPORT").Return(false).Once()

rctx := chi.NewRouteContext()
rctx.URLParams.Add("uuid", orgUUID)
req, err := http.NewRequestWithContext(context.WithValue(context.Background(), chi.RouteCtxKey, rctx), http.MethodGet, "/budget/history/"+orgUUID, nil)
if err != nil {
t.Fatal(err)
}

rr := httptest.NewRecorder()
http.HandlerFunc(oHandler.GetOrganizationBudgetHistory).ServeHTTP(rr, req)

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

t.Run("Should test that the right budget history is returned, if the user is the organization admin or has the ViewReport role", func(t *testing.T) {
orgUUID := "valid-uuid"
expectedBudgetHistory := []db.BudgetHistoryData{
{BudgetHistory: db.BudgetHistory{ID: 1, OrgUuid: orgUUID, Created: nil, Updated: nil}, SenderName: "Sender1"},
{BudgetHistory: db.BudgetHistory{ID: 2, OrgUuid: orgUUID, Created: nil, Updated: nil}, SenderName: "Sender2"},
}

mockDb.On("UserHasAccess", "test-key", orgUUID, "VIEW REPORT").Return(true).Once()
mockDb.On("GetOrganizationBudgetHistory", orgUUID).Return(expectedBudgetHistory).Once()

rctx := chi.NewRouteContext()
rctx.URLParams.Add("uuid", orgUUID)
req, err := http.NewRequestWithContext(context.WithValue(ctx, chi.RouteCtxKey, rctx), http.MethodGet, "/budget/history/"+orgUUID, nil)
if err != nil {
t.Fatal(err)
}

rr := httptest.NewRecorder()
http.HandlerFunc(oHandler.GetOrganizationBudgetHistory).ServeHTTP(rr, req)

assert.Equal(t, http.StatusOK, rr.Code)

var responseBudgetHistory []db.BudgetHistoryData
err = json.Unmarshal(rr.Body.Bytes(), &responseBudgetHistory)
if err != nil {
t.Fatal(err)
}

assert.Equal(t, expectedBudgetHistory, responseBudgetHistory)
})
}
26 changes: 15 additions & 11 deletions handlers/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@ import (
)

type organizationHandler struct {
db db.Database
db db.Database
generateBountyHandler func(bounties []db.Bounty) []db.BountyResponse
}

func NewOrganizationHandler(db db.Database) *organizationHandler {
return &organizationHandler{db: db}
return &organizationHandler{
db: db,
generateBountyHandler: GenerateBountyResponse,
}
}

func (oh *organizationHandler) CreateOrEditOrganization(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -517,18 +521,18 @@ func GetCreatedOrganizations(pubkey string) []db.Organization {
return organizations
}

func GetOrganizationBounties(w http.ResponseWriter, r *http.Request) {
func (oh *organizationHandler) GetOrganizationBounties(w http.ResponseWriter, r *http.Request) {
uuid := chi.URLParam(r, "uuid")

// get the organization bounties
organizationBounties := db.DB.GetOrganizationBounties(r, uuid)
organizationBounties := oh.db.GetOrganizationBounties(r, uuid)

var bountyResponse []db.BountyResponse = GenerateBountyResponse(organizationBounties)
var bountyResponse []db.BountyResponse = oh.generateBountyHandler(organizationBounties)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(bountyResponse)
}

func GetOrganizationBudget(w http.ResponseWriter, r *http.Request) {
func (oh *organizationHandler) GetOrganizationBudget(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)
uuid := chi.URLParam(r, "uuid")
Expand All @@ -540,35 +544,35 @@ func GetOrganizationBudget(w http.ResponseWriter, r *http.Request) {
}

// if not the organization admin
hasRole := db.UserHasAccess(pubKeyFromAuth, uuid, db.ViewReport)
hasRole := oh.db.UserHasAccess(pubKeyFromAuth, uuid, db.ViewReport)
if !hasRole {
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode("Don't have access to view budget")
return
}

// get the organization budget
organizationBudget := db.DB.GetOrganizationBudget(uuid)
organizationBudget := oh.db.GetOrganizationBudget(uuid)

w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(organizationBudget)
}

func GetOrganizationBudgetHistory(w http.ResponseWriter, r *http.Request) {
func (oh *organizationHandler) GetOrganizationBudgetHistory(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
pubKeyFromAuth, _ := ctx.Value(auth.ContextKey).(string)
uuid := chi.URLParam(r, "uuid")

// if not the organization admin
hasRole := db.UserHasAccess(pubKeyFromAuth, uuid, db.ViewReport)
hasRole := oh.db.UserHasAccess(pubKeyFromAuth, uuid, db.ViewReport)
if !hasRole {
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode("Don't have access to view budget history")
return
}

// get the organization budget
organizationBudget := db.DB.GetOrganizationBudgetHistory(uuid)
organizationBudget := oh.db.GetOrganizationBudgetHistory(uuid)

w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(organizationBudget)
Expand Down
48 changes: 48 additions & 0 deletions mocks/Database.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions routes/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func OrganizationRoutes() chi.Router {
r.Get("/{uuid}", handlers.GetOrganizationByUuid)
r.Get("/users/{uuid}", handlers.GetOrganizationUsers)
r.Get("/users/{uuid}/count", handlers.GetOrganizationUsersCount)
r.Get("/bounties/{uuid}", handlers.GetOrganizationBounties)
r.Get("/bounties/{uuid}", organizationHandlers.GetOrganizationBounties)
r.Get("/user/{userId}", handlers.GetUserOrganizations)
r.Get("/user/dropdown/{userId}", organizationHandlers.GetUserDropdownOrganizations)
})
Expand All @@ -31,8 +31,8 @@ func OrganizationRoutes() chi.Router {
r.Get("/foruser/{uuid}", handlers.GetOrganizationUser)
r.Get("/bounty/roles", handlers.GetBountyRoles)
r.Get("/users/role/{uuid}/{user}", handlers.GetUserRoles)
r.Get("/budget/{uuid}", handlers.GetOrganizationBudget)
r.Get("/budget/history/{uuid}", handlers.GetOrganizationBudgetHistory)
r.Get("/budget/{uuid}", organizationHandlers.GetOrganizationBudget)
r.Get("/budget/history/{uuid}", organizationHandlers.GetOrganizationBudgetHistory)
r.Get("/payments/{uuid}", handlers.GetPaymentHistory)
r.Get("/poll/invoices/{uuid}", handlers.PollBudgetInvoices)
r.Get("/invoices/count/{uuid}", handlers.GetInvoicesCount)
Expand Down

0 comments on commit 7bb3085

Please sign in to comment.