mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-05 16:16:31 +00:00
276 lines
8 KiB
Go
276 lines
8 KiB
Go
|
// Copyright 2024 Woodpecker Authors
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package api
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"net/http"
|
||
|
"net/http/httptest"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/gin-gonic/gin"
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
"github.com/stretchr/testify/mock"
|
||
|
|
||
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||
|
"go.woodpecker-ci.org/woodpecker/v2/server/queue"
|
||
|
queue_mocks "go.woodpecker-ci.org/woodpecker/v2/server/queue/mocks"
|
||
|
mocks_manager "go.woodpecker-ci.org/woodpecker/v2/server/services/mocks"
|
||
|
store_mocks "go.woodpecker-ci.org/woodpecker/v2/server/store/mocks"
|
||
|
"go.woodpecker-ci.org/woodpecker/v2/server/store/types"
|
||
|
)
|
||
|
|
||
|
var fakeAgent = &model.Agent{
|
||
|
ID: 1,
|
||
|
Name: "test-agent",
|
||
|
OwnerID: 1,
|
||
|
NoSchedule: false,
|
||
|
}
|
||
|
|
||
|
func TestGetAgents(t *testing.T) {
|
||
|
gin.SetMode(gin.TestMode)
|
||
|
|
||
|
t.Run("should get agents", func(t *testing.T) {
|
||
|
agents := []*model.Agent{fakeAgent}
|
||
|
|
||
|
mockStore := store_mocks.NewStore(t)
|
||
|
mockStore.On("AgentList", mock.Anything).Return(agents, nil)
|
||
|
|
||
|
w := httptest.NewRecorder()
|
||
|
c, _ := gin.CreateTestContext(w)
|
||
|
c.Set("store", mockStore)
|
||
|
|
||
|
GetAgents(c)
|
||
|
c.Writer.WriteHeaderNow()
|
||
|
|
||
|
mockStore.AssertCalled(t, "AgentList", mock.Anything)
|
||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||
|
|
||
|
var response []*model.Agent
|
||
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||
|
assert.NoError(t, err)
|
||
|
assert.Equal(t, agents, response)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestGetAgent(t *testing.T) {
|
||
|
gin.SetMode(gin.TestMode)
|
||
|
|
||
|
t.Run("should get agent", func(t *testing.T) {
|
||
|
mockStore := store_mocks.NewStore(t)
|
||
|
mockStore.On("AgentFind", int64(1)).Return(fakeAgent, nil)
|
||
|
|
||
|
w := httptest.NewRecorder()
|
||
|
c, _ := gin.CreateTestContext(w)
|
||
|
c.Set("store", mockStore)
|
||
|
c.Params = gin.Params{{Key: "agent_id", Value: "1"}}
|
||
|
|
||
|
GetAgent(c)
|
||
|
c.Writer.WriteHeaderNow()
|
||
|
|
||
|
mockStore.AssertCalled(t, "AgentFind", int64(1))
|
||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||
|
|
||
|
var response model.Agent
|
||
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||
|
assert.NoError(t, err)
|
||
|
assert.Equal(t, fakeAgent, &response)
|
||
|
})
|
||
|
|
||
|
t.Run("should return bad request for invalid agent id", func(t *testing.T) {
|
||
|
w := httptest.NewRecorder()
|
||
|
c, _ := gin.CreateTestContext(w)
|
||
|
c.Params = gin.Params{{Key: "agent_id", Value: "invalid"}}
|
||
|
|
||
|
GetAgent(c)
|
||
|
c.Writer.WriteHeaderNow()
|
||
|
|
||
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||
|
})
|
||
|
|
||
|
t.Run("should return not found for non-existent agent", func(t *testing.T) {
|
||
|
mockStore := store_mocks.NewStore(t)
|
||
|
mockStore.On("AgentFind", int64(2)).Return((*model.Agent)(nil), types.RecordNotExist)
|
||
|
|
||
|
w := httptest.NewRecorder()
|
||
|
c, _ := gin.CreateTestContext(w)
|
||
|
c.Set("store", mockStore)
|
||
|
c.Params = gin.Params{{Key: "agent_id", Value: "2"}}
|
||
|
|
||
|
GetAgent(c)
|
||
|
c.Writer.WriteHeaderNow()
|
||
|
|
||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestPatchAgent(t *testing.T) {
|
||
|
gin.SetMode(gin.TestMode)
|
||
|
|
||
|
t.Run("should update agent", func(t *testing.T) {
|
||
|
updatedAgent := *fakeAgent
|
||
|
updatedAgent.Name = "updated-agent"
|
||
|
|
||
|
mockStore := store_mocks.NewStore(t)
|
||
|
mockStore.On("AgentFind", int64(1)).Return(fakeAgent, nil)
|
||
|
mockStore.On("AgentUpdate", mock.AnythingOfType("*model.Agent")).Return(nil)
|
||
|
|
||
|
mockManager := mocks_manager.NewManager(t)
|
||
|
server.Config.Services.Manager = mockManager
|
||
|
|
||
|
w := httptest.NewRecorder()
|
||
|
c, _ := gin.CreateTestContext(w)
|
||
|
c.Set("store", mockStore)
|
||
|
c.Params = gin.Params{{Key: "agent_id", Value: "1"}}
|
||
|
c.Request, _ = http.NewRequest(http.MethodPatch, "/", strings.NewReader(`{"name":"updated-agent"}`))
|
||
|
c.Request.Header.Set("Content-Type", "application/json")
|
||
|
|
||
|
PatchAgent(c)
|
||
|
c.Writer.WriteHeaderNow()
|
||
|
|
||
|
mockStore.AssertCalled(t, "AgentFind", int64(1))
|
||
|
mockStore.AssertCalled(t, "AgentUpdate", mock.AnythingOfType("*model.Agent"))
|
||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||
|
|
||
|
var response model.Agent
|
||
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||
|
assert.NoError(t, err)
|
||
|
assert.Equal(t, "updated-agent", response.Name)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestPostAgent(t *testing.T) {
|
||
|
gin.SetMode(gin.TestMode)
|
||
|
|
||
|
t.Run("should create agent", func(t *testing.T) {
|
||
|
newAgent := &model.Agent{
|
||
|
Name: "new-agent",
|
||
|
NoSchedule: false,
|
||
|
}
|
||
|
|
||
|
mockStore := store_mocks.NewStore(t)
|
||
|
mockStore.On("AgentCreate", mock.AnythingOfType("*model.Agent")).Return(nil)
|
||
|
|
||
|
w := httptest.NewRecorder()
|
||
|
c, _ := gin.CreateTestContext(w)
|
||
|
c.Set("store", mockStore)
|
||
|
c.Set("user", &model.User{ID: 1})
|
||
|
c.Request, _ = http.NewRequest(http.MethodPost, "/", strings.NewReader(`{"name":"new-agent"}`))
|
||
|
c.Request.Header.Set("Content-Type", "application/json")
|
||
|
|
||
|
PostAgent(c)
|
||
|
c.Writer.WriteHeaderNow()
|
||
|
|
||
|
mockStore.AssertCalled(t, "AgentCreate", mock.AnythingOfType("*model.Agent"))
|
||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||
|
|
||
|
var response model.Agent
|
||
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||
|
assert.NoError(t, err)
|
||
|
assert.Equal(t, newAgent.Name, response.Name)
|
||
|
assert.NotEmpty(t, response.Token)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestDeleteAgent(t *testing.T) {
|
||
|
gin.SetMode(gin.TestMode)
|
||
|
|
||
|
t.Run("should delete agent", func(t *testing.T) {
|
||
|
mockStore := store_mocks.NewStore(t)
|
||
|
mockStore.On("AgentFind", int64(1)).Return(fakeAgent, nil)
|
||
|
mockStore.On("AgentDelete", mock.AnythingOfType("*model.Agent")).Return(nil)
|
||
|
|
||
|
mockManager := mocks_manager.NewManager(t)
|
||
|
server.Config.Services.Manager = mockManager
|
||
|
|
||
|
mockQueue := queue_mocks.NewQueue(t)
|
||
|
mockQueue.On("Info", mock.Anything).Return(queue.InfoT{})
|
||
|
mockQueue.On("KickAgentWorkers", int64(1)).Return()
|
||
|
server.Config.Services.Queue = mockQueue
|
||
|
|
||
|
w := httptest.NewRecorder()
|
||
|
c, _ := gin.CreateTestContext(w)
|
||
|
c.Set("store", mockStore)
|
||
|
c.Params = gin.Params{{Key: "agent_id", Value: "1"}}
|
||
|
|
||
|
DeleteAgent(c)
|
||
|
c.Writer.WriteHeaderNow()
|
||
|
|
||
|
mockStore.AssertCalled(t, "AgentFind", int64(1))
|
||
|
mockStore.AssertCalled(t, "AgentDelete", mock.AnythingOfType("*model.Agent"))
|
||
|
mockQueue.AssertCalled(t, "KickAgentWorkers", int64(1))
|
||
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
||
|
})
|
||
|
|
||
|
t.Run("should not delete agent with running tasks", func(t *testing.T) {
|
||
|
mockStore := store_mocks.NewStore(t)
|
||
|
mockStore.On("AgentFind", int64(1)).Return(fakeAgent, nil)
|
||
|
|
||
|
mockManager := mocks_manager.NewManager(t)
|
||
|
server.Config.Services.Manager = mockManager
|
||
|
|
||
|
mockQueue := queue_mocks.NewQueue(t)
|
||
|
mockQueue.On("Info", mock.Anything).Return(queue.InfoT{
|
||
|
Running: []*model.Task{{AgentID: 1}},
|
||
|
})
|
||
|
server.Config.Services.Queue = mockQueue
|
||
|
|
||
|
w := httptest.NewRecorder()
|
||
|
c, _ := gin.CreateTestContext(w)
|
||
|
c.Set("store", mockStore)
|
||
|
c.Params = gin.Params{{Key: "agent_id", Value: "1"}}
|
||
|
|
||
|
DeleteAgent(c)
|
||
|
c.Writer.WriteHeaderNow()
|
||
|
|
||
|
mockStore.AssertCalled(t, "AgentFind", int64(1))
|
||
|
mockStore.AssertNotCalled(t, "AgentDelete", mock.Anything)
|
||
|
assert.Equal(t, http.StatusConflict, w.Code)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestPostOrgAgent(t *testing.T) {
|
||
|
gin.SetMode(gin.TestMode)
|
||
|
|
||
|
t.Run("create org agent should succeed", func(t *testing.T) {
|
||
|
mockStore := store_mocks.NewStore(t)
|
||
|
mockStore.On("AgentCreate", mock.AnythingOfType("*model.Agent")).Return(nil)
|
||
|
|
||
|
w := httptest.NewRecorder()
|
||
|
c, _ := gin.CreateTestContext(w)
|
||
|
c.Set("store", mockStore)
|
||
|
|
||
|
// Set up a non-admin user
|
||
|
c.Set("user", &model.User{
|
||
|
ID: 1,
|
||
|
Admin: false,
|
||
|
})
|
||
|
|
||
|
c.Params = gin.Params{{Key: "org_id", Value: "1"}}
|
||
|
c.Request, _ = http.NewRequest(http.MethodPost, "/", strings.NewReader(`{"name":"new-agent"}`))
|
||
|
c.Request.Header.Set("Content-Type", "application/json")
|
||
|
|
||
|
PostOrgAgent(c)
|
||
|
c.Writer.WriteHeaderNow()
|
||
|
|
||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||
|
|
||
|
// Ensure an agent was created
|
||
|
mockStore.AssertCalled(t, "AgentCreate", mock.AnythingOfType("*model.Agent"))
|
||
|
})
|
||
|
}
|