Skip to content

Commit

Permalink
Merge pull request stakwork#2278 from MahtabBukhari/bounty_cards
Browse files Browse the repository at this point in the history
Implement a new bounty cards endpoint
  • Loading branch information
humansinstitute authored Dec 24, 2024
2 parents f4784d5 + 6742fc4 commit 7faa162
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 0 deletions.
9 changes: 9 additions & 0 deletions db/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,15 @@ type FilterStattuCount struct {
Failed int64 `json:"failed"`
}

type BountyCard struct {
BountyID uint `json:"id"`
Title string `json:"title"`
AssigneePic string `json:"assignee_img,omitempty"`
Features WorkspaceFeatures `json:"features"`
Phase FeaturePhase `json:"phase"`
Workspace Workspace `json:"workspace"`
}

type WfRequestStatus string

const (
Expand Down
13 changes: 13 additions & 0 deletions db/test_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,16 @@ func CleanDB() {
func DeleteAllChats() {
TestDB.db.Exec("DELETE FROM chats")
}

func CleanTestData() {

TestDB.DeleteAllBounties()

TestDB.db.Exec("DELETE FROM workspaces")

TestDB.db.Exec("DELETE FROM workspace_features")

TestDB.db.Exec("DELETE FROM feature_phases")

TestDB.db.Exec("DELETE FROM people")
}
42 changes: 42 additions & 0 deletions handlers/bounty.go
Original file line number Diff line number Diff line change
Expand Up @@ -1492,3 +1492,45 @@ func GetFilterCount(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(filterCount)
}

func (h *bountyHandler) GetBountyCards(w http.ResponseWriter, r *http.Request) {
bounties := h.db.GetAllBounties(r)
var bountyCardResponse []db.BountyCard = h.GenerateBountyCardResponse(bounties)

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

func (h *bountyHandler) GenerateBountyCardResponse(bounties []db.NewBounty) []db.BountyCard {
var bountyCardResponse []db.BountyCard

for i := 0; i < len(bounties); i++ {
bounty := bounties[i]

assignee := h.db.GetPersonByPubkey(bounty.Assignee)
workspace := h.db.GetWorkspaceByUuid(bounty.WorkspaceUuid)

var phase db.FeaturePhase
var feature db.WorkspaceFeatures

if bounty.PhaseUuid != "" {
phase, _ = h.db.GetPhaseByUuid(bounty.PhaseUuid)
}

if phase.FeatureUuid != "" {
feature = h.db.GetFeatureByUuid(phase.FeatureUuid)
}
b := db.BountyCard{
BountyID: bounty.ID,
Title: bounty.Title,
AssigneePic: assignee.Img,
Features: feature,
Phase: phase,
Workspace: workspace,
}

bountyCardResponse = append(bountyCardResponse, b)
}

return bountyCardResponse
}
190 changes: 190 additions & 0 deletions handlers/bounty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2202,3 +2202,193 @@ func TestPollInvoice(t *testing.T) {
mockHttpClient.AssertExpectations(t)
})
}

func TestGetBountyCards(t *testing.T) {
teardownSuite := SetupSuite(t)
defer teardownSuite(t)

mockHttpClient := mocks.NewHttpClient(t)
bHandler := NewBountyHandler(mockHttpClient, db.TestDB)

db.CleanTestData()

workspace := db.Workspace{
ID: 1,
Uuid: "test-workspace-uuid",
Name: "Test Workspace",
Description: "Test Workspace Description",
OwnerPubKey: "test-owner",
}
db.TestDB.CreateOrEditWorkspace(workspace)

phase := db.FeaturePhase{
Uuid: "test-phase-uuid",
Name: "Test Phase",
FeatureUuid: "test-feature-uuid",
}
db.TestDB.CreateOrEditFeaturePhase(phase)

feature := db.WorkspaceFeatures{
Uuid: "test-feature-uuid",
Name: "Test Feature",
WorkspaceUuid: workspace.Uuid,
}
db.TestDB.CreateOrEditFeature(feature)

assignee := db.Person{
OwnerPubKey: "test-assignee",
Img: "test-image-url",
}
db.TestDB.CreateOrEditPerson(assignee)

now := time.Now()
bounty := db.NewBounty{
ID: 1,
Type: "coding",
Title: "Test Bounty",
Description: "Test Description",
WorkspaceUuid: workspace.Uuid,
PhaseUuid: phase.Uuid,
Assignee: assignee.OwnerPubKey,
Show: true,
Created: now.Unix(),
OwnerID: "test-owner",
Price: 1000,
Paid: false,
}
db.TestDB.CreateOrEditBounty(bounty)

t.Run("should successfully return bounty cards", func(t *testing.T) {
rr := httptest.NewRecorder()
handler := http.HandlerFunc(bHandler.GetBountyCards)

req, err := http.NewRequest(http.MethodGet, "/gobounties/bounty-cards?workspace_uuid="+workspace.Uuid, nil)
assert.NoError(t, err)

handler.ServeHTTP(rr, req)

var response []db.BountyCard
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rr.Code)
assert.NotEmpty(t, response, "Response should not be empty")

firstCard := response[0]
assert.Equal(t, bounty.ID, firstCard.BountyID)
assert.Equal(t, bounty.Title, firstCard.Title)
assert.Equal(t, assignee.Img, firstCard.AssigneePic)

assert.Equal(t, feature.Uuid, firstCard.Features.Uuid)
assert.Equal(t, feature.Name, firstCard.Features.Name)
assert.Equal(t, feature.WorkspaceUuid, firstCard.Features.WorkspaceUuid)

assert.Equal(t, phase.Uuid, firstCard.Phase.Uuid)
assert.Equal(t, phase.Name, firstCard.Phase.Name)
assert.Equal(t, phase.FeatureUuid, firstCard.Phase.FeatureUuid)

assert.Equal(t, workspace, firstCard.Workspace)
})

t.Run("should return empty array when no bounties exist", func(t *testing.T) {

db.TestDB.DeleteAllBounties()

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

req, err := http.NewRequest(http.MethodGet, "/gobounties/bounty-cards", nil)
assert.NoError(t, err)

handler.ServeHTTP(rr, req)

var response []db.BountyCard
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rr.Code)
assert.Empty(t, response)
})

t.Run("should handle bounties without phase and feature", func(t *testing.T) {
bountyWithoutPhase := db.NewBounty{
ID: 2,
Type: "coding",
Title: "Test Bounty Without Phase",
Description: "Test Description",
WorkspaceUuid: workspace.Uuid,
Assignee: assignee.OwnerPubKey,
Show: true,
Created: now.Unix(),
OwnerID: "test-owner",
Price: 1000,
Paid: false,
}
db.TestDB.CreateOrEditBounty(bountyWithoutPhase)

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

req, err := http.NewRequest(http.MethodGet, "/gobounties/bounty-cards?workspace_uuid="+workspace.Uuid, nil)
assert.NoError(t, err)

handler.ServeHTTP(rr, req)

var response []db.BountyCard
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rr.Code)
assert.NotEmpty(t, response)

var cardWithoutPhase db.BountyCard
for _, card := range response {
if card.BountyID == bountyWithoutPhase.ID {
cardWithoutPhase = card
break
}
}

assert.Equal(t, bountyWithoutPhase.ID, cardWithoutPhase.BountyID)
assert.Equal(t, bountyWithoutPhase.Title, cardWithoutPhase.Title)
assert.Equal(t, assignee.Img, cardWithoutPhase.AssigneePic)
assert.Empty(t, cardWithoutPhase.Phase.Uuid)
assert.Empty(t, cardWithoutPhase.Features.Uuid)
})

t.Run("should handle bounties without assignee", func(t *testing.T) {

db.TestDB.DeleteAllBounties()

bountyWithoutAssignee := db.NewBounty{
ID: 1,
Type: "coding",
Title: "Test Bounty Without Assignee",
Description: "Test Description",
WorkspaceUuid: workspace.Uuid,
PhaseUuid: phase.Uuid,
Show: true,
Created: now.Unix(),
OwnerID: "test-owner",
Price: 1000,
Paid: false,
}
db.TestDB.CreateOrEditBounty(bountyWithoutAssignee)

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

req, err := http.NewRequest(http.MethodGet, "/gobounties/bounty-cards?workspace_uuid="+workspace.Uuid, nil)
assert.NoError(t, err)

handler.ServeHTTP(rr, req)

var response []db.BountyCard
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rr.Code)
assert.NotEmpty(t, response, "Response should not be empty")

cardWithoutAssignee := response[0]
assert.Equal(t, bountyWithoutAssignee.ID, cardWithoutAssignee.BountyID)
assert.Equal(t, bountyWithoutAssignee.Title, cardWithoutAssignee.Title)
assert.Empty(t, cardWithoutAssignee.AssigneePic)
})
}
1 change: 1 addition & 0 deletions routes/bounty.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func BountyRoutes() chi.Router {
r.Group(func(r chi.Router) {
r.Use(auth.PubKeyContext)

r.Get("/bounty-cards", bountyHandler.GetBountyCards)
r.Post("/budget/withdraw", bountyHandler.BountyBudgetWithdraw)
r.Post("/pay/{id}", bountyHandler.MakeBountyPayment)
r.Get("/payment/status/{id}", bountyHandler.GetBountyPaymentStatus)
Expand Down

0 comments on commit 7faa162

Please sign in to comment.